Do not set VR mode feature as handset default. am: 5ce292f55b am: f0f8f0a99d am: f27cfdd82e
am: b3d7a8c895
Change-Id: I9838611119c1b1ee75496548e6b2ad433ef2fdbe
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 4c0e242..8faf276 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -117,6 +117,17 @@
{ REQ, "/sys/kernel/debug/tracing/events/irq/enable" },
{ OPT, "/sys/kernel/debug/tracing/events/ipi/enable" },
} },
+ { "i2c", "I2C Events", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_read/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_write/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_result/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_reply/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_read/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_write/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_result/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/i2c/smbus_reply/enable" },
+ } },
{ "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" },
@@ -366,7 +377,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;
@@ -976,7 +987,7 @@
" -s N sleep for N seconds before tracing [default 0]\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"
@@ -1151,7 +1162,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, 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 e3cc9da..d486a3d 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -54,6 +54,15 @@
chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_lock/enable
chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_locked/enable
chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_write/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_result/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_reply/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_read/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_write/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_result/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_reply/enable
# Tracing disabled by default
write /sys/kernel/debug/tracing/tracing_on 0
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..73d274f 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;
}
};
@@ -54,13 +128,13 @@
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 +159,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 0433f31..695e464 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,20 +1,100 @@
LOCAL_PATH:= $(call my-dir)
+# ================#
+# Common settings #
+# ================#
+# ZipArchive support, the order matters here to get all symbols.
+COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto_static
+
+# 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 := \
+ utils.cpp
+COMMON_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ libhardware_legacy \
+ liblog \
+ libselinux
+
+# ==========#
+# dumpstate #
+# ==========#
include $(CLEAR_VARS)
ifdef BOARD_WLAN_DEVICE
LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
endif
-LOCAL_SRC_FILES := dumpstate.cpp utils.cpp
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+ 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 libcrypto_static
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES)
+
LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
+
+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) \
+ tests/dumpstate_test.cpp
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_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
+
+dist_zip_root := $(TARGET_OUT_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 := $(dist_zip_root)/$(dumpstate_tests_subpath_from_data)
+testdata_files := $(call find-subdir-files, testdata/*)
+
+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)
+LOCAL_PICKUP_FILES := $(dist_zip_root)
+
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index ca7d574..388b506 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).
@@ -63,8 +63,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 68edc36..29589cd 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -23,6 +23,7 @@
#include <memory>
#include <regex>
#include <set>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -35,10 +36,12 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
-#include <android-base/file.h>
#include <cutils/properties.h>
+#include <hardware_legacy/power.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -48,23 +51,18 @@
#include <openssl/sha.h>
-using android::base::StringPrintf;
-
/* 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;
+// TODO: variables and functions below should be part of dumpstate object
+
+// TODO: Can't be added to dumpstate.h because including "ziparchive/zip_writer.h" would not work.
+// That's probably because of the dumpstate -> libdumpstate -> device implementation setup, which
+// might be changed anyways - let's keep it here and wait
static std::unique_ptr<ZipWriter> zip_writer;
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"
@@ -81,6 +79,7 @@
#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
#define NUM_TOMBSTONES 10
#define WLUTIL "/vendor/xbin/wlutil"
+#define WAKE_LOCK_NAME "dumpstate_wakelock"
typedef struct {
char name[TOMBSTONE_MAX_LEN];
@@ -89,41 +88,52 @@
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 = CommandOptions::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);
+}
+bool IsUserBuild() {
+ return ds.IsUserBuild();
}
-/* 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";
+
+static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
+static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
+
+/* 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
@@ -140,7 +150,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);
@@ -149,12 +159,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, nullptr);
+ 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)
@@ -173,7 +183,7 @@
continue;
}
snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename);
- dump_file(title, path);
+ DumpFile(title, path);
}
closedir(d);
@@ -223,8 +233,8 @@
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;
}
@@ -241,7 +251,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;
}
@@ -294,7 +305,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)) {
@@ -310,11 +321,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;
@@ -331,17 +342,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())) {
@@ -351,13 +362,13 @@
}
static void dump_raft() {
- if (is_user_build()) {
+ if (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;
}
@@ -367,19 +378,19 @@
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));
}
}
}
@@ -393,7 +404,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;
}
@@ -575,39 +586,42 @@
return 10 * (property_size + worst_write_perf) / worst_write_perf;
}
-/* 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];
+void Dumpstate::PrintHeader() const {
+ std::string build, fingerprint, radio, bootloader, network;
+ char 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");
+ JustDumpFile("", "/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=%lu pid=%d dry_run=%d args=%s extra_options=%s\n", id_, getpid(),
+ dry_run_, args_.c_str(), extra_options_.c_str());
printf("\n");
}
+bool Dumpstate::IsZipping() const {
+ return zip_writer != nullptr;
+}
+
// List of file extensions that can cause a zip file attachment to be rejected by some email
// service providers.
static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
@@ -616,10 +630,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;
@@ -637,17 +651,17 @@
// 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) {
+ 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));
+ 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) {
@@ -662,7 +676,7 @@
}
err = zip_writer->FinishEntry();
- if (err) {
+ if (err != 0) {
MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
@@ -670,60 +684,55 @@
return true;
}
-bool add_zip_entry(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)));
+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, nullptr);
+ 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) {
+ 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));
+ ZipWriter::ErrorCodeString(err));
return false;
}
err = zip_writer->WriteBytes(content.c_str(), content.length());
- if (err) {
+ if (err != 0) {
MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ ZipWriter::ErrorCodeString(err));
return false;
}
err = zip_writer->FinishEntry();
- if (err) {
+ if (err != 0) {
MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
@@ -732,130 +741,128 @@
}
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);
+ RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
+ RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
+ RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
/* 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);
+ 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 dumpstate(const std::string& screenshot_path, const std::string& version) {
+static void dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
unsigned long timeout;
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", "-b", "-n", "1", "-H", "-s", "6",
- "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name", 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"}, CommandOptions::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", "-A", "-T", "-Z",
- "-O", "pri,nice,rtprio,sched,pcy", 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_10);
- run_command("PRINTENV", 10, "printenv", NULL);
- run_command("NETSTAT", 10, "netstat", "-n", NULL);
- run_command("LSMOD", 10, "lsmod", NULL);
+ RunCommand("PRINTENV", {"printenv"});
+ RunCommand("NETSTAT", {"netstat", "-n"});
+ 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_10);
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();
}
- // 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", "-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", "-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", "-d", "*:v"},
+ CommandOptions::WithTimeout(timeout / 1000).Build());
- run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
+ RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
/* 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);
+ DumpFile("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]) {
+ std::string anrTracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ if (anrTracesPath.empty()) {
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));
+ int fd = TEMP_FAILURE_RETRY(
+ open(anrTracesPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+ if (fd < 0) {
+ printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anrTracesPath.c_str(), strerror(errno));
} else {
- dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
+ dump_file_from_fd("VM TRACES AT LAST ANR", anrTracesPath.c_str(), 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] != '/') {
+ if (!anrTracesPath.empty()) {
+ int tail = anrTracesPath.size() - 1;
+ while (tail > 0 && anrTracesPath.at(tail) != '/') {
tail--;
}
int i = 0;
while (1) {
- sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
- if (stat(anr_traces_path, &st)) {
+ anrTracesPath =
+ anrTracesPath.substr(0, tail + 1) + android::base::StringPrintf("slow%02d.txt", i);
+ if (stat(anrTracesPath.c_str(), &st)) {
// No traces file at this index, done with the files.
break;
}
- dump_file("VM TRACES WHEN SLOW", anr_traces_path);
+ DumpFile("VM TRACES WHEN SLOW", anrTracesPath.c_str());
i++;
}
}
@@ -866,8 +873,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 {
@@ -881,225 +888,262 @@
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");
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");
}
/* 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", "-d", "*:v"});
/* 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);
+ RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT_5);
- run_command("DUMP WIFI INTERNAL COUNTERS (1)", 20,
- SU_PATH, "root", WLUTIL, "counters", NULL);
+ RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
- run_command("ND OFFLOAD STATUS (1)", 5,
- SU_PATH, "root", WLUTIL, "nd_status", NULL);
+ RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
#endif
- dump_file("INTERRUPTS (1)", "/proc/interrupts");
+ DumpFile("INTERRUPTS (1)", "/proc/interrupts");
- run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "-t", "10", "connectivity", "--diag", NULL);
+ RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
+ CommandOptions::WithTimeout(10).Build());
#ifdef FWDUMP_bcmdhd
- run_command("DUMP WIFI STATUS", 20,
- SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
+ RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, CommandOptions::AS_ROOT_20);
- run_command("DUMP WIFI INTERNAL COUNTERS (2)", 20,
- SU_PATH, "root", WLUTIL, "counters", NULL);
+ RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
- run_command("ND OFFLOAD STATUS (2)", 5,
- SU_PATH, "root", WLUTIL, "nd_status", NULL);
+ RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
#endif
- dump_file("INTERRUPTS (2)", "/proc/interrupts");
+ DumpFile("INTERRUPTS (2)", "/proc/interrupts");
print_properties();
- run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
- run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
+ RunCommand("VOLD DUMP", {"vdc", "dump"});
+ RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
- run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
+ RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
- 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");
- dumpstate_board();
- printf("\n");
+ {
+ DurationReporter tmpDr("dumpstate_board()");
+ 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);
+ 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 (!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", "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"});
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 (originally %d)\n", getpid(), ds.progress_,
+ ds.weight_total_, WEIGHT_TOTAL);
printf("========================================================\n");
- printf("== dumpstate: done\n");
+ printf("== dumpstate: done (id %lu)\n", ds.id_);
printf("========================================================\n");
}
-static void usage() {
- 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: sets the bugreport format version (valid values: %s)\n",
- VERSION_DEFAULT.c_str());
+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 sigpipe_handler(int n) {
- // don't complain to stderr or stdout
+static void ExitOnInvalidArgs() {
+ fprintf(stderr, "invalid combination of args\n");
+ ShowUsageAndExit();
+}
+
+static void wake_lock_releaser() {
+ if (release_wake_lock(WAKE_LOCK_NAME) < 0) {
+ MYLOGE("Failed to release wake lock: %s \n", strerror(errno));
+ } else {
+ MYLOGD("Wake lock released.\n");
+ }
+}
+
+static void sig_handler(int signo __attribute__((unused))) {
+ wake_lock_releaser();
_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 %lu 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;
}
+ // 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) {
+ 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));
+ // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
+ ds.zip_file.reset(nullptr);
+
+ if (IsUserBuild()) {
+ MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+ if (remove(tmp_path_.c_str()) != 0) {
+ ALOGW("remove(%s): %s\n", tmp_path_.c_str(), strerror(errno));
}
} else {
- MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
+ MYLOGD("Keeping temporary file %s on non-user build\n", tmp_path_.c_str())
}
return true;
@@ -1141,7 +1185,6 @@
}
int main(int argc, char *argv[]) {
- struct sigaction sigact;
int do_add_date = 0;
int do_zip_file = 0;
int do_vibrate = 1;
@@ -1150,31 +1193,13 @@
int use_control_socket = 0;
int do_fb = 0;
int do_broadcast = 0;
- int do_early_screenshot = 0;
int is_remote_mode = 0;
- 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);
+ bool show_header_only = false;
/* 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);
@@ -1188,59 +1213,116 @@
}
/* 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:svqzpPBRSV:")) != -1) {
switch (c) {
- 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': 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") {
+ 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 {
+ MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
+ }
+ // Reset the property
+ android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ }
+
+ 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) {
+ MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s')\n",
+ ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.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);
+ /* gets the sequential id */
+ int 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");
+
+ if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
+ MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
+ } else {
+ MYLOGD("Wake lock acquired.\n");
+ atexit(wake_lock_releaser);
+ register_sig_handler();
}
- if (version != VERSION_DEFAULT) {
- usage();
- exit(1);
+ if (ds.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=%lu, 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.
@@ -1250,87 +1332,62 @@
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);
+ ds.base_name_ = basename(use_outfile);
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;
+ std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
+ ds.base_name_ += "-" + buildId;
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";
+ ds.screenshot_path_ = ds.GetPath(".png");
}
- tmp_path = bugreport_dir + "/" + base_name + "-" + suffix + ".tmp";
- log_path = bugreport_dir + "/dumpstate_log-" + suffix + "-"
- + std::to_string(getpid()) + ".txt";
+ ds.tmp_path_ = ds.GetPath(".tmp");
+ ds.log_path_ = ds.GetPath("-dumpstate_log-" + 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());
+ 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()));
+ 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),
+ "--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(getpid()),
"--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
};
@@ -1338,7 +1395,7 @@
send_broadcast("android.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());
}
}
}
@@ -1359,46 +1416,42 @@
}
}
- 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));
}
}
// 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();
// 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
@@ -1407,40 +1460,43 @@
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 (!IsUserBuild()) {
+ ds.AddDir(PROFILE_DATA_DIR_CUR, true);
+ ds.AddDir(PROFILE_DATA_DIR_REF, true);
}
add_mountinfo();
dump_iptables();
// Capture any IPSec policies in play. No keys are exposed here.
- 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()) {
return -1;
}
- dumpstate(do_early_screenshot ? "": screenshot_path, version);
+ dumpstate();
/* close output if needed */
if (is_redirecting) {
@@ -1451,71 +1507,70 @@
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", getpid()), "");
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());
}
}
}
@@ -1530,27 +1585,27 @@
/* 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.ID", std::to_string(ds.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
+ "--ei", "android.intent.extra.MAX", std::to_string(ds.weight_total_),
+ "--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 (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));
+ am_args.push_back(SHA256_file_hash(ds.path_));
send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
} else {
send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
@@ -1560,16 +1615,16 @@
}
}
- MYLOGD("Final progress: %d/%d (originally %d)\n", progress, weight_total, WEIGHT_TOTAL);
- MYLOGI("done\n");
+ MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weight_total_, WEIGHT_TOTAL);
+ MYLOGI("done (id %lu)\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 1b28c49..568256a 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -14,20 +14,8 @@
* 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 FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
#ifndef MYLOGD
#define MYLOGD(...) fprintf(stderr, __VA_ARGS__); ALOGD(__VA_ARGS__);
@@ -45,18 +33,151 @@
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
+
+#include <string>
#include <vector>
-#define SU_PATH "/system/xbin/su"
+// 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: 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 *);
+/*
+ * Defines the Linux user that should be executing a command.
+ */
+enum RootMode {
+ /* 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
+};
-/* Estimated total weight of bugreport generation.
+/*
+ * Defines what should happen with the `stdout` stream of a command.
+ */
+enum StdoutMode {
+ /* Don't change `stdout`. */
+ NORMAL_STDOUT,
+ /* Redirect `stdout` to `stderr`. */
+ REDIRECT_TO_STDERR
+};
+
+/*
+ * 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 std::string& title);
+ DurationReporter(const std::string& title, FILE* out);
+
+ ~DurationReporter();
+
+ static uint64_t Nanotime();
+
+ private:
+ // TODO: use std::string for title, once dump_files() and other places that pass a char* are
+ // refactored as well.
+ std::string title_;
+ FILE* out_;
+ uint64_t started_;
+};
+
+/*
+ * 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(long timeout);
+
+ long timeout_;
+ bool always_;
+ RootMode root_mode_;
+ StdoutMode stdout_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 RootMode as `SU_ROOT` */
+ CommandOptionsBuilder& AsRoot();
+ /* Sets the command's RootMode as `DROP_ROOT` */
+ CommandOptionsBuilder& DropRoot();
+ /* Sets the command's StdoutMode `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(long timeout);
+ CommandOptionsValues values;
+ friend class CommandOptions;
+ };
+
+ /** Gets the command timeout, in seconds. */
+ long Timeout() const;
+ /* Checks whether the command should always be run, even on dry-run mode. */
+ bool Always() const;
+ /** Gets the RootMode of the command. */
+ RootMode RootMode() const;
+ /** Gets the StdoutMode of the command. */
+ StdoutMode StdoutMode() const;
+ /** Gets the logging message header, it any. */
+ std::string LoggingMessage() const;
+
+ /** Creates a builder with the requied timeout. */
+ static CommandOptionsBuilder WithTimeout(long timeout);
+
+ // Common options.
+ static CommandOptions DEFAULT;
+ static CommandOptions DEFAULT_DUMPSYS;
+ static CommandOptions AS_ROOT_5;
+ static CommandOptions AS_ROOT_10;
+ static CommandOptions AS_ROOT_20;
+};
+
+/*
+ * Estimated total weight of bugreport generation.
*
* 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.
@@ -68,39 +189,214 @@
* 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.
*/
+// TODO: move to dumpstate.cpp / utils.cpp once it's used in just one file
static const int WEIGHT_TOTAL = 6500;
-/* Most simple commands have 10 as timeout, so 5 is a good estimate */
-static const int WEIGHT_FILE = 5;
+/*
+ * List of supported zip format versions.
+ *
+ * See bugreport-format.md for more info.
+ */
+static std::string VERSION_CURRENT = "1.0";
/*
- * 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;
+ * "Alias" for the current version.
+ */
+static std::string VERSION_DEFAULT = "default";
-/* full path of the directory where the bugreport files will be written */
-extern std::string bugreport_dir;
+/*
+ * 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;
-/* root dir for all files copied as-is into the bugreport. */
-extern const std::string ZIP_ROOT_DIR;
+ public:
+ static Dumpstate& GetInstance();
-/* Checkes whether dumpstate is generating a zipped bugreport. */
-bool is_zipping();
+ /*
+ * 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.
+ */
+ bool IsDryRun() 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 whether device is running a `user` build.
+ */
+ bool IsUserBuild() const;
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
+ /* Checkes whether dumpstate is generating a zipped bugreport. */
+ bool IsZipping() const;
-/* adds all files from a directory to the zipped bugreport file */
-void add_dir(const char *dir, bool recursive);
+ /*
+ * 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 CommandOptions& options = CommandOptions::DEFAULT);
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path);
+ /*
+ * 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 CommandOptions& options = CommandOptions::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
+
+ /*
+ * Updates the overall progress of the bugreport generation by the given weight increment.
+ */
+ void UpdateProgress(int 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.
+ unsigned long id_;
+
+ // Whether progress updates should be published.
+ bool update_progress_ = false;
+
+ // Whether it should take an screenshot earlier in the process.
+ bool do_early_screenshot_ = false;
+
+ // Currrent progress.
+ int progress_ = 0;
+
+ // Total estimated progress.
+ int weight_total_ = WEIGHT_TOTAL;
+
+ // 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};
+
+ private:
+ // Used by GetInstance() only.
+ Dumpstate(const std::string& version = VERSION_CURRENT, bool dry_run = false,
+ const std::string& build_type = "user");
+
+ // Internal version of RunCommand that just runs it, without updating progress.
+ int JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
+ const CommandOptions& options) const;
+
+ // Internal version of RunCommand that just dumps it, without updating progress.
+ int JustDumpFile(const std::string& title, const std::string& path) const;
+
+ // Whether this is a dry run.
+ bool dry_run_;
+
+ // Build type (such as 'user' or 'eng').
+ std::string build_type_;
+};
+
+// 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,25 +412,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[]);
+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));
/* switch to non-root user and group */
bool drop_root_user();
@@ -142,9 +421,6 @@
/* 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();
@@ -154,9 +430,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);
@@ -190,9 +469,6 @@
/* 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);
@@ -211,31 +487,8 @@
/** 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:
- explicit 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 3448e91..2e72574 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,32 +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
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/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
new file mode 100644
index 0000000..4073c3c
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -0,0 +1,424 @@
+/*
+ * 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 "dumpstate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.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>
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+using ::testing::EndsWith;
+using ::testing::IsEmpty;
+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;
+
+// Not used on test cases yet...
+void dumpstate_board(void) {
+}
+
+class DumpstateTest : public Test {
+ public:
+ void SetUp() {
+ SetDryRun(false);
+ SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
+ ds.update_progress_ = false;
+ }
+
+ // 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 SetDryRun(bool dry_run) {
+ ALOGD("Setting dry_run_ to %s\n", dry_run ? "true" : "false");
+ ds.dry_run_ = dry_run;
+ }
+
+ void SetBuildType(const std::string& build_type) {
+ ALOGD("Setting build_type_ to '%s'\n", build_type.c_str());
+ ds.build_type_ = build_type;
+ }
+
+ bool IsUserBuild() {
+ return "user" == android::base::GetProperty("ro.build.type", "(unknown)");
+ }
+
+ void DropRoot() {
+ drop_root_user();
+ uid_t uid = getuid();
+ ASSERT_EQ(2000, (int)uid);
+ }
+
+ // TODO: remove when progress is set by Binder callbacks.
+ void AssertSystemProperty(const std::string& key, const std::string& expected_value) {
+ std::string actualValue = android::base::GetProperty(key, "not set");
+ EXPECT_THAT(expected_value, StrEq(actualValue)) << "invalid value for property " << key;
+ }
+
+ std::string GetProgressMessage(int progress, int weigh_total, int old_weigh_total = 0) {
+ EXPECT_EQ(progress, ds.progress_) << "invalid progress";
+ EXPECT_EQ(weigh_total, ds.weight_total_) << "invalid weigh_total";
+
+ AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.progress", getpid()),
+ std::to_string(progress));
+
+ bool max_increased = old_weigh_total > 0;
+
+ std::string adjustment_message = "";
+ if (max_increased) {
+ AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.max", getpid()),
+ std::to_string(weigh_total));
+ adjustment_message = android::base::StringPrintf(
+ "Adjusting total weight from %d to %d\n", old_weigh_total, weigh_total);
+ }
+
+ return android::base::StringPrintf("%sSetting progress (dumpstate.%d.progress): %d/%d\n",
+ adjustment_message.c_str(), getpid(), progress,
+ weigh_total);
+ }
+
+ // `stdout` and `stderr` from the last command ran.
+ std::string out, err;
+
+ std::string test_path = dirname(android::base::GetExecutablePath().c_str());
+ std::string fixtures_path = test_path + "/../dumpstate_test_fixture/";
+ std::string test_data_path = fixtures_path + "/testdata/";
+ std::string simple_command = fixtures_path + "dumpstate_test_fixture";
+ std::string echo_command = "/system/bin/echo";
+
+ Dumpstate& ds = Dumpstate::GetInstance();
+};
+
+TEST_F(DumpstateTest, RunCommandNoArgs) {
+ EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateTest, RunCommandNoTitle) {
+ EXPECT_EQ(0, RunCommand("", {simple_command}));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithTitle) {
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {simple_command}));
+ 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 (" + simple_command + ") ------\nstdout\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
+ EXPECT_EQ(
+ 0, RunCommand("", {simple_command},
+ 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("", {simple_command},
+ CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithOneArg) {
+ EXPECT_EQ(0, RunCommand("", {echo_command, "one"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
+ EXPECT_EQ(0, RunCommand("", {echo_command, "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", {simple_command}));
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simple_command +
+ ") ------\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("", {simple_command}));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunAlways) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {simple_command}, 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("", {simple_command, "--exit", "42"}));
+ EXPECT_THAT(out, StrEq("stdout\n*** command '" + simple_command +
+ " --exit 42' failed: exit code 42\n"));
+ EXPECT_THAT(err, StrEq("stderr\n*** command '" + simple_command +
+ " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandCrashes) {
+ EXPECT_NE(0, RunCommand("", {simple_command, "--crash"}));
+ // We don't know the exit code, so check just the prefix.
+ EXPECT_THAT(
+ out, StartsWith("stdout\n*** command '" + simple_command + " --crash' failed: exit code"));
+ EXPECT_THAT(
+ err, StartsWith("stderr\n*** command '" + simple_command + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateTest, RunCommandTimesout) {
+ EXPECT_EQ(-1, RunCommand("", {simple_command, "--sleep", "2"},
+ CommandOptions::WithTimeout(1).Build()));
+ EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simple_command +
+ " --sleep 2' timed out after 1"));
+ EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simple_command +
+ " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateTest, RunCommandIsKilled) {
+ CaptureStdout();
+ CaptureStderr();
+
+ std::thread t([=]() {
+ EXPECT_EQ(SIGTERM, ds.RunCommand("", {simple_command, "--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 '" + simple_command +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+ EXPECT_THAT(err, StrEq("*** command '" + simple_command +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandProgress) {
+ ds.update_progress_ = true;
+ ds.progress_ = 0;
+ ds.weight_total_ = 30;
+
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(20).Build()));
+ std::string progress_message = GetProgressMessage(20, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Build()));
+ progress_message = GetProgressMessage(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_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).Build()));
+ progress_message = GetProgressMessage(31, 36, 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_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(4).Build()));
+ progress_message = GetProgressMessage(35, 36);
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq(progress_message));
+}
+
+TEST_F(DumpstateTest, RunCommandDropRoot) {
+ // First check root case - only available when running with 'adb root'.
+ uid_t uid = getuid();
+ if (uid == 0) {
+ EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"}));
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ return;
+ }
+ // Then drop root.
+
+ EXPECT_EQ(0, RunCommand("", {simple_command, "--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 (!IsUserBuild()) {
+ // Emulates user build if necessarily.
+ SetBuildType("user");
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {simple_command}, 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 " + simple_command + "' on user build.\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
+ if (IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+ return;
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {simple_command, "--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("", test_data_path + "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("", test_data_path + "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("", test_data_path + "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("", test_data_path + "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("", test_data_path + "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!", test_data_path + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + test_data_path +
+ "single-line.txt) ------\n\t(skipped on dry run)\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+ ds.update_progress_ = true;
+ ds.progress_ = 0;
+ ds.weight_total_ = 30;
+
+ EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
+
+ std::string progress_message = GetProgressMessage(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
+}
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 285c01e..31c8697 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -39,6 +39,8 @@
#define LOG_TAG "dumpstate"
#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <cutils/debugger.h>
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -49,8 +51,25 @@
#include "dumpstate.h"
+#define SU_PATH "/system/xbin/su"
+
static const int64_t NANOS_PER_SEC = 1000000000;
+static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
+
+// 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);
+}
+static bool IsDryRun() {
+ return Dumpstate::GetInstance().IsDryRun();
+}
+static void UpdateProgress(int delta) {
+ ds.UpdateProgress(delta);
+}
+
/* list of native processes to include in the native dumps */
// This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
static const char* native_processes_to_dump[] = {
@@ -67,37 +86,138 @@
NULL,
};
-DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
+/* Most simple commands have 10 as timeout, so 5 is a good estimate */
+static const int WEIGHT_FILE = 5;
-DurationReporter::DurationReporter(const char *title, FILE *out) {
- title_ = title;
- if (title) {
- started_ = DurationReporter::nanotime();
+CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
+CommandOptions CommandOptions::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
+CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
+CommandOptions CommandOptions::AS_ROOT_10 = CommandOptions::WithTimeout(10).AsRoot().Build();
+CommandOptions CommandOptions::AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
+
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : values(timeout) {
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
+ values.always_ = true;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
+ values.root_mode_ = SU_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
+ values.root_mode_ = DROP_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
+ values.stdout_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(long timeout)
+ : timeout_(timeout),
+ always_(false),
+ root_mode_(DONT_DROP_ROOT),
+ stdout_mode_(NORMAL_STDOUT),
+ logging_message_("") {
+}
+
+CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
+}
+
+long CommandOptions::Timeout() const {
+ return values.timeout_;
+}
+
+bool CommandOptions::Always() const {
+ return values.always_;
+}
+
+RootMode CommandOptions::RootMode() const {
+ return values.root_mode_;
+}
+
+StdoutMode CommandOptions::StdoutMode() const {
+ return values.stdout_mode_;
+}
+
+std::string CommandOptions::LoggingMessage() const {
+ return values.logging_message_;
+}
+
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(long timeout) {
+ return CommandOptions::CommandOptionsBuilder(timeout);
+}
+
+Dumpstate::Dumpstate(const std::string& version, bool dry_run, const std::string& build_type)
+ : version_(version), now_(time(nullptr)), dry_run_(dry_run), build_type_(build_type) {
+}
+
+Dumpstate& Dumpstate::GetInstance() {
+ static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT),
+ android::base::GetBoolProperty("dumpstate.dry_run", false),
+ android::base::GetProperty("ro.build.type", "(unknown)"));
+ return singleton_;
+}
+
+DurationReporter::DurationReporter(const std::string& title) : DurationReporter(title, stdout) {
+}
+
+DurationReporter::DurationReporter(const std::string& title, FILE* out) : title_(title), out_(out) {
+ if (!title_.empty()) {
+ started_ = DurationReporter::Nanotime();
}
- out_ = out;
}
DurationReporter::~DurationReporter() {
- if (title_) {
- uint64_t elapsed = DurationReporter::nanotime() - started_;
+ if (!title_.empty()) {
+ uint64_t elapsed = DurationReporter::Nanotime() - started_;
// Use "Yoda grammar" to make it easier to grep|sort sections.
- if (out_) {
+ if (out_ != nullptr) {
fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
- (float) elapsed / NANOS_PER_SEC, title_);
+ (float)elapsed / NANOS_PER_SEC, title_.c_str());
} else {
- MYLOGD("Duration of '%s': %.3fs\n", title_, (float) elapsed / NANOS_PER_SEC);
+ MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
}
}
}
-uint64_t DurationReporter::DurationReporter::nanotime() {
+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;
}
+bool Dumpstate::IsDryRun() const {
+ return dry_run_;
+}
+
+bool Dumpstate::IsUserBuild() const {
+ return "user" == build_type_;
+}
+
+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 for_each_userid(void (*func)(int), const char *header) {
- ON_DRY_RUN_RETURN();
+ if (IsDryRun()) return;
+
DIR *d;
struct dirent *de;
@@ -179,7 +299,8 @@
}
void for_each_pid(for_each_pid_func func, const char *header) {
- ON_DRY_RUN_RETURN();
+ if (IsDryRun()) return;
+
__for_each_pid(for_each_pid_helper, header, (void *) func);
}
@@ -232,12 +353,14 @@
}
void for_each_tid(for_each_tid_func func, const char *header) {
- ON_DRY_RUN_RETURN();
+ if (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 (IsDryRun()) return;
+
char path[255];
char buffer[255];
int fd, ret, save_errno;
@@ -303,7 +426,8 @@
}
void show_showtime(int pid, const char *name) {
- ON_DRY_RUN_RETURN();
+ if (IsDryRun()) return;
+
char path[255];
char buffer[1023];
int fd, ret, save_errno;
@@ -370,7 +494,8 @@
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
- ON_DRY_RUN_RETURN();
+ if (IsDryRun()) return;
+
/* Get size of kernel buffer */
int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (size <= 0) {
@@ -400,12 +525,13 @@
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_10);
}
-static int _dump_file_from_fd(const char *title, const char *path, int fd) {
- if (title) {
- printf("------ %s (%s", title, path);
+// TODO: when converted to a Dumpstate function, it should be const
+static int _dump_file_from_fd(const std::string& title, const char* path, int fd) {
+ if (!title.empty()) {
+ printf("------ %s (%s", title.c_str(), path);
struct stat st;
// Only show the modification time of non-device files.
@@ -421,7 +547,6 @@
}
printf(") ------\n");
}
- ON_DRY_RUN({ update_progress(WEIGHT_FILE); close(fd); return 0; });
bool newline = false;
fd_set read_set;
@@ -432,14 +557,14 @@
/* Timeout if no data is read for 30 seconds. */
tm.tv_sec = 30;
tm.tv_usec = 0;
- uint64_t elapsed = DurationReporter::nanotime();
+ 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;
+ elapsed = DurationReporter::Nanotime() - elapsed;
printf("*** %s: Timed out after %.3fs\n", path,
(float) elapsed / NANOS_PER_SEC);
newline = true;
@@ -459,25 +584,39 @@
}
}
}
- update_progress(WEIGHT_FILE);
+ UpdateProgress(WEIGHT_FILE);
close(fd);
if (!newline) printf("\n");
- if (title) printf("\n");
+ if (!title.empty()) 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 (IsDryRun()) {
+ if (!title.empty()) {
+ printf("------ %s (%s) ------\n", title.c_str(), path.c_str());
+ printf("\t(skipped on dry run)\n");
+ }
+ UpdateProgress(WEIGHT_FILE);
+ return 0;
+ }
+ return JustDumpFile(title, path);
+}
+
+int Dumpstate::JustDumpFile(const std::string& title, const std::string& path) const {
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
if (fd < 0) {
int err = errno;
- printf("*** %s: %s\n", path, strerror(err));
- if (title) printf("\n");
+ if (title.empty()) {
+ printf("*** Error dumping %s: %s\n", path.c_str(), strerror(err));
+ } else {
+ printf("*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err));
+ }
return -1;
}
- return _dump_file_from_fd(title, path, fd);
+ return _dump_file_from_fd(title, path.c_str(), fd);
}
int read_file_as_long(const char *path, long int *output) {
@@ -507,9 +646,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;
@@ -517,10 +655,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 (IsDryRun()) return 0;
if (dir[strlen(dir) - 1] == '/') {
++slash;
@@ -551,7 +689,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;
}
@@ -566,7 +704,7 @@
(*dump_from_fd)(NULL, newpath, fd);
}
closedir(dirp);
- if (title) {
+ if (!title.empty()) {
printf("\n");
}
return retval;
@@ -577,7 +715,8 @@
* stuck.
*/
int dump_file_from_fd(const char *title, const char *path, int fd) {
- ON_DRY_RUN_RETURN(0);
+ if (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));
@@ -635,96 +774,87 @@
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, ...) {
- 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());
+int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options) {
+ if (full_command.empty()) {
+ MYLOGE("No arguments on command '%s'\n", title.c_str());
return -1;
}
- ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
+ int size = full_command.size() + 1; // null terminated
+ int starting_index = 0;
+ if (options.RootMode() == SU_ROOT) {
+ starting_index = 2; // "su" "root"
+ size += starting_index;
+ }
- int status = run_command_always(title, DONT_DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
- va_end(ap);
- return status;
-}
+ std::vector<const char*> args;
+ args.resize(size);
-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;
+ std::string command_string;
+ if (options.RootMode() == SU_ROOT) {
+ args[0] = SU_PATH;
+ command_string += SU_PATH;
+ args[1] = "root";
+ command_string += " root ";
+ }
+ int i = starting_index;
+ for (auto arg = full_command.begin(); arg != full_command.end(); ++arg) {
+ args[i++] = arg->c_str();
+ command_string += arg->c_str();
+ if (arg != full_command.end() - 1) {
+ command_string += " ";
}
- // 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");
+ args[i] = nullptr;
+ const char* path = args[0];
+ const char* command = command_string.c_str();
+
+ if (options.RootMode() == SU_ROOT && ds.IsUserBuild()) {
+ printf("Skipping '%s' on user build.\n", command);
+ return 0;
+ }
+
+ if (!title.empty()) {
+ printf("------ %s (%s) ------\n", title.c_str(), command);
+ }
+
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;
+ DurationReporter duration_reporter(title);
+
+ const std::string& logging_message = options.LoggingMessage();
+ if (!logging_message.empty()) {
+ MYLOGI(logging_message.c_str(), command_string.c_str());
}
- ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
+ if (IsDryRun() && !options.Always()) {
+ if (!title.empty()) {
+ printf("\t(skipped on dry run)\n");
+ }
+ UpdateProgress(options.Timeout());
+ return 0;
+ }
- int status = run_command_always(title, DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
- va_end(ap);
+ int status = JustRunCommand(command, path, args, options);
+
+ /* 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...*/
+ int weight = options.Timeout();
+
+ if (weight > 0) {
+ UpdateProgress(weight);
+ }
+
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
+int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
+ const CommandOptions& options) const {
+ bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
- /* 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();
+ uint64_t start = DurationReporter::Nanotime();
pid_t pid = fork();
/* handle error case */
@@ -736,9 +866,9 @@
/* 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));
+ if (options.RootMode() == DROP_ROOT && !drop_root_user()) {
+ if (!silent)
+ printf("*** 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;
}
@@ -757,68 +887,71 @@
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));
+ 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));
fflush(stdout);
- // Must call _exit (instead of exit), otherwise it will corrupt the zip file.
+ // 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
+ bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
+ uint64_t elapsed = DurationReporter::Nanotime() - start;
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);
+ if (!silent)
+ printf("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+ (float)elapsed / NANOS_PER_SEC, pid);
+ MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+ (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);
+ if (!silent)
+ printf("*** command '%s': Error after %.4fs (killing pid %d)\n", command,
+ (float)elapsed / NANOS_PER_SEC, pid);
+ MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
+ (float)elapsed / NANOS_PER_SEC, pid);
}
kill(pid, SIGTERM);
- if (!waitpid_with_timeout(pid, 5, NULL)) {
+ if (!waitpid_with_timeout(pid, 5, nullptr)) {
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);
+ if (!waitpid_with_timeout(pid, 5, nullptr)) {
+ 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));
+ if (!silent)
+ printf("*** 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) {
- if (!silent) printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
- MYLOGE("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
+ status = WEXITSTATUS(status);
+ if (!silent) printf("*** command '%s' failed: exit code %d\n", command, status);
+ MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
}
- if (weight > 0) {
- update_progress(weight);
- }
return status;
}
+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);
+}
+
bool drop_root_user() {
if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
- MYLOGD("drop_root_user(): already running as Shell");
+ MYLOGD("drop_root_user(): already running as Shell\n");
return true;
}
/* ensure we will keep capabilities when we drop root */
@@ -828,7 +961,8 @@
}
gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
- AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_BLUETOOTH };
+ AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_WAKELOCK,
+ AID_BLUETOOTH };
if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
return false;
@@ -849,8 +983,10 @@
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[CAP_TO_INDEX(CAP_SYSLOG)].permitted =
+ (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective =
+ (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
capdata[0].inheritable = 0;
capdata[1].inheritable = 0;
@@ -863,22 +999,16 @@
}
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);
+ std::vector<std::string> am = {"/system/bin/am", "broadcast", "--user", "0", "-a", action};
+
+ am.insert(am.end(), args.begin(), args.end());
+
+ RunCommand("", am, CommandOptions::WithTimeout(20)
+ .Log("Sending broadcast: '%s'\n")
+ .Always()
+ .DropRoot()
+ .RedirectStderr()
+ .Build());
}
size_t num_props = 0;
@@ -902,7 +1032,7 @@
const char* title = "SYSTEM PROPERTIES";
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
- ON_DRY_RUN_RETURN();
+ if (IsDryRun()) return;
size_t i;
num_props = 0;
property_list(print_prop, NULL);
@@ -973,11 +1103,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));
@@ -988,6 +1118,14 @@
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_native_traces(const char* path) {
for (const char** p = native_processes_to_dump; *p; p++) {
if (!strcmp(*p, path)) {
@@ -999,35 +1137,34 @@
/* 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", nullptr);
+ if (IsDryRun()) return nullptr;
- 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 tracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ if (tracesPath.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 anrTracesPath = tracesPath + ".anr";
+ if (rename(tracesPath.c_str(), anrTracesPath.c_str()) && errno != ENOENT) {
+ MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), anrTracesPath.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(tracesPath.c_str(),
+ O_CREAT | O_WRONLY | 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", tracesPath.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", tracesPath.c_str(), strerror(errno));
close(fd);
- return NULL;
+ return nullptr;
}
/* Variables below must be initialized before 'goto' statements */
@@ -1048,9 +1185,9 @@
goto error_close_fd;
}
- wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
+ wfd = inotify_add_watch(ifd, tracesPath.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", tracesPath.c_str(), strerror(errno));
goto error_close_ifd;
}
@@ -1083,7 +1220,7 @@
}
++dalvik_found;
- uint64_t start = DurationReporter::nanotime();
+ uint64_t start = DurationReporter::Nanotime();
if (kill(pid, SIGQUIT)) {
MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
continue;
@@ -1091,7 +1228,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) {
@@ -1104,8 +1241,8 @@
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)(DurationReporter::Nanotime() - start) / NANOS_PER_SEC);
}
} else if (should_dump_native_traces(data)) {
/* dump native process if appropriate */
@@ -1113,7 +1250,7 @@
MYLOGE("lseek: %s\n", strerror(errno));
} else {
static uint16_t timeout_failures = 0;
- uint64_t start = DurationReporter::nanotime();
+ uint64_t start = DurationReporter::Nanotime();
/* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
if (timeout_failures == 3) {
@@ -1124,8 +1261,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)(DurationReporter::Nanotime() - start) / NANOS_PER_SEC);
}
}
}
@@ -1134,17 +1271,15 @@
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 dumpTracesPath = tracesPath + ".bugreport";
+ if (rename(tracesPath.c_str(), dumpTracesPath.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), dumpTracesPath.c_str(), strerror(errno));
goto error_close_ifd;
}
- result = dump_traces_path;
+ result = dumpTracesPath.c_str();
/* replace the saved [ANR] traces.txt file */
- rename(anr_traces_path, traces_path);
+ rename(anrTracesPath.c_str(), tracesPath.c_str());
error_close_ifd:
close(ifd);
@@ -1155,9 +1290,9 @@
void dump_route_tables() {
DurationReporter duration_reporter("DUMP ROUTE TABLES");
- ON_DRY_RUN_RETURN();
+ if (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));
@@ -1168,55 +1303,50 @@
// 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(int delta) {
+ if (!update_progress_) return;
- progress += delta;
+ progress_ += delta;
char key[PROPERTY_KEY_MAX];
char value[PROPERTY_VALUE_MAX];
// 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;
+ 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);
+ snprintf(value, sizeof(value), "%d", weight_total_);
int status = property_set(key, value);
- if (status) {
+ if (status != 0) {
MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
key, value, status);
}
}
snprintf(key, sizeof(key), "dumpstate.%d.progress", getpid());
- snprintf(value, sizeof(value), "%d", progress);
+ snprintf(value, sizeof(value), "%d", progress_);
- if (progress % 100 == 0) {
+ 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);
+ MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total_);
} 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);
+ fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total_);
}
- if (control_socket_fd >= 0) {
- dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
- fsync(control_socket_fd);
+ 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);
@@ -1226,9 +1356,16 @@
}
}
-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 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 {
+ MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
+ }
}
void vibrate(FILE* vibrator, int ms) {
@@ -1365,30 +1502,3 @@
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
index 38442e7..3476964 100644
--- a/cmds/dumpsys/Android.bp
+++ b/cmds/dumpsys/Android.bp
@@ -1,7 +1,14 @@
-cc_binary {
- name: "dumpsys",
+cc_defaults {
+ name: "dumpsys_defaults",
- srcs: ["dumpsys.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "dumpsys.cpp",
+ ],
shared_libs: [
"libbase",
@@ -10,6 +17,34 @@
"libbinder",
],
- cflags: ["-DXP_UNIX"],
- //shared_libs: ["librt"],
+ 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/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 772d17f..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) {
@@ -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..a61cb00
--- /dev/null
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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/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 = String16::std_string(actual);
+ 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/installd/Android.mk b/cmds/installd/Android.mk
index 3ded400..54f6b5f 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -6,7 +6,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := otapreopt
-LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS := -Wall -Werror
# Base & ASLR boundaries for boot image creation.
@@ -33,7 +32,6 @@
libselinux \
LOCAL_STATIC_LIBRARIES := libdiskusage
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 1d291ca..2df48aa 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -60,6 +60,8 @@
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";
@@ -80,8 +82,6 @@
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) {
diff --git a/cmds/surfacereplayer/proto/Android.mk b/cmds/surfacereplayer/proto/Android.mk
new file mode 100644
index 0000000..b87d34f
--- /dev/null
+++ b/cmds/surfacereplayer/proto/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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_SHARED_LIBRARIES := \
+ libprotobuf-cpp-full
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+
+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..45060af
--- /dev/null
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -0,0 +1,178 @@
+syntax = "proto2";
+
+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..dac4314
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -0,0 +1,72 @@
+# 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-full \
+
+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-full \
+ libsurfacereplayer \
+ libutils \
+
+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..ace10d1
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -0,0 +1,698 @@
+/* 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 <binder/IMemory.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::fstream input(filename, std::ios::in | std::ios::binary);
+
+ mLoaded = mTrace.ParseFromIstream(&input);
+ if (!mLoaded) {
+ std::cerr << "Trace did not load. Does " << filename << " exist?" << 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..f757fc3
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -0,0 +1,144 @@
+/*
+ * 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 <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/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index f9464e8..467e0fd 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -42,6 +42,7 @@
<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.print" />
<!-- Feature to specify if the device supports adding device admins. -->
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 8128165..7f545e6 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -42,6 +42,7 @@
<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.print" />
<!-- Feature to specify if the device supports adding device admins. -->
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
index 9097cb3..2e62957 100644
--- a/include/binder/IBinder.h
+++ b/include/binder/IBinder.h
@@ -38,6 +38,7 @@
class IInterface;
class Parcel;
class IResultReceiver;
+class IShellCallback;
/**
* Base class and low-level protocol for a remotable object.
@@ -82,7 +83,7 @@
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,
+ Vector<String16>& args, const sp<IShellCallback>& callback,
const sp<IResultReceiver>& resultReceiver);
virtual status_t transact( uint32_t code,
diff --git a/include/binder/IShellCallback.h b/include/binder/IShellCallback.h
new file mode 100644
index 0000000..fda9ee6
--- /dev/null
+++ b/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/include/binder/Parcel.h b/include/binder/Parcel.h
index 74e75d7..954b976 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -153,6 +153,8 @@
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>
@@ -176,16 +178,21 @@
// 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.
@@ -332,6 +339,10 @@
// 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;
@@ -847,7 +858,16 @@
return this->writeInt32(-1);
}
- return unsafeWriteTypedVector(*val, &Parcel::writeParcelable);
+ 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>);
}
// ---------------------------------------------------------------------------
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 266f0aa..f24f135 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -79,7 +79,8 @@
// needed gralloc buffers.
static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
- const sp<IGraphicBufferAlloc>& allocator = NULL);
+ const sp<IGraphicBufferAlloc>& allocator = NULL,
+ bool consumerIsSurfaceFlinger = false);
private:
BufferQueue(); // Create through createBufferQueue
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 79e7af2..65dea0d 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -29,7 +29,7 @@
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.
@@ -218,6 +218,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/IConsumerListener.h b/include/gui/IConsumerListener.h
index 460a03d..0ab7590 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -49,7 +49,8 @@
// 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.
+ // 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.
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index c2dba50..982cc9d 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -340,20 +340,19 @@
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;
};
// QueueBufferOutput must be a POD structure
struct 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
@@ -380,10 +379,10 @@
nextFrameNumber = inNextFrameNumber;
}
private:
- uint32_t width;
- uint32_t height;
- uint32_t transformHint;
- uint32_t numPendingBuffers;
+ uint32_t width{0};
+ uint32_t height{0};
+ uint32_t transformHint{0};
+ uint32_t numPendingBuffers{0};
uint64_t nextFrameNumber{0};
};
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 555a0cc..a3ee798 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -171,6 +171,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;
};
// ----------------------------------------------------------------------------
@@ -202,6 +206,8 @@
GET_DISPLAY_COLOR_MODES,
GET_ACTIVE_COLOR_MODE,
SET_ACTIVE_COLOR_MODE,
+ ENABLE_VSYNC_INJECTIONS,
+ INJECT_VSYNC
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 489d5ea..1d97d8b 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -204,7 +204,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,6 +223,7 @@
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;
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index f2932f2..9352c57 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -131,6 +131,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();
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index 7ae07ad..b18fd54 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";
@@ -545,6 +546,8 @@
case OMX_IndexConfigAndroidIntraRefresh: return "ConfigAndroidIntraRefresh";
case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering";
case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering";
+ case OMX_IndexParamMaxFrameDurationForBitrateControl:
+ return "ParamMaxFrameDurationForBitrateControl";
case OMX_IndexConfigAutoFramerateConversion: return "ConfigAutoFramerateConversion";
case OMX_IndexConfigPriority: return "ConfigPriority";
case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate";
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_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..63fbff8 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -62,6 +62,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,
@@ -75,11 +76,15 @@
OMX_IndexConfigVideoVp8ReferenceFrame, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMETYPE */
OMX_IndexConfigVideoVp8ReferenceFrameType, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE */
OMX_IndexParamVideoAndroidVp8Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE */
+ OMX_IndexParamVideoVp9, /**< reference: OMX_VIDEO_PARAM_VP9TYPE */
+ OMX_IndexParamVideoAndroidVp9Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP9ENCODERTYPE */
OMX_IndexParamVideoHevc, /**< reference: OMX_VIDEO_PARAM_HEVCTYPE */
OMX_IndexParamSliceSegments, /**< reference: OMX_VIDEO_SLICESEGMENTSTYPE */
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_IndexExtVideoEndUnused,
/* Image & Video common configurations */
OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000,
@@ -90,6 +95,7 @@
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_IndexExtOtherEndUnused,
/* Time configurations */
OMX_IndexExtTimeStartUnused = OMX_IndexKhronosExtensions + 0x00900000,
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/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4b3fcc6..292dd3b 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -74,10 +74,10 @@
status_t read(const Parcel& input);
struct matrix22_t {
- float dsdx;
- float dtdx;
- float dsdy;
- float dtdy;
+ float dsdx{0};
+ float dtdx{0};
+ float dsdy{0};
+ float dtdy{0};
};
sp<IBinder> surface;
uint32_t what;
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
index 799944f..842806e 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -26,16 +26,16 @@
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/GrallocAllocator.h b/include/ui/GrallocAllocator.h
new file mode 100644
index 0000000..23b78ed
--- /dev/null
+++ b/include/ui/GrallocAllocator.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_UI_GRALLOC_ALLOCATOR_H
+#define ANDROID_UI_GRALLOC_ALLOCATOR_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::Error;
+using hardware::graphics::allocator::V2_0::PixelFormat;
+using hardware::graphics::allocator::V2_0::ProducerUsage;
+using hardware::graphics::allocator::V2_0::ConsumerUsage;
+using hardware::graphics::allocator::V2_0::BufferDescriptor;
+using hardware::graphics::allocator::V2_0::Buffer;
+using hardware::graphics::allocator::V2_0::IAllocator;
+
+// Allocator is a wrapper to IAllocator, a proxy to server-side allocator.
+class Allocator {
+public:
+ Allocator();
+
+ // this will be removed and Allocator will be always valid
+ bool valid() const { return (mService != nullptr); }
+
+ std::string dumpDebugInfo() const;
+
+ Error createBufferDescriptor(
+ const IAllocator::BufferDescriptorInfo& descriptorInfo,
+ BufferDescriptor& descriptor) const;
+ void destroyBufferDescriptor(BufferDescriptor descriptor) const;
+
+ Error allocate(BufferDescriptor descriptor, Buffer& buffer) const;
+ void free(Buffer buffer) const;
+
+ Error exportHandle(BufferDescriptor descriptor, Buffer buffer,
+ native_handle_t*& bufferHandle) const;
+
+private:
+ sp<IAllocator> mService;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_ALLOCATOR_H
diff --git a/include/ui/GrallocMapper.h b/include/ui/GrallocMapper.h
new file mode 100644
index 0000000..5517449
--- /dev/null
+++ b/include/ui/GrallocMapper.h
@@ -0,0 +1,127 @@
+/*
+ * 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_MAPPER_H
+#define ANDROID_UI_GRALLOC_MAPPER_H
+
+#include <memory>
+
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <system/window.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::Error;
+using hardware::graphics::allocator::V2_0::PixelFormat;
+using hardware::graphics::allocator::V2_0::ProducerUsage;
+using hardware::graphics::allocator::V2_0::ConsumerUsage;
+using hardware::graphics::mapper::V2_0::FlexLayout;
+using hardware::graphics::mapper::V2_0::BackingStore;
+using hardware::graphics::mapper::V2_0::Device;
+using hardware::graphics::mapper::V2_0::IMapper;
+
+// Mapper is a wrapper to IMapper, a client-side graphics buffer mapper.
+class Mapper {
+public:
+ Mapper();
+ ~Mapper();
+
+ // this will be removed and Mapper will be always valid
+ bool valid() const { return (mMapper != nullptr); }
+
+ Error retain(buffer_handle_t handle) const
+ {
+ return mMapper->retain(mDevice, handle);
+ }
+
+ void release(buffer_handle_t handle) const;
+
+ Error getDimensions(buffer_handle_t handle,
+ uint32_t& width, uint32_t& height) const
+ {
+ return mMapper->getDimensions(mDevice, handle, &width, &height);
+ }
+
+ Error getFormat(buffer_handle_t handle,
+ PixelFormat& format) const
+ {
+ return mMapper->getFormat(mDevice, handle, &format);
+ }
+
+ Error getProducerUsageMask(buffer_handle_t handle,
+ uint64_t& usageMask) const
+ {
+ return mMapper->getProducerUsageMask(mDevice, handle, &usageMask);
+ }
+
+ Error getConsumerUsageMask(buffer_handle_t handle,
+ uint64_t& usageMask) const
+ {
+ return mMapper->getConsumerUsageMask(mDevice, handle, &usageMask);
+ }
+
+ Error getBackingStore(buffer_handle_t handle,
+ BackingStore& store) const
+ {
+ return mMapper->getBackingStore(mDevice, handle, &store);
+ }
+
+ Error getStride(buffer_handle_t handle, uint32_t& stride) const
+ {
+ return mMapper->getStride(mDevice, handle, &stride);
+ }
+
+ Error getNumFlexPlanes(buffer_handle_t handle, uint32_t& numPlanes) const
+ {
+ return mMapper->getNumFlexPlanes(mDevice, handle, &numPlanes);
+ }
+
+ Error lock(buffer_handle_t handle,
+ uint64_t producerUsageMask,
+ uint64_t consumerUsageMask,
+ const Device::Rect& accessRegion,
+ int acquireFence, void*& data) const
+ {
+ return mMapper->lock(mDevice, handle,
+ producerUsageMask, consumerUsageMask,
+ &accessRegion, acquireFence, &data);
+ }
+
+ Error lock(buffer_handle_t handle,
+ uint64_t producerUsageMask,
+ uint64_t consumerUsageMask,
+ const Device::Rect& accessRegion,
+ int acquireFence, FlexLayout& flexLayout) const
+ {
+ return mMapper->lockFlex(mDevice, handle,
+ producerUsageMask, consumerUsageMask,
+ &accessRegion, acquireFence, &flexLayout);
+ }
+
+ int unlock(buffer_handle_t handle) const;
+
+private:
+ const IMapper* mMapper;
+ Device* mDevice;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_MAPPER_H
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 28d0238..9cc5806 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -32,7 +32,12 @@
namespace android {
+namespace Gralloc2 {
+class Allocator;
+}
+
class Gralloc1Loader;
+class GraphicBufferMapper;
class String8;
class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
@@ -86,6 +91,9 @@
GraphicBufferAllocator();
~GraphicBufferAllocator();
+ const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
+ GraphicBufferMapper& mMapper;
+
std::unique_ptr<Gralloc1::Loader> mLoader;
std::unique_ptr<Gralloc1::Device> mDevice;
};
diff --git a/include/ui/GraphicBufferMapper.h b/include/ui/GraphicBufferMapper.h
index a25809c..b6de1b2 100644
--- a/include/ui/GraphicBufferMapper.h
+++ b/include/ui/GraphicBufferMapper.h
@@ -28,6 +28,10 @@
// ---------------------------------------------------------------------------
+namespace Gralloc2 {
+class Mapper;
+}
+
class Rect;
class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -57,11 +61,18 @@
status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
+ const Gralloc2::Mapper& getGrallocMapper() const
+ {
+ return *mMapper;
+ }
+
private:
friend class Singleton<GraphicBufferMapper>;
GraphicBufferMapper();
+ const std::unique_ptr<const Gralloc2::Mapper> mMapper;
+
std::unique_ptr<Gralloc1::Loader> mLoader;
std::unique_ptr<Gralloc1::Device> mDevice;
};
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 4780757..175e982 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -32,6 +32,7 @@
"IProcessInfoService.cpp",
"IResultReceiver.cpp",
"IServiceManager.cpp",
+ "IShellCallback.cpp",
"MemoryBase.cpp",
"MemoryDealer.cpp",
"MemoryHeapBase.cpp",
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 7ce2a31..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,6 +235,8 @@
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());
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/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/IShellCallback.cpp b/libs/binder/IShellCallback.cpp
new file mode 100644
index 0000000..8b97301
--- /dev/null
+++ b/libs/binder/IShellCallback.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 <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 ? dup(fd) : 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/Parcel.cpp b/libs/binder/Parcel.cpp
index 061cb08..601df46 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1153,6 +1153,12 @@
return err;
}
+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());
}
@@ -1427,13 +1433,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;
}
@@ -1984,7 +1990,6 @@
return h;
}
-
int Parcel::readFileDescriptor() const
{
const flat_binder_object* flat = readObject(true);
@@ -1996,6 +2001,17 @@
return BAD_TYPE;
}
+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();
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 17479ca..cb0e965 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -44,6 +44,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 +56,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)
@@ -387,7 +389,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);
@@ -437,7 +439,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;
@@ -629,7 +631,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 */
@@ -668,6 +670,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(fb->type, BINDER_TYPE_HANDLE);
+ EXPECT_EQ(ProcessState::self()->getStrongProxyForHandle(fb->handle), m_server);
+ EXPECT_EQ(fb->cookie, (binder_uintptr_t)0);
+ EXPECT_EQ(fb->binder >> 32, (binder_uintptr_t)0);
+}
+
+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:
@@ -769,6 +827,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;
@@ -882,6 +943,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;
};
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 1357a4a..669124e 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -81,6 +81,9 @@
addAligned(size, mIsDroppable);
addAligned(size, mAcquireCalled);
addAligned(size, mTransformToDisplayInverse);
+ addAligned(size, mAutoRefresh);
+ addAligned(size, mQueuedBuffer);
+ addAligned(size, mIsStale);
return size;
}
@@ -166,6 +169,9 @@
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;
}
@@ -227,6 +233,9 @@
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/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 6de98f5..47f5eba 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -72,7 +72,8 @@
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
- const sp<IGraphicBufferAlloc>& allocator) {
+ const sp<IGraphicBufferAlloc>& allocator,
+ bool consumerIsSurfaceFlinger) {
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
"BufferQueue: outProducer must not be NULL");
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
@@ -82,7 +83,7 @@
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/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index d74d32c..6e6cce2 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -93,6 +93,7 @@
mSharedBufferSlot(INVALID_BUFFER_SLOT),
mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
HAL_DATASPACE_UNKNOWN),
+ mLastQueuedSlot(INVALID_BUFFER_SLOT),
mUniqueId(getUniqueId())
{
if (allocator == NULL) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index f8f3872..90b4b9d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -42,12 +42,15 @@
namespace android {
-BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
+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),
@@ -915,9 +918,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
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a8e6a5..6c1662c 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -383,6 +383,48 @@
}
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
@@ -635,6 +677,26 @@
}
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/Surface.cpp b/libs/gui/Surface.cpp
index 8e6ab1c..351d184 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1393,14 +1393,18 @@
int isSingleBuffered;
res = parcel->readInt32(&isSingleBuffered);
if (res != OK) {
+ ALOGE("Can't read isSingleBuffered");
return res;
}
}
sp<IBinder> binder;
- res = parcel->readStrongBinder(&binder);
- if (res != OK) return res;
+ res = parcel->readNullableStrongBinder(&binder);
+ if (res != OK) {
+ ALOGE("%s: Can't read strong binder", __FUNCTION__);
+ return res;
+ }
graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 43506e9..78afc76 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -129,6 +129,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);
@@ -190,6 +192,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 +263,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;
@@ -652,6 +672,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) {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d5ff753..37b2873 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -45,6 +45,8 @@
"FrameStats.cpp",
"Gralloc1.cpp",
"Gralloc1On0Adapter.cpp",
+ "GrallocAllocator.cpp",
+ "GrallocMapper.cpp",
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
@@ -55,10 +57,17 @@
"UiConfig.cpp",
],
+ static_libs: [
+ "android.hardware.graphics.mapper@2.0",
+ ],
+
shared_libs: [
+ "android.hardware.graphics.allocator@2.0",
"libbinder",
"libcutils",
"libhardware",
+ "libhidl",
+ "libhwbinder",
"libsync",
"libutils",
"liblog",
diff --git a/libs/ui/GrallocAllocator.cpp b/libs/ui/GrallocAllocator.cpp
new file mode 100644
index 0000000..2eb1988
--- /dev/null
+++ b/libs/ui/GrallocAllocator.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "GrallocAllocator"
+
+#include <log/log.h>
+#include <ui/GrallocAllocator.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+Allocator::Allocator()
+{
+ mService = IAllocator::getService("gralloc");
+}
+
+std::string Allocator::dumpDebugInfo() const
+{
+ std::string info;
+
+ mService->dumpDebugInfo([&](const auto& tmpInfo) {
+ info = tmpInfo.c_str();
+ });
+
+ return info;
+}
+
+Error Allocator::createBufferDescriptor(
+ const IAllocator::BufferDescriptorInfo& descriptorInfo,
+ BufferDescriptor& descriptor) const
+{
+ Error error = kDefaultError;
+ mService->createDescriptor(descriptorInfo,
+ [&](const auto& tmpError, const auto& tmpDescriptor) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ descriptor = tmpDescriptor;
+ });
+
+ return error;
+}
+
+void Allocator::destroyBufferDescriptor(BufferDescriptor descriptor) const
+{
+ mService->destroyDescriptor(descriptor);
+}
+
+Error Allocator::allocate(BufferDescriptor descriptor, Buffer& buffer) const
+{
+ hardware::hidl_vec<BufferDescriptor> descriptors;
+ descriptors.setToExternal(&descriptor, 1);
+
+ Error error = kDefaultError;
+ auto status = mService->allocate(descriptors,
+ [&](const auto& tmpError, const auto& tmpBuffers) {
+ error = tmpError;
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ buffer = tmpBuffers[0];
+ });
+
+ return error;
+}
+
+void Allocator::free(Buffer buffer) const
+{
+ mService->free(buffer);
+}
+
+Error Allocator::exportHandle(BufferDescriptor descriptor, Buffer buffer,
+ native_handle_t*& bufferHandle) const
+{
+ Error error = kDefaultError;
+ auto status = mService->exportHandle(descriptor, buffer,
+ [&](const auto& tmpError, const auto& tmpBufferHandle) {
+ error = tmpError;
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ bufferHandle = native_handle_clone(tmpBufferHandle);
+ if (!bufferHandle) {
+ error = Error::NO_RESOURCES;
+ }
+ });
+
+ return error;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GrallocMapper.cpp b/libs/ui/GrallocMapper.cpp
new file mode 100644
index 0000000..d568b68
--- /dev/null
+++ b/libs/ui/GrallocMapper.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 "GrallocMapper"
+
+#include <array>
+#include <string>
+
+#include <log/log.h>
+#include <ui/GrallocMapper.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+typedef const void*(*FetchInterface)(const char* name);
+
+static FetchInterface loadHalLib(const char* pkg_name)
+{
+ static const std::array<const char*, 3> sSearchDirs = {{
+ HAL_LIBRARY_PATH_ODM,
+ HAL_LIBRARY_PATH_VENDOR,
+ HAL_LIBRARY_PATH_SYSTEM,
+ }};
+ static const char sSymbolName[] = "HALLIB_FETCH_Interface";
+
+ void* handle = nullptr;
+ std::string path;
+ for (auto dir : sSearchDirs) {
+ path = dir;
+ path += pkg_name;
+ path += ".hallib.so";
+ handle = dlopen(path.c_str(), RTLD_LOCAL | RTLD_NOW);
+ if (handle) {
+ break;
+ }
+ }
+ if (!handle) {
+ return nullptr;
+ }
+
+ void* symbol = dlsym(handle, sSymbolName);
+ if (!symbol) {
+ ALOGE("%s is missing from %s", sSymbolName, path.c_str());
+ dlclose(handle);
+ return nullptr;
+ }
+
+ return reinterpret_cast<FetchInterface>(symbol);
+}
+
+Mapper::Mapper()
+ : mMapper(nullptr), mDevice(nullptr)
+{
+ static const char sHalLibName[] = "android.hardware.graphics.mapper";
+ static const char sSupportedInterface[] =
+ "android.hardware.graphics.mapper@2.0::IMapper";
+
+ FetchInterface fetchInterface = loadHalLib(sHalLibName);
+ if (!fetchInterface) {
+ return;
+ }
+
+ mMapper = static_cast<const IMapper*>(
+ fetchInterface(sSupportedInterface));
+ if (!mMapper) {
+ ALOGE("%s is not supported", sSupportedInterface);
+ return;
+ }
+
+ if (mMapper->createDevice(&mDevice) != Error::NONE) {
+ ALOGE("failed to create mapper device");
+ mMapper = nullptr;
+ }
+}
+
+Mapper::~Mapper()
+{
+ if (mMapper) {
+ mMapper->destroyDevice(mDevice);
+ }
+}
+
+void Mapper::release(buffer_handle_t handle) const
+{
+ auto error = mMapper->release(mDevice, handle);
+ ALOGE_IF(error != Error::NONE,
+ "release(%p) failed with %d", handle, error);
+}
+
+int Mapper::unlock(buffer_handle_t handle) const
+{
+ int releaseFence;
+ auto error = mMapper->unlock(mDevice, handle, &releaseFence);
+ if (error != Error::NONE) {
+ ALOGE("unlock(%p) failed with %d", handle, error);
+ releaseFence = -1;
+ }
+
+ return releaseFence;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 97b948d..6d72900 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -23,6 +23,7 @@
#include <utils/Errors.h>
#include <utils/Log.h>
+#include <ui/GrallocMapper.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
@@ -108,8 +109,10 @@
{
if (mOwner == ownHandle) {
mBufferMapper.unregisterBuffer(handle);
- native_handle_close(handle);
- native_handle_delete(const_cast<native_handle*>(handle));
+ if (!mBufferMapper.getGrallocMapper().valid()) {
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle*>(handle));
+ }
} else if (mOwner == ownData) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index edfff4d..693c7cb 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -26,6 +26,9 @@
#include <ui/GraphicBufferAllocator.h>
#include <ui/Gralloc1On0Adapter.h>
+#include <ui/GrallocAllocator.h>
+#include <ui/GrallocMapper.h>
+#include <ui/GraphicBufferMapper.h>
namespace android {
// ---------------------------------------------------------------------------
@@ -37,8 +40,14 @@
GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
GraphicBufferAllocator::GraphicBufferAllocator()
- : mLoader(std::make_unique<Gralloc1::Loader>()),
- mDevice(mLoader->getDevice()) {}
+ : mAllocator(std::make_unique<Gralloc2::Allocator>()),
+ mMapper(GraphicBufferMapper::getInstance())
+{
+ if (!mAllocator->valid()) {
+ mLoader = std::make_unique<Gralloc1::Loader>();
+ mDevice = mLoader->getDevice();
+ }
+}
GraphicBufferAllocator::~GraphicBufferAllocator() {}
@@ -70,7 +79,14 @@
}
snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0f);
result.append(buffer);
- std::string deviceDump = mDevice->dump();
+
+ std::string deviceDump;
+ if (mAllocator->valid()) {
+ deviceDump = mAllocator->dumpDebugInfo();
+ } else {
+ deviceDump = mDevice->dump();
+ }
+
result.append(deviceDump.c_str(), deviceDump.size());
}
@@ -81,6 +97,103 @@
ALOGD("%s", s.string());
}
+namespace {
+
+class HalBuffer {
+public:
+ HalBuffer(const Gralloc2::Allocator* allocator,
+ uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t usage)
+ : mAllocator(allocator), mBufferValid(false)
+ {
+ Gralloc2::IAllocator::BufferDescriptorInfo info = {};
+ info.width = width;
+ info.height = height;
+ info.format = static_cast<Gralloc2::PixelFormat>(format);
+ info.producerUsageMask = usage;
+ info.consumerUsageMask = usage;
+
+ Gralloc2::BufferDescriptor descriptor;
+ auto error = mAllocator->createBufferDescriptor(info, descriptor);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGE("Failed to create desc (%u x %u) format %d usage %u: %d",
+ width, height, format, usage, error);
+ return;
+ }
+
+ error = mAllocator->allocate(descriptor, mBuffer);
+ if (error == Gralloc2::Error::NOT_SHARED) {
+ error = Gralloc2::Error::NONE;
+ }
+
+ if (error != Gralloc2::Error::NONE) {
+ ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
+ width, height, format, usage, error);
+ mAllocator->destroyBufferDescriptor(descriptor);
+ return;
+ }
+
+ error = mAllocator->exportHandle(descriptor, mBuffer, mHandle);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGE("Failed to export handle");
+ mAllocator->free(mBuffer);
+ mAllocator->destroyBufferDescriptor(descriptor);
+ return;
+ }
+
+ mAllocator->destroyBufferDescriptor(descriptor);
+
+ mBufferValid = true;
+ }
+
+ ~HalBuffer()
+ {
+ if (mBufferValid) {
+ if (mHandle) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+
+ mAllocator->free(mBuffer);
+ }
+ }
+
+ bool exportHandle(GraphicBufferMapper& mapper,
+ buffer_handle_t* handle, uint32_t* stride)
+ {
+ if (!mBufferValid) {
+ return false;
+ }
+
+ if (mapper.registerBuffer(mHandle)) {
+ return false;
+ }
+
+ *handle = mHandle;
+
+ auto error = mapper.getGrallocMapper().getStride(mHandle, *stride);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGW("Failed to get stride from buffer: %d", error);
+ *stride = 0;
+ }
+
+ mHandle = nullptr;
+ mAllocator->free(mBuffer);
+ mBufferValid = false;
+
+ return true;
+ }
+
+private:
+ const Gralloc2::Allocator* mAllocator;
+
+ bool mBufferValid;
+ Gralloc2::Buffer mBuffer;
+ native_handle_t* mHandle;
+};
+
+} // namespace
+
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
PixelFormat format, uint32_t usage, buffer_handle_t* handle,
uint32_t* stride, uint64_t graphicBufferId, std::string requestorName)
@@ -95,40 +208,51 @@
// Filter out any usage bits that should not be passed to the gralloc module
usage &= GRALLOC_USAGE_ALLOC_MASK;
- auto descriptor = mDevice->createDescriptor();
- auto error = descriptor->setDimensions(width, height);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set dimensions to (%u, %u): %d", width, height, error);
- return BAD_VALUE;
- }
- error = descriptor->setFormat(static_cast<android_pixel_format_t>(format));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set format to %d: %d", format, error);
- return BAD_VALUE;
- }
- error = descriptor->setProducerUsage(
- static_cast<gralloc1_producer_usage_t>(usage));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set producer usage to %u: %d", usage, error);
- return BAD_VALUE;
- }
- error = descriptor->setConsumerUsage(
- static_cast<gralloc1_consumer_usage_t>(usage));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set consumer usage to %u: %d", usage, error);
- return BAD_VALUE;
- }
+ gralloc1_error_t error;
+ if (mAllocator->valid()) {
+ HalBuffer buffer(mAllocator.get(), width, height, format, usage);
+ if (!buffer.exportHandle(mMapper, handle, stride)) {
+ return NO_MEMORY;
+ }
+ error = GRALLOC1_ERROR_NONE;
+ } else {
+ auto descriptor = mDevice->createDescriptor();
+ error = descriptor->setDimensions(width, height);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set dimensions to (%u, %u): %d",
+ width, height, error);
+ return BAD_VALUE;
+ }
+ error = descriptor->setFormat(
+ static_cast<android_pixel_format_t>(format));
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set format to %d: %d", format, error);
+ return BAD_VALUE;
+ }
+ error = descriptor->setProducerUsage(
+ static_cast<gralloc1_producer_usage_t>(usage));
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set producer usage to %u: %d", usage, error);
+ return BAD_VALUE;
+ }
+ error = descriptor->setConsumerUsage(
+ static_cast<gralloc1_consumer_usage_t>(usage));
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set consumer usage to %u: %d", usage, error);
+ return BAD_VALUE;
+ }
- error = mDevice->allocate(descriptor, graphicBufferId, handle);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
- width, height, format, usage, error);
- return NO_MEMORY;
- }
+ error = mDevice->allocate(descriptor, graphicBufferId, handle);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
+ width, height, format, usage, error);
+ return NO_MEMORY;
+ }
- error = mDevice->getStride(*handle, stride);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGW("Failed to get stride from buffer: %d", error);
+ error = mDevice->getStride(*handle, stride);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGW("Failed to get stride from buffer: %d", error);
+ }
}
if (error == NO_ERROR) {
@@ -153,7 +277,14 @@
{
ATRACE_CALL();
- auto error = mDevice->release(handle);
+ gralloc1_error_t error;
+ if (mAllocator->valid()) {
+ error = static_cast<gralloc1_error_t>(
+ mMapper.unregisterBuffer(handle));
+ } else {
+ error = mDevice->release(handle);
+ }
+
if (error != GRALLOC1_ERROR_NONE) {
ALOGE("Failed to free buffer: %d", error);
}
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 481d43c..fb55bf1 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -33,6 +33,7 @@
#include <utils/Trace.h>
#include <ui/Gralloc1On0Adapter.h>
+#include <ui/GrallocMapper.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/Rect.h>
@@ -44,8 +45,13 @@
ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
GraphicBufferMapper::GraphicBufferMapper()
- : mLoader(std::make_unique<Gralloc1::Loader>()),
- mDevice(mLoader->getDevice()) {}
+ : mMapper(std::make_unique<const Gralloc2::Mapper>())
+{
+ if (!mMapper->valid()) {
+ mLoader = std::make_unique<Gralloc1::Loader>();
+ mDevice = mLoader->getDevice();
+ }
+}
@@ -53,7 +59,13 @@
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->retain(handle);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ error = static_cast<gralloc1_error_t>(mMapper->retain(handle));
+ } else {
+ error = mDevice->retain(handle);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
handle, error);
@@ -64,7 +76,14 @@
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->retain(buffer);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ error = static_cast<gralloc1_error_t>(
+ mMapper->retain(buffer->getNativeBuffer()->handle));
+ } else {
+ error = mDevice->retain(buffer);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
buffer->getNativeBuffer()->handle, error);
@@ -75,7 +94,14 @@
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->release(handle);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ mMapper->release(handle);
+ error = GRALLOC1_ERROR_NONE;
+ } else {
+ error = mDevice->release(handle);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d",
handle, error);
@@ -120,11 +146,20 @@
ATRACE_CALL();
gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
- sp<Fence> fence = new Fence(fenceFd);
- gralloc1_error_t error = mDevice->lock(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, vaddr, fence);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ const Gralloc2::Device::Rect& accessRect =
+ *reinterpret_cast<Gralloc2::Device::Rect*>(&accessRegion);
+ error = static_cast<gralloc1_error_t>(mMapper->lock(
+ handle, usage, usage, accessRect, fenceFd, *vaddr));
+ } else {
+ sp<Fence> fence = new Fence(fenceFd);
+ error = mDevice->lock(handle,
+ static_cast<gralloc1_producer_usage_t>(usage),
+ static_cast<gralloc1_consumer_usage_t>(usage),
+ &accessRegion, vaddr, fence);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle,
error);
@@ -160,20 +195,29 @@
ATRACE_CALL();
gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
- sp<Fence> fence = new Fence(fenceFd);
- if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
- gralloc1_error_t error = mDevice->lockYCbCr(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, ycbcr, fence);
- ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lockYCbCr(%p, ...) failed: %d",
- handle, error);
- return error;
+ if (!mMapper->valid()) {
+ if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
+ sp<Fence> fence = new Fence(fenceFd);
+ gralloc1_error_t error = mDevice->lockYCbCr(handle,
+ static_cast<gralloc1_producer_usage_t>(usage),
+ static_cast<gralloc1_consumer_usage_t>(usage),
+ &accessRegion, ycbcr, fence);
+ ALOGW_IF(error != GRALLOC1_ERROR_NONE,
+ "lockYCbCr(%p, ...) failed: %d", handle, error);
+ return error;
+ }
}
uint32_t numPlanes = 0;
- gralloc1_error_t error = mDevice->getNumFlexPlanes(handle, &numPlanes);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ error = static_cast<gralloc1_error_t>(
+ mMapper->getNumFlexPlanes(handle, numPlanes));
+ } else {
+ error = mDevice->getNumFlexPlanes(handle, &numPlanes);
+ }
+
if (error != GRALLOC1_ERROR_NONE) {
ALOGV("Failed to retrieve number of flex planes: %d", error);
return error;
@@ -188,10 +232,21 @@
flexLayout.num_planes = numPlanes;
flexLayout.planes = planes.data();
- error = mDevice->lockFlex(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, &flexLayout, fence);
+ if (mMapper->valid()) {
+ const Gralloc2::Device::Rect& accessRect =
+ *reinterpret_cast<Gralloc2::Device::Rect*>(&accessRegion);
+ Gralloc2::FlexLayout& layout =
+ *reinterpret_cast<Gralloc2::FlexLayout*>(&flexLayout);
+ error = static_cast<gralloc1_error_t>(mMapper->lock(
+ handle, usage, usage, accessRect, fenceFd, layout));
+ } else {
+ sp<Fence> fence = new Fence(fenceFd);
+ error = mDevice->lockFlex(handle,
+ static_cast<gralloc1_producer_usage_t>(usage),
+ static_cast<gralloc1_consumer_usage_t>(usage),
+ &accessRegion, &flexLayout, fence);
+ }
+
if (error != GRALLOC1_ERROR_NONE) {
ALOGW("lockFlex(%p, ...) failed: %d", handle, error);
return error;
@@ -276,14 +331,20 @@
{
ATRACE_CALL();
- sp<Fence> fence = Fence::NO_FENCE;
- gralloc1_error_t error = mDevice->unlock(handle, &fence);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("unlock(%p) failed: %d", handle, error);
- return error;
- }
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ *fenceFd = mMapper->unlock(handle);
+ error = GRALLOC1_ERROR_NONE;
+ } else {
+ sp<Fence> fence = Fence::NO_FENCE;
+ error = mDevice->unlock(handle, &fence);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("unlock(%p) failed: %d", handle, error);
+ return error;
+ }
- *fenceFd = fence->dup();
+ *fenceFd = fence->dup();
+ }
return error;
}
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 5e72794..3f2861f 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1139,6 +1139,16 @@
{
clearError();
+ // Generate an error quietly when client extensions (as defined by
+ // EGL_EXT_client_extensions) are queried. We do not want to rely on
+ // validate_display to generate the error as validate_display would log
+ // the error, which can be misleading.
+ //
+ // If we want to support EGL_EXT_client_extensions later, we can return
+ // the client extension string here instead.
+ if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
+ return setErrorQuiet(EGL_BAD_DISPLAY, nullptr);
+
const egl_display_ptr dp = validate_display(dpy);
if (!dp) return (const char *) NULL;
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 7b10319..d6e6187 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -10,7 +10,6 @@
OrientationSensor.cpp \
RecentEventLogger.cpp \
RotationVectorSensor.cpp \
- SensorDevice.cpp \
SensorEventConnection.cpp \
SensorFusion.cpp \
SensorInterface.cpp \
@@ -19,13 +18,19 @@
SensorService.cpp \
SensorServiceUtils.cpp \
-
LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
LOCAL_CFLAGS += -Wall -Werror -Wextra
LOCAL_CFLAGS += -fvisibility=hidden
+ifeq ($(ENABLE_TREBLE), true)
+LOCAL_SRC_FILES += SensorDeviceTreble.cpp
+LOCAL_CFLAGS += -DENABLE_TREBLE=1
+else
+LOCAL_SRC_FILES += SensorDevice.cpp
+endif
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libhardware \
@@ -35,7 +40,20 @@
libbinder \
libui \
libgui \
- libcrypto
+ libcrypto \
+
+ifeq ($(ENABLE_TREBLE), true)
+
+LOCAL_SHARED_LIBRARIES += \
+ libbase \
+ libhidl \
+ libhwbinder \
+ android.hardware.sensors@1.0
+
+LOCAL_STATIC_LIBRARIES := \
+ android.hardware.sensors@1.0-convert
+
+endif # ENABLE_TREBLE
LOCAL_MODULE:= libsensorservice
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index ac03742..0245b26 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -53,13 +53,15 @@
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
if (mSensorDevice) {
- if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
- mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {
- ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");
- }
sensor_t const* list;
ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
+
+ if (mSensorDevice->common.version < SENSORS_DEVICE_API_VERSION_1_3) {
+ ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3, ignoring sensors reported by this device");
+ count = 0;
+ }
+
mActivationCount.setCapacity(count);
Info model;
for (size_t i=0 ; i<size_t(count) ; i++) {
@@ -164,6 +166,8 @@
ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
if (isClientDisabledLocked(ident)) {
+ ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+ ident, handle);
return INVALID_OPERATION;
}
@@ -347,6 +351,7 @@
void SensorDevice::enableAllSensors() {
Mutex::Autolock _l(mLock);
mDisabledClients.clear();
+ ALOGI("cleared mDisabledClients");
const int halVersion = getHalDeviceVersion();
for (size_t i = 0; i< mActivationCount.size(); ++i) {
Info& info = mActivationCount.editValueAt(i);
@@ -395,6 +400,7 @@
// clients list.
for (size_t j = 0; j < info.batchParams.size(); ++j) {
mDisabledClients.add(info.batchParams.keyAt(j));
+ ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
}
}
}
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d340da3..0bb0752 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -27,19 +27,29 @@
#include <stdint.h>
#include <sys/types.h>
+#ifdef ENABLE_TREBLE
+#include <map>
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
+
// ---------------------------------------------------------------------------
using SensorServiceUtil::Dumpable;
class SensorDevice : public Singleton<SensorDevice>, public Dumpable {
public:
ssize_t getSensorList(sensor_t const** list);
+
void handleDynamicSensorConnection(int handle, bool connected);
status_t initCheck() const;
int getHalDeviceVersion() const;
+
ssize_t poll(sensors_event_t* buffer, size_t count);
+
status_t activate(void* ident, int handle, int enabled);
status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs);
@@ -50,6 +60,7 @@
void disableAllSensors();
void enableAllSensors();
void autoDisable(void *ident, int handle);
+
status_t injectSensorData(const sensors_event_t *event);
void notifyConnectionDestroyed(void *ident);
@@ -57,8 +68,15 @@
virtual std::string dump() const;
private:
friend class Singleton<SensorDevice>;
+#ifdef ENABLE_TREBLE
+ sp<android::hardware::sensors::V1_0::ISensors> mSensors;
+ Vector<sensor_t> mSensorList;
+ std::map<int32_t, sensor_t*> mConnectedDynamicSensors;
+#else
sensors_poll_device_1_t* mSensorDevice;
struct sensors_module_t* mSensorModule;
+#endif
+
static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
mutable Mutex mLock; // protect mActivationCount[].batchParams
// fixed-size array after construction
@@ -111,6 +129,18 @@
bool isClientDisabled(void* ident);
bool isClientDisabledLocked(void* ident);
+
+#ifdef ENABLE_TREBLE
+ using Event = hardware::sensors::V1_0::Event;
+ using SensorInfo = hardware::sensors::V1_0::SensorInfo;
+
+ void convertToSensorEvent(const Event &src, sensors_event_t *dst);
+
+ void convertToSensorEvents(
+ const hardware::hidl_vec<Event> &src,
+ const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
+ sensors_event_t *dst);
+#endif // ENABLE_TREBLE
};
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDeviceTreble.cpp b/services/sensorservice/SensorDeviceTreble.cpp
new file mode 100644
index 0000000..22b1105
--- /dev/null
+++ b/services/sensorservice/SensorDeviceTreble.cpp
@@ -0,0 +1,610 @@
+/*
+ * 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 <inttypes.h>
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <hidl/IServiceManager.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+
+#include "SensorDevice.h"
+#include "SensorService.h"
+
+#include "convert.h"
+
+using android::hardware::sensors::V1_0::ISensors;
+using android::hardware::hidl_vec;
+
+using Event = android::hardware::sensors::V1_0::Event;
+using SensorInfo = android::hardware::sensors::V1_0::SensorInfo;
+using SensorType = android::hardware::sensors::V1_0::SensorType;
+using DynamicSensorInfo = android::hardware::sensors::V1_0::DynamicSensorInfo;
+using SensorInfo = android::hardware::sensors::V1_0::SensorInfo;
+using Result = android::hardware::sensors::V1_0::Result;
+
+using namespace android::hardware::sensors::V1_0::implementation;
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SensorDevice)
+
+static status_t StatusFromResult(Result result) {
+ switch (result) {
+ case Result::OK:
+ return OK;
+ case Result::BAD_VALUE:
+ return BAD_VALUE;
+ case Result::PERMISSION_DENIED:
+ return PERMISSION_DENIED;
+ case Result::INVALID_OPERATION:
+ return INVALID_OPERATION;
+ }
+}
+
+SensorDevice::SensorDevice() {
+ mSensors = ISensors::getService("sensors");
+
+ if (mSensors == NULL) {
+ return;
+ }
+
+ mSensors->getSensorsList(
+ [&](const auto &list) {
+ const size_t count = list.size();
+
+ mActivationCount.setCapacity(count);
+ Info model;
+ for (size_t i=0 ; i < count; i++) {
+ sensor_t sensor;
+ convertToSensor(list[i], &sensor);
+ mSensorList.push_back(sensor);
+
+ mActivationCount.add(list[i].sensorHandle, model);
+
+ /* auto result = */mSensors->activate(
+ list[i].sensorHandle, 0 /* enabled */);
+ }
+ });
+}
+
+void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
+ if (connected) {
+ Info model;
+ mActivationCount.add(handle, model);
+ /* auto result = */mSensors->activate(handle, 0 /* enabled */);
+ } else {
+ mActivationCount.removeItem(handle);
+ }
+}
+
+std::string SensorDevice::dump() const {
+ if (mSensors == NULL) return "HAL not initialized\n";
+
+#if 0
+ result.appendFormat("HAL: %s (%s), version %#010x\n",
+ mSensorModule->common.name,
+ mSensorModule->common.author,
+ getHalDeviceVersion());
+#endif
+
+ String8 result;
+ mSensors->getSensorsList([&](const auto &list) {
+ const size_t count = list.size();
+
+ result.appendFormat(
+ "Total %zu h/w sensors, %zu running:\n",
+ count,
+ mActivationCount.size());
+
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0 ; i < count ; i++) {
+ const Info& info = mActivationCount.valueFor(
+ list[i].sensorHandle);
+
+ if (info.batchParams.isEmpty()) continue;
+ result.appendFormat(
+ "0x%08x) active-count = %zu; ",
+ list[i].sensorHandle,
+ info.batchParams.size());
+
+ result.append("sampling_period(ms) = {");
+ for (size_t j = 0; j < info.batchParams.size(); j++) {
+ const BatchParams& params = info.batchParams.valueAt(j);
+ result.appendFormat(
+ "%.1f%s",
+ params.batchDelay / 1e6f,
+ j < info.batchParams.size() - 1 ? ", " : "");
+ }
+ result.appendFormat(
+ "}, selected = %.1f ms; ",
+ info.bestBatchParams.batchDelay / 1e6f);
+
+ result.append("batching_period(ms) = {");
+ for (size_t j = 0; j < info.batchParams.size(); j++) {
+ BatchParams params = info.batchParams.valueAt(j);
+
+ result.appendFormat(
+ "%.1f%s",
+ params.batchTimeout / 1e6f,
+ j < info.batchParams.size() - 1 ? ", " : "");
+ }
+
+ result.appendFormat(
+ "}, selected = %.1f ms\n",
+ info.bestBatchParams.batchTimeout / 1e6f);
+ }
+ });
+
+ return result.string();
+}
+
+ssize_t SensorDevice::getSensorList(sensor_t const** list) {
+ *list = &mSensorList[0];
+
+ return mSensorList.size();
+}
+
+status_t SensorDevice::initCheck() const {
+ return mSensors != NULL ? NO_ERROR : NO_INIT;
+}
+
+ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
+ if (mSensors == NULL) return NO_INIT;
+
+ ssize_t err;
+
+ mSensors->poll(
+ count,
+ [&](auto result,
+ const auto &events,
+ const auto &dynamicSensorsAdded) {
+ if (result == Result::OK) {
+ convertToSensorEvents(events, dynamicSensorsAdded, buffer);
+ err = (ssize_t)events.size();
+ } else {
+ err = StatusFromResult(result);
+ }
+ });
+
+ return err;
+}
+
+void SensorDevice::autoDisable(void *ident, int handle) {
+ Info& info( mActivationCount.editValueFor(handle) );
+ Mutex::Autolock _l(mLock);
+ info.removeBatchParamsForIdent(ident);
+}
+
+status_t SensorDevice::activate(void* ident, int handle, int enabled) {
+ if (mSensors == NULL) return NO_INIT;
+
+ status_t err(NO_ERROR);
+ bool actuateHardware = false;
+
+ Mutex::Autolock _l(mLock);
+ Info& info( mActivationCount.editValueFor(handle) );
+
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",
+ ident, handle, enabled, info.batchParams.size());
+
+ if (enabled) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
+
+ if (isClientDisabledLocked(ident)) {
+ ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+ ident, handle);
+ return INVALID_OPERATION;
+ }
+
+ if (info.batchParams.indexOfKey(ident) >= 0) {
+ if (info.numActiveClients() == 1) {
+ // This is the first connection, we need to activate the underlying h/w sensor.
+ actuateHardware = true;
+ }
+ } else {
+ // Log error. Every activate call should be preceded by a batch() call.
+ ALOGE("\t >>>ERROR: activate called without batch");
+ }
+ } else {
+ ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%zd", info.batchParams.indexOfKey(ident));
+
+ // If a connected dynamic sensor is deactivated, remove it from the
+ // dictionary.
+ auto it = mConnectedDynamicSensors.find(handle);
+ if (it != mConnectedDynamicSensors.end()) {
+ delete it->second;
+ mConnectedDynamicSensors.erase(it);
+ }
+
+ if (info.removeBatchParamsForIdent(ident) >= 0) {
+ if (info.numActiveClients() == 0) {
+ // This is the last connection, we need to de-activate the underlying h/w sensor.
+ actuateHardware = true;
+ } else {
+ const int halVersion = getHalDeviceVersion();
+ if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
+ // Call batch for this sensor with the previously calculated best effort
+ // batch_rate and timeout. One of the apps has unregistered for sensor
+ // events, and the best effort batch parameters might have changed.
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "\t>>> actuating h/w batch %d %d %" PRId64 " %" PRId64, handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ /* auto result = */mSensors->batch(
+ handle,
+ info.bestBatchParams.flags,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ }
+ }
+ } else {
+ // sensor wasn't enabled for this ident
+ }
+
+ if (isClientDisabledLocked(ident)) {
+ return NO_ERROR;
+ }
+ }
+
+ if (actuateHardware) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
+ enabled);
+ err = StatusFromResult(mSensors->activate(handle, enabled));
+ ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
+ strerror(-err));
+
+ if (err != NO_ERROR && enabled) {
+ // Failure when enabling the sensor. Clean up on failure.
+ info.removeBatchParamsForIdent(ident);
+ }
+ }
+
+ // On older devices which do not support batch, call setDelay().
+ if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1 && info.numActiveClients() > 0) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w setDelay %d %" PRId64, handle,
+ info.bestBatchParams.batchDelay);
+ /* auto result = */mSensors->setDelay(
+ handle, info.bestBatchParams.batchDelay);
+ }
+ return err;
+}
+
+status_t SensorDevice::batch(
+ void* ident,
+ int handle,
+ int flags,
+ int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
+ if (mSensors == NULL) return NO_INIT;
+
+ if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
+ samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
+ }
+
+ const int halVersion = getHalDeviceVersion();
+ if (halVersion < SENSORS_DEVICE_API_VERSION_1_1 && maxBatchReportLatencyNs != 0) {
+ // Batch is not supported on older devices return invalid operation.
+ return INVALID_OPERATION;
+ }
+
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
+ ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+
+ Mutex::Autolock _l(mLock);
+ Info& info(mActivationCount.editValueFor(handle));
+
+ if (info.batchParams.indexOfKey(ident) < 0) {
+ BatchParams params(flags, samplingPeriodNs, maxBatchReportLatencyNs);
+ info.batchParams.add(ident, params);
+ } else {
+ // A batch has already been called with this ident. Update the batch parameters.
+ info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+ }
+
+ BatchParams prevBestBatchParams = info.bestBatchParams;
+ // Find the minimum of all timeouts and batch_rates for this sensor.
+ info.selectBatchParams();
+
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "\t>>> curr_period=%" PRId64 " min_period=%" PRId64
+ " curr_timeout=%" PRId64 " min_timeout=%" PRId64,
+ prevBestBatchParams.batchDelay, info.bestBatchParams.batchDelay,
+ prevBestBatchParams.batchTimeout, info.bestBatchParams.batchTimeout);
+
+ status_t err(NO_ERROR);
+ // If the min period or min timeout has changed since the last batch call, call batch.
+ if (prevBestBatchParams != info.bestBatchParams) {
+ if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ err = StatusFromResult(
+ mSensors->batch(
+ handle,
+ info.bestBatchParams.flags,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout));
+ } else {
+ // For older devices which do not support batch, call setDelay() after activate() is
+ // called. Some older devices may not support calling setDelay before activate(), so
+ // call setDelay in SensorDevice::activate() method.
+ }
+ if (err != NO_ERROR) {
+ ALOGE("sensor batch failed %p %d %d %" PRId64 " %" PRId64 " err=%s",
+ mSensors.get(), handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout, strerror(-err));
+ info.removeBatchParamsForIdent(ident);
+ }
+ }
+ return err;
+}
+
+status_t SensorDevice::setDelay(void* ident, int handle, int64_t samplingPeriodNs) {
+ if (mSensors == NULL) return NO_INIT;
+ if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
+ samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
+ }
+ Mutex::Autolock _l(mLock);
+ if (isClientDisabledLocked(ident)) return INVALID_OPERATION;
+ Info& info( mActivationCount.editValueFor(handle) );
+ // If the underlying sensor is NOT in continuous mode, setDelay() should return an error.
+ // Calling setDelay() in batch mode is an invalid operation.
+ if (info.bestBatchParams.batchTimeout != 0) {
+ return INVALID_OPERATION;
+ }
+ ssize_t index = info.batchParams.indexOfKey(ident);
+ if (index < 0) {
+ return BAD_INDEX;
+ }
+ BatchParams& params = info.batchParams.editValueAt(index);
+ params.batchDelay = samplingPeriodNs;
+ info.selectBatchParams();
+
+ return StatusFromResult(
+ mSensors->setDelay(handle, info.bestBatchParams.batchDelay));
+}
+
+int SensorDevice::getHalDeviceVersion() const {
+ if (mSensors == NULL) return -1;
+ return SENSORS_DEVICE_API_VERSION_1_4;
+}
+
+status_t SensorDevice::flush(void* ident, int handle) {
+ if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1) {
+ return INVALID_OPERATION;
+ }
+ if (isClientDisabled(ident)) return INVALID_OPERATION;
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
+ return StatusFromResult(mSensors->flush(handle));
+}
+
+bool SensorDevice::isClientDisabled(void* ident) {
+ Mutex::Autolock _l(mLock);
+ return isClientDisabledLocked(ident);
+}
+
+bool SensorDevice::isClientDisabledLocked(void* ident) {
+ return mDisabledClients.indexOf(ident) >= 0;
+}
+
+void SensorDevice::enableAllSensors() {
+ Mutex::Autolock _l(mLock);
+ mDisabledClients.clear();
+ ALOGI("cleared mDisabledClients");
+ const int halVersion = getHalDeviceVersion();
+ for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ Info& info = mActivationCount.editValueAt(i);
+ if (info.batchParams.isEmpty()) continue;
+ info.selectBatchParams();
+ const int sensor_handle = mActivationCount.keyAt(i);
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
+ sensor_handle);
+ status_t err(NO_ERROR);
+ if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
+ err = StatusFromResult(
+ mSensors->batch(
+ sensor_handle,
+ info.bestBatchParams.flags,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout));
+ ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
+ }
+
+ if (err == NO_ERROR) {
+ err = StatusFromResult(
+ mSensors->activate(sensor_handle, 1 /* enabled */));
+ ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
+ }
+
+ if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0) {
+ err = StatusFromResult(
+ mSensors->setDelay(
+ sensor_handle, info.bestBatchParams.batchDelay));
+ ALOGE_IF(err, "Error calling setDelay sensor %d (%s)", sensor_handle, strerror(-err));
+ }
+ }
+}
+
+void SensorDevice::disableAllSensors() {
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ const Info& info = mActivationCount.valueAt(i);
+ // Check if this sensor has been activated previously and disable it.
+ if (info.batchParams.size() > 0) {
+ const int sensor_handle = mActivationCount.keyAt(i);
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
+ sensor_handle);
+ /* auto result = */mSensors->activate(
+ sensor_handle, 0 /* enabled */);
+
+ // Add all the connections that were registered for this sensor to the disabled
+ // clients list.
+ for (size_t j = 0; j < info.batchParams.size(); ++j) {
+ mDisabledClients.add(info.batchParams.keyAt(j));
+ ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
+ }
+ }
+ }
+}
+
+status_t SensorDevice::injectSensorData(
+ const sensors_event_t *injected_sensor_event) {
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
+ injected_sensor_event->sensor,
+ injected_sensor_event->timestamp, injected_sensor_event->data[0],
+ injected_sensor_event->data[1], injected_sensor_event->data[2],
+ injected_sensor_event->data[3], injected_sensor_event->data[4],
+ injected_sensor_event->data[5]);
+
+ if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
+ return INVALID_OPERATION;
+ }
+
+ Event ev;
+ convertFromSensorEvent(*injected_sensor_event, &ev);
+
+ return StatusFromResult(mSensors->injectSensorData(ev));
+}
+
+status_t SensorDevice::setMode(uint32_t mode) {
+ if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
+ return INVALID_OPERATION;
+ }
+
+ return StatusFromResult(
+ mSensors->setOperationMode(
+ static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+}
+
+// ---------------------------------------------------------------------------
+
+int SensorDevice::Info::numActiveClients() {
+ SensorDevice& device(SensorDevice::getInstance());
+ int num = 0;
+ for (size_t i = 0; i < batchParams.size(); ++i) {
+ if (!device.isClientDisabledLocked(batchParams.keyAt(i))) {
+ ++num;
+ }
+ }
+ return num;
+}
+
+status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int flags,
+ int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
+ ssize_t index = batchParams.indexOfKey(ident);
+ if (index < 0) {
+ ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64 ") failed (%s)",
+ ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index));
+ return BAD_INDEX;
+ }
+ BatchParams& params = batchParams.editValueAt(index);
+ params.flags = flags;
+ params.batchDelay = samplingPeriodNs;
+ params.batchTimeout = maxBatchReportLatencyNs;
+ return NO_ERROR;
+}
+
+void SensorDevice::Info::selectBatchParams() {
+ BatchParams bestParams(0, -1, -1);
+ SensorDevice& device(SensorDevice::getInstance());
+
+ for (size_t i = 0; i < batchParams.size(); ++i) {
+ if (device.isClientDisabledLocked(batchParams.keyAt(i))) continue;
+ BatchParams params = batchParams.valueAt(i);
+ if (bestParams.batchDelay == -1 || params.batchDelay < bestParams.batchDelay) {
+ bestParams.batchDelay = params.batchDelay;
+ }
+ if (bestParams.batchTimeout == -1 || params.batchTimeout < bestParams.batchTimeout) {
+ bestParams.batchTimeout = params.batchTimeout;
+ }
+ }
+ bestBatchParams = bestParams;
+}
+
+ssize_t SensorDevice::Info::removeBatchParamsForIdent(void* ident) {
+ ssize_t idx = batchParams.removeItem(ident);
+ if (idx >= 0) {
+ selectBatchParams();
+ }
+ return idx;
+}
+
+void SensorDevice::notifyConnectionDestroyed(void* ident) {
+ Mutex::Autolock _l(mLock);
+ mDisabledClients.remove(ident);
+}
+
+void SensorDevice::convertToSensorEvent(
+ const Event &src, sensors_event_t *dst) {
+ ::android::hardware::sensors::V1_0::implementation::convertToSensorEvent(
+ src, dst);
+
+ if (src.sensorType == SensorType::SENSOR_TYPE_DYNAMIC_SENSOR_META) {
+ const DynamicSensorInfo &dyn = src.u.dynamic;
+
+ dst->dynamic_sensor_meta.connected = dyn.connected;
+ dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
+ if (dyn.connected) {
+ auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
+ CHECK(it != mConnectedDynamicSensors.end());
+
+ dst->dynamic_sensor_meta.sensor = it->second;
+
+ memcpy(dst->dynamic_sensor_meta.uuid,
+ dyn.uuid.data(),
+ sizeof(dst->dynamic_sensor_meta.uuid));
+ }
+ }
+}
+
+void SensorDevice::convertToSensorEvents(
+ const hidl_vec<Event> &src,
+ const hidl_vec<SensorInfo> &dynamicSensorsAdded,
+ sensors_event_t *dst) {
+ // Allocate a sensor_t structure for each dynamic sensor added and insert
+ // it into the dictionary of connected dynamic sensors keyed by handle.
+ for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
+ const SensorInfo &info = dynamicSensorsAdded[i];
+
+ auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+ CHECK(it == mConnectedDynamicSensors.end());
+
+ sensor_t *sensor = new sensor_t;
+ convertToSensor(info, sensor);
+
+ mConnectedDynamicSensors.insert(
+ std::make_pair(sensor->handle, sensor));
+ }
+
+ for (size_t i = 0; i < src.size(); ++i) {
+ convertToSensorEvent(src[i], &dst[i]);
+ }
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 170faa8..f695edb 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -18,7 +18,9 @@
MessageQueue.cpp \
MonitoredProducer.cpp \
SurfaceFlingerConsumer.cpp \
+ SurfaceInterceptor.cpp \
Transform.cpp \
+ DisplayHardware/ComposerHal.cpp \
DisplayHardware/FramebufferSurface.cpp \
DisplayHardware/HWC2.cpp \
DisplayHardware/HWC2On1Adapter.cpp \
@@ -34,13 +36,12 @@
RenderEngine/GLExtensions.cpp \
RenderEngine/RenderEngine.cpp \
RenderEngine/Texture.cpp \
- RenderEngine/GLES10RenderEngine.cpp \
- RenderEngine/GLES11RenderEngine.cpp \
- RenderEngine/GLES20RenderEngine.cpp
+ RenderEngine/GLES20RenderEngine.cpp \
+LOCAL_MODULE := libsurfaceflinger
LOCAL_C_INCLUDES := \
- frameworks/native/vulkan/include \
- external/vulkan-validation-layers/libs/vkjson
+ frameworks/native/vulkan/include \
+ external/vulkan-validation-layers/libs/vkjson \
LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
@@ -125,12 +126,16 @@
LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
-LOCAL_STATIC_LIBRARIES := libvkjson
+LOCAL_STATIC_LIBRARIES := libtrace_proto libvkjson
LOCAL_SHARED_LIBRARIES := \
+ android.hardware.graphics.allocator@2.0 \
+ android.hardware.graphics.composer@2.1 \
libcutils \
liblog \
libdl \
libhardware \
+ libhidl \
+ libhwbinder \
libutils \
libEGL \
libGLESv1_CM \
@@ -139,9 +144,19 @@
libui \
libgui \
libpowermanager \
- libvulkan
+ libvulkan \
+ libprotobuf-cpp-full \
+ libhidl \
+ libhwbinder \
+ libbase \
+ libutils \
+ android.hardware.power@1.0
-LOCAL_MODULE := libsurfaceflinger
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+ android.hardware.graphics.allocator@2.0 \
+ android.hardware.graphics.composer@2.1 \
+ libhidl \
+ libhwbinder
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
@@ -179,6 +194,7 @@
libdl
LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_STATIC_LIBRARIES := libtrace_proto
LOCAL_MODULE := surfaceflinger
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 1a9820d..836fb89 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -64,7 +64,7 @@
class DispSyncThread: public Thread {
public:
- DispSyncThread(const char* name):
+ explicit DispSyncThread(const char* name):
mName(name),
mStop(false),
mPeriod(0),
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5c2c0ad..6c18ef7 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -613,3 +613,17 @@
mDisplaySurface->dumpAsString(surfaceDump);
result.append(surfaceDump);
}
+
+std::atomic<int32_t> DisplayDeviceState::nextDisplayId(1);
+
+DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure)
+ : type(type),
+ layerStack(DisplayDevice::NO_LAYER_STACK),
+ orientation(0),
+ width(0),
+ height(0),
+ isSecure(isSecure)
+{
+ viewport.makeInvalid();
+ frame.makeInvalid();
+}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 105e980..92ede08 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -263,6 +263,28 @@
#endif
};
+struct DisplayDeviceState {
+ DisplayDeviceState() = default;
+ DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
+
+ bool isValid() const { return type >= 0; }
+ bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
+ bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
+
+ static std::atomic<int32_t> nextDisplayId;
+ int32_t displayId = nextDisplayId++;
+ DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_ID_INVALID;
+ sp<IGraphicBufferProducer> surface;
+ uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
+ Rect viewport;
+ Rect frame;
+ uint8_t orientation = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ String8 displayName;
+ bool isSecure = false;
+};
+
}; // namespace android
#endif // ANDROID_DISPLAY_DEVICE_H
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
new file mode 100644
index 0000000..128e2b0
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -0,0 +1,650 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HwcComposer"
+
+#include <inttypes.h>
+#include <log/log.h>
+
+#include "ComposerHal.h"
+
+namespace android {
+
+using hardware::Return;
+using hardware::hidl_vec;
+
+namespace Hwc2 {
+
+namespace {
+
+class BufferHandle {
+public:
+ BufferHandle(const native_handle_t* buffer)
+ {
+ // nullptr is not a valid handle to HIDL
+ mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
+ }
+
+ operator const native_handle_t*() const
+ {
+ return mHandle;
+ }
+
+private:
+ NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 0, 0);
+ const native_handle_t* mHandle;
+};
+
+class FenceHandle
+{
+public:
+ FenceHandle(int fd, bool owned)
+ : mOwned(owned)
+ {
+ if (fd >= 0) {
+ mHandle = native_handle_init(mStorage, 1, 0);
+ mHandle->data[0] = fd;
+ } else {
+ // nullptr is not a valid handle to HIDL
+ mHandle = native_handle_init(mStorage, 0, 0);
+ }
+ }
+
+ ~FenceHandle()
+ {
+ if (mOwned) {
+ native_handle_close(mHandle);
+ }
+ }
+
+ operator const native_handle_t*() const
+ {
+ return mHandle;
+ }
+
+private:
+ bool mOwned;
+ NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 1, 0);
+ native_handle_t* mHandle;
+};
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+template<typename T, typename U>
+T unwrapRet(Return<T>& ret, const U& default_val)
+{
+ return (ret.getStatus().isOk()) ? static_cast<T>(ret) :
+ static_cast<T>(default_val);
+}
+
+Error unwrapRet(Return<Error>& ret)
+{
+ return unwrapRet(ret, kDefaultError);
+}
+
+template<typename T>
+void assignFromHidlVec(std::vector<T>& vec, const hidl_vec<T>& data)
+{
+ vec.clear();
+ vec.insert(vec.begin(), &data[0], &data[data.size()]);
+}
+
+} // anonymous namespace
+
+Composer::Composer()
+{
+ mService = IComposer::getService("hwcomposer");
+ if (mService == nullptr) {
+ LOG_ALWAYS_FATAL("failed to get hwcomposer service");
+ }
+}
+
+std::vector<IComposer::Capability> Composer::getCapabilities() const
+{
+ std::vector<IComposer::Capability> capabilities;
+ mService->getCapabilities(
+ [&](const auto& tmpCapabilities) {
+ assignFromHidlVec(capabilities, tmpCapabilities);
+ });
+
+ return capabilities;
+}
+
+std::string Composer::dumpDebugInfo() const
+{
+ std::string info;
+ mService->dumpDebugInfo([&](const auto& tmpInfo) {
+ info = tmpInfo.c_str();
+ });
+
+ return info;
+}
+
+void Composer::registerCallback(const sp<IComposerCallback>& callback) const
+{
+ auto ret = mService->registerCallback(callback);
+ if (!ret.getStatus().isOk()) {
+ ALOGE("failed to register IComposerCallback");
+ }
+}
+
+uint32_t Composer::getMaxVirtualDisplayCount() const
+{
+ auto ret = mService->getMaxVirtualDisplayCount();
+ return unwrapRet(ret, 0);
+}
+
+Error Composer::createVirtualDisplay(uint32_t width, uint32_t height,
+ PixelFormat& format, Display& display) const
+{
+ Error error = kDefaultError;
+ mService->createVirtualDisplay(width, height, format,
+ [&](const auto& tmpError, const auto& tmpDisplay,
+ const auto& tmpFormat) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ display = tmpDisplay;
+ format = tmpFormat;
+ });
+
+ return error;
+}
+
+Error Composer::destroyVirtualDisplay(Display display) const
+{
+ auto ret = mService->destroyVirtualDisplay(display);
+ return unwrapRet(ret);
+}
+
+Error Composer::acceptDisplayChanges(Display display) const
+{
+ auto ret = mService->acceptDisplayChanges(display);
+ return unwrapRet(ret);
+}
+
+Error Composer::createLayer(Display display, Layer& layer) const
+{
+ Error error = kDefaultError;
+ mService->createLayer(display,
+ [&](const auto& tmpError, const auto& tmpLayer) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ layer = tmpLayer;
+ });
+
+ return error;
+}
+
+Error Composer::destroyLayer(Display display, Layer layer) const
+{
+ auto ret = mService->destroyLayer(display, layer);
+ return unwrapRet(ret);
+}
+
+Error Composer::getActiveConfig(Display display, Config& config) const
+{
+ Error error = kDefaultError;
+ mService->getActiveConfig(display,
+ [&](const auto& tmpError, const auto& tmpConfig) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ config = tmpConfig;
+ });
+
+ return error;
+}
+
+Error Composer::getChangedCompositionTypes(Display display,
+ std::vector<Layer>& layers,
+ std::vector<IComposer::Composition>& types) const
+{
+ Error error = kDefaultError;
+ mService->getChangedCompositionTypes(display,
+ [&](const auto& tmpError, const auto& tmpLayers,
+ const auto& tmpTypes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ assignFromHidlVec(layers, tmpLayers);
+ assignFromHidlVec(types, tmpTypes);
+ });
+
+ return error;
+}
+
+Error Composer::getColorModes(Display display,
+ std::vector<ColorMode>& modes) const
+{
+ Error error = kDefaultError;
+ mService->getColorModes(display,
+ [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ assignFromHidlVec(modes, tmpModes);
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayAttribute(Display display, Config config,
+ IComposer::Attribute attribute, int32_t& value) const
+{
+ Error error = kDefaultError;
+ mService->getDisplayAttribute(display, config, attribute,
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ value = tmpValue;
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayConfigs(Display display,
+ std::vector<Config>& configs) const
+{
+ Error error = kDefaultError;
+ mService->getDisplayConfigs(display,
+ [&](const auto& tmpError, const auto& tmpConfigs) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ assignFromHidlVec(configs, tmpConfigs);
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayName(Display display, std::string& name) const
+{
+ Error error = kDefaultError;
+ mService->getDisplayName(display,
+ [&](const auto& tmpError, const auto& tmpName) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ name = tmpName.c_str();
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayRequests(Display display,
+ uint32_t& displayRequestMask, std::vector<Layer>& layers,
+ std::vector<uint32_t>& layerRequestMasks) const
+{
+ Error error = kDefaultError;
+ mService->getDisplayRequests(display,
+ [&](const auto& tmpError, const auto& tmpDisplayRequestMask,
+ const auto& tmpLayers, const auto& tmpLayerRequestMasks) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ displayRequestMask = tmpDisplayRequestMask;
+ assignFromHidlVec(layers, tmpLayers);
+ assignFromHidlVec(layerRequestMasks, tmpLayerRequestMasks);
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayType(Display display, IComposer::DisplayType& type) const
+{
+ Error error = kDefaultError;
+ mService->getDisplayType(display,
+ [&](const auto& tmpError, const auto& tmpType) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ type = tmpType;
+ });
+
+ return error;
+}
+
+Error Composer::getDozeSupport(Display display, bool& support) const
+{
+ Error error = kDefaultError;
+ mService->getDozeSupport(display,
+ [&](const auto& tmpError, const auto& tmpSupport) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ support = tmpSupport;
+ });
+
+ return error;
+}
+
+Error Composer::getHdrCapabilities(Display display, std::vector<Hdr>& types,
+ float& maxLuminance, float& maxAverageLuminance,
+ float& minLuminance) const
+{
+ Error error = kDefaultError;
+ mService->getHdrCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpTypes,
+ const auto& tmpMaxLuminance,
+ const auto& tmpMaxAverageLuminance,
+ const auto& tmpMinLuminance) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ assignFromHidlVec(types, tmpTypes);
+ maxLuminance = tmpMaxLuminance;
+ maxAverageLuminance = tmpMaxAverageLuminance;
+ minLuminance = tmpMinLuminance;
+ });
+
+ return error;
+}
+
+Error Composer::getReleaseFences(Display display, std::vector<Layer>& layers,
+ std::vector<int>& releaseFences) const
+{
+ Error error = kDefaultError;
+ mService->getReleaseFences(display,
+ [&](const auto& tmpError, const auto& tmpLayers,
+ const auto& tmpReleaseFences) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ if (static_cast<int>(tmpLayers.size()) !=
+ tmpReleaseFences->numFds) {
+ ALOGE("invalid releaseFences outputs: "
+ "layer count %zu != fence count %d",
+ tmpLayers.size(), tmpReleaseFences->numFds);
+ error = Error::NO_RESOURCES;
+ return;
+ }
+
+ // dup the file descriptors
+ std::vector<int> tmpFds;
+ tmpFds.reserve(tmpReleaseFences->numFds);
+ for (int i = 0; i < tmpReleaseFences->numFds; i++) {
+ int fd = dup(tmpReleaseFences->data[i]);
+ if (fd < 0) {
+ break;
+ }
+ tmpFds.push_back(fd);
+ }
+ if (static_cast<int>(tmpFds.size()) <
+ tmpReleaseFences->numFds) {
+ for (auto fd : tmpFds) {
+ close(fd);
+ }
+
+ error = Error::NO_RESOURCES;
+ return;
+ }
+
+ assignFromHidlVec(layers, tmpLayers);
+ releaseFences = std::move(tmpFds);
+ });
+
+ return error;
+}
+
+Error Composer::presentDisplay(Display display, int& presentFence) const
+{
+ Error error = kDefaultError;
+ mService->presentDisplay(display,
+ [&](const auto& tmpError, const auto& tmpPresentFence) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ if (tmpPresentFence->numFds == 1) {
+ int fd = dup(tmpPresentFence->data[0]);
+ if (fd >= 0) {
+ presentFence = fd;
+ } else {
+ error = Error::NO_RESOURCES;
+ }
+ } else {
+ presentFence = -1;
+ }
+ });
+
+ return error;
+}
+
+Error Composer::setActiveConfig(Display display, Config config) const
+{
+ auto ret = mService->setActiveConfig(display, config);
+ return unwrapRet(ret);
+}
+
+Error Composer::setClientTarget(Display display, const native_handle_t* target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposer::Rect>& damage) const
+{
+ BufferHandle tmpTarget(target);
+ FenceHandle tmpAcquireFence(acquireFence, true);
+
+ hidl_vec<IComposer::Rect> tmpDamage;
+ tmpDamage.setToExternal(const_cast<IComposer::Rect*>(damage.data()),
+ damage.size());
+
+ auto ret = mService->setClientTarget(display, tmpTarget,
+ tmpAcquireFence, dataspace, tmpDamage);
+ return unwrapRet(ret);
+}
+
+Error Composer::setColorMode(Display display, ColorMode mode) const
+{
+ auto ret = mService->setColorMode(display, mode);
+ return unwrapRet(ret);
+}
+
+Error Composer::setColorTransform(Display display, const float* matrix,
+ ColorTransform hint) const
+{
+ hidl_vec<float> tmpMatrix;
+ tmpMatrix.setToExternal(const_cast<float*>(matrix), 16);
+
+ auto ret = mService->setColorTransform(display, tmpMatrix, hint);
+ return unwrapRet(ret);
+}
+
+Error Composer::setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence) const
+{
+ BufferHandle tmpBuffer(buffer);
+ FenceHandle tmpReleaseFence(releaseFence, false);
+
+ auto ret = mService->setOutputBuffer(display, tmpBuffer, tmpReleaseFence);
+ return unwrapRet(ret);
+}
+
+Error Composer::setPowerMode(Display display, IComposer::PowerMode mode) const
+{
+ auto ret = mService->setPowerMode(display, mode);
+ return unwrapRet(ret);
+}
+
+Error Composer::setVsyncEnabled(Display display, IComposer::Vsync enabled) const
+{
+ auto ret = mService->setVsyncEnabled(display, enabled);
+ return unwrapRet(ret);
+}
+
+Error Composer::validateDisplay(Display display, uint32_t& numTypes, uint32_t&
+ numRequests) const
+{
+ Error error = kDefaultError;
+ mService->validateDisplay(display,
+ [&](const auto& tmpError, const auto& tmpNumTypes,
+ const auto& tmpNumRequests) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ numTypes = tmpNumTypes;
+ numRequests = tmpNumRequests;
+ });
+
+ return error;
+}
+
+Error Composer::setCursorPosition(Display display, Layer layer,
+ int32_t x, int32_t y) const
+{
+ auto ret = mService->setCursorPosition(display, layer, x, y);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerBuffer(Display display, Layer layer,
+ const native_handle_t* buffer, int acquireFence) const
+{
+ BufferHandle tmpBuffer(buffer);
+ FenceHandle tmpAcquireFence(acquireFence, true);
+
+ auto ret = mService->setLayerBuffer(display, layer,
+ tmpBuffer, tmpAcquireFence);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposer::Rect>& damage) const
+{
+ hidl_vec<IComposer::Rect> tmpDamage;
+ tmpDamage.setToExternal(const_cast<IComposer::Rect*>(damage.data()),
+ damage.size());
+
+ auto ret = mService->setLayerSurfaceDamage(display, layer, tmpDamage);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerBlendMode(Display display, Layer layer,
+ IComposer::BlendMode mode) const
+{
+ auto ret = mService->setLayerBlendMode(display, layer, mode);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerColor(Display display, Layer layer,
+ const IComposer::Color& color) const
+{
+ auto ret = mService->setLayerColor(display, layer, color);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerCompositionType(Display display, Layer layer,
+ IComposer::Composition type) const
+{
+ auto ret = mService->setLayerCompositionType(display, layer, type);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerDataspace(Display display, Layer layer,
+ Dataspace dataspace) const
+{
+ auto ret = mService->setLayerDataspace(display, layer, dataspace);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerDisplayFrame(Display display, Layer layer,
+ const IComposer::Rect& frame) const
+{
+ auto ret = mService->setLayerDisplayFrame(display, layer, frame);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerPlaneAlpha(Display display, Layer layer,
+ float alpha) const
+{
+ auto ret = mService->setLayerPlaneAlpha(display, layer, alpha);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream) const
+{
+ BufferHandle tmpStream(stream);
+
+ auto ret = mService->setLayerSidebandStream(display, layer, tmpStream);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerSourceCrop(Display display, Layer layer,
+ const IComposer::FRect& crop) const
+{
+ auto ret = mService->setLayerSourceCrop(display, layer, crop);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerTransform(Display display, Layer layer,
+ Transform transform) const
+{
+ auto ret = mService->setLayerTransform(display, layer, transform);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposer::Rect>& visible) const
+{
+ hidl_vec<IComposer::Rect> tmpVisible;
+ tmpVisible.setToExternal(const_cast<IComposer::Rect*>(visible.data()),
+ visible.size());
+
+ auto ret = mService->setLayerVisibleRegion(display, layer, tmpVisible);
+ return unwrapRet(ret);
+}
+
+Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z) const
+{
+ auto ret = mService->setLayerZOrder(display, layer, z);
+ return unwrapRet(ret);
+}
+
+} // namespace Hwc2
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
new file mode 100644
index 0000000..fd74a92
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -0,0 +1,142 @@
+/*
+ * 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_SF_COMPOSER_HAL_H
+#define ANDROID_SF_COMPOSER_HAL_H
+
+#include <string>
+#include <vector>
+
+#include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace Hwc2 {
+
+using android::hardware::graphics::allocator::V2_0::PixelFormat;
+
+using android::hardware::graphics::composer::V2_1::IComposer;
+using android::hardware::graphics::composer::V2_1::IComposerCallback;
+using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_1::Display;
+using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_1::Config;
+
+using android::hardware::graphics::composer::V2_1::ColorMode;
+using android::hardware::graphics::composer::V2_1::Hdr;
+using android::hardware::graphics::composer::V2_1::Dataspace;
+using android::hardware::graphics::composer::V2_1::ColorTransform;
+using android::hardware::graphics::composer::V2_1::Transform;
+
+// Composer is a wrapper to IComposer, a proxy to server-side composer.
+class Composer {
+public:
+ Composer();
+
+ std::vector<IComposer::Capability> getCapabilities() const;
+ std::string dumpDebugInfo() const;
+
+ void registerCallback(const sp<IComposerCallback>& callback) const;
+
+ uint32_t getMaxVirtualDisplayCount() const;
+ Error createVirtualDisplay(uint32_t width, uint32_t height,
+ PixelFormat& format, Display& display) const;
+ Error destroyVirtualDisplay(Display display) const;
+
+ Error acceptDisplayChanges(Display display) const;
+
+ Error createLayer(Display display, Layer& layer) const;
+ Error destroyLayer(Display display, Layer layer) const;
+
+ Error getActiveConfig(Display display, Config& config) const;
+ Error getChangedCompositionTypes(Display display,
+ std::vector<Layer>& layers,
+ std::vector<IComposer::Composition>& types) const;
+ Error getColorModes(Display display, std::vector<ColorMode>& modes) const;
+ Error getDisplayAttribute(Display display, Config config,
+ IComposer::Attribute attribute, int32_t& value) const;
+ Error getDisplayConfigs(Display display,
+ std::vector<Config>& configs) const;
+ Error getDisplayName(Display display, std::string& name) const;
+
+ Error getDisplayRequests(Display display, uint32_t& displayRequestMask,
+ std::vector<Layer>& layers,
+ std::vector<uint32_t>& layerRequestMasks) const;
+
+ Error getDisplayType(Display display, IComposer::DisplayType& type) const;
+ Error getDozeSupport(Display display, bool& support) const;
+ Error getHdrCapabilities(Display display, std::vector<Hdr>& types,
+ float& maxLuminance, float& maxAverageLuminance,
+ float& minLuminance) const;
+
+ Error getReleaseFences(Display display, std::vector<Layer>& layers,
+ std::vector<int>& releaseFences) const;
+
+ Error presentDisplay(Display display, int& presentFence) const;
+
+ Error setActiveConfig(Display display, Config config) const;
+ Error setClientTarget(Display display, const native_handle_t* target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposer::Rect>& damage) const;
+ Error setColorMode(Display display, ColorMode mode) const;
+ Error setColorTransform(Display display, const float* matrix,
+ ColorTransform hint) const;
+ Error setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence) const;
+ Error setPowerMode(Display display, IComposer::PowerMode mode) const;
+ Error setVsyncEnabled(Display display, IComposer::Vsync enabled) const;
+
+ Error validateDisplay(Display display, uint32_t& numTypes,
+ uint32_t& numRequests) const;
+
+ Error setCursorPosition(Display display, Layer layer,
+ int32_t x, int32_t y) const;
+ Error setLayerBuffer(Display display, Layer layer,
+ const native_handle_t* buffer, int acquireFence) const;
+ Error setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposer::Rect>& damage) const;
+ Error setLayerBlendMode(Display display, Layer layer,
+ IComposer::BlendMode mode) const;
+ Error setLayerColor(Display display, Layer layer,
+ const IComposer::Color& color) const;
+ Error setLayerCompositionType(Display display, Layer layer,
+ IComposer::Composition type) const;
+ Error setLayerDataspace(Display display, Layer layer,
+ Dataspace dataspace) const;
+ Error setLayerDisplayFrame(Display display, Layer layer,
+ const IComposer::Rect& frame) const;
+ Error setLayerPlaneAlpha(Display display, Layer layer,
+ float alpha) const;
+ Error setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream) const;
+ Error setLayerSourceCrop(Display display, Layer layer,
+ const IComposer::FRect& crop) const;
+ Error setLayerTransform(Display display, Layer layer,
+ Transform transform) const;
+ Error setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposer::Rect>& visible) const;
+ Error setLayerZOrder(Display display, Layer layer, uint32_t z) const;
+
+private:
+ sp<IComposer> mService;
+};
+
+} // namespace Hwc2
+
+} // namespace android
+
+#endif // ANDROID_SF_COMPOSER_HAL_H
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 4fe3cfd..c79caf4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -21,6 +21,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "HWC2.h"
+#include "ComposerHal.h"
#include "FloatRect.h"
@@ -79,11 +80,16 @@
using android::Rect;
using android::Region;
using android::sp;
+using android::hardware::Return;
+using android::hardware::Void;
namespace HWC2 {
+namespace Hwc2 = android::Hwc2;
+
// Device methods
+#ifdef BYPASS_IHWC
Device::Device(hwc2_device_t* device)
: mHwcDevice(device),
mCreateVirtualDisplay(nullptr),
@@ -128,6 +134,10 @@
mSetLayerTransform(nullptr),
mSetLayerVisibleRegion(nullptr),
mSetLayerZOrder(nullptr),
+#else
+Device::Device()
+ : mComposer(std::make_unique<Hwc2::Composer>()),
+#endif // BYPASS_IHWC
mCapabilities(),
mDisplays(),
mHotplug(),
@@ -144,9 +154,11 @@
Device::~Device()
{
+#ifdef BYPASS_IHWC
if (mHwcDevice == nullptr) {
return;
}
+#endif
for (auto element : mDisplays) {
auto display = element.second.lock();
@@ -175,13 +187,16 @@
}
}
+#ifdef BYPASS_IHWC
hwc2_close(mHwcDevice);
+#endif
}
// Required by HWC2 device
std::string Device::dump() const
{
+#ifdef BYPASS_IHWC
uint32_t numBytes = 0;
mDump(mHwcDevice, &numBytes, nullptr);
@@ -189,11 +204,18 @@
mDump(mHwcDevice, &numBytes, buffer.data());
return std::string(buffer.data(), buffer.size());
+#else
+ return mComposer->dumpDebugInfo();
+#endif
}
uint32_t Device::getMaxVirtualDisplayCount() const
{
+#ifdef BYPASS_IHWC
return mGetMaxVirtualDisplayCount(mHwcDevice);
+#else
+ return mComposer->getMaxVirtualDisplayCount();
+#endif
}
Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
@@ -202,9 +224,15 @@
ALOGI("Creating virtual display");
hwc2_display_t displayId = 0;
+#ifdef BYPASS_IHWC
int32_t intFormat = static_cast<int32_t>(*format);
int32_t intError = mCreateVirtualDisplay(mHwcDevice, width, height,
&intFormat, &displayId);
+#else
+ auto intFormat = static_cast<Hwc2::PixelFormat>(*format);
+ auto intError = mComposer->createVirtualDisplay(width, height,
+ intFormat, displayId);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -315,6 +343,7 @@
{
static_assert(sizeof(Capability) == sizeof(int32_t),
"Capability size has changed");
+#ifdef BYPASS_IHWC
uint32_t numCapabilities = 0;
mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
std::vector<Capability> capabilities(numCapabilities);
@@ -323,6 +352,12 @@
for (auto capability : capabilities) {
mCapabilities.emplace(capability);
}
+#else
+ auto capabilities = mComposer->getCapabilities();
+ for (auto capability : capabilities) {
+ mCapabilities.emplace(static_cast<Capability>(capability));
+ }
+#endif
}
bool Device::hasCapability(HWC2::Capability capability) const
@@ -333,6 +368,7 @@
void Device::loadFunctionPointers()
{
+#ifdef BYPASS_IHWC
// For all of these early returns, we log an error message inside
// loadFunctionPointer specifying which function failed to load
@@ -426,13 +462,48 @@
mSetLayerVisibleRegion)) return;
if (!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
mSetLayerZOrder)) return;
+#endif // BYPASS_IHWC
}
+namespace {
+class ComposerCallback : public Hwc2::IComposerCallback {
+public:
+ ComposerCallback(Device* device) : mDevice(device) {}
+
+ Return<void> onHotplug(Hwc2::Display display,
+ Connection connected) override
+ {
+ hotplug_hook(mDevice, display, static_cast<int32_t>(connected));
+ return Void();
+ }
+
+ Return<void> onRefresh(Hwc2::Display display) override
+ {
+ refresh_hook(mDevice, display);
+ return Void();
+ }
+
+ Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
+ {
+ vsync_hook(mDevice, display, timestamp);
+ return Void();
+ }
+
+private:
+ Device* mDevice;
+};
+} // namespace anonymous
+
void Device::registerCallbacks()
{
+#ifdef BYPASS_IHWC
registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
+#else
+ sp<ComposerCallback> callback = new ComposerCallback(this);
+ mComposer->registerCallback(callback);
+#endif
}
@@ -441,7 +512,11 @@
void Device::destroyVirtualDisplay(hwc2_display_t display)
{
ALOGI("Destroying virtual display");
+#ifdef BYPASS_IHWC
int32_t intError = mDestroyVirtualDisplay(mHwcDevice, display);
+#else
+ auto intError = mComposer->destroyVirtualDisplay(display);
+#endif
auto error = static_cast<Error>(intError);
ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:"
" %s (%d)", display, to_string(error).c_str(), intError);
@@ -498,14 +573,22 @@
Error Display::acceptChanges()
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mAcceptDisplayChanges(mDevice.mHwcDevice, mId);
+#else
+ auto intError = mDevice.mComposer->acceptDisplayChanges(mId);
+#endif
return static_cast<Error>(intError);
}
Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
{
hwc2_layer_t layerId = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
+#else
+ auto intError = mDevice.mComposer->createLayer(mId, layerId);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -522,8 +605,12 @@
{
ALOGV("[%" PRIu64 "] getActiveConfig", mId);
hwc2_config_t configId = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mGetActiveConfig(mDevice.mHwcDevice, mId,
&configId);
+#else
+ auto intError = mDevice.mComposer->getActiveConfig(mId, configId);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
@@ -546,6 +633,7 @@
Error Display::getChangedCompositionTypes(
std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes)
{
+#ifdef BYPASS_IHWC
uint32_t numElements = 0;
int32_t intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice,
mId, &numElements, nullptr, nullptr);
@@ -558,6 +646,14 @@
std::vector<int32_t> types(numElements);
intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice, mId,
&numElements, layerIds.data(), types.data());
+#else
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<Hwc2::IComposer::Composition> types;
+ auto intError = mDevice.mComposer->getChangedCompositionTypes(mId,
+ layerIds, types);
+ uint32_t numElements = layerIds.size();
+ auto error = static_cast<Error>(intError);
+#endif
error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -583,6 +679,7 @@
Error Display::getColorModes(std::vector<android_color_mode_t>* outModes) const
{
+#ifdef BYPASS_IHWC
uint32_t numModes = 0;
int32_t intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId,
&numModes, nullptr);
@@ -595,6 +692,12 @@
intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId, &numModes,
modes.data());
error = static_cast<Error>(intError);
+#else
+ std::vector<Hwc2::ColorMode> modes;
+ auto intError = mDevice.mComposer->getColorModes(mId, modes);
+ uint32_t numModes = modes.size();
+ auto error = static_cast<Error>(intError);
+#endif
if (error != Error::None) {
return error;
}
@@ -617,6 +720,7 @@
Error Display::getName(std::string* outName) const
{
+#ifdef BYPASS_IHWC
uint32_t size;
int32_t intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size,
nullptr);
@@ -635,12 +739,17 @@
*outName = std::string(rawName.cbegin(), rawName.cend());
return Error::None;
+#else
+ auto intError = mDevice.mComposer->getDisplayName(mId, *outName);
+ return static_cast<Error>(intError);
+#endif
}
Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
std::unordered_map<std::shared_ptr<Layer>, LayerRequest>*
outLayerRequests)
{
+#ifdef BYPASS_IHWC
int32_t intDisplayRequests = 0;
uint32_t numElements = 0;
int32_t intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId,
@@ -656,6 +765,15 @@
&intDisplayRequests, &numElements, layerIds.data(),
layerRequests.data());
error = static_cast<Error>(intError);
+#else
+ uint32_t intDisplayRequests;
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<uint32_t> layerRequests;
+ auto intError = mDevice.mComposer->getDisplayRequests(mId,
+ intDisplayRequests, layerIds, layerRequests);
+ uint32_t numElements = layerIds.size();
+ auto error = static_cast<Error>(intError);
+#endif
if (error != Error::None) {
return error;
}
@@ -680,9 +798,15 @@
Error Display::getType(DisplayType* outType) const
{
+#ifdef BYPASS_IHWC
int32_t intType = 0;
int32_t intError = mDevice.mGetDisplayType(mDevice.mHwcDevice, mId,
&intType);
+#else
+ Hwc2::IComposer::DisplayType intType =
+ Hwc2::IComposer::DisplayType::INVALID;
+ auto intError = mDevice.mComposer->getDisplayType(mId, intType);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -694,9 +818,14 @@
Error Display::supportsDoze(bool* outSupport) const
{
+#ifdef BYPASS_IHWC
int32_t intSupport = 0;
int32_t intError = mDevice.mGetDozeSupport(mDevice.mHwcDevice, mId,
&intSupport);
+#else
+ bool intSupport = false;
+ auto intError = mDevice.mComposer->getDozeSupport(mId, intSupport);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -712,6 +841,7 @@
float maxLuminance = -1.0f;
float maxAverageLuminance = -1.0f;
float minLuminance = -1.0f;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId,
&numTypes, nullptr, &maxLuminance, &maxAverageLuminance,
&minLuminance);
@@ -724,6 +854,18 @@
intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId, &numTypes,
types.data(), &maxLuminance, &maxAverageLuminance, &minLuminance);
error = static_cast<HWC2::Error>(intError);
+#else
+ std::vector<Hwc2::Hdr> intTypes;
+ auto intError = mDevice.mComposer->getHdrCapabilities(mId, intTypes,
+ maxLuminance, maxAverageLuminance, minLuminance);
+ auto error = static_cast<HWC2::Error>(intError);
+
+ std::vector<int32_t> types;
+ for (auto type : intTypes) {
+ types.push_back(static_cast<int32_t>(type));
+ }
+ numTypes = types.size();
+#endif
if (error != Error::None) {
return error;
}
@@ -736,6 +878,7 @@
Error Display::getReleaseFences(
std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const
{
+#ifdef BYPASS_IHWC
uint32_t numElements = 0;
int32_t intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId,
&numElements, nullptr, nullptr);
@@ -749,6 +892,14 @@
intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId, &numElements,
layerIds.data(), fenceFds.data());
error = static_cast<Error>(intError);
+#else
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<int> fenceFds;
+ auto intError = mDevice.mComposer->getReleaseFences(mId,
+ layerIds, fenceFds);
+ auto error = static_cast<Error>(intError);
+ uint32_t numElements = layerIds.size();
+#endif
if (error != Error::None) {
return error;
}
@@ -774,8 +925,12 @@
Error Display::present(sp<Fence>* outRetireFence)
{
int32_t retireFenceFd = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
&retireFenceFd);
+#else
+ auto intError = mDevice.mComposer->presentDisplay(mId, retireFenceFd);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -793,8 +948,12 @@
config->getDisplayId(), mId);
return Error::BadConfig;
}
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetActiveConfig(mDevice.mHwcDevice, mId,
config->getId());
+#else
+ auto intError = mDevice.mComposer->setActiveConfig(mId, config->getId());
+#endif
return static_cast<Error>(intError);
}
@@ -803,22 +962,38 @@
{
// TODO: Properly encode client target surface damage
int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target,
fenceFd, static_cast<int32_t>(dataspace), {0, nullptr});
+#else
+ auto intError = mDevice.mComposer->setClientTarget(mId, target, fenceFd,
+ static_cast<Hwc2::Dataspace>(dataspace),
+ std::vector<Hwc2::IComposer::Rect>());
+#endif
return static_cast<Error>(intError);
}
Error Display::setColorMode(android_color_mode_t mode)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode);
+#else
+ auto intError = mDevice.mComposer->setColorMode(mId,
+ static_cast<Hwc2::ColorMode>(mode));
+#endif
return static_cast<Error>(intError);
}
Error Display::setColorTransform(const android::mat4& matrix,
android_color_transform_t hint)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetColorTransform(mDevice.mHwcDevice, mId,
matrix.asArray(), static_cast<int32_t>(hint));
+#else
+ auto intError = mDevice.mComposer->setColorTransform(mId,
+ matrix.asArray(), static_cast<Hwc2::ColorTransform>(hint));
+#endif
return static_cast<Error>(intError);
}
@@ -827,24 +1002,38 @@
{
int32_t fenceFd = releaseFence->dup();
auto handle = buffer->getNativeBuffer()->handle;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle,
fenceFd);
+#else
+ auto intError = mDevice.mComposer->setOutputBuffer(mId, handle, fenceFd);
+#endif
close(fenceFd);
return static_cast<Error>(intError);
}
Error Display::setPowerMode(PowerMode mode)
{
+#ifdef BYPASS_IHWC
auto intMode = static_cast<int32_t>(mode);
int32_t intError = mDevice.mSetPowerMode(mDevice.mHwcDevice, mId, intMode);
+#else
+ auto intMode = static_cast<Hwc2::IComposer::PowerMode>(mode);
+ auto intError = mDevice.mComposer->setPowerMode(mId, intMode);
+#endif
return static_cast<Error>(intError);
}
Error Display::setVsyncEnabled(Vsync enabled)
{
+#ifdef BYPASS_IHWC
auto intEnabled = static_cast<int32_t>(enabled);
int32_t intError = mDevice.mSetVsyncEnabled(mDevice.mHwcDevice, mId,
intEnabled);
+#else
+ auto intEnabled = static_cast<Hwc2::IComposer::Vsync>(enabled);
+ auto intError = mDevice.mComposer->setVsyncEnabled(mId, intEnabled);
+#endif
return static_cast<Error>(intError);
}
@@ -852,8 +1041,13 @@
{
uint32_t numTypes = 0;
uint32_t numRequests = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mValidateDisplay(mDevice.mHwcDevice, mId,
&numTypes, &numRequests);
+#else
+ auto intError = mDevice.mComposer->validateDisplay(mId,
+ numTypes, numRequests);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None && error != Error::HasChanges) {
return error;
@@ -869,8 +1063,14 @@
int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
{
int32_t value = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mGetDisplayAttribute(mDevice.mHwcDevice, mId,
configId, static_cast<int32_t>(attribute), &value);
+#else
+ auto intError = mDevice.mComposer->getDisplayAttribute(mId,
+ configId, static_cast<Hwc2::IComposer::Attribute>(attribute),
+ value);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
@@ -899,6 +1099,7 @@
{
ALOGV("[%" PRIu64 "] loadConfigs", mId);
+#ifdef BYPASS_IHWC
uint32_t numConfigs = 0;
int32_t intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId,
&numConfigs, nullptr);
@@ -913,6 +1114,11 @@
intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId, &numConfigs,
configIds.data());
error = static_cast<Error>(intError);
+#else
+ std::vector<Hwc2::Config> configIds;
+ auto intError = mDevice.mComposer->getDisplayConfigs(mId, configIds);
+ auto error = static_cast<Error>(intError);
+#endif
if (error != Error::None) {
ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
to_string(error).c_str(), intError);
@@ -928,7 +1134,11 @@
void Display::destroyLayer(hwc2_layer_t layerId)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mDestroyLayer(mDevice.mHwcDevice, mId, layerId);
+#else
+ auto intError =mDevice.mComposer->destroyLayer(mId, layerId);
+#endif
auto error = static_cast<Error>(intError);
ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")"
" failed: %s (%d)", mId, layerId, to_string(error).c_str(),
@@ -970,8 +1180,13 @@
Error Layer::setCursorPosition(int32_t x, int32_t y)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetCursorPosition(mDevice.mHwcDevice,
mDisplayId, mId, x, y);
+#else
+ auto intError = mDevice.mComposer->setCursorPosition(mDisplayId,
+ mId, x, y);
+#endif
return static_cast<Error>(intError);
}
@@ -979,8 +1194,13 @@
const sp<Fence>& acquireFence)
{
int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
mId, buffer, fenceFd);
+#else
+ auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId,
+ mId, buffer, fenceFd);
+#endif
return static_cast<Error>(intError);
}
@@ -988,26 +1208,44 @@
{
// We encode default full-screen damage as INVALID_RECT upstream, but as 0
// rects for HWC
+#ifdef BYPASS_IHWC
int32_t intError = 0;
+#else
+ Hwc2::Error intError = Hwc2::Error::NONE;
+#endif
if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) {
+#ifdef BYPASS_IHWC
intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
mDisplayId, mId, {0, nullptr});
+#else
+ intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+ mId, std::vector<Hwc2::IComposer::Rect>());
+#endif
} else {
size_t rectCount = 0;
auto rectArray = damage.getArray(&rectCount);
+#ifdef BYPASS_IHWC
std::vector<hwc_rect_t> hwcRects;
+#else
+ std::vector<Hwc2::IComposer::Rect> hwcRects;
+#endif
for (size_t rect = 0; rect < rectCount; ++rect) {
hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
rectArray[rect].right, rectArray[rect].bottom});
}
+#ifdef BYPASS_IHWC
hwc_region_t hwcRegion = {};
hwcRegion.numRects = rectCount;
hwcRegion.rects = hwcRects.data();
intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
mDisplayId, mId, hwcRegion);
+#else
+ intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+ mId, hwcRects);
+#endif
}
return static_cast<Error>(intError);
@@ -1015,47 +1253,83 @@
Error Layer::setBlendMode(BlendMode mode)
{
+#ifdef BYPASS_IHWC
auto intMode = static_cast<int32_t>(mode);
int32_t intError = mDevice.mSetLayerBlendMode(mDevice.mHwcDevice,
mDisplayId, mId, intMode);
+#else
+ auto intMode = static_cast<Hwc2::IComposer::BlendMode>(mode);
+ auto intError = mDevice.mComposer->setLayerBlendMode(mDisplayId,
+ mId, intMode);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setColor(hwc_color_t color)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerColor(mDevice.mHwcDevice, mDisplayId,
mId, color);
+#else
+ Hwc2::IComposer::Color hwcColor{color.r, color.g, color.b, color.a};
+ auto intError = mDevice.mComposer->setLayerColor(mDisplayId,
+ mId, hwcColor);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setCompositionType(Composition type)
{
+#ifdef BYPASS_IHWC
auto intType = static_cast<int32_t>(type);
int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,
mDisplayId, mId, intType);
+#else
+ auto intType = static_cast<Hwc2::IComposer::Composition>(type);
+ auto intError = mDevice.mComposer->setLayerCompositionType(mDisplayId,
+ mId, intType);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setDataspace(android_dataspace_t dataspace)
{
+#ifdef BYPASS_IHWC
auto intDataspace = static_cast<int32_t>(dataspace);
int32_t intError = mDevice.mSetLayerDataspace(mDevice.mHwcDevice,
mDisplayId, mId, intDataspace);
+#else
+ auto intDataspace = static_cast<Hwc2::Dataspace>(dataspace);
+ auto intError = mDevice.mComposer->setLayerDataspace(mDisplayId,
+ mId, intDataspace);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setDisplayFrame(const Rect& frame)
{
+#ifdef BYPASS_IHWC
hwc_rect_t hwcRect{frame.left, frame.top, frame.right, frame.bottom};
int32_t intError = mDevice.mSetLayerDisplayFrame(mDevice.mHwcDevice,
mDisplayId, mId, hwcRect);
+#else
+ Hwc2::IComposer::Rect hwcRect{frame.left, frame.top,
+ frame.right, frame.bottom};
+ auto intError = mDevice.mComposer->setLayerDisplayFrame(mDisplayId,
+ mId, hwcRect);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setPlaneAlpha(float alpha)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerPlaneAlpha(mDevice.mHwcDevice,
mDisplayId, mId, alpha);
+#else
+ auto intError = mDevice.mComposer->setLayerPlaneAlpha(mDisplayId,
+ mId, alpha);
+#endif
return static_cast<Error>(intError);
}
@@ -1066,24 +1340,42 @@
"device supports sideband streams");
return Error::Unsupported;
}
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice,
mDisplayId, mId, stream);
+#else
+ auto intError = mDevice.mComposer->setLayerSidebandStream(mDisplayId,
+ mId, stream);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setSourceCrop(const FloatRect& crop)
{
+#ifdef BYPASS_IHWC
hwc_frect_t hwcRect{crop.left, crop.top, crop.right, crop.bottom};
int32_t intError = mDevice.mSetLayerSourceCrop(mDevice.mHwcDevice,
mDisplayId, mId, hwcRect);
+#else
+ Hwc2::IComposer::FRect hwcRect{
+ crop.left, crop.top, crop.right, crop.bottom};
+ auto intError = mDevice.mComposer->setLayerSourceCrop(mDisplayId,
+ mId, hwcRect);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setTransform(Transform transform)
{
+#ifdef BYPASS_IHWC
auto intTransform = static_cast<int32_t>(transform);
int32_t intError = mDevice.mSetLayerTransform(mDevice.mHwcDevice,
mDisplayId, mId, intTransform);
+#else
+ auto intTransform = static_cast<Hwc2::Transform>(transform);
+ auto intError = mDevice.mComposer->setLayerTransform(mDisplayId,
+ mId, intTransform);
+#endif
return static_cast<Error>(intError);
}
@@ -1092,25 +1384,38 @@
size_t rectCount = 0;
auto rectArray = region.getArray(&rectCount);
+#ifdef BYPASS_IHWC
std::vector<hwc_rect_t> hwcRects;
+#else
+ std::vector<Hwc2::IComposer::Rect> hwcRects;
+#endif
for (size_t rect = 0; rect < rectCount; ++rect) {
hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
rectArray[rect].right, rectArray[rect].bottom});
}
+#ifdef BYPASS_IHWC
hwc_region_t hwcRegion = {};
hwcRegion.numRects = rectCount;
hwcRegion.rects = hwcRects.data();
int32_t intError = mDevice.mSetLayerVisibleRegion(mDevice.mHwcDevice,
mDisplayId, mId, hwcRegion);
+#else
+ auto intError = mDevice.mComposer->setLayerVisibleRegion(mDisplayId,
+ mId, hwcRects);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setZOrder(uint32_t z)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerZOrder(mDevice.mHwcDevice, mDisplayId,
mId, z);
+#else
+ auto intError = mDevice.mComposer->setLayerZOrder(mDisplayId, mId, z);
+#endif
return static_cast<Error>(intError);
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 32a9de0..1145ba1 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -17,6 +17,10 @@
#ifndef ANDROID_SF_HWC2_H
#define ANDROID_SF_HWC2_H
+#ifndef USE_HWC2
+#define BYPASS_IHWC
+#endif
+
#define HWC2_INCLUDE_STRINGIFICATION
#define HWC2_USE_CPP11
#include <hardware/hwcomposer2.h>
@@ -42,6 +46,9 @@
class GraphicBuffer;
class Rect;
class Region;
+ namespace Hwc2 {
+ class Composer;
+ };
}
namespace HWC2 {
@@ -57,7 +64,11 @@
class Device
{
public:
+#ifdef BYPASS_IHWC
explicit Device(hwc2_device_t* device);
+#else
+ Device();
+#endif
~Device();
friend class HWC2::Display;
@@ -98,6 +109,7 @@
private:
// Initialization methods
+#ifdef BYPASS_IHWC
template <typename PFN>
[[clang::warn_unused_result]] bool loadFunctionPointer(
FunctionDescriptor desc, PFN& outPFN) {
@@ -121,6 +133,7 @@
auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
}
+#endif
void loadCapabilities();
void loadFunctionPointers();
@@ -132,6 +145,7 @@
// Member variables
+#ifdef BYPASS_IHWC
hwc2_device_t* mHwcDevice;
// Device function pointers
@@ -181,6 +195,9 @@
HWC2_PFN_SET_LAYER_TRANSFORM mSetLayerTransform;
HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
+#else
+ std::unique_ptr<android::Hwc2::Composer> mComposer;
+#endif // BYPASS_IHWC
std::unordered_set<Capability> mCapabilities;
std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index cc0dfb0..b699b2c 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -93,7 +93,7 @@
class HWC2On1Adapter::Callbacks : public hwc_procs_t {
public:
- Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
+ explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
invalidate = &invalidateHook;
vsync = &vsyncHook;
hotplug = &hotplugHook;
@@ -560,6 +560,7 @@
mHwc1Id(-1),
mConfigs(),
mActiveConfig(nullptr),
+ mActiveColorMode(static_cast<android_color_mode_t>(-1)),
mName(),
mType(type),
mPowerMode(PowerMode::Off),
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index 962361e..047e3aa 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -247,6 +247,7 @@
public:
Config(Display& display)
: mDisplay(display),
+ mId(0),
mAttributes() {}
bool isOnDisplay(const Display& display) const {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index c87ba72..bb2e45a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -109,6 +109,7 @@
{
ALOGV("loadHwcModule");
+#ifdef BYPASS_IHWC
hw_module_t const* module;
if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
@@ -140,6 +141,9 @@
mHwcDevice = std::make_unique<HWC2::Device>(
static_cast<hwc2_device_t*>(mAdapter.get()));
}
+#else
+ mHwcDevice = std::make_unique<HWC2::Device>();
+#endif
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index ef41658..2102457 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -933,7 +933,7 @@
protected:
HWCTYPE* const mLayerList;
HWCTYPE* mCurrentLayer;
- Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
+ explicit Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
mIndex(0) { }
inline HWCTYPE const * getLayer() const { return mCurrentLayer; }
inline HWCTYPE* getLayer() { return mCurrentLayer; }
diff --git a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
index bd50b4a..1c0a1fe 100644
--- a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/hardware/power/1.0/IPower.h>
#include <stdint.h>
#include <sys/types.h>
@@ -26,6 +27,7 @@
#include "PowerHAL.h"
+using android::hardware::power::V1_0::PowerHint;
namespace android {
// ---------------------------------------------------------------------------
@@ -39,7 +41,9 @@
}
mPowerManager = interface_cast<IPowerManager>(bs);
}
- status_t status = mPowerManager->powerHint(POWER_HINT_VSYNC, enabled ? 1 : 0);
+ status_t status;
+ status = mPowerManager->powerHint(static_cast<int>(PowerHint::VSYNC),
+ enabled ? 1 : 0);
if(status == DEAD_OBJECT) {
mPowerManager = NULL;
}
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index dd88adb..d1bc7eb 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -44,13 +44,14 @@
return;
}
-EventThread::EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger)
+EventThread::EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger, bool interceptVSyncs)
: mVSyncSource(src),
mFlinger(flinger),
mUseSoftwareVSync(false),
mVsyncEnabled(false),
mDebugVsyncEnabled(false),
- mVsyncHintSent(false) {
+ mVsyncHintSent(false),
+ mInterceptVSyncs(interceptVSyncs) {
for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
mVSyncEvent[i].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
@@ -226,6 +227,9 @@
timestamp = mVSyncEvent[i].header.timestamp;
if (timestamp) {
// we have a vsync event to dispatch
+ if (mInterceptVSyncs) {
+ mFlinger.mInterceptor.saveVSyncEvent(timestamp);
+ }
*event = mVSyncEvent[i];
mVSyncEvent[i].header.timestamp = 0;
vsyncCount = mVSyncEvent[i].vsync.count;
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index b635115..3f1d0bb 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -77,7 +77,7 @@
public:
- EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger);
+ EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger, bool interceptVSyncs);
sp<Connection> createEventConnection() const;
status_t registerDisplayEventConnection(const sp<Connection>& connection);
@@ -132,6 +132,7 @@
bool mDebugVsyncEnabled;
bool mVsyncHintSent;
+ const bool mInterceptVSyncs;
timer_t mTimerId;
};
diff --git a/services/surfaceflinger/GpuService.cpp b/services/surfaceflinger/GpuService.cpp
index 70d9682..b993dfb 100644
--- a/services/surfaceflinger/GpuService.cpp
+++ b/services/surfaceflinger/GpuService.cpp
@@ -27,7 +27,7 @@
class BpGpuService : public BpInterface<IGpuService>
{
public:
- BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
+ explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
};
IMPLEMENT_META_INTERFACE(GpuService, "android.ui.IGpuService");
@@ -92,82 +92,27 @@
}
fprintf(outs,
"GPU Service commands:\n"
- " vkjson dump Vulkan device capabilities as JSON\n");
+ " vkjson dump Vulkan properties as JSON\n");
fclose(outs);
return NO_ERROR;
}
-VkResult vkjsonPrint(FILE* out, FILE* err) {
- VkResult result;
-
- const VkApplicationInfo app_info = {
- VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr,
- "vkjson", 1, /* app name, version */
- "", 0, /* engine name, version */
- VK_API_VERSION_1_0
- };
- const VkInstanceCreateInfo instance_info = {
- VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, nullptr,
- 0, /* flags */
- &app_info,
- 0, nullptr, /* layers */
- 0, nullptr, /* extensions */
- };
- VkInstance instance;
- result = vkCreateInstance(&instance_info, nullptr, &instance);
- if (result != VK_SUCCESS) {
- fprintf(err, "vkCreateInstance failed: %d\n", result);
- return result;
- }
-
- uint32_t ngpu = 0;
- result = vkEnumeratePhysicalDevices(instance, &ngpu, nullptr);
- if (result != VK_SUCCESS) {
- fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
- return result;
- }
- std::vector<VkPhysicalDevice> gpus(ngpu, VK_NULL_HANDLE);
- result = vkEnumeratePhysicalDevices(instance, &ngpu, gpus.data());
- if (result != VK_SUCCESS) {
- fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
- return result;
- }
-
- for (size_t i = 0, n = gpus.size(); i < n; i++) {
- auto props = VkJsonGetAllProperties(gpus[i]);
- std::string json = VkJsonAllPropertiesToJson(props);
- fwrite(json.data(), 1, json.size(), out);
- if (i < n - 1)
- fputc(',', out);
- fputc('\n', out);
- }
-
- vkDestroyInstance(instance, nullptr);
-
- return VK_SUCCESS;
+void vkjsonPrint(FILE* out) {
+ std::string json = VkJsonInstanceToJson(VkJsonGetInstance());
+ fwrite(json.data(), 1, json.size(), out);
+ fputc('\n', out);
}
-status_t cmd_vkjson(int out, int err) {
- int errnum;
+status_t cmd_vkjson(int out, int /*err*/) {
FILE* outs = fdopen(out, "w");
if (!outs) {
- errnum = errno;
+ int errnum = errno;
ALOGE("vkjson: failed to create output stream: %s", strerror(errnum));
return -errnum;
}
- FILE* errs = fdopen(err, "w");
- if (!errs) {
- errnum = errno;
- ALOGE("vkjson: failed to create error stream: %s", strerror(errnum));
- fclose(outs);
- return -errnum;
- }
- fprintf(outs, "[\n");
- VkResult result = vkjsonPrint(outs, errs);
- fprintf(outs, "]\n");
- fclose(errs);
+ vkjsonPrint(outs);
fclose(outs);
- return result >= 0 ? NO_ERROR : UNKNOWN_ERROR;
+ return NO_ERROR;
}
} // anonymous namespace
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d13b6db..d420f0f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -155,10 +155,9 @@
// Creates a custom BufferQueue for SurfaceFlingerConsumer to use
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
+ BufferQueue::createBufferQueue(&producer, &consumer, nullptr, true);
mProducer = new MonitoredProducer(producer, mFlinger);
- mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName,
- this);
+ mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName, this);
mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
mSurfaceFlingerConsumer->setContentsChangedListener(this);
mSurfaceFlingerConsumer->setName(mName);
@@ -212,7 +211,8 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
Mutex::Autolock lock(mQueueItemLock);
-
+ mFlinger->mInterceptor.saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+ item.mGraphicBuffer->getHeight(), item.mFrameNumber);
// Reset the frame number tracker when we receive the first buffer after
// a frame number reset
if (item.mFrameNumber == 1) {
@@ -312,22 +312,6 @@
return NO_ERROR;
}
-/*
- * The layer handle is just a BBinder object passed to the client
- * (remote process) -- we don't keep any reference on our side such that
- * the dtor is called when the remote side let go of its reference.
- *
- * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
- * this layer when the handle is destroyed.
- */
-class Layer::Handle : public BBinder, public LayerCleaner {
- public:
- Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : LayerCleaner(flinger, layer), owner(layer) {}
-
- wp<Layer> owner;
-};
-
sp<IBinder> Layer::getHandle() {
Mutex::Autolock _l(mLock);
@@ -1819,127 +1803,81 @@
}
Region outDirtyRegion;
- if (mQueuedFrames > 0 || mAutoRefresh) {
+ if (mQueuedFrames <= 0 && !mAutoRefresh) {
+ return outDirtyRegion;
+ }
- // if we've already called updateTexImage() without going through
- // a composition step, we have to skip this layer at this point
- // because we cannot call updateTeximage() without a corresponding
- // compositionComplete() call.
- // we'll trigger an update in onPreComposition().
- if (mRefreshPending) {
- return outDirtyRegion;
+ // if we've already called updateTexImage() without going through
+ // a composition step, we have to skip this layer at this point
+ // because we cannot call updateTeximage() without a corresponding
+ // compositionComplete() call.
+ // we'll trigger an update in onPreComposition().
+ if (mRefreshPending) {
+ return outDirtyRegion;
+ }
+
+ // If the head buffer's acquire fence hasn't signaled yet, return and
+ // try again later
+ if (!headFenceHasSignaled()) {
+ mFlinger->signalLayerUpdate();
+ return outDirtyRegion;
+ }
+
+ // Capture the old state of the layer for comparisons later
+ const State& s(getDrawingState());
+ const bool oldOpacity = isOpaque(s);
+ sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
+
+ struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
+ Layer::State& front;
+ Layer::State& current;
+ bool& recomputeVisibleRegions;
+ bool stickyTransformSet;
+ const char* name;
+ int32_t overrideScalingMode;
+ bool& freezePositionUpdates;
+
+ Reject(Layer::State& front, Layer::State& current,
+ bool& recomputeVisibleRegions, bool stickySet,
+ const char* name,
+ int32_t overrideScalingMode,
+ bool& freezePositionUpdates)
+ : front(front), current(current),
+ recomputeVisibleRegions(recomputeVisibleRegions),
+ stickyTransformSet(stickySet),
+ name(name),
+ overrideScalingMode(overrideScalingMode),
+ freezePositionUpdates(freezePositionUpdates) {
}
- // If the head buffer's acquire fence hasn't signaled yet, return and
- // try again later
- if (!headFenceHasSignaled()) {
- mFlinger->signalLayerUpdate();
- return outDirtyRegion;
- }
-
- // Capture the old state of the layer for comparisons later
- const State& s(getDrawingState());
- const bool oldOpacity = isOpaque(s);
- sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
-
- struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
- Layer::State& front;
- Layer::State& current;
- bool& recomputeVisibleRegions;
- bool stickyTransformSet;
- const char* name;
- int32_t overrideScalingMode;
- bool& freezePositionUpdates;
-
- Reject(Layer::State& front, Layer::State& current,
- bool& recomputeVisibleRegions, bool stickySet,
- const char* name,
- int32_t overrideScalingMode,
- bool& freezePositionUpdates)
- : front(front), current(current),
- recomputeVisibleRegions(recomputeVisibleRegions),
- stickyTransformSet(stickySet),
- name(name),
- overrideScalingMode(overrideScalingMode),
- freezePositionUpdates(freezePositionUpdates) {
+ virtual bool reject(const sp<GraphicBuffer>& buf,
+ const BufferItem& item) {
+ if (buf == NULL) {
+ return false;
}
- virtual bool reject(const sp<GraphicBuffer>& buf,
- const BufferItem& item) {
- if (buf == NULL) {
- return false;
- }
+ uint32_t bufWidth = buf->getWidth();
+ uint32_t bufHeight = buf->getHeight();
- uint32_t bufWidth = buf->getWidth();
- uint32_t bufHeight = buf->getHeight();
+ // check that we received a buffer of the right size
+ // (Take the buffer's orientation into account)
+ if (item.mTransform & Transform::ROT_90) {
+ swap(bufWidth, bufHeight);
+ }
- // check that we received a buffer of the right size
- // (Take the buffer's orientation into account)
- if (item.mTransform & Transform::ROT_90) {
- swap(bufWidth, bufHeight);
- }
+ int actualScalingMode = overrideScalingMode >= 0 ?
+ overrideScalingMode : item.mScalingMode;
+ bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+ if (front.active != front.requested) {
- int actualScalingMode = overrideScalingMode >= 0 ?
- overrideScalingMode : item.mScalingMode;
- bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
- if (front.active != front.requested) {
-
- if (isFixedSize ||
- (bufWidth == front.requested.w &&
- bufHeight == front.requested.h))
- {
- // Here we pretend the transaction happened by updating the
- // current and drawing states. Drawing state is only accessed
- // in this thread, no need to have it locked
- front.active = front.requested;
-
- // We also need to update the current state so that
- // we don't end-up overwriting the drawing state with
- // this stale current state during the next transaction
- //
- // NOTE: We don't need to hold the transaction lock here
- // because State::active is only accessed from this thread.
- current.active = front.active;
- current.modified = true;
-
- // recompute visible region
- recomputeVisibleRegions = true;
- }
-
- ALOGD_IF(DEBUG_RESIZE,
- "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
- " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
- " requested={ wh={%4u,%4u} }}\n",
- name,
- bufWidth, bufHeight, item.mTransform, item.mScalingMode,
- front.active.w, front.active.h,
- front.crop.left,
- front.crop.top,
- front.crop.right,
- front.crop.bottom,
- front.crop.getWidth(),
- front.crop.getHeight(),
- front.requested.w, front.requested.h);
- }
-
- if (!isFixedSize && !stickyTransformSet) {
- if (front.active.w != bufWidth ||
- front.active.h != bufHeight) {
- // reject this buffer
- ALOGE("[%s] rejecting buffer: "
- "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
- name, bufWidth, bufHeight, front.active.w, front.active.h);
- return true;
- }
- }
-
- // if the transparent region has changed (this test is
- // conservative, but that's fine, worst case we're doing
- // a bit of extra work), we latch the new one and we
- // trigger a visible-region recompute.
- if (!front.activeTransparentRegion.isTriviallyEqual(
- front.requestedTransparentRegion)) {
- front.activeTransparentRegion = front.requestedTransparentRegion;
+ if (isFixedSize ||
+ (bufWidth == front.requested.w &&
+ bufHeight == front.requested.h))
+ {
+ // Here we pretend the transaction happened by updating the
+ // current and drawing states. Drawing state is only accessed
+ // in this thread, no need to have it locked
+ front.active = front.requested;
// We also need to update the current state so that
// we don't end-up overwriting the drawing state with
@@ -1947,206 +1885,254 @@
//
// NOTE: We don't need to hold the transaction lock here
// because State::active is only accessed from this thread.
- current.activeTransparentRegion = front.activeTransparentRegion;
+ current.active = front.active;
+ current.modified = true;
// recompute visible region
recomputeVisibleRegions = true;
}
- if (front.crop != front.requestedCrop) {
- front.crop = front.requestedCrop;
- current.crop = front.requestedCrop;
- recomputeVisibleRegions = true;
+ ALOGD_IF(DEBUG_RESIZE,
+ "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
+ " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
+ " requested={ wh={%4u,%4u} }}\n",
+ name,
+ bufWidth, bufHeight, item.mTransform, item.mScalingMode,
+ front.active.w, front.active.h,
+ front.crop.left,
+ front.crop.top,
+ front.crop.right,
+ front.crop.bottom,
+ front.crop.getWidth(),
+ front.crop.getHeight(),
+ front.requested.w, front.requested.h);
+ }
+
+ if (!isFixedSize && !stickyTransformSet) {
+ if (front.active.w != bufWidth ||
+ front.active.h != bufHeight) {
+ // reject this buffer
+ ALOGE("[%s] rejecting buffer: "
+ "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
+ name, bufWidth, bufHeight, front.active.w, front.active.h);
+ return true;
}
- freezePositionUpdates = false;
-
- return false;
- }
- };
-
- Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
- getProducerStickyTransform() != 0, mName.string(),
- mOverrideScalingMode, mFreezePositionUpdates);
-
-
- // Check all of our local sync points to ensure that all transactions
- // which need to have been applied prior to the frame which is about to
- // be latched have signaled
-
- auto headFrameNumber = getHeadFrameNumber();
- bool matchingFramesFound = false;
- bool allTransactionsApplied = true;
- {
- Mutex::Autolock lock(mLocalSyncPointMutex);
- for (auto& point : mLocalSyncPoints) {
- if (point->getFrameNumber() > headFrameNumber) {
- break;
- }
-
- matchingFramesFound = true;
-
- if (!point->frameIsAvailable()) {
- // We haven't notified the remote layer that the frame for
- // this point is available yet. Notify it now, and then
- // abort this attempt to latch.
- point->setFrameAvailable();
- allTransactionsApplied = false;
- break;
- }
-
- allTransactionsApplied &= point->transactionIsApplied();
- }
- }
-
- if (matchingFramesFound && !allTransactionsApplied) {
- mFlinger->signalLayerUpdate();
- return outDirtyRegion;
- }
-
- // This boolean is used to make sure that SurfaceFlinger's shadow copy
- // of the buffer queue isn't modified when the buffer queue is returning
- // BufferItem's that weren't actually queued. This can happen in shared
- // buffer mode.
- bool queuedBuffer = false;
- status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
- mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
- mLastFrameNumberReceived);
- if (updateResult == BufferQueue::PRESENT_LATER) {
- // Producer doesn't want buffer to be displayed yet. Signal a
- // layer update so we check again at the next opportunity.
- mFlinger->signalLayerUpdate();
- return outDirtyRegion;
- } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
- // If the buffer has been rejected, remove it from the shadow queue
- // and return early
- if (queuedBuffer) {
- Mutex::Autolock lock(mQueueItemLock);
- mQueueItems.removeAt(0);
- android_atomic_dec(&mQueuedFrames);
- }
- return outDirtyRegion;
- } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
- // This can occur if something goes wrong when trying to create the
- // EGLImage for this buffer. If this happens, the buffer has already
- // been released, so we need to clean up the queue and bug out
- // early.
- if (queuedBuffer) {
- Mutex::Autolock lock(mQueueItemLock);
- mQueueItems.clear();
- android_atomic_and(0, &mQueuedFrames);
}
- // Once we have hit this state, the shadow queue may no longer
- // correctly reflect the incoming BufferQueue's contents, so even if
- // updateTexImage starts working, the only safe course of action is
- // to continue to ignore updates.
- mUpdateTexImageFailed = true;
+ // if the transparent region has changed (this test is
+ // conservative, but that's fine, worst case we're doing
+ // a bit of extra work), we latch the new one and we
+ // trigger a visible-region recompute.
+ if (!front.activeTransparentRegion.isTriviallyEqual(
+ front.requestedTransparentRegion)) {
+ front.activeTransparentRegion = front.requestedTransparentRegion;
- return outDirtyRegion;
- }
+ // We also need to update the current state so that
+ // we don't end-up overwriting the drawing state with
+ // this stale current state during the next transaction
+ //
+ // NOTE: We don't need to hold the transaction lock here
+ // because State::active is only accessed from this thread.
+ current.activeTransparentRegion = front.activeTransparentRegion;
- if (queuedBuffer) {
- // Autolock scope
- auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
- Mutex::Autolock lock(mQueueItemLock);
-
- // Remove any stale buffers that have been dropped during
- // updateTexImage
- while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mQueueItems.removeAt(0);
- android_atomic_dec(&mQueuedFrames);
- }
-
- mQueueItems.removeAt(0);
- }
-
-
- // Decrement the queued-frames count. Signal another event if we
- // have more frames pending.
- if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
- || mAutoRefresh) {
- mFlinger->signalLayerUpdate();
- }
-
- if (updateResult != NO_ERROR) {
- // something happened!
- recomputeVisibleRegions = true;
- return outDirtyRegion;
- }
-
- // update the active buffer
- mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
- if (mActiveBuffer == NULL) {
- // this can only happen if the very first buffer was rejected.
- return outDirtyRegion;
- }
-
- mRefreshPending = true;
- mFrameLatencyNeeded = true;
- if (oldActiveBuffer == NULL) {
- // the first time we receive a buffer, we need to trigger a
- // geometry invalidation.
- recomputeVisibleRegions = true;
- }
-
- Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
- const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
- const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
- if ((crop != mCurrentCrop) ||
- (transform != mCurrentTransform) ||
- (scalingMode != mCurrentScalingMode))
- {
- mCurrentCrop = crop;
- mCurrentTransform = transform;
- mCurrentScalingMode = scalingMode;
- recomputeVisibleRegions = true;
- }
-
- if (oldActiveBuffer != NULL) {
- uint32_t bufWidth = mActiveBuffer->getWidth();
- uint32_t bufHeight = mActiveBuffer->getHeight();
- if (bufWidth != uint32_t(oldActiveBuffer->width) ||
- bufHeight != uint32_t(oldActiveBuffer->height)) {
+ // recompute visible region
recomputeVisibleRegions = true;
}
+
+ if (front.crop != front.requestedCrop) {
+ front.crop = front.requestedCrop;
+ current.crop = front.requestedCrop;
+ recomputeVisibleRegions = true;
+ }
+ freezePositionUpdates = false;
+
+ return false;
+ }
+ };
+
+ Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+ getProducerStickyTransform() != 0, mName.string(),
+ mOverrideScalingMode, mFreezePositionUpdates);
+
+
+ // Check all of our local sync points to ensure that all transactions
+ // which need to have been applied prior to the frame which is about to
+ // be latched have signaled
+
+ auto headFrameNumber = getHeadFrameNumber();
+ bool matchingFramesFound = false;
+ bool allTransactionsApplied = true;
+ {
+ Mutex::Autolock lock(mLocalSyncPointMutex);
+ for (auto& point : mLocalSyncPoints) {
+ if (point->getFrameNumber() > headFrameNumber) {
+ break;
+ }
+
+ matchingFramesFound = true;
+
+ if (!point->frameIsAvailable()) {
+ // We haven't notified the remote layer that the frame for
+ // this point is available yet. Notify it now, and then
+ // abort this attempt to latch.
+ point->setFrameAvailable();
+ allTransactionsApplied = false;
+ break;
+ }
+
+ allTransactionsApplied &= point->transactionIsApplied();
+ }
+ }
+
+ if (matchingFramesFound && !allTransactionsApplied) {
+ mFlinger->signalLayerUpdate();
+ return outDirtyRegion;
+ }
+
+ // This boolean is used to make sure that SurfaceFlinger's shadow copy
+ // of the buffer queue isn't modified when the buffer queue is returning
+ // BufferItem's that weren't actually queued. This can happen in shared
+ // buffer mode.
+ bool queuedBuffer = false;
+ status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
+ mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
+ mLastFrameNumberReceived);
+ if (updateResult == BufferQueue::PRESENT_LATER) {
+ // Producer doesn't want buffer to be displayed yet. Signal a
+ // layer update so we check again at the next opportunity.
+ mFlinger->signalLayerUpdate();
+ return outDirtyRegion;
+ } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
+ // If the buffer has been rejected, remove it from the shadow queue
+ // and return early
+ if (queuedBuffer) {
+ Mutex::Autolock lock(mQueueItemLock);
+ mQueueItems.removeAt(0);
+ android_atomic_dec(&mQueuedFrames);
+ }
+ return outDirtyRegion;
+ } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
+ // This can occur if something goes wrong when trying to create the
+ // EGLImage for this buffer. If this happens, the buffer has already
+ // been released, so we need to clean up the queue and bug out
+ // early.
+ if (queuedBuffer) {
+ Mutex::Autolock lock(mQueueItemLock);
+ mQueueItems.clear();
+ android_atomic_and(0, &mQueuedFrames);
}
- mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
- if (oldOpacity != isOpaque(s)) {
+ // Once we have hit this state, the shadow queue may no longer
+ // correctly reflect the incoming BufferQueue's contents, so even if
+ // updateTexImage starts working, the only safe course of action is
+ // to continue to ignore updates.
+ mUpdateTexImageFailed = true;
+
+ return outDirtyRegion;
+ }
+
+ if (queuedBuffer) {
+ // Autolock scope
+ auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+ Mutex::Autolock lock(mQueueItemLock);
+
+ // Remove any stale buffers that have been dropped during
+ // updateTexImage
+ while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+ mQueueItems.removeAt(0);
+ android_atomic_dec(&mQueuedFrames);
+ }
+
+ mQueueItems.removeAt(0);
+ }
+
+
+ // Decrement the queued-frames count. Signal another event if we
+ // have more frames pending.
+ if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
+ || mAutoRefresh) {
+ mFlinger->signalLayerUpdate();
+ }
+
+ if (updateResult != NO_ERROR) {
+ // something happened!
+ recomputeVisibleRegions = true;
+ return outDirtyRegion;
+ }
+
+ // update the active buffer
+ mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
+ if (mActiveBuffer == NULL) {
+ // this can only happen if the very first buffer was rejected.
+ return outDirtyRegion;
+ }
+
+ mRefreshPending = true;
+ mFrameLatencyNeeded = true;
+ if (oldActiveBuffer == NULL) {
+ // the first time we receive a buffer, we need to trigger a
+ // geometry invalidation.
+ recomputeVisibleRegions = true;
+ }
+
+ Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
+ const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
+ const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
+ if ((crop != mCurrentCrop) ||
+ (transform != mCurrentTransform) ||
+ (scalingMode != mCurrentScalingMode))
+ {
+ mCurrentCrop = crop;
+ mCurrentTransform = transform;
+ mCurrentScalingMode = scalingMode;
+ recomputeVisibleRegions = true;
+ }
+
+ if (oldActiveBuffer != NULL) {
+ uint32_t bufWidth = mActiveBuffer->getWidth();
+ uint32_t bufHeight = mActiveBuffer->getHeight();
+ if (bufWidth != uint32_t(oldActiveBuffer->width) ||
+ bufHeight != uint32_t(oldActiveBuffer->height)) {
recomputeVisibleRegions = true;
}
+ }
- mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+ mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
+ if (oldOpacity != isOpaque(s)) {
+ recomputeVisibleRegions = true;
+ }
- // Remove any sync points corresponding to the buffer which was just
- // latched
- {
- Mutex::Autolock lock(mLocalSyncPointMutex);
- auto point = mLocalSyncPoints.begin();
- while (point != mLocalSyncPoints.end()) {
- if (!(*point)->frameIsAvailable() ||
- !(*point)->transactionIsApplied()) {
- // This sync point must have been added since we started
- // latching. Don't drop it yet.
- ++point;
- continue;
- }
+ mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
- if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
- point = mLocalSyncPoints.erase(point);
- } else {
- ++point;
- }
+ // Remove any sync points corresponding to the buffer which was just
+ // latched
+ {
+ Mutex::Autolock lock(mLocalSyncPointMutex);
+ auto point = mLocalSyncPoints.begin();
+ while (point != mLocalSyncPoints.end()) {
+ if (!(*point)->frameIsAvailable() ||
+ !(*point)->transactionIsApplied()) {
+ // This sync point must have been added since we started
+ // latching. Don't drop it yet.
+ ++point;
+ continue;
+ }
+
+ if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
+ point = mLocalSyncPoints.erase(point);
+ } else {
+ ++point;
}
}
-
- // FIXME: postedRegion should be dirty & bounds
- Region dirtyRegion(Rect(s.active.w, s.active.h));
-
- // transform the dirty region to window-manager space
- outDirtyRegion = (s.active.transform.transform(dirtyRegion));
}
+
+ // FIXME: postedRegion should be dirty & bounds
+ Region dirtyRegion(Rect(s.active.w, s.active.h));
+
+ // transform the dirty region to window-manager space
+ outDirtyRegion = (s.active.transform.transform(dirtyRegion));
+
return outDirtyRegion;
}
@@ -2340,17 +2326,6 @@
// ---------------------------------------------------------------------------
-Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
- const sp<Layer>& layer)
- : mFlinger(flinger), mLayer(layer) {
-}
-
-Layer::LayerCleaner::~LayerCleaner() {
- // destroy client resources
- mFlinger->onLayerDestroyed(mLayer);
-}
-
-// ---------------------------------------------------------------------------
}; // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 24de87e..efb0fb8 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -180,11 +180,6 @@
Rect computeBounds(const Region& activeTransparentRegion) const;
Rect computeBounds() const;
- class Handle;
- sp<IBinder> getHandle();
- sp<IGraphicBufferProducer> getProducer() const;
- const String8& getName() const;
-
int32_t getSequence() const { return sequence; }
// -----------------------------------------------------------------------
@@ -427,9 +422,6 @@
protected:
// constant
sp<SurfaceFlinger> mFlinger;
-
- virtual void onFirstRef();
-
/*
* Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
* is called.
@@ -438,13 +430,24 @@
sp<SurfaceFlinger> mFlinger;
wp<Layer> mLayer;
protected:
- ~LayerCleaner();
+ ~LayerCleaner() {
+ // destroy client resources
+ mFlinger->onLayerDestroyed(mLayer);
+ }
public:
- LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer);
+ LayerCleaner(const sp<SurfaceFlinger>& flinger,
+ const sp<Layer>& layer)
+ : mFlinger(flinger), mLayer(layer) {
+ }
};
+ virtual void onFirstRef();
+
+
+
private:
+ friend class SurfaceInterceptor;
// Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
virtual void onFrameAvailable(const BufferItem& item) override;
virtual void onFrameReplaced(const BufferItem& item) override;
@@ -531,6 +534,25 @@
// the Surface Controller) if set.
uint32_t getEffectiveScalingMode() const;
public:
+ /*
+ * The layer handle is just a BBinder object passed to the client
+ * (remote process) -- we don't keep any reference on our side such that
+ * the dtor is called when the remote side let go of its reference.
+ *
+ * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
+ * this layer when the handle is destroyed.
+ */
+ class Handle : public BBinder, public LayerCleaner {
+ public:
+ Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+ : LayerCleaner(flinger, layer), owner(layer) {}
+
+ wp<Layer> owner;
+ };
+
+ sp<IBinder> getHandle();
+ sp<IGraphicBufferProducer> getProducer() const;
+ const String8& getName() const;
void notifyAvailableFrames();
private:
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
deleted file mode 100644
index 579affb..0000000
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
+++ /dev/null
@@ -1,78 +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.
- */
-
-#include <GLES/gl.h>
-
-#include <cutils/compiler.h>
-
-#include "GLES10RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-GLES10RenderEngine::~GLES10RenderEngine() {
-}
-
-void GLES10RenderEngine::setupLayerBlending(
-#ifdef USE_HWC2
- bool premultipliedAlpha, bool opaque, float alpha) {
-#else
- bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
- // OpenGL ES 1.0 doesn't support texture combiners.
- // This path doesn't properly handle opaque layers that have non-opaque
- // alpha values. The alpha channel will be copied into the framebuffer or
- // screenshot, so if the framebuffer or screenshot is blended on top of
- // something else, whatever is below the window will incorrectly show
- // through.
-#ifdef USE_HWC2
- if (CC_UNLIKELY(alpha < 1.0f)) {
- if (premultipliedAlpha) {
- glColor4f(alpha, alpha, alpha, alpha);
- } else {
- glColor4f(1.0f, 1.0f, 1.0f, alpha);
- }
-#else
- if (CC_UNLIKELY(alpha < 0xFF)) {
- GLfloat floatAlpha = alpha * (1.0f / 255.0f);
- if (premultipliedAlpha) {
- glColor4f(floatAlpha, floatAlpha, floatAlpha, floatAlpha);
- } else {
- glColor4f(1.0f, 1.0f, 1.0f, floatAlpha);
- }
-#endif
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- } else {
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- }
-
-#ifdef USE_HWC2
- if (alpha < 1.0f || !opaque) {
-#else
- if (alpha < 0xFF || !opaque) {
-#endif
- glEnable(GL_BLEND);
- glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA,
- GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
deleted file mode 100644
index 61abd6a..0000000
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
+++ /dev/null
@@ -1,45 +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 SF_GLES10RENDERENGINE_H_
-#define SF_GLES10RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "GLES11RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class GLES10RenderEngine : public GLES11RenderEngine {
- virtual ~GLES10RenderEngine();
-protected:
-#ifdef USE_HWC2
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- float alpha) override;
-#else
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha);
-#endif
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES10RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
deleted file mode 100644
index 847cdb3..0000000
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
+++ /dev/null
@@ -1,304 +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.
- */
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-#include <ui/Rect.h>
-
-#include <utils/String8.h>
-#include <cutils/compiler.h>
-#include <gui/ISurfaceComposer.h>
-
-#include "GLES11RenderEngine.h"
-#include "Mesh.h"
-#include "Texture.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-GLES11RenderEngine::GLES11RenderEngine() {
-
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
- glEnableClientState(GL_VERTEX_ARRAY);
- glShadeModel(GL_FLAT);
- glDisable(GL_DITHER);
- glDisable(GL_CULL_FACE);
-
- const uint16_t protTexData[] = { 0 };
- glGenTextures(1, &mProtectedTexName);
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0,
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-}
-
-GLES11RenderEngine::~GLES11RenderEngine() {
-}
-
-
-size_t GLES11RenderEngine::getMaxTextureSize() const {
- return mMaxTextureSize;
-}
-
-size_t GLES11RenderEngine::getMaxViewportDims() const {
- return
- mMaxViewportDims[0] < mMaxViewportDims[1] ?
- mMaxViewportDims[0] : mMaxViewportDims[1];
-}
-
-void GLES11RenderEngine::setViewportAndProjection(
- size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap,
- Transform::orientation_flags rotation) {
- glViewport(0, 0, vpw, vph);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- size_t l = sourceCrop.left;
- size_t r = sourceCrop.right;
-
- // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
- size_t t = hwh - sourceCrop.top;
- size_t b = hwh - sourceCrop.bottom;
-
- if (yswap) {
- glOrthof(l, r, t, b, 0, 1);
- } else {
- glOrthof(l, r, b, t, 0, 1);
- }
-
- switch (rotation) {
- case Transform::ROT_0:
- break;
- case Transform::ROT_90:
- glRotatef(90, 0, 0, 1);
- break;
- case Transform::ROT_180:
- glRotatef(180, 0, 0, 1);
- break;
- case Transform::ROT_270:
- glRotatef(270, 0, 0, 1);
- break;
- default:
- break;
- }
-
- glMatrixMode(GL_MODELVIEW);
-}
-
-#ifdef USE_HWC2
-void GLES11RenderEngine::setupLayerBlending(bool premultipliedAlpha,
- bool opaque, float alpha) {
-#else
-void GLES11RenderEngine::setupLayerBlending(
- bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
- GLenum combineRGB;
- GLenum combineAlpha;
- GLenum src0Alpha;
- GLfloat envColor[4];
-
-#ifdef USE_HWC2
- if (CC_UNLIKELY(alpha < 1.0f)) {
-#else
- if (CC_UNLIKELY(alpha < 0xFF)) {
-#endif
- // Cv = premultiplied ? Cs*alpha : Cs
- // Av = !opaque ? As*alpha : As
- combineRGB = premultipliedAlpha ? GL_MODULATE : GL_REPLACE;
- combineAlpha = !opaque ? GL_MODULATE : GL_REPLACE;
- src0Alpha = GL_CONSTANT;
-#ifdef USE_HWC2
- envColor[0] = alpha;
-#else
- envColor[0] = alpha * (1.0f / 255.0f);
-#endif
- } else {
- // Cv = Cs
- // Av = opaque ? 1.0 : As
- combineRGB = GL_REPLACE;
- combineAlpha = GL_REPLACE;
- src0Alpha = opaque ? GL_CONSTANT : GL_TEXTURE;
- envColor[0] = 1.0f;
- }
-
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, combineRGB);
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
- if (combineRGB == GL_MODULATE) {
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
- }
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, combineAlpha);
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, src0Alpha);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
- if (combineAlpha == GL_MODULATE) {
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
- }
- if (combineRGB == GL_MODULATE || src0Alpha == GL_CONSTANT) {
- envColor[1] = envColor[0];
- envColor[2] = envColor[0];
- envColor[3] = envColor[0];
- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, envColor);
- }
-
-#ifdef USE_HWC2
- if (alpha < 1.0f || !opaque) {
-#else
- if (alpha < 0xFF || !opaque) {
-#endif
- glEnable(GL_BLEND);
- glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA,
- GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-#ifdef USE_HWC2
-void GLES11RenderEngine::setupDimLayerBlending(float alpha) {
-#else
-void GLES11RenderEngine::setupDimLayerBlending(int alpha) {
-#endif
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
-#ifdef USE_HWC2
- if (alpha == 1.0f) {
-#else
- if (alpha == 0xFF) {
-#endif
- glDisable(GL_BLEND);
- } else {
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- }
-#ifdef USE_HWC2
- glColor4f(0, 0, 0, alpha);
-#else
- glColor4f(0, 0, 0, alpha/255.0f);
-#endif
-}
-
-void GLES11RenderEngine::setupLayerTexturing(const Texture& texture) {
- GLuint target = texture.getTextureTarget();
- glBindTexture(target, texture.getTextureName());
- GLenum filter = GL_NEAREST;
- if (texture.getFiltering()) {
- filter = GL_LINEAR;
- }
- glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_MAG_FILTER, filter);
- glTexParameterx(target, GL_TEXTURE_MIN_FILTER, filter);
- glMatrixMode(GL_TEXTURE);
- glLoadMatrixf(texture.getMatrix().asArray());
- glMatrixMode(GL_MODELVIEW);
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_TEXTURE_EXTERNAL_OES);
-}
-
-void GLES11RenderEngine::setupLayerBlackedOut() {
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glEnable(GL_TEXTURE_2D);
-}
-
-void GLES11RenderEngine::disableTexturing() {
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
-}
-
-void GLES11RenderEngine::disableBlending() {
- glDisable(GL_BLEND);
-}
-
-void GLES11RenderEngine::bindImageAsFramebuffer(EGLImageKHR image,
- uint32_t* texName, uint32_t* fbName, uint32_t* status) {
- GLuint tname, name;
- // turn our EGLImage into a texture
- glGenTextures(1, &tname);
- glBindTexture(GL_TEXTURE_2D, tname);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
-
- // create a Framebuffer Object to render into
- glGenFramebuffersOES(1, &name);
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
- glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
- GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
-
- *status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
- *texName = tname;
- *fbName = name;
-}
-
-void GLES11RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) {
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- glDeleteFramebuffersOES(1, &fbName);
- glDeleteTextures(1, &texName);
-}
-
-void GLES11RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
- glColor4f(r, g, b, a);
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
-}
-
-void GLES11RenderEngine::drawMesh(const Mesh& mesh) {
- if (mesh.getTexCoordsSize()) {
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(mesh.getTexCoordsSize(),
- GL_FLOAT,
- mesh.getByteStride(),
- mesh.getTexCoords());
- }
-
- glVertexPointer(mesh.getVertexSize(),
- GL_FLOAT,
- mesh.getByteStride(),
- mesh.getPositions());
-
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
- if (mesh.getTexCoordsSize()) {
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- }
-}
-
-void GLES11RenderEngine::dump(String8& result) {
- RenderEngine::dump(result);
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#if defined(__gl2_h_)
-#error "don't include gl2/gl2.h in this file"
-#endif
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
deleted file mode 100644
index 4cd968d..0000000
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
+++ /dev/null
@@ -1,81 +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 SF_GLES11RENDERENGINE_H_
-#define SF_GLES11RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <GLES/gl.h>
-#include <Transform.h>
-
-#include "RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
-class Mesh;
-class Texture;
-
-class GLES11RenderEngine : public RenderEngine {
- GLuint mProtectedTexName;
- GLint mMaxViewportDims[2];
- GLint mMaxTextureSize;
-
- virtual void bindImageAsFramebuffer(EGLImageKHR image,
- uint32_t* texName, uint32_t* fbName, uint32_t* status);
- virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName);
-
-public:
- GLES11RenderEngine();
-
-protected:
- virtual ~GLES11RenderEngine();
-
- virtual void dump(String8& result);
- virtual void setViewportAndProjection(size_t vpw, size_t vph,
- Rect sourceCrop, size_t hwh, bool yswap,
- Transform::orientation_flags rotation);
-#ifdef USE_HWC2
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- float alpha) override;
- virtual void setupDimLayerBlending(float alpha) override;
-#else
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- int alpha);
- virtual void setupDimLayerBlending(int alpha);
-#endif
- virtual void setupLayerTexturing(const Texture& texture);
- virtual void setupLayerBlackedOut();
- virtual void setupFillWithColor(float r, float g, float b, float a) ;
- virtual void disableTexturing();
- virtual void disableBlending();
-
- virtual void drawMesh(const Mesh& mesh);
-
- virtual size_t getMaxTextureSize() const;
- virtual size_t getMaxViewportDims() const;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES11RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index d6a032f..2b82d0e 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -19,8 +19,6 @@
#include <ui/Region.h>
#include "RenderEngine.h"
-#include "GLES10RenderEngine.h"
-#include "GLES11RenderEngine.h"
#include "GLES20RenderEngine.h"
#include "GLExtensions.h"
#include "Mesh.h"
@@ -122,10 +120,8 @@
RenderEngine* engine = NULL;
switch (version) {
case GLES_VERSION_1_0:
- engine = new GLES10RenderEngine();
- break;
case GLES_VERSION_1_1:
- engine = new GLES11RenderEngine();
+ LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
break;
case GLES_VERSION_2_0:
case GLES_VERSION_3_0:
@@ -317,7 +313,7 @@
KeyedVector<Attribute, EGLint> mList;
struct Attribute {
Attribute() : v(0) {};
- Attribute(EGLint v) : v(v) { }
+ explicit Attribute(EGLint v) : v(v) { }
EGLint v;
bool operator < (const Attribute& other) const {
// this places EGL_NONE at the end
@@ -338,18 +334,18 @@
public:
void operator = (EGLint value) {
if (attribute != EGL_NONE) {
- v.mList.add(attribute, value);
+ v.mList.add(Attribute(attribute), value);
}
}
operator EGLint () const { return v.mList[attribute]; }
};
public:
EGLAttributeVector() {
- mList.add(EGL_NONE, EGL_NONE);
+ mList.add(Attribute(EGL_NONE), EGL_NONE);
}
void remove(EGLint attribute) {
if (attribute != EGL_NONE) {
- mList.removeItem(attribute);
+ mList.removeItem(Attribute(attribute));
}
}
Adder operator [] (EGLint attribute) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 30ebfd5..a0e040b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <errno.h>
#include <math.h>
+#include <mutex>
#include <dlfcn.h>
#include <inttypes.h>
#include <stdatomic.h>
@@ -162,6 +163,7 @@
mLastTransactionTime(0),
mBootFinished(false),
mForceFullDamage(false),
+ mInterceptor(),
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
@@ -248,7 +250,7 @@
flinger->setTransactionFlags(eDisplayTransactionNeeded);
}
public:
- DisplayToken(const sp<SurfaceFlinger>& flinger)
+ explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
: flinger(flinger) {
}
};
@@ -259,7 +261,7 @@
DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
info.displayName = displayName;
mCurrentState.displays.add(token, info);
-
+ mInterceptor.saveDisplayCreation(info);
return token;
}
@@ -277,7 +279,7 @@
ALOGE("destroyDisplay called for non-virtual display");
return;
}
-
+ mInterceptor.saveDisplayDeletion(info.displayId);
mCurrentState.displays.removeItemsAt(idx);
setTransactionFlags(eDisplayTransactionNeeded);
}
@@ -290,6 +292,7 @@
// All non-virtual displays are currently considered secure.
DisplayDeviceState info(type, true);
mCurrentState.displays.add(mBuiltinDisplays[type], info);
+ mInterceptor.saveDisplayCreation(info);
}
sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
@@ -462,6 +465,30 @@
bool mEnabled;
};
+class InjectVSyncSource : public VSyncSource {
+public:
+ InjectVSyncSource() {}
+
+ virtual ~InjectVSyncSource() {}
+
+ virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ mCallback = callback;
+ }
+
+ virtual void onInjectSyncEvent(nsecs_t when) {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ mCallback->onVSyncEvent(when);
+ }
+
+ virtual void setVSyncEnabled(bool) {}
+ virtual void setPhaseOffset(nsecs_t) {}
+
+private:
+ std::mutex mCallbackMutex; // Protects the following
+ sp<VSyncSource::Callback> mCallback;
+};
+
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
@@ -476,10 +503,10 @@
// start the EventThread
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
- mEventThread = new EventThread(vsyncSrc, *this);
+ mEventThread = new EventThread(vsyncSrc, *this, false);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = new EventThread(sfVsyncSrc, *this);
+ mSFEventThread = new EventThread(sfVsyncSrc, *this, true);
mEventQueue.setEventThread(mSFEventThread);
// set SFEventThread to SCHED_FIFO to minimize jitter
@@ -859,6 +886,40 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
+ if (enable == mInjectVSyncs) {
+ return NO_ERROR;
+ }
+
+ if (enable) {
+ mInjectVSyncs = enable;
+ ALOGV("VSync Injections enabled");
+ if (mVSyncInjector.get() == nullptr) {
+ mVSyncInjector = new InjectVSyncSource();
+ mInjectorEventThread = new EventThread(mVSyncInjector, *this, false);
+ }
+ mEventQueue.setEventThread(mInjectorEventThread);
+ } else {
+ mInjectVSyncs = enable;
+ ALOGV("VSync Injections disabled");
+ mEventQueue.setEventThread(mSFEventThread);
+ mVSyncInjector.clear();
+ }
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::injectVSync(nsecs_t when) {
+ if (!mInjectVSyncs) {
+ ALOGE("VSync Injections not enabled");
+ return BAD_VALUE;
+ }
+ if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
+ ALOGV("Injecting VSync inside SurfaceFlinger");
+ mVSyncInjector->onInjectSyncEvent(when);
+ }
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
@@ -1414,13 +1475,7 @@
mHwc->commit(hwcId);
}
displayDevice->onSwapBuffersCompleted();
- if (displayId == 0) {
- // Make the default display current because the VirtualDisplayDevice
- // code cannot deal with dequeueBuffer() being called outside of the
- // composition loop; however the code below can call glFlush() which
- // is allowed to (and does in some case) call dequeueBuffer().
- displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
- }
+ displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
sp<Fence> releaseFence = Fence::NO_FENCE;
if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) {
@@ -2317,6 +2372,10 @@
}
if (transactionFlags) {
+ if (mInterceptor.isEnabled()) {
+ mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+ }
+
// this triggers the transaction
setTransactionFlags(transactionFlags);
@@ -2477,7 +2536,6 @@
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
{
- //ALOGD("createLayer for (%d x %d), name=%s", w, h, name.string());
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -2512,6 +2570,7 @@
if (result != NO_ERROR) {
return result;
}
+ mInterceptor.saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
return result;
@@ -2559,6 +2618,7 @@
status_t err = NO_ERROR;
sp<Layer> l(client->getLayerUser(handle));
if (l != NULL) {
+ mInterceptor.saveSurfaceDeletion(l);
err = removeLayer(l);
ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
"error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -2602,7 +2662,7 @@
class MessageScreenInitialized : public MessageBase {
SurfaceFlinger* flinger;
public:
- MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
+ explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
virtual bool handler() {
flinger->onInitializeDisplays();
return true;
@@ -2630,6 +2690,16 @@
return;
}
+ if (mInterceptor.isEnabled()) {
+ Mutex::Autolock _l(mStateLock);
+ ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken());
+ if (idx < 0) {
+ ALOGW("Surface Interceptor SavePowerMode: invalid display token");
+ return;
+ }
+ mInterceptor.savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
+ }
+
if (currentMode == HWC_POWER_MODE_OFF) {
// Turn on the display
getHwComposer().setPowerMode(type, mode);
@@ -3315,6 +3385,18 @@
mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
return NO_ERROR;
}
+ case 1020: { // Layer updates interceptor
+ n = data.readInt32();
+ if (n) {
+ ALOGV("Interceptor enabled");
+ mInterceptor.enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
+ }
+ else{
+ ALOGV("Interceptor disabled");
+ mInterceptor.disable();
+ }
+ return NO_ERROR;
+ }
case 1021: { // Disable HWC virtual displays
n = data.readInt32();
mUseHwcVirtualDisplays = !n;
@@ -3416,7 +3498,7 @@
}
public:
- GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+ explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
: impl(impl),
looper(new Looper(true)),
result(NO_ERROR),
@@ -3840,31 +3922,6 @@
return l->sequence - r->sequence;
}
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState()
- : type(DisplayDevice::DISPLAY_ID_INVALID),
- layerStack(DisplayDevice::NO_LAYER_STACK),
- orientation(0),
- width(0),
- height(0),
- isSecure(false) {
-}
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(
- DisplayDevice::DisplayType type, bool isSecure)
- : type(type),
- layerStack(DisplayDevice::NO_LAYER_STACK),
- orientation(0),
- width(0),
- height(0),
- isSecure(isSecure) {
- viewport.makeInvalid();
- frame.makeInvalid();
-}
-
-// ---------------------------------------------------------------------------
-
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b98924b..6449e57 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -56,6 +56,7 @@
#include "FenceTracker.h"
#include "FrameTracker.h"
#include "MessageQueue.h"
+#include "SurfaceInterceptor.h"
#include "DisplayHardware/HWComposer.h"
#include "Effects/Daltonizer.h"
@@ -76,6 +77,8 @@
class Surface;
class RenderEngine;
class EventControlThread;
+class VSyncSource;
+class InjectVSyncSource;
// ---------------------------------------------------------------------------
@@ -148,6 +151,7 @@
private:
friend class Client;
friend class DisplayEventConnection;
+ friend class EventThread;
friend class Layer;
friend class MonitoredProducer;
@@ -171,23 +175,6 @@
virtual int do_compare(const void* lhs, const void* rhs) const;
};
- struct DisplayDeviceState {
- DisplayDeviceState();
- DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
- bool isValid() const { return type >= 0; }
- bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
- bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
- DisplayDevice::DisplayType type;
- sp<IGraphicBufferProducer> surface;
- uint32_t layerStack;
- Rect viewport;
- Rect frame;
- uint8_t orientation;
- uint32_t width, height;
- String8 displayName;
- bool isSecure;
- };
-
struct State {
LayerVector layersSortedByZ;
DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
@@ -234,6 +221,9 @@
virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
virtual status_t getHdrCapabilities(const sp<IBinder>& display,
HdrCapabilities* outCapabilities) const;
+ virtual status_t enableVSyncInjections(bool enable);
+ virtual status_t injectVSync(nsecs_t when);
+
/* ------------------------------------------------------------------------
* DeathRecipient interface
@@ -489,6 +479,8 @@
bool mGpuToCpuSupported;
sp<EventThread> mEventThread;
sp<EventThread> mSFEventThread;
+ sp<EventThread> mInjectorEventThread;
+ sp<InjectVSyncSource> mVSyncInjector;
sp<EventControlThread> mEventControlThread;
EGLContext mEGLContext;
EGLDisplay mEGLDisplay;
@@ -529,6 +521,7 @@
#ifdef USE_HWC2
bool mPropagateBackpressure = true;
#endif
+ SurfaceInterceptor mInterceptor;
bool mUseHwcVirtualDisplays = true;
// these are thread safe
@@ -549,6 +542,8 @@
* Feature prototyping
*/
+ bool mInjectVSyncs;
+
Daltonizer mDaltonizer;
#ifndef USE_HWC2
bool mDaltonize;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b0f418c..3ae2c04 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <errno.h>
#include <math.h>
+#include <mutex>
#include <dlfcn.h>
#include <inttypes.h>
#include <stdatomic.h>
@@ -92,6 +93,19 @@
EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+// Workaround for b/30067360: /proc/self/environ inaccessible in SurfaceFlinger
+// => ASan fails to read ASAN_OPTIONS => alloc-dealloc-mismatch bug is not
+// suppressed and prevents the device from booting.
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+#if __has_feature(address_sanitizer)
+__attribute__((visibility("default")))
+extern "C" const char *__asan_default_options() {
+ return "alloc_dealloc_mismatch=0";
+}
+#endif
+
namespace android {
// This is the phase offset in nanoseconds of the software vsync event
@@ -150,6 +164,7 @@
mLastTransactionTime(0),
mBootFinished(false),
mForceFullDamage(false),
+ mInterceptor(),
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
@@ -233,7 +248,7 @@
flinger->setTransactionFlags(eDisplayTransactionNeeded);
}
public:
- DisplayToken(const sp<SurfaceFlinger>& flinger)
+ explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
: flinger(flinger) {
}
};
@@ -244,7 +259,7 @@
DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
info.displayName = displayName;
mCurrentState.displays.add(token, info);
-
+ mInterceptor.saveDisplayCreation(info);
return token;
}
@@ -262,7 +277,7 @@
ALOGE("destroyDisplay called for non-virtual display");
return;
}
-
+ mInterceptor.saveDisplayDeletion(info.displayId);
mCurrentState.displays.removeItemsAt(idx);
setTransactionFlags(eDisplayTransactionNeeded);
}
@@ -274,6 +289,7 @@
// All non-virtual displays are currently considered secure.
DisplayDeviceState info(type, true);
mCurrentState.displays.add(mBuiltinDisplays[type], info);
+ mInterceptor.saveDisplayCreation(info);
}
sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
@@ -446,6 +462,30 @@
bool mEnabled;
};
+class InjectVSyncSource : public VSyncSource {
+public:
+ InjectVSyncSource() {}
+
+ virtual ~InjectVSyncSource() {}
+
+ virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ mCallback = callback;
+ }
+
+ virtual void onInjectSyncEvent(nsecs_t when) {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ mCallback->onVSyncEvent(when);
+ }
+
+ virtual void setVSyncEnabled(bool) {}
+ virtual void setPhaseOffset(nsecs_t) {}
+
+private:
+ std::mutex mCallbackMutex; // Protects the following
+ sp<VSyncSource::Callback> mCallback;
+};
+
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
@@ -459,10 +499,10 @@
// start the EventThread
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
- mEventThread = new EventThread(vsyncSrc, *this);
+ mEventThread = new EventThread(vsyncSrc, *this, false);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = new EventThread(sfVsyncSrc, *this);
+ mSFEventThread = new EventThread(sfVsyncSrc, *this, true);
mEventQueue.setEventThread(mSFEventThread);
// set SFEventThread to SCHED_FIFO to minimize jitter
@@ -809,6 +849,40 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
+ if (enable == mInjectVSyncs) {
+ return NO_ERROR;
+ }
+
+ if (enable) {
+ mInjectVSyncs = enable;
+ ALOGV("VSync Injections enabled");
+ if (mVSyncInjector.get() == nullptr) {
+ mVSyncInjector = new InjectVSyncSource();
+ mInjectorEventThread = new EventThread(mVSyncInjector, *this, false);
+ }
+ mEventQueue.setEventThread(mInjectorEventThread);
+ } else {
+ mInjectVSyncs = enable;
+ ALOGV("VSync Injections disabled");
+ mEventQueue.setEventThread(mSFEventThread);
+ mVSyncInjector.clear();
+ }
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::injectVSync(nsecs_t when) {
+ if (!mInjectVSyncs) {
+ ALOGE("VSync Injections not enabled");
+ return BAD_VALUE;
+ }
+ if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
+ ALOGV("Injecting VSync inside SurfaceFlinger");
+ mVSyncInjector->onInjectSyncEvent(when);
+ }
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
@@ -2220,6 +2294,10 @@
}
if (transactionFlags) {
+ if (mInterceptor.isEnabled()) {
+ mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+ }
+
// this triggers the transaction
setTransactionFlags(transactionFlags);
@@ -2380,7 +2458,6 @@
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
{
- //ALOGD("createLayer for (%d x %d), name=%s", w, h, name.string());
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -2415,6 +2492,7 @@
if (result != NO_ERROR) {
return result;
}
+ mInterceptor.saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
return result;
@@ -2462,6 +2540,7 @@
status_t err = NO_ERROR;
sp<Layer> l(client->getLayerUser(handle));
if (l != NULL) {
+ mInterceptor.saveSurfaceDeletion(l);
err = removeLayer(l);
ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
"error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -2505,7 +2584,7 @@
class MessageScreenInitialized : public MessageBase {
SurfaceFlinger* flinger;
public:
- MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
+ explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
virtual bool handler() {
flinger->onInitializeDisplays();
return true;
@@ -2533,6 +2612,16 @@
return;
}
+ if (mInterceptor.isEnabled()) {
+ Mutex::Autolock _l(mStateLock);
+ ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken());
+ if (idx < 0) {
+ ALOGW("Surface Interceptor SavePowerMode: invalid display token");
+ return;
+ }
+ mInterceptor.savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
+ }
+
if (currentMode == HWC_POWER_MODE_OFF) {
// Turn on the display
getHwComposer().setPowerMode(type, mode);
@@ -3195,6 +3284,18 @@
mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
return NO_ERROR;
}
+ case 1020: { // Layer updates interceptor
+ n = data.readInt32();
+ if (n) {
+ ALOGV("Interceptor enabled");
+ mInterceptor.enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
+ }
+ else{
+ ALOGV("Interceptor disabled");
+ mInterceptor.disable();
+ }
+ return NO_ERROR;
+ }
case 1021: { // Disable HWC virtual displays
n = data.readInt32();
mUseHwcVirtualDisplays = !n;
@@ -3296,7 +3397,7 @@
}
public:
- GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+ explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
: impl(impl),
looper(new Looper(true)),
result(NO_ERROR),
@@ -3714,31 +3815,6 @@
return l->sequence - r->sequence;
}
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState()
- : type(DisplayDevice::DISPLAY_ID_INVALID),
- layerStack(DisplayDevice::NO_LAYER_STACK),
- orientation(0),
- width(0),
- height(0),
- isSecure(false) {
-}
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(
- DisplayDevice::DisplayType type, bool isSecure)
- : type(type),
- layerStack(DisplayDevice::NO_LAYER_STACK),
- orientation(0),
- width(0),
- height(0),
- isSecure(isSecure) {
- viewport.makeInvalid();
- frame.makeInvalid();
-}
-
-// ---------------------------------------------------------------------------
-
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
new file mode 100644
index 0000000..4ae3580
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -0,0 +1,575 @@
+/*
+ * 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.
+ */
+#undef LOG_TAG
+#define LOG_TAG "SurfaceInterceptor"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+#include "SurfaceInterceptor.h"
+#include <cutils/log.h>
+
+#include <utils/Trace.h>
+
+#include <fstream>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
+{
+ if (mEnabled) {
+ return;
+ }
+ ATRACE_CALL();
+ mEnabled = true;
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ saveExistingDisplaysLocked(displays);
+ saveExistingSurfacesLocked(layers);
+}
+
+void SurfaceInterceptor::disable() {
+ if (!mEnabled) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ mEnabled = false;
+ status_t err(writeProtoFileLocked());
+ ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
+ ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
+ mTrace.Clear();
+}
+
+bool SurfaceInterceptor::isEnabled() {
+ return mEnabled;
+}
+
+void SurfaceInterceptor::saveExistingDisplaysLocked(
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
+{
+ // Caveat: The initial snapshot does not capture the power mode of the existing displays
+ ATRACE_CALL();
+ for (size_t i = 0 ; i < displays.size() ; i++) {
+ addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]);
+ addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]);
+ }
+}
+
+void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) {
+ ATRACE_CALL();
+ for (const auto& layer : layers) {
+ addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
+ addInitialSurfaceStateLocked(createTraceIncrementLocked(), layer);
+ }
+}
+
+void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
+ const sp<const Layer>& layer)
+{
+ Transaction* transaction(increment->mutable_transaction());
+ transaction->set_synchronous(layer->mTransactionFlags & BnSurfaceComposer::eSynchronous);
+ transaction->set_animation(layer->mTransactionFlags & BnSurfaceComposer::eAnimation);
+
+ const int32_t layerId(getLayerId(layer));
+ addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(),
+ layer->mCurrentState.active.transform.ty());
+ addDepthLocked(transaction, layerId, layer->mCurrentState.z);
+ addAlphaLocked(transaction, layerId, layer->mCurrentState.alpha);
+ addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion);
+ addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
+ addCropLocked(transaction, layerId, layer->mCurrentState.crop);
+ if (layer->mCurrentState.handle != nullptr) {
+ addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.handle,
+ layer->mCurrentState.frameNumber);
+ }
+ addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop);
+ addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
+ addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
+}
+
+void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
+ const DisplayDeviceState& display)
+{
+ Transaction* transaction(increment->mutable_transaction());
+ transaction->set_synchronous(false);
+ transaction->set_animation(false);
+
+ addDisplaySurfaceLocked(transaction, display.displayId, display.surface);
+ addDisplayLayerStackLocked(transaction, display.displayId, display.layerStack);
+ addDisplaySizeLocked(transaction, display.displayId, display.width, display.height);
+ addDisplayProjectionLocked(transaction, display.displayId, display.orientation,
+ display.viewport, display.frame);
+}
+
+status_t SurfaceInterceptor::writeProtoFileLocked() {
+ ATRACE_CALL();
+ std::ofstream output(mOutputFileName, std::ios::out | std::ios::trunc | std::ios::binary);
+ // SerializeToOstream returns false when it's missing required data or when it could not write
+ if (!mTrace.IsInitialized()) {
+ return NOT_ENOUGH_DATA;
+ }
+ if (!mTrace.SerializeToOstream(&output)) {
+ return PERMISSION_DENIED;
+ }
+ return NO_ERROR;
+}
+
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) {
+ const sp<const IBinder>& handle(weakHandle.promote());
+ const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
+ const sp<const Layer> layer(layerHandle->owner.promote());
+ // layer could be a nullptr at this point
+ return layer;
+}
+
+const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) {
+ return layer->getName().string();
+}
+
+int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) {
+ return layer->sequence;
+}
+
+Increment* SurfaceInterceptor::createTraceIncrementLocked() {
+ Increment* increment(mTrace.add_increment());
+ increment->set_time_stamp(systemTime());
+ return increment;
+}
+
+SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction,
+ int32_t layerId)
+{
+ SurfaceChange* change(transaction->add_surface_change());
+ change->set_id(layerId);
+ return change;
+}
+
+DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
+ int32_t displayId)
+{
+ DisplayChange* dispChange(transaction->add_display_change());
+ dispChange->set_id(displayId);
+ return dispChange;
+}
+
+void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
+ protoRect->set_left(rect.left);
+ protoRect->set_top(rect.top);
+ protoRect->set_right(rect.right);
+ protoRect->set_bottom(rect.bottom);
+}
+
+void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
+ float x, float y)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ PositionChange* posChange(change->mutable_position());
+ posChange->set_x(x);
+ posChange->set_y(y);
+}
+
+void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId,
+ uint32_t z)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ LayerChange* depthChange(change->mutable_layer());
+ depthChange->set_layer(z);
+}
+
+void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
+ uint32_t h)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ SizeChange* sizeChange(change->mutable_size());
+ sizeChange->set_w(w);
+ sizeChange->set_h(h);
+}
+
+void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId,
+ float alpha)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ AlphaChange* alphaChange(change->mutable_alpha());
+ alphaChange->set_alpha(alpha);
+}
+
+void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
+ const layer_state_t::matrix22_t& matrix)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ MatrixChange* matrixChange(change->mutable_matrix());
+ matrixChange->set_dsdx(matrix.dsdx);
+ matrixChange->set_dtdx(matrix.dtdx);
+ matrixChange->set_dsdy(matrix.dsdy);
+ matrixChange->set_dtdy(matrix.dtdy);
+}
+
+void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction,
+ int32_t layerId, const Region& transRegion)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
+
+ for (const auto& rect : transRegion) {
+ Rectangle* protoRect(transparentChange->add_region());
+ setProtoRectLocked(protoRect, rect);
+ }
+}
+
+void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId,
+ uint8_t flags)
+{
+ // There can be multiple flags changed
+ if (flags & layer_state_t::eLayerHidden) {
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ HiddenFlagChange* flagChange(change->mutable_hidden_flag());
+ flagChange->set_hidden_flag(true);
+ }
+ if (flags & layer_state_t::eLayerOpaque) {
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
+ flagChange->set_opaque_flag(true);
+ }
+ if (flags & layer_state_t::eLayerSecure) {
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ SecureFlagChange* flagChange(change->mutable_secure_flag());
+ flagChange->set_secure_flag(true);
+ }
+}
+
+void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
+ uint32_t layerStack)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ LayerStackChange* layerStackChange(change->mutable_layer_stack());
+ layerStackChange->set_layer_stack(layerStack);
+}
+
+void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
+ const Rect& rect)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ CropChange* cropChange(change->mutable_crop());
+ Rectangle* protoRect(cropChange->mutable_rectangle());
+ setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addFinalCropLocked(Transaction* transaction, int32_t layerId,
+ const Rect& rect)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ FinalCropChange* finalCropChange(change->mutable_final_crop());
+ Rectangle* protoRect(finalCropChange->mutable_rectangle());
+ setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+ const wp<const IBinder>& weakHandle, uint64_t frameNumber)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ const sp<const Layer> layer(getLayer(weakHandle));
+ if (layer == nullptr) {
+ ALOGE("An existing layer could not be retrieved with the handle"
+ " for the deferred transaction");
+ return;
+ }
+ DeferredTransactionChange* deferTransaction(change->mutable_deferred_transaction());
+ deferTransaction->set_layer_id(getLayerId(layer));
+ deferTransaction->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction,
+ int32_t layerId, int32_t overrideScalingMode)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode());
+ overrideChange->set_override_scaling_mode(overrideScalingMode);
+}
+
+void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
+ const layer_state_t& state)
+{
+ const sp<const Layer> layer(getLayer(state.surface));
+ if (layer == nullptr) {
+ ALOGE("An existing layer could not be retrieved with the surface "
+ "from the layer_state_t surface in the update transaction");
+ return;
+ }
+
+ const int32_t layerId(getLayerId(layer));
+
+ if (state.what & layer_state_t::ePositionChanged) {
+ addPositionLocked(transaction, layerId, state.x, state.y);
+ }
+ if (state.what & layer_state_t::eLayerChanged) {
+ addDepthLocked(transaction, layerId, state.z);
+ }
+ if (state.what & layer_state_t::eSizeChanged) {
+ addSizeLocked(transaction, layerId, state.w, state.h);
+ }
+ if (state.what & layer_state_t::eAlphaChanged) {
+ addAlphaLocked(transaction, layerId, state.alpha);
+ }
+ if (state.what & layer_state_t::eMatrixChanged) {
+ addMatrixLocked(transaction, layerId, state.matrix);
+ }
+ if (state.what & layer_state_t::eTransparentRegionChanged) {
+ addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
+ }
+ if (state.what & layer_state_t::eFlagsChanged) {
+ addFlagsLocked(transaction, layerId, state.flags);
+ }
+ if (state.what & layer_state_t::eLayerStackChanged) {
+ addLayerStackLocked(transaction, layerId, state.layerStack);
+ }
+ if (state.what & layer_state_t::eCropChanged) {
+ addCropLocked(transaction, layerId, state.crop);
+ }
+ if (state.what & layer_state_t::eDeferTransaction) {
+ addDeferTransactionLocked(transaction, layerId, state.handle, state.frameNumber);
+ }
+ if (state.what & layer_state_t::eFinalCropChanged) {
+ addFinalCropLocked(transaction, layerId, state.finalCrop);
+ }
+ if (state.what & layer_state_t::eOverrideScalingModeChanged) {
+ addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
+ }
+}
+
+void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
+ const DisplayState& state, int32_t displayId)
+{
+ if (state.what & DisplayState::eSurfaceChanged) {
+ addDisplaySurfaceLocked(transaction, displayId, state.surface);
+ }
+ if (state.what & DisplayState::eLayerStackChanged) {
+ addDisplayLayerStackLocked(transaction, displayId, state.layerStack);
+ }
+ if (state.what & DisplayState::eDisplaySizeChanged) {
+ addDisplaySizeLocked(transaction, displayId, state.width, state.height);
+ }
+ if (state.what & DisplayState::eDisplayProjectionChanged) {
+ addDisplayProjectionLocked(transaction, displayId, state.orientation, state.viewport,
+ state.frame);
+ }
+}
+
+void SurfaceInterceptor::addTransactionLocked(Increment* increment,
+ const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags)
+{
+ Transaction* transaction(increment->mutable_transaction());
+ transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
+ transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+ for (const auto& compState: stateUpdates) {
+ addSurfaceChangesLocked(transaction, compState.state);
+ }
+ for (const auto& disp: changedDisplays) {
+ ssize_t dpyIdx = displays.indexOfKey(disp.token);
+ if (dpyIdx >= 0) {
+ const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
+ addDisplayChangesLocked(transaction, disp, dispState.displayId);
+ }
+ }
+}
+
+void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment,
+ const sp<const Layer>& layer)
+{
+ SurfaceCreation* creation(increment->mutable_surface_creation());
+ creation->set_id(getLayerId(layer));
+ creation->set_name(getLayerName(layer));
+ creation->set_w(layer->mCurrentState.active.w);
+ creation->set_h(layer->mCurrentState.active.h);
+}
+
+void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
+ const sp<const Layer>& layer)
+{
+ SurfaceDeletion* deletion(increment->mutable_surface_deletion());
+ deletion->set_id(getLayerId(layer));
+}
+
+void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer,
+ uint32_t width, uint32_t height, uint64_t frameNumber)
+{
+ BufferUpdate* update(increment->mutable_buffer_update());
+ update->set_id(getLayerId(layer));
+ update->set_w(width);
+ update->set_h(height);
+ update->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) {
+ VSyncEvent* event(increment->mutable_vsync_event());
+ event->set_when(timestamp);
+}
+
+void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+ const sp<const IGraphicBufferProducer>& surface)
+{
+ if (surface == nullptr) {
+ return;
+ }
+ uint64_t bufferQueueId = 0;
+ status_t err(surface->getUniqueId(&bufferQueueId));
+ if (err == NO_ERROR) {
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
+ surfaceChange->set_buffer_queue_id(bufferQueueId);
+ surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
+ }
+ else {
+ ALOGE("invalid graphic buffer producer received while tracing a display change (%s)",
+ strerror(-err));
+ }
+}
+
+void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
+ int32_t displayId, uint32_t layerStack)
+{
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
+ layerStackChange->set_layer_stack(layerStack);
+}
+
+void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t displayId,
+ uint32_t w, uint32_t h)
+{
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ SizeChange* sizeChange(dispChange->mutable_size());
+ sizeChange->set_w(w);
+ sizeChange->set_h(h);
+}
+
+void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
+ int32_t displayId, int32_t orientation, const Rect& viewport, const Rect& frame)
+{
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ ProjectionChange* projectionChange(dispChange->mutable_projection());
+ projectionChange->set_orientation(orientation);
+ Rectangle* viewportRect(projectionChange->mutable_viewport());
+ setProtoRectLocked(viewportRect, viewport);
+ Rectangle* frameRect(projectionChange->mutable_frame());
+ setProtoRectLocked(frameRect, frame);
+}
+
+void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment,
+ const DisplayDeviceState& info)
+{
+ DisplayCreation* creation(increment->mutable_display_creation());
+ creation->set_id(info.displayId);
+ creation->set_name(info.displayName);
+ creation->set_type(info.type);
+ creation->set_is_secure(info.isSecure);
+}
+
+void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t displayId) {
+ DisplayDeletion* deletion(increment->mutable_display_deletion());
+ deletion->set_id(displayId);
+}
+
+void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t displayId,
+ int32_t mode)
+{
+ PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
+ powerModeUpdate->set_id(displayId);
+ powerModeUpdate->set_mode(mode);
+}
+
+void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t flags)
+{
+ if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
+ flags);
+}
+
+void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
+ if (!mEnabled || layer == nullptr) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) {
+ if (!mEnabled || layer == nullptr) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveBufferUpdate(const sp<const Layer>& layer, uint32_t width,
+ uint32_t height, uint64_t frameNumber)
+{
+ if (!mEnabled || layer == nullptr) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addBufferUpdateLocked(createTraceIncrementLocked(), layer, width, height, frameNumber);
+}
+
+void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
+ if (!mEnabled) {
+ return;
+ }
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp);
+}
+
+void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) {
+ if (!mEnabled) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addDisplayCreationLocked(createTraceIncrementLocked(), info);
+}
+
+void SurfaceInterceptor::saveDisplayDeletion(int32_t displayId) {
+ if (!mEnabled) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addDisplayDeletionLocked(createTraceIncrementLocked(), displayId);
+}
+
+void SurfaceInterceptor::savePowerModeUpdate(int32_t displayId, int32_t mode) {
+ if (!mEnabled) {
+ return;
+ }
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addPowerModeUpdateLocked(createTraceIncrementLocked(), displayId, mode);
+}
+
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
new file mode 100644
index 0000000..4695138
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -0,0 +1,133 @@
+/*
+ * 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_SURFACEINTERCEPTOR_H
+#define ANDROID_SURFACEINTERCEPTOR_H
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <mutex>
+
+namespace android {
+
+class BufferItem;
+class Layer;
+struct DisplayState;
+struct layer_state_t;
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+/*
+ * SurfaceInterceptor intercepts and stores incoming streams of window
+ * properties on SurfaceFlinger.
+ */
+class SurfaceInterceptor {
+public:
+ // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
+ void enable(const SortedVector<sp<Layer>>& layers,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+ void disable();
+ bool isEnabled();
+
+ // Intercept display and surface transactions
+ void saveTransaction(const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t flags);
+
+ // Intercept surface data
+ void saveSurfaceCreation(const sp<const Layer>& layer);
+ void saveSurfaceDeletion(const sp<const Layer>& layer);
+ void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+ uint64_t frameNumber);
+
+ // Intercept display data
+ void saveDisplayCreation(const DisplayDeviceState& info);
+ void saveDisplayDeletion(int32_t displayId);
+ void savePowerModeUpdate(int32_t displayId, int32_t mode);
+ void saveVSyncEvent(nsecs_t timestamp);
+
+private:
+ // The creation increments of Surfaces and Displays do not contain enough information to capture
+ // the initial state of each object, so a transaction with all of the missing properties is
+ // performed at the initial snapshot for each display and surface.
+ void saveExistingDisplaysLocked(
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+ void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
+ void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
+ void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
+
+ status_t writeProtoFileLocked();
+ const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle);
+ const std::string getLayerName(const sp<const Layer>& layer);
+ int32_t getLayerId(const sp<const Layer>& layer);
+
+ Increment* createTraceIncrementLocked();
+ void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
+ void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
+ void addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+ uint32_t height, uint64_t frameNumber);
+ void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
+ void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
+ void addDisplayDeletionLocked(Increment* increment, int32_t displayId);
+ void addPowerModeUpdateLocked(Increment* increment, int32_t displayId, int32_t mode);
+
+ // Add surface transactions to the trace
+ SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
+ void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
+ void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
+ void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
+ void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
+ void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
+ void addMatrixLocked(Transaction* transaction, int32_t layerId,
+ const layer_state_t::matrix22_t& matrix);
+ void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
+ const Region& transRegion);
+ void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
+ void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+ void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+ const wp<const IBinder>& weakHandle, uint64_t frameNumber);
+ void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
+ int32_t overrideScalingMode);
+ void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
+ void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+
+ // Add display transactions to the trace
+ DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t displayId);
+ void addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+ const sp<const IGraphicBufferProducer>& surface);
+ void addDisplayLayerStackLocked(Transaction* transaction, int32_t displayId,
+ uint32_t layerStack);
+ void addDisplaySizeLocked(Transaction* transaction, int32_t displayId, uint32_t w,
+ uint32_t h);
+ void addDisplayProjectionLocked(Transaction* transaction, int32_t displayId,
+ int32_t orientation, const Rect& viewport, const Rect& frame);
+ void addDisplayChangesLocked(Transaction* transaction,
+ const DisplayState& state, int32_t displayId);
+
+
+ bool mEnabled {false};
+ std::string mOutputFileName {DEFAULT_FILENAME};
+ std::mutex mTraceMutex {};
+ Trace mTrace {};
+};
+
+}
+
+#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
index 979062e..4f1a8e6 100644
--- a/services/surfaceflinger/tests/Android.mk
+++ b/services/surfaceflinger/tests/Android.mk
@@ -9,15 +9,21 @@
LOCAL_SRC_FILES := \
Transaction_test.cpp \
+ SurfaceInterceptor_test.cpp
LOCAL_SHARED_LIBRARIES := \
- libEGL \
- libGLESv2 \
- libbinder \
- libcutils \
- libgui \
- libui \
- libutils \
+ libEGL \
+ libGLESv2 \
+ libbinder \
+ libcutils \
+ libgui \
+ libprotobuf-cpp-full \
+ libui \
+ libutils \
+
+LOCAL_STATIC_LIBRARIES := libtrace_proto
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
# to integrate with auto-test framework.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
new file mode 100644
index 0000000..1a3f89f
--- /dev/null
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -0,0 +1,856 @@
+/*
+ * 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 <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <gtest/gtest.h>
+
+#include <android/native_window.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+#include <ui/DisplayInfo.h>
+
+#include <fstream>
+#include <random>
+#include <thread>
+
+namespace android {
+
+constexpr int32_t SCALING_UPDATE = 1;
+constexpr uint32_t BUFFER_UPDATES = 18;
+constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
+constexpr uint32_t SIZE_UPDATE = 134;
+constexpr uint32_t STACK_UPDATE = 1;
+constexpr uint64_t DEFERRED_UPDATE = 13;
+constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float POSITION_UPDATE = 121;
+const Rect CROP_UPDATE(16, 16, 32, 32);
+
+const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
+constexpr auto LAYER_NAME = "Layer Create and Delete Test";
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
+ ANativeWindow_Buffer outBuffer;
+ sp<Surface> s = sc->getSurface();
+ ASSERT_TRUE(s != nullptr);
+ ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+ uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+ for (int y = 0; y < outBuffer.height; y++) {
+ for (int x = 0; x < outBuffer.width; x++) {
+ uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ pixel[3] = 255;
+ }
+ }
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+}
+
+static status_t readProtoFile(Trace* trace) {
+ std::ifstream input(DEFAULT_FILENAME, std::ios::in | std::ios::binary);
+ if (input && !trace->ParseFromIstream(&input)) {
+ return PERMISSION_DENIED;
+ }
+ return NO_ERROR;
+}
+
+static void enableInterceptor() {
+ system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
+}
+
+static void disableInterceptor() {
+ system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
+}
+
+int32_t getSurfaceId(const std::string& surfaceName) {
+ enableInterceptor();
+ disableInterceptor();
+ Trace capturedTrace;
+ readProtoFile(&capturedTrace);
+ int32_t layerId = 0;
+ for (const auto& increment : *capturedTrace.mutable_increment()) {
+ if (increment.increment_case() == increment.kSurfaceCreation) {
+ if (increment.surface_creation().name() == surfaceName) {
+ layerId = increment.surface_creation().id();
+ break;
+ }
+ }
+ }
+ return layerId;
+}
+
+int32_t getDisplayId(const std::string& displayName) {
+ enableInterceptor();
+ disableInterceptor();
+ Trace capturedTrace;
+ readProtoFile(&capturedTrace);
+ int32_t displayId = 0;
+ for (const auto& increment : *capturedTrace.mutable_increment()) {
+ if (increment.increment_case() == increment.kDisplayCreation) {
+ if (increment.display_creation().name() == displayName) {
+ displayId = increment.display_creation().id();
+ break;
+ }
+ }
+ }
+ return displayId;
+}
+
+class SurfaceInterceptorTest : public ::testing::Test {
+protected:
+ virtual void SetUp() {
+ // Allow SurfaceInterceptor write to /data
+ system("setenforce 0");
+
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ssize_t displayWidth = info.w;
+ ssize_t displayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl = mComposerClient->createSurface(
+ String8("BG Interceptor Test Surface"), displayWidth, displayHeight,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != NULL);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ mBGLayerId = getSurfaceId("BG Interceptor Test Surface");
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mComposerClient->setDisplayLayerStack(display, 0);
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-3));
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ }
+
+ virtual void TearDown() {
+ mComposerClient->dispose();
+ mBGSurfaceControl.clear();
+ mComposerClient.clear();
+ }
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mBGSurfaceControl;
+ int32_t mBGLayerId;
+ // Used to verify creation and destruction of surfaces and displays
+ int32_t mTargetId;
+
+public:
+ void captureTest(void (SurfaceInterceptorTest::* action)(void),
+ bool (SurfaceInterceptorTest::* verification)(Trace *));
+ void captureTest(void (SurfaceInterceptorTest::* action)(void),
+ SurfaceChange::SurfaceChangeCase changeCase);
+ void captureTest(void (SurfaceInterceptorTest::* action)(void),
+ Increment::IncrementCase incrementCase);
+ void runInTransaction(void (SurfaceInterceptorTest::* action)(void), bool intercepted = false);
+
+ // Verification of changes to a surface
+ bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
+ bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
+ bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
+ bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
+ bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
+ bool finalCropUpdateFound(const SurfaceChange& change, bool foundFinalCrop);
+ bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
+ bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
+ bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
+ bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
+ bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
+ bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
+ bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
+ bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
+ bool surfaceUpdateFound(Trace* trace, SurfaceChange::SurfaceChangeCase changeCase);
+ void assertAllUpdatesFound(Trace* trace);
+
+ // Verification of creation and deletion of a surface
+ bool surfaceCreationFound(const Increment& increment, bool foundSurface);
+ bool surfaceDeletionFound(const Increment& increment, bool foundSurface);
+ bool displayCreationFound(const Increment& increment, bool foundDisplay);
+ bool displayDeletionFound(const Increment& increment, bool foundDisplay);
+ bool singleIncrementFound(Trace* trace, Increment::IncrementCase incrementCase);
+
+ // Verification of buffer updates
+ bool bufferUpdatesFound(Trace* trace);
+
+ // Perform each of the possible changes to a surface
+ void positionUpdate();
+ void sizeUpdate();
+ void alphaUpdate();
+ void layerUpdate();
+ void cropUpdate();
+ void finalCropUpdate();
+ void matrixUpdate();
+ void overrideScalingModeUpdate();
+ void transparentRegionHintUpdate();
+ void layerStackUpdate();
+ void hiddenFlagUpdate();
+ void opaqueFlagUpdate();
+ void secureFlagUpdate();
+ void deferredTransactionUpdate();
+ void runAllUpdates();
+ void surfaceCreation();
+ void nBufferUpdates();
+ void displayCreation();
+ void displayDeletion();
+};
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ bool (SurfaceInterceptorTest::* verification)(Trace *))
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE((this->*verification)(&capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ Increment::IncrementCase incrementCase)
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(&capturedTrace, incrementCase));
+}
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ SurfaceChange::SurfaceChangeCase changeCase)
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase));
+}
+
+void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(void),
+ bool intercepted)
+{
+ if (intercepted) {
+ enableInterceptor();
+ }
+ SurfaceComposerClient::openGlobalTransaction();
+ (this->*action)();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ if (intercepted) {
+ disableInterceptor();
+ }
+}
+
+void SurfaceInterceptorTest::positionUpdate() {
+ mBGSurfaceControl->setPosition(POSITION_UPDATE, POSITION_UPDATE);
+}
+
+void SurfaceInterceptorTest::sizeUpdate() {
+ mBGSurfaceControl->setSize(SIZE_UPDATE, SIZE_UPDATE);
+}
+
+void SurfaceInterceptorTest::alphaUpdate() {
+ mBGSurfaceControl->setAlpha(ALPHA_UPDATE);
+}
+
+void SurfaceInterceptorTest::layerUpdate() {
+ mBGSurfaceControl->setLayer(LAYER_UPDATE);
+}
+
+void SurfaceInterceptorTest::cropUpdate() {
+ mBGSurfaceControl->setCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::finalCropUpdate() {
+ mBGSurfaceControl->setFinalCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::matrixUpdate() {
+ mBGSurfaceControl->setMatrix(M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
+}
+
+void SurfaceInterceptorTest::overrideScalingModeUpdate() {
+ mBGSurfaceControl->setOverrideScalingMode(SCALING_UPDATE);
+}
+
+void SurfaceInterceptorTest::transparentRegionHintUpdate() {
+ Region region(CROP_UPDATE);
+ mBGSurfaceControl->setTransparentRegionHint(region);
+}
+
+void SurfaceInterceptorTest::layerStackUpdate() {
+ mBGSurfaceControl->setLayerStack(STACK_UPDATE);
+}
+
+void SurfaceInterceptorTest::hiddenFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
+}
+
+void SurfaceInterceptorTest::opaqueFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+}
+
+void SurfaceInterceptorTest::secureFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+}
+
+void SurfaceInterceptorTest::deferredTransactionUpdate() {
+ mBGSurfaceControl->deferTransactionUntil(mBGSurfaceControl->getHandle(), DEFERRED_UPDATE);
+}
+
+void SurfaceInterceptorTest::displayCreation() {
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+ SurfaceComposerClient::destroyDisplay(testDisplay);
+}
+
+void SurfaceInterceptorTest::displayDeletion() {
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
+ mTargetId = getDisplayId(DISPLAY_NAME.string());
+ SurfaceComposerClient::destroyDisplay(testDisplay);
+}
+
+void SurfaceInterceptorTest::runAllUpdates() {
+ runInTransaction(&SurfaceInterceptorTest::positionUpdate);
+ runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
+ runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+ runInTransaction(&SurfaceInterceptorTest::layerUpdate);
+ runInTransaction(&SurfaceInterceptorTest::cropUpdate);
+ runInTransaction(&SurfaceInterceptorTest::finalCropUpdate);
+ runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
+ runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
+ runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
+ runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
+ runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
+}
+
+void SurfaceInterceptorTest::surfaceCreation() {
+ mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
+ PIXEL_FORMAT_RGBA_8888, 0);
+}
+
+void SurfaceInterceptorTest::nBufferUpdates() {
+ std::random_device rd;
+ std::mt19937_64 gen(rd());
+ // This makes testing fun
+ std::uniform_int_distribution<uint8_t> dis;
+ for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
+ fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
+ }
+}
+
+bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
+ // There should only be one position transaction with x and y = POSITION_UPDATE
+ bool hasX(change.position().x() == POSITION_UPDATE);
+ bool hasY(change.position().y() == POSITION_UPDATE);
+ if (hasX && hasY && !foundPosition) {
+ foundPosition = true;
+ }
+ // Failed because the position update was found a second time
+ else if (hasX && hasY && foundPosition) {
+ [] () { FAIL(); }();
+ }
+ return foundPosition;
+}
+
+bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool foundSize) {
+ bool hasWidth(change.size().h() == SIZE_UPDATE);
+ bool hasHeight(change.size().w() == SIZE_UPDATE);
+ if (hasWidth && hasHeight && !foundSize) {
+ foundSize = true;
+ }
+ else if (hasWidth && hasHeight && foundSize) {
+ [] () { FAIL(); }();
+ }
+ return foundSize;
+}
+
+bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
+ bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
+ if (hasAlpha && !foundAlpha) {
+ foundAlpha = true;
+ }
+ else if (hasAlpha && foundAlpha) {
+ [] () { FAIL(); }();
+ }
+ return foundAlpha;
+}
+
+bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
+ bool hasLayer(change.layer().layer() == LAYER_UPDATE);
+ if (hasLayer && !foundLayer) {
+ foundLayer = true;
+ }
+ else if (hasLayer && foundLayer) {
+ [] () { FAIL(); }();
+ }
+ return foundLayer;
+}
+
+bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
+ bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
+ bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
+ bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
+ bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
+ foundCrop = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
+ [] () { FAIL(); }();
+ }
+ return foundCrop;
+}
+
+bool SurfaceInterceptorTest::finalCropUpdateFound(const SurfaceChange& change,
+ bool foundFinalCrop)
+{
+ bool hasLeft(change.final_crop().rectangle().left() == CROP_UPDATE.left);
+ bool hasTop(change.final_crop().rectangle().top() == CROP_UPDATE.top);
+ bool hasRight(change.final_crop().rectangle().right() == CROP_UPDATE.right);
+ bool hasBottom(change.final_crop().rectangle().bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundFinalCrop) {
+ foundFinalCrop = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundFinalCrop) {
+ [] () { FAIL(); }();
+ }
+ return foundFinalCrop;
+}
+
+bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
+ bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
+ bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
+ bool hasSy((float)change.matrix().dsdy() == (float)-M_SQRT1_2);
+ bool hasTy((float)change.matrix().dtdy() == (float)M_SQRT1_2);
+ if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
+ foundMatrix = true;
+ }
+ else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
+ [] () { FAIL(); }();
+ }
+ return foundMatrix;
+}
+
+bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
+ bool foundScalingMode)
+{
+ bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
+ if (hasScalingUpdate && !foundScalingMode) {
+ foundScalingMode = true;
+ }
+ else if (hasScalingUpdate && foundScalingMode) {
+ [] () { FAIL(); }();
+ }
+ return foundScalingMode;
+}
+
+bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
+ bool foundTransparentRegion)
+{
+ auto traceRegion = change.transparent_region_hint().region(0);
+ bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
+ bool hasTop(traceRegion.top() == CROP_UPDATE.top);
+ bool hasRight(traceRegion.right() == CROP_UPDATE.right);
+ bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
+ foundTransparentRegion = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
+ [] () { FAIL(); }();
+ }
+ return foundTransparentRegion;
+}
+
+bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
+ bool foundLayerStack)
+{
+ bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
+ if (hasLayerStackUpdate && !foundLayerStack) {
+ foundLayerStack = true;
+ }
+ else if (hasLayerStackUpdate && foundLayerStack) {
+ [] () { FAIL(); }();
+ }
+ return foundLayerStack;
+}
+
+bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
+ bool foundHiddenFlag)
+{
+ bool hasHiddenFlag(change.hidden_flag().hidden_flag());
+ if (hasHiddenFlag && !foundHiddenFlag) {
+ foundHiddenFlag = true;
+ }
+ else if (hasHiddenFlag && foundHiddenFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundHiddenFlag;
+}
+
+bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
+ bool foundOpaqueFlag)
+{
+ bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
+ if (hasOpaqueFlag && !foundOpaqueFlag) {
+ foundOpaqueFlag = true;
+ }
+ else if (hasOpaqueFlag && foundOpaqueFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundOpaqueFlag;
+}
+
+bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
+ bool foundSecureFlag)
+{
+ bool hasSecureFlag(change.secure_flag().secure_flag());
+ if (hasSecureFlag && !foundSecureFlag) {
+ foundSecureFlag = true;
+ }
+ else if (hasSecureFlag && foundSecureFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundSecureFlag;
+}
+
+bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
+ bool foundDeferred)
+{
+ bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
+ bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
+ if (hasId && hasFrameNumber && !foundDeferred) {
+ foundDeferred = true;
+ }
+ else if (hasId && hasFrameNumber && foundDeferred) {
+ [] () { FAIL(); }();
+ }
+ return foundDeferred;
+}
+
+bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace,
+ SurfaceChange::SurfaceChangeCase changeCase)
+{
+ bool foundUpdate = false;
+ for (const auto& increment : *trace->mutable_increment()) {
+ if (increment.increment_case() == increment.kTransaction) {
+ for (const auto& change : increment.transaction().surface_change()) {
+ if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
+ switch (changeCase) {
+ case SurfaceChange::SurfaceChangeCase::kPosition:
+ // foundUpdate is sent for the tests to fail on duplicated increments
+ foundUpdate = positionUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSize:
+ foundUpdate = sizeUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kAlpha:
+ foundUpdate = alphaUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayer:
+ foundUpdate = layerUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kCrop:
+ foundUpdate = cropUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+ foundUpdate = finalCropUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kMatrix:
+ foundUpdate = matrixUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+ foundUpdate = scalingModeUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+ foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayerStack:
+ foundUpdate = layerStackUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+ foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+ foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+ foundUpdate = secureFlagUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+ foundUpdate = deferredTransactionUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
+ break;
+ }
+ }
+ }
+ }
+ }
+ return foundUpdate;
+}
+
+void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) {
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kFinalCrop));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
+}
+
+bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
+ bool isMatch(increment.surface_creation().name() == LAYER_NAME &&
+ increment.surface_creation().w() == SIZE_UPDATE &&
+ increment.surface_creation().h() == SIZE_UPDATE);
+ if (isMatch && !foundSurface) {
+ foundSurface = true;
+ }
+ else if (isMatch && foundSurface) {
+ [] () { FAIL(); }();
+ }
+ return foundSurface;
+}
+
+bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, bool foundSurface) {
+ bool isMatch(increment.surface_deletion().id() == mTargetId);
+ if (isMatch && !foundSurface) {
+ foundSurface = true;
+ }
+ else if (isMatch && foundSurface) {
+ [] () { FAIL(); }();
+ }
+ return foundSurface;
+}
+
+bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
+ bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
+ increment.display_creation().is_secure());
+ if (isMatch && !foundDisplay) {
+ foundDisplay = true;
+ }
+ else if (isMatch && foundDisplay) {
+ [] () { FAIL(); }();
+ }
+ return foundDisplay;
+}
+
+bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, bool foundDisplay) {
+ bool isMatch(increment.display_deletion().id() == mTargetId);
+ if (isMatch && !foundDisplay) {
+ foundDisplay = true;
+ }
+ else if (isMatch && foundDisplay) {
+ [] () { FAIL(); }();
+ }
+ return foundDisplay;
+}
+
+bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace,
+ Increment::IncrementCase incrementCase)
+{
+ bool foundIncrement = false;
+ for (const auto& increment : *trace->mutable_increment()) {
+ if (increment.increment_case() == incrementCase) {
+ switch (incrementCase) {
+ case Increment::IncrementCase::kSurfaceCreation:
+ foundIncrement = surfaceCreationFound(increment, foundIncrement);
+ break;
+ case Increment::IncrementCase::kSurfaceDeletion:
+ foundIncrement = surfaceDeletionFound(increment, foundIncrement);
+ break;
+ case Increment::IncrementCase::kDisplayCreation:
+ foundIncrement = displayCreationFound(increment, foundIncrement);
+ break;
+ case Increment::IncrementCase::kDisplayDeletion:
+ foundIncrement = displayDeletionFound(increment, foundIncrement);
+ break;
+ default:
+ /* code */
+ break;
+ }
+ }
+ }
+ return foundIncrement;
+}
+
+bool SurfaceInterceptorTest::bufferUpdatesFound(Trace* trace) {
+ uint32_t updates = 0;
+ for (const auto& inc : *trace->mutable_increment()) {
+ if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
+ updates++;
+ }
+ }
+ return updates == BUFFER_UPDATES;
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::positionUpdate,
+ SurfaceChange::SurfaceChangeCase::kPosition);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptFinalCropUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::finalCropUpdate,
+ SurfaceChange::SurfaceChangeCase::kFinalCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
+ SurfaceChange::SurfaceChangeCase::kOverrideScalingMode);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
+ SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::layerStackUpdate,
+ SurfaceChange::SurfaceChangeCase::kLayerStack);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
+ SurfaceChange::SurfaceChangeCase::kHiddenFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
+ SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
+ SurfaceChange::SurfaceChangeCase::kSecureFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::deferredTransactionUpdate,
+ SurfaceChange::SurfaceChangeCase::kDeferredTransaction);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
+ enableInterceptor();
+ runAllUpdates();
+ disableInterceptor();
+
+ // Find all of the updates in the single trace
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ assertAllUpdatesFound(&capturedTrace);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
+ captureTest(&SurfaceInterceptorTest::surfaceCreation,
+ Increment::IncrementCase::kSurfaceCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSurfaceDeletionWorks) {
+ sp<SurfaceControl> layerToDelete = mComposerClient->createSurface(String8(LAYER_NAME),
+ SIZE_UPDATE, SIZE_UPDATE, PIXEL_FORMAT_RGBA_8888, 0);
+ this->mTargetId = getSurfaceId(LAYER_NAME);
+ enableInterceptor();
+ mComposerClient->destroySurface(layerToDelete->getHandle());
+ disableInterceptor();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceDeletion));
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
+ captureTest(&SurfaceInterceptorTest::displayCreation,
+ Increment::IncrementCase::kDisplayCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
+ captureTest(&SurfaceInterceptorTest::displayDeletion,
+ Increment::IncrementCase::kDisplayDeletion);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::nBufferUpdates,
+ &SurfaceInterceptorTest::bufferUpdatesFound);
+}
+
+// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
+// first create a snapshot of the existing displays and surfaces and then start capturing
+// the buffer updates
+TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
+ std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+ enableInterceptor();
+ disableInterceptor();
+ bufferUpdates.join();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ const auto& firstIncrement = capturedTrace.mutable_increment(0);
+ ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
+ enableInterceptor();
+ std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+ std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
+ runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
+ bufferUpdates.join();
+ surfaceUpdates.join();
+ disableInterceptor();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+
+ assertAllUpdatesFound(&capturedTrace);
+ ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceCreation));
+}
+
+}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index f8d4d13..79cd245 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -61,7 +61,6 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- IGraphicBufferProducer::QueueBufferOutput bufferOutput;
sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sp<IBinder> display(sf->getBuiltInDisplay(
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 870f8eb..37cc448 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -28,7 +28,7 @@
// API version (major.minor.patch)
define VERSION_MAJOR 1
define VERSION_MINOR 0
-define VERSION_PATCH 13
+define VERSION_PATCH 22
// API limits
define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -78,7 +78,7 @@
@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 5
@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME "VK_ANDROID_native_buffer"
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 2
+@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 3
@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME "VK_EXT_debug_report"
@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION 1
@@ -93,9 +93,21 @@
@extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1
@extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_NAME "VK_AMD_rasterization_order"
+@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
+@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
+
+@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
+@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
+
@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION 3
@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_NAME "VK_EXT_debug_marker"
+@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_SPEC_VERSION 1
+@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader"
+
+@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
+@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
+
/////////////
// Types //
@@ -684,6 +696,15 @@
//@extension("VK_EXT_debug_marker")
VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002,
+
+ //@extension("VK_NV_dedicated_allocation")
+ VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
+
+ //@extension("VK_NV_dedicated_allocation")
+ VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
+
+ //@extension("VK_NV_dedicated_allocation")
+ VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
}
enum VkSubpassContents {
@@ -721,6 +742,7 @@
VK_ERROR_INCOMPATIBLE_DRIVER = 0xFFFFFFF7, // -9
VK_ERROR_TOO_MANY_OBJECTS = 0xFFFFFFF6, // -10
VK_ERROR_FORMAT_NOT_SUPPORTED = 0xFFFFFFF5, // -11
+ VK_ERROR_FRAGMENTED_POOL = 0xFFFFFFF4, // -12
//@extension("VK_KHR_surface")
VK_ERROR_SURFACE_LOST_KHR = 0xC4653600, // -1000000000
@@ -2693,6 +2715,28 @@
f32[4] color
}
+@extension("VK_NV_dedicated_allocation")
+class VkDedicatedAllocationImageCreateInfoNV {
+ VkStructureType sType
+ const void* pNext
+ VkBool32 dedicatedAllocation
+}
+
+@extension("VK_NV_dedicated_allocation")
+class VkDedicatedAllocationBufferCreateInfoNV {
+ VkStructureType sType
+ const void* pNext
+ VkBool32 dedicatedAllocation
+}
+
+@extension("VK_NV_dedicated_allocation")
+class VkDedicatedAllocationMemoryAllocateInfoNV {
+ VkStructureType sType
+ const void* pNext
+ VkImage image
+ VkBuffer buffer
+}
+
////////////////
// Commands //
@@ -4570,7 +4614,7 @@
VkBuffer dstBuffer,
VkDeviceSize dstOffset,
VkDeviceSize dataSize,
- const u32* pData) {
+ const void* pData) {
commandBufferObject := GetCommandBuffer(commandBuffer)
dstBufferObject := GetBuffer(dstBuffer)
assert(commandBufferObject.device == dstBufferObject.device)
diff --git a/vulkan/include/vulkan/vk_platform.h b/vulkan/include/vulkan/vk_platform.h
index 5d0fc76..c2232ec 100644
--- a/vulkan/include/vulkan/vk_platform.h
+++ b/vulkan/include/vulkan/vk_platform.h
@@ -51,13 +51,13 @@
#define VKAPI_ATTR
#define VKAPI_CALL __stdcall
#define VKAPI_PTR VKAPI_CALL
-#elif defined(__ANDROID__) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__)
- // Android does not support Vulkan in native code using the "armeabi" ABI.
- #error "Vulkan requires the 'armeabi-v7a' or 'armeabi-v7a-hard' ABI on 32-bit ARM CPUs"
-#elif defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
- // On Android/ARMv7a, Vulkan functions use the armeabi-v7a-hard calling
- // convention, even if the application's native code is compiled with the
- // armeabi-v7a calling convention.
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
+ #error "Vulkan isn't supported for the 'armeabi' NDK ABI"
+#elif defined(__ANDROID__) && __ARM_ARCH >= 7 && __ARM_32BIT_STATE
+ // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
+ // calling convention, i.e. float parameters are passed in registers. This
+ // is true even if the rest of the application passes floats on the stack,
+ // as it does by default when compiling for the armeabi-v7a NDK ABI.
#define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
#define VKAPI_CALL
#define VKAPI_PTR VKAPI_ATTR
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 2f18076..ddbb311 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -43,7 +43,7 @@
#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
// Version of this file
-#define VK_HEADER_VERSION 13
+#define VK_HEADER_VERSION 22
#define VK_NULL_HANDLE 0
@@ -53,7 +53,7 @@
#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
-#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
#else
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
@@ -135,6 +135,7 @@
VK_ERROR_INCOMPATIBLE_DRIVER = -9,
VK_ERROR_TOO_MANY_OBJECTS = -10,
VK_ERROR_FORMAT_NOT_SUPPORTED = -11,
+ VK_ERROR_FRAGMENTED_POOL = -12,
VK_ERROR_SURFACE_LOST_KHR = -1000000000,
VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001,
VK_SUBOPTIMAL_KHR = 1000001003,
@@ -142,9 +143,9 @@
VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001,
VK_ERROR_VALIDATION_FAILED_EXT = -1000011001,
VK_ERROR_INVALID_SHADER_NV = -1000012000,
- VK_RESULT_BEGIN_RANGE = VK_ERROR_FORMAT_NOT_SUPPORTED,
+ VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL,
VK_RESULT_END_RANGE = VK_INCOMPLETE,
- VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FORMAT_NOT_SUPPORTED + 1),
+ VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1),
VK_RESULT_MAX_ENUM = 0x7FFFFFFF
} VkResult;
@@ -214,6 +215,9 @@
VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000,
VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001,
VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002,
+ VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
+ VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
+ VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -2347,7 +2351,7 @@
typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -3032,7 +3036,7 @@
VkBuffer dstBuffer,
VkDeviceSize dstOffset,
VkDeviceSize dataSize,
- const uint32_t* pData);
+ const void* pData);
VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer(
VkCommandBuffer commandBuffer,
@@ -3715,7 +3719,7 @@
#define VK_EXT_debug_report 1
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
-#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 2
+#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 3
#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report"
#define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT
@@ -3855,6 +3859,16 @@
+#define VK_AMD_shader_trinary_minmax 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
+
+
+#define VK_AMD_shader_explicit_vertex_parameter 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
+
+
#define VK_EXT_debug_marker 1
#define VK_EXT_DEBUG_MARKER_SPEC_VERSION 3
#define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker"
@@ -3912,6 +3926,36 @@
VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
#endif
+#define VK_AMD_gcn_shader 1
+#define VK_AMD_GCN_SHADER_SPEC_VERSION 1
+#define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader"
+
+
+#define VK_NV_dedicated_allocation 1
+#define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
+#define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
+
+typedef struct VkDedicatedAllocationImageCreateInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ VkBool32 dedicatedAllocation;
+} VkDedicatedAllocationImageCreateInfoNV;
+
+typedef struct VkDedicatedAllocationBufferCreateInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ VkBool32 dedicatedAllocation;
+} VkDedicatedAllocationBufferCreateInfoNV;
+
+typedef struct VkDedicatedAllocationMemoryAllocateInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ VkImage image;
+ VkBuffer buffer;
+} VkDedicatedAllocationMemoryAllocateInfoNV;
+
+
+
#ifdef __cplusplus
}
#endif
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 0a1dda2..a57ba72 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -394,7 +394,7 @@
VKAPI_ATTR void CmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
VKAPI_ATTR void CmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
VKAPI_ATTR void CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
VKAPI_ATTR void CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
VKAPI_ATTR void CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
VKAPI_ATTR void CmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -1075,7 +1075,7 @@
GetData(commandBuffer).dispatch.CmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions);
}
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData) {
GetData(commandBuffer).dispatch.CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
}
@@ -1795,7 +1795,7 @@
}
__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData) {
vulkan::api::CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
}
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 82169ff..8f35f4d 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -68,7 +68,7 @@
class LayerLibrary {
public:
- LayerLibrary(const std::string& path)
+ explicit LayerLibrary(const std::string& path)
: path_(path), dlhandle_(nullptr), refcount_(0) {}
LayerLibrary(LayerLibrary&& other)
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 3bf3ff7..05ebcac 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -1334,7 +1334,7 @@
void CmdCopyImageToBuffer(VkCommandBuffer cmdBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer destBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions) {
}
-void CmdUpdateBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+void CmdUpdateBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize dataSize, const void* pData) {
}
void CmdFillBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize fillSize, uint32_t data) {
diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl
index 3a84971..209d61d 100644
--- a/vulkan/nulldrv/null_driver.tmpl
+++ b/vulkan/nulldrv/null_driver.tmpl
@@ -158,16 +158,7 @@
}
¶
PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {«
- PFN_vkVoidFunction pfn;
- if ((pfn = Lookup(name, kInstanceProcs)))
- return pfn;
- if (strcmp(name, "vkGetSwapchainGrallocUsageANDROID") == 0)
- return reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID));
- if (strcmp(name, "vkAcquireImageANDROID") == 0)
- return reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID));
- if (strcmp(name, "vkQueueSignalReleaseImageANDROID") == 0)
- return reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID));
- return nullptr;
+ return Lookup(name, kInstanceProcs);
»}
¶
} // namespace null_driver
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 10da993..b078ad1 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -54,6 +54,7 @@
const NameProc kInstanceProcs[] = {
// clang-format off
+ {"vkAcquireImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID))},
{"vkAllocateCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateCommandBuffers>(AllocateCommandBuffers))},
{"vkAllocateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateDescriptorSets>(AllocateDescriptorSets))},
{"vkAllocateMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateMemory>(AllocateMemory))},
@@ -179,10 +180,12 @@
{"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
{"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
{"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+ {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
{"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
{"vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMergePipelineCaches>(MergePipelineCaches))},
{"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueBindSparse>(QueueBindSparse))},
+ {"vkQueueSignalReleaseImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID))},
{"vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSubmit>(QueueSubmit))},
{"vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueWaitIdle>(QueueWaitIdle))},
{"vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetCommandBuffer>(ResetCommandBuffer))},
@@ -206,21 +209,7 @@
}
PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {
- PFN_vkVoidFunction pfn;
- if ((pfn = Lookup(name, kInstanceProcs)))
- return pfn;
- if (strcmp(name, "vkGetSwapchainGrallocUsageANDROID") == 0)
- return reinterpret_cast<PFN_vkVoidFunction>(
- static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(
- GetSwapchainGrallocUsageANDROID));
- if (strcmp(name, "vkAcquireImageANDROID") == 0)
- return reinterpret_cast<PFN_vkVoidFunction>(
- static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID));
- if (strcmp(name, "vkQueueSignalReleaseImageANDROID") == 0)
- return reinterpret_cast<PFN_vkVoidFunction>(
- static_cast<PFN_vkQueueSignalReleaseImageANDROID>(
- QueueSignalReleaseImageANDROID));
- return nullptr;
+ return Lookup(name, kInstanceProcs);
}
} // namespace null_driver
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 98952b8..4052d26 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -145,7 +145,7 @@
VKAPI_ATTR void CmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
VKAPI_ATTR void CmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
VKAPI_ATTR void CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
VKAPI_ATTR void CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
VKAPI_ATTR void CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
VKAPI_ATTR void CmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -165,6 +165,9 @@
VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents);
VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer);
VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
+VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
VKAPI_ATTR VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
VKAPI_ATTR void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);