Merge "Rely on the platform -std default." am: 53b6fc5ddc am: 0ef01bab39 am: b48bc88b75
am: a16f14f826
Change-Id: Idadc8d71628f57e8c50abc6dd1a928dc42590fbc
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 c2f8891..f3b12b1 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..35700b4 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -21,6 +21,7 @@
#include <binder/ProcessState.h>
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
#include <binder/TextOutput.h>
#include <utils/Vector.h>
@@ -29,7 +30,14 @@
#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>
using namespace android;
@@ -38,6 +46,45 @@
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:
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+ String8 path8(path);
+ char cwd[256];
+ getcwd(cwd, 256);
+ String8 fullPath(cwd);
+ fullPath.appendPath(path8);
+ 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:
@@ -91,6 +138,6 @@
// TODO: block until a result is returned to MyResultReceiver.
IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
- new MyResultReceiver());
+ new MyShellCallback(), new MyResultReceiver());
return 0;
}
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 e478651..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 libhardware_legacy
-# 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..c33fc1f 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).
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index e67b67f..32dbf8d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -35,9 +35,10 @@
#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>
@@ -51,23 +52,22 @@
#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;
+// Command-line arguments as string
+static std::string args;
+
// TODO: variables below should be part of dumpstate object
-static unsigned long id;
-static char build_type[PROPERTY_VALUE_MAX];
static time_t now;
static std::unique_ptr<ZipWriter> zip_writer;
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;
+static std::string extraOptions;
#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
@@ -93,22 +93,39 @@
static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
-const std::string ZIP_ROOT_DIR = "FS";
-std::string bugreport_dir;
+// 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();
+}
/*
* List of supported zip format versions.
*
- * See bugreport-format.txt for more info.
+ * See bugreport-format.md for more info.
*/
static std::string VERSION_DEFAULT = "1.0";
-bool is_user_build() {
- return 0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1);
-}
+// Relative directory (inside the zip) for all files copied as-is into the bugreport.
+static const std::string ZIP_ROOT_DIR = "FS";
-/* 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 constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
+static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+
+/* 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;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -116,18 +133,18 @@
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) &&
+ if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
(zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) {
- data[i].fd = fd;
+ 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
@@ -154,11 +171,11 @@
void add_mountinfo() {
if (!zip_writer) return;
- const char *title = "MOUNT INFO";
+ 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 durationReporter(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)
@@ -177,7 +194,7 @@
continue;
}
snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename);
- dump_file(title, path);
+ DumpFile(title, path);
}
closedir(d);
@@ -245,7 +262,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;
}
@@ -318,7 +336,7 @@
MYLOGD("Not dumping systrace because zip_writer is not set\n");
return;
}
- std::string systrace_path = bugreport_dir + "/systrace-" + suffix + ".txt";
+ std::string systrace_path = ds.bugreportDir_ + "/systrace-" + suffix + ".txt";
if (systrace_path.empty()) {
MYLOGE("Not dumping systrace because path is empty\n");
return;
@@ -335,15 +353,15 @@
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)) {
MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
@@ -355,11 +373,11 @@
}
static void dump_raft() {
- if (is_user_build()) {
+ if (IsUserBuild()) {
return;
}
- std::string raft_log_path = bugreport_dir + "/raft_log.txt";
+ std::string raft_log_path = ds.bugreportDir_ + "/raft_log.txt";
if (raft_log_path.empty()) {
MYLOGD("raft_log_path is empty\n");
return;
@@ -371,14 +389,14 @@
return;
}
+ CommandOptions options = CommandOptions::WithTimeout(600).Build();
if (!zip_writer) {
// Write compressed and encoded raft logs to stdout if not zip_writer.
- run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+ 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);
+ RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_log_path}, options);
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());
} else {
@@ -397,7 +415,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;
}
@@ -603,6 +621,7 @@
return value <= maximum;
}
+// TODO: migrate to logd/LogBuffer.cpp or use android::base::GetProperty
static unsigned long property_get_size(const char *key) {
unsigned long value;
char *cp, property[PROPERTY_VALUE_MAX];
@@ -668,17 +687,15 @@
/* End copy from system/core/logd/LogBuffer.cpp */
/* 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 print_header(const std::string& version) {
+ 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)");
+ 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");
@@ -686,17 +703,19 @@
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");
+ DumpFile("", "/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("Dumpstate info: id=%lu pid=%d dryRun=%d args=%s extraOptions=%s\n", ds.id_, getpid(),
+ ds.IsDryRun(), args.c_str(), extraOptions.c_str());
printf("\n");
}
@@ -739,7 +758,7 @@
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) {
@@ -774,19 +793,19 @@
}
/* adds a file to the existing zipped bugreport */
-static int _add_file_from_fd(const char *title, const char *path, int fd) {
+static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}
// TODO: move to util.cpp
-void add_dir(const char *dir, bool recursive) {
+void add_dir(const std::string& dir, bool recursive) {
if (!zip_writer) {
- MYLOGD("Not adding dir %s because zip_writer is not set\n", dir);
+ MYLOGD("Not adding dir %s because zip_writer is not set\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 durationReporter(dir, nullptr);
+ dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
}
/* adds a text entry entry to the existing zip file. */
@@ -820,51 +839,57 @@
}
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) {
- DurationReporter duration_reporter("DUMPSTATE");
+static void dumpstate(const std::string& screenshot_path,
+ const std::string& version __attribute__((unused))) {
+ DurationReporter durationReporter("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)");
@@ -878,72 +903,66 @@
MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
}
- // 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++;
}
}
@@ -969,191 +988,190 @@
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.weightTotal_, 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: sets the bugreport format version (valid values: %s)\n",
+ VERSION_DEFAULT.c_str());
+ exit(exitCode);
+}
+
+static void ExitOnInvalidArgs() {
+ fprintf(stderr, "invalid combination of args\n");
+ ShowUsageAndExit();
}
static void wake_lock_releaser() {
@@ -1164,7 +1182,7 @@
}
}
-static void sig_handler(int signo) {
+static void sig_handler(int signo __attribute__((unused))) {
wake_lock_releaser();
_exit(EXIT_FAILURE);
}
@@ -1185,7 +1203,14 @@
temporary file.
*/
static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
- time_t now) {
+ const std::string& log_path, time_t now) {
+ // 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 - now);
+
if (!add_zip_entry(bugreport_name, bugreport_path)) {
MYLOGE("Failed to add text entry to .zip file\n");
return false;
@@ -1195,13 +1220,23 @@
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 (!add_zip_entry("dumpstate_log.txt", 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*>(log_path.c_str()));
+ fprintf(stderr, "\n");
+
int32_t err = zip_writer->Finish();
if (err) {
MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
- if (is_user_build()) {
+ if (IsUserBuild()) {
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));
@@ -1261,7 +1296,7 @@
int is_remote_mode = 0;
std::string version = VERSION_DEFAULT;
- now = time(NULL);
+ now = time(nullptr);
MYLOGI("begin\n");
@@ -1273,13 +1308,26 @@
register_sig_handler();
}
+ if (ds.IsDryRun()) {
+ MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
+ }
+
+ // TODO: use helper function to convert argv into a string
+ for (int i = 0; i < argc; i++) {
+ args += argv[i];
+ if (i < argc - 1) {
+ args += " ";
+ }
+ }
+
+ extraOptions = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ MYLOGI("Dumpstate args: %s (extra options: %s)\n", args.c_str(), extraOptions.c_str());
+
/* 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);
+ int lastId = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+ ds.id_ = ++lastId;
+ android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(lastId));
+ MYLOGI("dumpstate id: %lu\n", ds.id_);
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
@@ -1298,12 +1346,10 @@
}
/* 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) {
+ // clang-format off
case 'd': do_add_date = 1; break;
case 'z': do_zip_file = 1; break;
case 'o': use_outfile = optarg; break;
@@ -1312,45 +1358,65 @@
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 'P': ds.updateProgress_ = 1; break;
case 'R': is_remote_mode = 1; break;
case 'B': do_broadcast = 1; break;
case 'V': version = optarg; break;
- case '?': printf("\n");
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);
+ if (!extraOptions.empty()) {
+ // Framework uses a system property to override some command-line args.
+ // Currently, it contains the type of the requested bugreport.
+ if (extraOptions == "bugreportplus") {
+ MYLOGD("Running as bugreportplus: add -P, remove -p\n");
+ ds.updateProgress_ = 1;
+ do_fb = 0;
+ } else if (extraOptions == "bugreportremote") {
+ MYLOGD("Running as bugreportremote: add -q -R, remove -p\n");
+ do_vibrate = 0;
+ is_remote_mode = 1;
+ do_fb = 0;
+ } else if (extraOptions == "bugreportwear") {
+ MYLOGD("Running as bugreportwear: add -P\n");
+ ds.updateProgress_ = 1;
+ } else {
+ MYLOGE("Unknown extra option: %s\n", extraOptions.c_str());
+ }
+ // Reset the property
+ android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ }
+
+ if ((do_zip_file || do_add_date || ds.updateProgress_ || do_broadcast) && !use_outfile) {
+ ExitOnInvalidArgs();
}
if (use_control_socket && !do_zip_file) {
- usage();
- exit(1);
+ ExitOnInvalidArgs();
}
- if (do_update_progress && !do_broadcast) {
- usage();
- exit(1);
+ if (ds.updateProgress_ && !do_broadcast) {
+ ExitOnInvalidArgs();
}
- if (is_remote_mode && (do_update_progress || !do_broadcast || !do_zip_file || !do_add_date)) {
- usage();
- exit(1);
+ if (is_remote_mode && (ds.updateProgress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
+ ExitOnInvalidArgs();
}
if (version != VERSION_DEFAULT) {
- usage();
- exit(1);
+ ShowUsageAndExit();
}
MYLOGI("bugreport format version: %s\n", version.c_str());
- do_early_screenshot = do_update_progress;
+ do_early_screenshot = ds.updateProgress_;
// If we are going to use a socket, do it as early as possible
// to avoid timeouts from bugreport.
@@ -1360,14 +1426,14 @@
if (use_control_socket) {
MYLOGD("Opening control socket\n");
- control_socket_fd = open_socket("dumpstate");
- do_update_progress = 1;
+ ds.controlSocketFd_ = open_socket("dumpstate");
+ ds.updateProgress_ = 1;
}
/* full path of the temporary file containing the bugreport */
std::string tmp_path;
- /* full path of the file containing the dumpstate logs*/
+ /* full path of the file containing the dumpstate logs */
std::string log_path;
/* full path of the systrace file, when enabled */
@@ -1389,7 +1455,7 @@
bool is_redirecting = !use_socket && use_outfile;
if (is_redirecting) {
- bugreport_dir = dirname(use_outfile);
+ ds.bugreportDir_ = dirname(use_outfile);
base_name = basename(use_outfile);
if (do_add_date) {
char date[80];
@@ -1398,30 +1464,30 @@
} else {
suffix = "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");
+ base_name = 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";
+ screenshot_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".png";
}
- tmp_path = bugreport_dir + "/" + base_name + "-" + suffix + ".tmp";
- log_path = bugreport_dir + "/dumpstate_log-" + suffix + "-"
- + std::to_string(getpid()) + ".txt";
+ tmp_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".tmp";
+ log_path =
+ ds.bugreportDir_ + "/dumpstate_log-" + suffix + "-" + std::to_string(getpid()) + ".txt";
- MYLOGD("Bugreport dir: %s\n"
- "Base name: %s\n"
- "Suffix: %s\n"
- "Log path: %s\n"
- "Temporary path: %s\n"
- "Screenshot path: %s\n",
- bugreport_dir.c_str(), base_name.c_str(), suffix.c_str(),
- log_path.c_str(), tmp_path.c_str(), screenshot_path.c_str());
+ 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.bugreportDir_.c_str(), base_name.c_str(), suffix.c_str(), log_path.c_str(),
+ tmp_path.c_str(), screenshot_path.c_str());
if (do_zip_file) {
- path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
+ path = ds.bugreportDir_ + "/" + 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"));
@@ -1434,13 +1500,13 @@
add_text_zip_entry("version.txt", version);
}
- if (do_update_progress) {
+ if (ds.updateProgress_) {
if (do_broadcast) {
// clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
"--es", "android.intent.extra.NAME", suffix,
- "--ei", "android.intent.extra.ID", std::to_string(id),
+ "--ei", "android.intent.extra.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),
};
@@ -1448,7 +1514,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.controlSocketFd_, "BEGIN:%s\n", path.c_str());
}
}
}
@@ -1517,13 +1583,15 @@
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();
@@ -1533,7 +1601,7 @@
add_dir(RECOVERY_DIR, true);
add_dir(RECOVERY_DATA_DIR, true);
add_dir(LOGPERSIST_DATA_DIR, false);
- if (!is_user_build()) {
+ if (!IsUserBuild()) {
add_dir(PROFILE_DATA_DIR_CUR, true);
add_dir(PROFILE_DATA_DIR_REF, true);
}
@@ -1541,10 +1609,11 @@
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;
@@ -1561,26 +1630,24 @@
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;
+ MYLOGI("changing suffix from %s to %s\n", suffix.c_str(), name.c_str());
+ suffix = name;
if (!screenshot_path.empty()) {
std::string new_screenshot_path =
- bugreport_dir + "/" + base_name + "-" + suffix + ".png";
+ ds.bugreportDir_ + "/" + 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));
@@ -1594,13 +1661,13 @@
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 (!finish_zip_file(entry_name, tmp_path, log_path, now)) {
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";
+ std::string new_path = ds.bugreportDir_ + "/" + 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())) {
@@ -1613,7 +1680,7 @@
}
}
if (do_text_file) {
- path = bugreport_dir + "/" + base_name + "-" + suffix + ".txt";
+ path = ds.bugreportDir_ + "/" + 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));
@@ -1622,10 +1689,12 @@
}
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.controlSocketFd_,
+ "FAIL:could not create zip file, check %s "
+ "for more details\n",
+ log_path.c_str());
} else {
- dprintf(control_socket_fd, "OK:%s\n", path.c_str());
+ dprintf(ds.controlSocketFd_, "OK:%s\n", path.c_str());
}
}
}
@@ -1645,9 +1714,9 @@
// 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),
+ "--ei", "android.intent.extra.MAX", std::to_string(ds.weightTotal_),
"--es", "android.intent.extra.BUGREPORT", path,
"--es", "android.intent.extra.DUMPSTATE_LOG", log_path
};
@@ -1670,16 +1739,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.weightTotal_, 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.controlSocketFd_ != -1) {
+ MYLOGD("Closing control socket\n");
+ close(ds.controlSocketFd_);
}
return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 905fc22..adaf29e 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 rootMode_;
+ StdoutMode stdoutMode_;
+ std::string loggingMessage_;
+
+ 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,24 +189,116 @@
* 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;
-
/*
- * 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;
+ * 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;
-/* full path of the directory where the bugreport files will be written */
-extern std::string bugreport_dir;
+ public:
+ static Dumpstate& GetInstance();
-/* root dir for all files copied as-is into the bugreport. */
-extern const std::string ZIP_ROOT_DIR;
+ /*
+ * 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();
+
+ /*
+ * Gets whether device is running a `user` build.
+ */
+ bool IsUserBuild();
+
+ /*
+ * 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);
+
+ /*
+ * 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.
+ * |dumpsysTimeout| 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>& dumpsysArgs,
+ const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS,
+ long dumpsysTimeout = 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);
+
+ // 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);
+
+ // TODO: initialize fields on constructor
+
+ // dumpstate id - unique after each device reboot.
+ unsigned long id_;
+
+ // Whether progress updates should be published.
+ bool updateProgress_ = false;
+
+ // Currrent progress.
+ int progress_ = 0;
+
+ // Total estimated progress.
+ int weightTotal_ = WEIGHT_TOTAL;
+
+ // When set, defines a socket file-descriptor use to report progress to bugreportz.
+ int controlSocketFd_ = -1;
+
+
+ // Full path of the directory where the bugreport files will be written;
+ std::string bugreportDir_;
+
+ private:
+ // Used by GetInstance() only.
+ Dumpstate(bool dryRun = false, const std::string& buildType = "user");
+
+ // Whether this is a dry run.
+ bool dryRun_;
+
+ // Build type (such as 'user' or 'eng').
+ std::string buildType_;
+};
+
+// 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*);
/* adds a new entry to the existing zip file. */
bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
@@ -94,10 +307,7 @@
bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
/* adds all files from a directory to the zipped bugreport file */
-void add_dir(const char *dir, bool recursive);
-
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path);
+void add_dir(const std::string& dir, bool recursive);
/* saves the the contents of a file as a long */
int read_file_as_long(const char *path, long int *output);
@@ -113,25 +323,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();
@@ -139,9 +332,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();
@@ -151,9 +341,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);
@@ -208,31 +401,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..8d70704
--- /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.updateProgress_ = false;
+ }
+
+ // Runs a command and capture `stdout` and `stderr`.
+ int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ CaptureStdout();
+ CaptureStderr();
+ int status = ds.RunCommand(title, fullCommand, 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 dryRun) {
+ ALOGD("Setting dryRun_ to %s\n", dryRun ? "true" : "false");
+ ds.dryRun_ = dryRun;
+ }
+
+ void SetBuildType(const std::string& buildType) {
+ ALOGD("Setting buildType_ to '%s'\n", buildType.c_str());
+ ds.buildType_ = buildType;
+ }
+
+ 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& expectedValue) {
+ std::string actualValue = android::base::GetProperty(key, "not set");
+ EXPECT_THAT(expectedValue, StrEq(actualValue)) << "invalid value for property " << key;
+ }
+
+ std::string GetProgressMessage(int progress, int weightTotal, int oldWeightTotal = 0) {
+ EXPECT_EQ(progress, ds.progress_) << "invalid progress";
+ EXPECT_EQ(weightTotal, ds.weightTotal_) << "invalid weightTotal";
+
+ AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.progress", getpid()),
+ std::to_string(progress));
+
+ bool maxIncreased = oldWeightTotal > 0;
+
+ std::string adjustmentMessage = "";
+ if (maxIncreased) {
+ AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.max", getpid()),
+ std::to_string(weightTotal));
+ adjustmentMessage = android::base::StringPrintf(
+ "Adjusting total weight from %d to %d\n", oldWeightTotal, weightTotal);
+ }
+
+ return android::base::StringPrintf("%sSetting progress (dumpstate.%d.progress): %d/%d\n",
+ adjustmentMessage.c_str(), getpid(), progress,
+ weightTotal);
+ }
+
+ // `stdout` and `stderr` from the last command ran.
+ std::string out, err;
+
+ std::string testPath = dirname(android::base::GetExecutablePath().c_str());
+ std::string fixturesPath = testPath + "/../dumpstate_test_fixture/";
+ std::string testDataPath = fixturesPath + "/testdata/";
+ std::string simpleCommand = fixturesPath + "dumpstate_test_fixture";
+ std::string echoCommand = "/system/bin/echo";
+
+ Dumpstate& ds = Dumpstate::GetInstance();
+};
+
+TEST_F(DumpstateTest, RunCommandNoArgs) {
+ EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateTest, RunCommandNoTitle) {
+ EXPECT_EQ(0, RunCommand("", {simpleCommand}));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithTitle) {
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {simpleCommand}));
+ 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 (" + simpleCommand + ") ------\nstdout\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
+ EXPECT_EQ(
+ 0, RunCommand("", {simpleCommand},
+ 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("", {simpleCommand},
+ CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithOneArg) {
+ EXPECT_EQ(0, RunCommand("", {echoCommand, "one"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
+ EXPECT_EQ(0, RunCommand("", {echoCommand, "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", {simpleCommand}));
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simpleCommand +
+ ") ------\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("", {simpleCommand}));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunAlways) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {simpleCommand}, 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("", {simpleCommand, "--exit", "42"}));
+ EXPECT_THAT(
+ out, StrEq("stdout\n*** command '" + simpleCommand + " --exit 42' failed: exit code 42\n"));
+ EXPECT_THAT(
+ err, StrEq("stderr\n*** command '" + simpleCommand + " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandCrashes) {
+ EXPECT_NE(0, RunCommand("", {simpleCommand, "--crash"}));
+ // We don't know the exit code, so check just the prefix.
+ EXPECT_THAT(
+ out, StartsWith("stdout\n*** command '" + simpleCommand + " --crash' failed: exit code"));
+ EXPECT_THAT(
+ err, StartsWith("stderr\n*** command '" + simpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateTest, RunCommandTimesout) {
+ EXPECT_EQ(-1, RunCommand("", {simpleCommand, "--sleep", "2"},
+ CommandOptions::WithTimeout(1).Build()));
+ EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simpleCommand +
+ " --sleep 2' timed out after 1"));
+ EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simpleCommand +
+ " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateTest, RunCommandIsKilled) {
+ CaptureStdout();
+ CaptureStderr();
+
+ std::thread t([=]() {
+ EXPECT_EQ(SIGTERM, ds.RunCommand("", {simpleCommand, "--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 '" + simpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+ EXPECT_THAT(err, StrEq("*** command '" + simpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandProgress) {
+ ds.updateProgress_ = true;
+ ds.progress_ = 0;
+ ds.weightTotal_ = 30;
+
+ EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(20).Build()));
+ std::string progressMessage = GetProgressMessage(20, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+
+ EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(10).Build()));
+ progressMessage = GetProgressMessage(30, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+
+ // Run a command that will increase maximum timeout.
+ EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(1).Build()));
+ progressMessage = GetProgressMessage(31, 36, 30); // 20% increase
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+
+ // Make sure command ran while in dryRun is counted.
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(4).Build()));
+ progressMessage = GetProgressMessage(35, 36);
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq(progressMessage));
+}
+
+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("", {simpleCommand, "--uid"}));
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ return;
+ }
+ // Then drop root.
+
+ EXPECT_EQ(0, RunCommand("", {simpleCommand, "--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("", {simpleCommand}, 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 " + simpleCommand + "' 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("", {simpleCommand, "--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("", testDataPath + "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("", testDataPath + "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("", testDataPath + "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("", testDataPath + "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("", testDataPath + "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!", testDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + testDataPath +
+ "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.updateProgress_ = true;
+ ds.progress_ = 0;
+ ds.weightTotal_ = 30;
+
+ EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+
+ std::string progressMessage = GetProgressMessage(5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
+
+ EXPECT_THAT(err, StrEq(progressMessage));
+ 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 0d9cce2..fc1f721 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -39,6 +39,7 @@
#define LOG_TAG "dumpstate"
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <cutils/debugger.h>
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -49,8 +50,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>& fullCommand,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ return ds.RunCommand(title, fullCommand, 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 +85,132 @@
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_.rootMode_ = SU_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
+ values_.rootMode_ = DROP_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
+ values_.stdoutMode_ = REDIRECT_TO_STDERR;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
+ const std::string& message) {
+ values_.loggingMessage_ = message;
+ return *this;
+}
+
+CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
+ return CommandOptions(values_);
+}
+
+CommandOptions::CommandOptionsValues::CommandOptionsValues(long timeout)
+ : timeout_(timeout),
+ always_(false),
+ rootMode_(DONT_DROP_ROOT),
+ stdoutMode_(NORMAL_STDOUT),
+ loggingMessage_("") {
+}
+
+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_.rootMode_;
+}
+
+StdoutMode CommandOptions::StdoutMode() const {
+ return values_.stdoutMode_;
+}
+
+std::string CommandOptions::LoggingMessage() const {
+ return values_.loggingMessage_;
+}
+
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(long timeout) {
+ return CommandOptions::CommandOptionsBuilder(timeout);
+}
+
+Dumpstate::Dumpstate(bool dryRun, const std::string& buildType)
+ : dryRun_(dryRun), buildType_(buildType) {
+}
+
+Dumpstate& Dumpstate::GetInstance() {
+ static Dumpstate sSingleton(android::base::GetBoolProperty("dumpstate.dry_run", false),
+ android::base::GetProperty("ro.build.type", "(unknown)"));
+ return sSingleton;
+}
+
+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() {
+ return dryRun_;
+}
+
+bool Dumpstate::IsUserBuild() {
+ return "user" == buildType_;
+}
+
void for_each_userid(void (*func)(int), const char *header) {
- ON_DRY_RUN_RETURN();
+ if (IsDryRun()) return;
+
DIR *d;
struct dirent *de;
@@ -179,7 +292,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 +346,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 +419,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 +487,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 +518,12 @@
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);
+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 +539,11 @@
}
printf(") ------\n");
}
- ON_DRY_RUN({ update_progress(WEIGHT_FILE); close(fd); return 0; });
+ if (IsDryRun()) {
+ UpdateProgress(WEIGHT_FILE);
+ close(fd);
+ return 0;
+ }
bool newline = false;
fd_set read_set;
@@ -432,14 +554,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 +581,36 @@
}
}
}
- 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) {
- DurationReporter duration_reporter(title);
- int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
+ DurationReporter durationReporter(title);
+ 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;
+ }
+
+ 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 +640,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 +649,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 +683,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 +698,7 @@
(*dump_from_fd)(NULL, newpath, fd);
}
closedir(dirp);
- if (title) {
+ if (!title.empty()) {
printf("\n");
}
return retval;
@@ -577,7 +709,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 +768,76 @@
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>& fullCommand,
+ const CommandOptions& options) {
+ if (fullCommand.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 = fullCommand.size() + 1; // null terminated
+ int startingIndex = 0;
+ if (options.RootMode() == SU_ROOT) {
+ startingIndex = 2; // "su" "root"
+ size += startingIndex;
+ }
- 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 commandString;
+ if (options.RootMode() == SU_ROOT) {
+ args[0] = SU_PATH;
+ commandString += SU_PATH;
+ args[1] = "root";
+ commandString += " root ";
+ }
+ int i = startingIndex;
+ for (auto arg = fullCommand.begin(); arg != fullCommand.end(); ++arg) {
+ args[i++] = arg->c_str();
+ commandString += arg->c_str();
+ if (arg != fullCommand.end() - 1) {
+ commandString += " ";
}
- // 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 = commandString.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 durationReporter(title);
+
+ const std::string& loggingMessage = options.LoggingMessage();
+ if (!loggingMessage.empty()) {
+ MYLOGI(loggingMessage.c_str(), commandString.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);
- return status;
-}
+ bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
-/* forks a command and waits for it to finish */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
- int timeout_seconds, const char *args[]) {
- bool silent = (stdout_mode == REDIRECT_TO_STDERR);
- // TODO: need to check if args is null-terminated, otherwise execvp will crash dumpstate
+ /* TODO: for now we're simplifying the progress calculation by using the
+ * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
+ * where its weight should be much higher proportionally to its timeout.
+ * Ideally, it should use a options.EstimatedDuration() instead...*/
+ int weight = options.Timeout();
- /* 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 +849,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 +870,74 @@
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);
+ UpdateProgress(weight);
}
return status;
}
+void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ 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(), dumpsysArgs.begin(), dumpsysArgs.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 */
@@ -866,22 +985,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;
@@ -905,7 +1018,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);
@@ -976,11 +1089,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));
@@ -991,6 +1104,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)) {
@@ -1002,35 +1123,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 */
@@ -1051,9 +1171,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;
}
@@ -1086,7 +1206,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;
@@ -1094,7 +1214,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) {
@@ -1107,8 +1227,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 */
@@ -1116,7 +1236,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) {
@@ -1127,8 +1247,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);
}
}
}
@@ -1137,17 +1257,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);
@@ -1158,9 +1276,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));
@@ -1171,55 +1289,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 (!updateProgress_) 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_ > weightTotal_) {
+ int newTotal = weightTotal_ * 1.2;
+ MYLOGD("Adjusting total weight from %d to %d\n", weightTotal_, newTotal);
+ weightTotal_ = newTotal;
snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
- snprintf(value, sizeof(value), "%d", weight_total);
+ snprintf(value, sizeof(value), "%d", weightTotal_);
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, weightTotal_);
} 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, weightTotal_);
}
- if (control_socket_fd >= 0) {
- dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
- fsync(control_socket_fd);
+ if (controlSocketFd_ >= 0) {
+ dprintf(controlSocketFd_, "PROGRESS:%d/%d\n", progress_, weightTotal_);
+ fsync(controlSocketFd_);
}
int status = property_set(key, value);
@@ -1230,8 +1343,8 @@
}
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);
+ RunCommand("", {"/system/bin/screencap", "-p", path},
+ CommandOptions::WithTimeout(10).Always().RedirectStderr().Build());
}
void vibrate(FILE* vibrator, int ms) {
@@ -1368,30 +1481,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/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..4c74bc5 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";
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_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index b688d1d..78d1f5d 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -75,6 +75,8 @@
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 */
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/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/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/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/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 170faa8..5c180dc 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -17,11 +17,14 @@
LayerDim.cpp \
MessageQueue.cpp \
MonitoredProducer.cpp \
+ SurfaceFlinger.cpp \
SurfaceFlingerConsumer.cpp \
+ SurfaceInterceptor.cpp \
Transform.cpp \
DisplayHardware/FramebufferSurface.cpp \
DisplayHardware/HWC2.cpp \
DisplayHardware/HWC2On1Adapter.cpp \
+ DisplayHardware/HWComposer.cpp \
DisplayHardware/PowerHAL.cpp \
DisplayHardware/VirtualDisplaySurface.cpp \
Effects/Daltonizer.cpp \
@@ -36,26 +39,16 @@
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
-ifeq ($(TARGET_USES_HWC2),true)
- LOCAL_CFLAGS += -DUSE_HWC2
- LOCAL_SRC_FILES += \
- SurfaceFlinger.cpp \
- DisplayHardware/HWComposer.cpp
-else
- LOCAL_SRC_FILES += \
- SurfaceFlinger_hwc1.cpp \
- DisplayHardware/HWComposer_hwc1.cpp
-endif
-
ifeq ($(TARGET_BOARD_PLATFORM),omap4)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
endif
@@ -125,7 +118,7 @@
LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
-LOCAL_STATIC_LIBRARIES := libvkjson
+LOCAL_STATIC_LIBRARIES := libtrace_proto libvkjson
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
@@ -139,9 +132,8 @@
libui \
libgui \
libpowermanager \
- libvulkan
-
-LOCAL_MODULE := libsurfaceflinger
+ libvulkan \
+ libprotobuf-cpp-full
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
@@ -163,10 +155,6 @@
LOCAL_CFLAGS += -DENABLE_CPUSETS
endif
-ifeq ($(TARGET_USES_HWC2),true)
- LOCAL_CFLAGS += -DUSE_HWC2
-endif
-
LOCAL_SRC_FILES := \
main_surfaceflinger.cpp
@@ -179,6 +167,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..43fb19d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -37,9 +37,7 @@
#include "DisplayHardware/DisplaySurface.h"
#include "DisplayHardware/HWComposer.h"
-#ifdef USE_HWC2
#include "DisplayHardware/HWC2.h"
-#endif
#include "RenderEngine/RenderEngine.h"
#include "clz.h"
@@ -74,9 +72,6 @@
const sp<SurfaceFlinger>& flinger,
DisplayType type,
int32_t hwcId,
-#ifndef USE_HWC2
- int format,
-#endif
bool isSecure,
const wp<IBinder>& displayToken,
const sp<DisplaySurface>& displaySurface,
@@ -92,9 +87,6 @@
mSurface(EGL_NO_SURFACE),
mDisplayWidth(),
mDisplayHeight(),
-#ifndef USE_HWC2
- mFormat(),
-#endif
mFlags(),
mPageFlipCount(),
mIsSecure(isSecure),
@@ -114,11 +106,7 @@
EGLSurface eglSurface;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (config == EGL_NO_CONFIG) {
-#ifdef USE_HWC2
config = RenderEngine::chooseEglConfig(display, PIXEL_FORMAT_RGBA_8888);
-#else
- config = RenderEngine::chooseEglConfig(display, format);
-#endif
}
eglSurface = eglCreateWindowSurface(display, config, window, NULL);
eglQuerySurface(display, eglSurface, EGL_WIDTH, &mDisplayWidth);
@@ -137,9 +125,6 @@
mConfig = config;
mDisplay = display;
mSurface = eglSurface;
-#ifndef USE_HWC2
- mFormat = format;
-#endif
mPageFlipCount = 0;
mViewport.makeInvalid();
mFrame.makeInvalid();
@@ -180,10 +165,6 @@
void DisplayDevice::disconnect(HWComposer& hwc) {
if (mHwcDisplayId >= 0) {
hwc.disconnectDisplay(mHwcDisplayId);
-#ifndef USE_HWC2
- if (mHwcDisplayId >= DISPLAY_VIRTUAL)
- hwc.freeDisplayId(mHwcDisplayId);
-#endif
mHwcDisplayId = -1;
}
}
@@ -200,12 +181,6 @@
return mDisplayHeight;
}
-#ifndef USE_HWC2
-PixelFormat DisplayDevice::getFormat() const {
- return mFormat;
-}
-#endif
-
EGLSurface DisplayDevice::getEGLSurface() const {
return mSurface;
}
@@ -221,12 +196,6 @@
return mPageFlipCount;
}
-#ifndef USE_HWC2
-status_t DisplayDevice::compositionComplete() const {
- return mDisplaySurface->compositionComplete();
-}
-#endif
-
void DisplayDevice::flip(const Region& dirty) const
{
mFlinger->getRenderEngine().checkErrors();
@@ -247,7 +216,6 @@
return mDisplaySurface->beginFrame(mustRecompose);
}
-#ifdef USE_HWC2
status_t DisplayDevice::prepareFrame(HWComposer& hwc) {
status_t error = hwc.prepare(*this);
if (error != NO_ERROR) {
@@ -271,41 +239,9 @@
}
return mDisplaySurface->prepareFrame(compositionType);
}
-#else
-status_t DisplayDevice::prepareFrame(const HWComposer& hwc) const {
- DisplaySurface::CompositionType compositionType;
- bool haveGles = hwc.hasGlesComposition(mHwcDisplayId);
- bool haveHwc = hwc.hasHwcComposition(mHwcDisplayId);
- if (haveGles && haveHwc) {
- compositionType = DisplaySurface::COMPOSITION_MIXED;
- } else if (haveGles) {
- compositionType = DisplaySurface::COMPOSITION_GLES;
- } else if (haveHwc) {
- compositionType = DisplaySurface::COMPOSITION_HWC;
- } else {
- // Nothing to do -- when turning the screen off we get a frame like
- // this. Call it a HWC frame since we won't be doing any GLES work but
- // will do a prepare/set cycle.
- compositionType = DisplaySurface::COMPOSITION_HWC;
- }
- return mDisplaySurface->prepareFrame(compositionType);
-}
-#endif
void DisplayDevice::swapBuffers(HWComposer& hwc) const {
-#ifdef USE_HWC2
if (hwc.hasClientComposition(mHwcDisplayId)) {
-#else
- // We need to call eglSwapBuffers() if:
- // (1) we don't have a hardware composer, or
- // (2) we did GLES composition this frame, and either
- // (a) we have framebuffer target support (not present on legacy
- // devices, where HWComposer::commit() handles things); or
- // (b) this is a virtual display
- if (hwc.initCheck() != NO_ERROR ||
- (hwc.hasGlesComposition(mHwcDisplayId) &&
- (hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL))) {
-#endif
EGLBoolean success = eglSwapBuffers(mDisplay, mSurface);
if (!success) {
EGLint error = eglGetError();
@@ -327,17 +263,9 @@
}
}
-#ifdef USE_HWC2
void DisplayDevice::onSwapBuffersCompleted() const {
mDisplaySurface->onFrameCommitted();
}
-#else
-void DisplayDevice::onSwapBuffersCompleted(HWComposer& hwc) const {
- if (hwc.initCheck() == NO_ERROR) {
- mDisplaySurface->onFrameCommitted();
- }
-}
-#endif
uint32_t DisplayDevice::getFlags() const
{
@@ -415,7 +343,6 @@
}
// ----------------------------------------------------------------------------
-#ifdef USE_HWC2
void DisplayDevice::setActiveColorMode(android_color_mode_t mode) {
mActiveColorMode = mode;
}
@@ -423,7 +350,6 @@
android_color_mode_t DisplayDevice::getActiveColorMode() const {
return mActiveColorMode;
}
-#endif
// ----------------------------------------------------------------------------
@@ -613,3 +539,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..7e8f156 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -21,27 +21,20 @@
#include <stdlib.h>
-#ifndef USE_HWC2
-#include <ui/PixelFormat.h>
-#endif
#include <ui/Region.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#ifdef USE_HWC2
#include <binder/IBinder.h>
#include <utils/RefBase.h>
-#endif
#include <utils/Mutex.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <hardware/hwcomposer_defs.h>
-#ifdef USE_HWC2
#include <memory>
-#endif
struct ANativeWindow;
@@ -87,9 +80,6 @@
const sp<SurfaceFlinger>& flinger,
DisplayType type,
int32_t hwcId,
-#ifndef USE_HWC2
- int format,
-#endif
bool isSecure,
const wp<IBinder>& displayToken,
const sp<DisplaySurface>& displaySurface,
@@ -112,9 +102,6 @@
int getWidth() const;
int getHeight() const;
-#ifndef USE_HWC2
- PixelFormat getFormat() const;
-#endif
uint32_t getFlags() const;
EGLSurface getEGLSurface() const;
@@ -144,23 +131,12 @@
// We pass in mustRecompose so we can keep VirtualDisplaySurface's state
// machine happy without actually queueing a buffer if nothing has changed
status_t beginFrame(bool mustRecompose) const;
-#ifdef USE_HWC2
status_t prepareFrame(HWComposer& hwc);
-#else
- status_t prepareFrame(const HWComposer& hwc) const;
-#endif
void swapBuffers(HWComposer& hwc) const;
-#ifndef USE_HWC2
- status_t compositionComplete() const;
-#endif
// called after h/w composer has completed its set() call
-#ifdef USE_HWC2
void onSwapBuffersCompleted() const;
-#else
- void onSwapBuffersCompleted(HWComposer& hwc) const;
-#endif
Rect getBounds() const {
return Rect(mDisplayWidth, mDisplayHeight);
@@ -182,10 +158,8 @@
void setPowerMode(int mode);
bool isDisplayOn() const;
-#ifdef USE_HWC2
android_color_mode_t getActiveColorMode() const;
void setActiveColorMode(android_color_mode_t mode);
-#endif
/* ------------------------------------------------------------------------
* Display active config management.
@@ -220,9 +194,6 @@
EGLSurface mSurface;
int mDisplayWidth;
int mDisplayHeight;
-#ifndef USE_HWC2
- PixelFormat mFormat;
-#endif
uint32_t mFlags;
mutable uint32_t mPageFlipCount;
String8 mDisplayName;
@@ -257,10 +228,30 @@
int mPowerMode;
// Current active config
int mActiveConfig;
-#ifdef USE_HWC2
// current active color mode
android_color_mode_t mActiveColorMode;
-#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
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
index d801bb3..d39a3aa 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
@@ -49,14 +49,6 @@
};
virtual status_t prepareFrame(CompositionType compositionType) = 0;
-#ifndef USE_HWC2
- // Should be called when composition rendering is complete for a frame (but
- // eglSwapBuffers hasn't necessarily been called). Required by certain
- // older drivers for synchronization.
- // TODO: Remove this when we drop support for HWC 1.0.
- virtual status_t compositionComplete() = 0;
-#endif
-
// Inform the surface that GLES composition is complete for this frame, and
// the surface should make sure that HWComposer has the correct buffer for
// this frame. Some implementations may only push a new buffer to
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 18c7945..a50b550 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -62,32 +62,20 @@
mCurrentBufferSlot(-1),
mCurrentBuffer(),
mCurrentFence(Fence::NO_FENCE),
-#ifdef USE_HWC2
mHwc(hwc),
mHasPendingRelease(false),
mPreviousBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
mPreviousBuffer()
-#else
- mHwc(hwc)
-#endif
{
-#ifdef USE_HWC2
ALOGV("Creating for display %d", disp);
-#endif
-
mName = "FramebufferSurface";
mConsumer->setConsumerName(mName);
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER);
-#ifdef USE_HWC2
const auto& activeConfig = mHwc.getActiveConfig(disp);
mConsumer->setDefaultBufferSize(activeConfig->getWidth(),
activeConfig->getHeight());
-#else
- mConsumer->setDefaultBufferFormat(mHwc.getFormat(disp));
- mConsumer->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp));
-#endif
mConsumer->setMaxAcquiredBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS - 1);
}
@@ -100,7 +88,6 @@
}
status_t FramebufferSurface::advanceFrame() {
-#ifdef USE_HWC2
sp<GraphicBuffer> buf;
sp<Fence> acquireFence(Fence::NO_FENCE);
android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
@@ -115,20 +102,10 @@
ALOGE("error posting framebuffer: %d", result);
}
return result;
-#else
- // Once we remove FB HAL support, we can call nextBuffer() from here
- // instead of using onFrameAvailable(). No real benefit, except it'll be
- // more like VirtualDisplaySurface.
- return NO_ERROR;
-#endif
}
-#ifdef USE_HWC2
status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer,
sp<Fence>& outFence, android_dataspace_t& outDataspace) {
-#else
-status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
-#endif
Mutex::Autolock lock(mMutex);
BufferItem item;
@@ -151,19 +128,9 @@
// had released the old buffer first.
if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &&
item.mSlot != mCurrentBufferSlot) {
-#ifdef USE_HWC2
mHasPendingRelease = true;
mPreviousBufferSlot = mCurrentBufferSlot;
mPreviousBuffer = mCurrentBuffer;
-#else
- // Release the previous buffer.
- err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
- if (err < NO_ERROR) {
- ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
- return err;
- }
-#endif
}
mCurrentBufferSlot = item.mSlot;
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
@@ -171,30 +138,10 @@
outFence = item.mFence;
outBuffer = mCurrentBuffer;
-#ifdef USE_HWC2
outDataspace = item.mDataSpace;
-#endif
return NO_ERROR;
}
-#ifndef USE_HWC2
-// Overrides ConsumerBase::onFrameAvailable(), does not call base class impl.
-void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
- sp<GraphicBuffer> buf;
- sp<Fence> acquireFence;
- status_t err = nextBuffer(buf, acquireFence);
- if (err != NO_ERROR) {
- ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
- strerror(-err), err);
- return;
- }
- err = mHwc.fbPost(mDisplayType, acquireFence, buf);
- if (err != NO_ERROR) {
- ALOGE("error posting framebuffer: %d", err);
- }
-}
-#endif
-
void FramebufferSurface::freeBufferLocked(int slotIndex) {
ConsumerBase::freeBufferLocked(slotIndex);
if (slotIndex == mCurrentBufferSlot) {
@@ -203,7 +150,6 @@
}
void FramebufferSurface::onFrameCommitted() {
-#ifdef USE_HWC2
if (mHasPendingRelease) {
sp<Fence> fence = mHwc.getRetireFence(mDisplayType);
if (fence->isValid()) {
@@ -220,34 +166,14 @@
mPreviousBuffer.clear();
mHasPendingRelease = false;
}
-#else
- sp<Fence> fence = mHwc.getAndResetReleaseFence(mDisplayType);
- if (fence->isValid() &&
- mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) {
- status_t err = addReleaseFence(mCurrentBufferSlot,
- mCurrentBuffer, fence);
- ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)",
- strerror(-err), err);
- }
-#endif
}
-#ifndef USE_HWC2
-status_t FramebufferSurface::compositionComplete()
-{
- return mHwc.fbCompositionComplete();
-}
-#endif
-
void FramebufferSurface::dumpAsString(String8& result) const {
ConsumerBase::dumpState(result);
}
void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const
{
-#ifndef USE_HWC2
- mHwc.fbDump(result);
-#endif
ConsumerBase::dumpLocked(result, prefix);
}
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 439435a..eb70479 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -41,9 +41,6 @@
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
-#ifndef USE_HWC2
- virtual status_t compositionComplete();
-#endif
virtual status_t advanceFrame();
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
@@ -57,9 +54,6 @@
private:
virtual ~FramebufferSurface() { }; // this class cannot be overloaded
-#ifndef USE_HWC2
- virtual void onFrameAvailable(const BufferItem& item);
-#endif
virtual void freeBufferLocked(int slotIndex);
virtual void dumpLocked(String8& result, const char* prefix) const;
@@ -67,12 +61,8 @@
// nextBuffer waits for and then latches the next buffer from the
// BufferQueue and releases the previously latched buffer to the
// BufferQueue. The new buffer is returned in the 'buffer' argument.
-#ifdef USE_HWC2
status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
android_dataspace_t& outDataspace);
-#else
- status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence);
-#endif
// mDisplayType must match one of the HWC display types
int mDisplayType;
@@ -92,12 +82,10 @@
// Hardware composer, owned by SurfaceFlinger.
HWComposer& mHwc;
-#ifdef USE_HWC2
// Previous buffer to release after getting an updated retire fence
bool mHasPendingRelease;
int mPreviousBufferSlot;
sp<GraphicBuffer> mPreviousBuffer;
-#endif
};
// ---------------------------------------------------------------------------
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.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 41671f6..b25f5ad 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-#ifndef USE_HWC2
-#include "HWComposer_hwc1.h"
-#else
-
#ifndef ANDROID_SF_HWCOMPOSER_H
#define ANDROID_SF_HWCOMPOSER_H
@@ -224,5 +220,3 @@
}; // namespace android
#endif // ANDROID_SF_HWCOMPOSER_H
-
-#endif // #ifdef USE_HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
deleted file mode 100644
index ef41658..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ /dev/null
@@ -1,1344 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/misc.h>
-#include <utils/NativeHandle.h>
-#include <utils/String8.h>
-#include <utils/Thread.h>
-#include <utils/Trace.h>
-#include <utils/Vector.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <android/configuration.h>
-
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
-#include <system/graphics.h>
-
-#include "HWComposer.h"
-
-#include "../Layer.h" // needed only for debugging
-#include "../SurfaceFlinger.h"
-
-namespace android {
-
-#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION
-
-static uint32_t hwcApiVersion(const hwc_composer_device_1_t* hwc) {
- uint32_t hwcVersion = hwc->common.version;
- return hwcVersion & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
-}
-
-static uint32_t hwcHeaderVersion(const hwc_composer_device_1_t* hwc) {
- uint32_t hwcVersion = hwc->common.version;
- return hwcVersion & HARDWARE_API_VERSION_2_HEADER_MASK;
-}
-
-static bool hwcHasApiVersion(const hwc_composer_device_1_t* hwc,
- uint32_t version) {
- return hwcApiVersion(hwc) >= (version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK);
-}
-
-// ---------------------------------------------------------------------------
-
-struct HWComposer::cb_context {
- struct callbacks : public hwc_procs_t {
- // these are here to facilitate the transition when adding
- // new callbacks (an implementation can check for NULL before
- // calling a new callback).
- void (*zero[4])(void);
- };
- callbacks procs;
- HWComposer* hwc;
-};
-
-// ---------------------------------------------------------------------------
-
-HWComposer::HWComposer(
- const sp<SurfaceFlinger>& flinger,
- EventHandler& handler)
- : mFlinger(flinger),
- mFbDev(0), mHwc(0), mNumDisplays(1),
- mCBContext(new cb_context),
- mEventHandler(handler),
- mDebugForceFakeVSync(false)
-{
- for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) {
- mLists[i] = 0;
- }
-
- for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
- mLastHwVSync[i] = 0;
- mVSyncCounts[i] = 0;
- }
-
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.no_hw_vsync", value, "0");
- mDebugForceFakeVSync = atoi(value);
-
- bool needVSyncThread = true;
-
- // Note: some devices may insist that the FB HAL be opened before HWC.
- int fberr = loadFbHalModule();
- loadHwcModule();
-
- if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- // close FB HAL if we don't needed it.
- // FIXME: this is temporary until we're not forced to open FB HAL
- // before HWC.
- framebuffer_close(mFbDev);
- mFbDev = NULL;
- }
-
- // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory.
- if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
- && !mFbDev) {
- ALOGE("ERROR: failed to open framebuffer (%s), aborting",
- strerror(-fberr));
- abort();
- }
-
- // these display IDs are always reserved
- for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
- mAllocatedDisplayIDs.markBit(i);
- }
-
- if (mHwc) {
- ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
- (hwcApiVersion(mHwc) >> 24) & 0xff,
- (hwcApiVersion(mHwc) >> 16) & 0xff);
- if (mHwc->registerProcs) {
- mCBContext->hwc = this;
- mCBContext->procs.invalidate = &hook_invalidate;
- mCBContext->procs.vsync = &hook_vsync;
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
- mCBContext->procs.hotplug = &hook_hotplug;
- else
- mCBContext->procs.hotplug = NULL;
- memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
- mHwc->registerProcs(mHwc, &mCBContext->procs);
- }
-
- // don't need a vsync thread if we have a hardware composer
- needVSyncThread = false;
- // always turn vsync off when we start
- eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
-
- // the number of displays we actually have depends on the
- // hw composer version
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
- // 1.3 adds support for virtual displays
- mNumDisplays = MAX_HWC_DISPLAYS;
- } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- // 1.1 adds support for multiple displays
- mNumDisplays = NUM_BUILTIN_DISPLAYS;
- } else {
- mNumDisplays = 1;
- }
- }
-
- if (mFbDev) {
- ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)),
- "should only have fbdev if no hwc or hwc is 1.0");
-
- DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
- disp.connected = true;
- disp.format = mFbDev->format;
- DisplayConfig config = DisplayConfig();
- config.width = mFbDev->width;
- config.height = mFbDev->height;
- config.xdpi = mFbDev->xdpi;
- config.ydpi = mFbDev->ydpi;
- config.refresh = nsecs_t(1e9 / mFbDev->fps);
- disp.configs.push_back(config);
- disp.currentConfig = 0;
- } else if (mHwc) {
- // here we're guaranteed to have at least HWC 1.1
- for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
- queryDisplayProperties(i);
- }
- }
-
- if (needVSyncThread) {
- // we don't have VSYNC support, we need to fake it
- mVSyncThread = new VSyncThread(*this);
- }
-}
-
-HWComposer::~HWComposer() {
- if (mHwc) {
- eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
- }
- if (mVSyncThread != NULL) {
- mVSyncThread->requestExitAndWait();
- }
- if (mHwc) {
- hwc_close_1(mHwc);
- }
- if (mFbDev) {
- framebuffer_close(mFbDev);
- }
- delete mCBContext;
-}
-
-// Load and prepare the hardware composer module. Sets mHwc.
-void HWComposer::loadHwcModule()
-{
- hw_module_t const* module;
-
- if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
- ALOGE("%s module not found", HWC_HARDWARE_MODULE_ID);
- return;
- }
-
- int err = hwc_open_1(module, &mHwc);
- if (err) {
- ALOGE("%s device failed to initialize (%s)",
- HWC_HARDWARE_COMPOSER, strerror(-err));
- return;
- }
-
- if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_0) ||
- hwcHeaderVersion(mHwc) < MIN_HWC_HEADER_VERSION ||
- hwcHeaderVersion(mHwc) > HWC_HEADER_VERSION) {
- ALOGE("%s device version %#x unsupported, will not be used",
- HWC_HARDWARE_COMPOSER, mHwc->common.version);
- hwc_close_1(mHwc);
- mHwc = NULL;
- return;
- }
-}
-
-// Load and prepare the FB HAL, which uses the gralloc module. Sets mFbDev.
-int HWComposer::loadFbHalModule()
-{
- hw_module_t const* module;
-
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err != 0) {
- ALOGE("%s module not found", GRALLOC_HARDWARE_MODULE_ID);
- return err;
- }
-
- return framebuffer_open(module, &mFbDev);
-}
-
-status_t HWComposer::initCheck() const {
- return mHwc ? NO_ERROR : NO_INIT;
-}
-
-void HWComposer::hook_invalidate(const struct hwc_procs* procs) {
- cb_context* ctx = reinterpret_cast<cb_context*>(
- const_cast<hwc_procs_t*>(procs));
- ctx->hwc->invalidate();
-}
-
-void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
- int64_t timestamp) {
- cb_context* ctx = reinterpret_cast<cb_context*>(
- const_cast<hwc_procs_t*>(procs));
- ctx->hwc->vsync(disp, timestamp);
-}
-
-void HWComposer::hook_hotplug(const struct hwc_procs* procs, int disp,
- int connected) {
- cb_context* ctx = reinterpret_cast<cb_context*>(
- const_cast<hwc_procs_t*>(procs));
- ctx->hwc->hotplug(disp, connected);
-}
-
-void HWComposer::invalidate() {
- mFlinger->repaintEverything();
-}
-
-void HWComposer::vsync(int disp, int64_t timestamp) {
- if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
- {
- Mutex::Autolock _l(mLock);
-
- // There have been reports of HWCs that signal several vsync events
- // with the same timestamp when turning the display off and on. This
- // is a bug in the HWC implementation, but filter the extra events
- // out here so they don't cause havoc downstream.
- if (timestamp == mLastHwVSync[disp]) {
- ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")",
- timestamp);
- return;
- }
-
- mLastHwVSync[disp] = timestamp;
- }
-
- char tag[16];
- snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
- ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
-
- mEventHandler.onVSyncReceived(disp, timestamp);
- }
-}
-
-void HWComposer::hotplug(int disp, int connected) {
- if (disp >= VIRTUAL_DISPLAY_ID_BASE) {
- ALOGE("hotplug event received for invalid display: disp=%d connected=%d",
- disp, connected);
- return;
- }
- queryDisplayProperties(disp);
- // Do not teardown or recreate the primary display
- if (disp != HWC_DISPLAY_PRIMARY) {
- mEventHandler.onHotplugReceived(disp, bool(connected));
- }
-}
-
-static float getDefaultDensity(uint32_t width, uint32_t height) {
- // Default density is based on TVs: 1080p displays get XHIGH density,
- // lower-resolution displays get TV density. Maybe eventually we'll need
- // to update it for 4K displays, though hopefully those just report
- // accurate DPI information to begin with. This is also used for virtual
- // displays and even primary displays with older hwcomposers, so be
- // careful about orientation.
-
- uint32_t h = width < height ? width : height;
- if (h >= 1080) return ACONFIGURATION_DENSITY_XHIGH;
- else return ACONFIGURATION_DENSITY_TV;
-}
-
-static const uint32_t DISPLAY_ATTRIBUTES[] = {
- HWC_DISPLAY_VSYNC_PERIOD,
- HWC_DISPLAY_WIDTH,
- HWC_DISPLAY_HEIGHT,
- HWC_DISPLAY_DPI_X,
- HWC_DISPLAY_DPI_Y,
- HWC_DISPLAY_COLOR_TRANSFORM,
- HWC_DISPLAY_NO_ATTRIBUTE,
-};
-#define NUM_DISPLAY_ATTRIBUTES (sizeof(DISPLAY_ATTRIBUTES) / sizeof(DISPLAY_ATTRIBUTES)[0])
-
-static const uint32_t PRE_HWC15_DISPLAY_ATTRIBUTES[] = {
- HWC_DISPLAY_VSYNC_PERIOD,
- HWC_DISPLAY_WIDTH,
- HWC_DISPLAY_HEIGHT,
- HWC_DISPLAY_DPI_X,
- HWC_DISPLAY_DPI_Y,
- HWC_DISPLAY_NO_ATTRIBUTE,
-};
-
-status_t HWComposer::queryDisplayProperties(int disp) {
-
- LOG_ALWAYS_FATAL_IF(!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1));
-
- // use zero as default value for unspecified attributes
- int32_t values[NUM_DISPLAY_ATTRIBUTES - 1];
- memset(values, 0, sizeof(values));
-
- const size_t MAX_NUM_CONFIGS = 128;
- uint32_t configs[MAX_NUM_CONFIGS] = {0};
- size_t numConfigs = MAX_NUM_CONFIGS;
- status_t err = mHwc->getDisplayConfigs(mHwc, disp, configs, &numConfigs);
- if (err != NO_ERROR) {
- // this can happen if an unpluggable display is not connected
- mDisplayData[disp].connected = false;
- return err;
- }
-
- mDisplayData[disp].currentConfig = 0;
- for (size_t c = 0; c < numConfigs; ++c) {
- err = mHwc->getDisplayAttributes(mHwc, disp, configs[c],
- DISPLAY_ATTRIBUTES, values);
- // If this is a pre-1.5 HWC, it may not know about color transform, so
- // try again with a smaller set of attributes
- if (err != NO_ERROR) {
- err = mHwc->getDisplayAttributes(mHwc, disp, configs[c],
- PRE_HWC15_DISPLAY_ATTRIBUTES, values);
- }
- if (err != NO_ERROR) {
- // we can't get this display's info. turn it off.
- mDisplayData[disp].connected = false;
- return err;
- }
-
- DisplayConfig config = DisplayConfig();
- for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) {
- switch (DISPLAY_ATTRIBUTES[i]) {
- case HWC_DISPLAY_VSYNC_PERIOD:
- config.refresh = nsecs_t(values[i]);
- break;
- case HWC_DISPLAY_WIDTH:
- config.width = values[i];
- break;
- case HWC_DISPLAY_HEIGHT:
- config.height = values[i];
- break;
- case HWC_DISPLAY_DPI_X:
- config.xdpi = values[i] / 1000.0f;
- break;
- case HWC_DISPLAY_DPI_Y:
- config.ydpi = values[i] / 1000.0f;
- break;
- case HWC_DISPLAY_COLOR_TRANSFORM:
- config.colorMode = static_cast<android_color_mode_t>(values[i]);
- break;
- default:
- ALOG_ASSERT(false, "unknown display attribute[%zu] %#x",
- i, DISPLAY_ATTRIBUTES[i]);
- break;
- }
- }
-
- if (config.xdpi == 0.0f || config.ydpi == 0.0f) {
- float dpi = getDefaultDensity(config.width, config.height);
- config.xdpi = dpi;
- config.ydpi = dpi;
- }
-
- mDisplayData[disp].configs.push_back(config);
- }
-
- // FIXME: what should we set the format to?
- mDisplayData[disp].format = HAL_PIXEL_FORMAT_RGBA_8888;
- mDisplayData[disp].connected = true;
- return NO_ERROR;
-}
-
-status_t HWComposer::setVirtualDisplayProperties(int32_t id,
- uint32_t w, uint32_t h, uint32_t format) {
- if (id < VIRTUAL_DISPLAY_ID_BASE || id >= int32_t(mNumDisplays) ||
- !mAllocatedDisplayIDs.hasBit(id)) {
- return BAD_INDEX;
- }
- size_t configId = mDisplayData[id].currentConfig;
- mDisplayData[id].format = format;
- DisplayConfig& config = mDisplayData[id].configs.editItemAt(configId);
- config.width = w;
- config.height = h;
- config.xdpi = config.ydpi = getDefaultDensity(w, h);
- return NO_ERROR;
-}
-
-int32_t HWComposer::allocateDisplayId() {
- if (mAllocatedDisplayIDs.count() >= mNumDisplays) {
- return NO_MEMORY;
- }
- int32_t id = mAllocatedDisplayIDs.firstUnmarkedBit();
- mAllocatedDisplayIDs.markBit(id);
- mDisplayData[id].connected = true;
- mDisplayData[id].configs.resize(1);
- mDisplayData[id].currentConfig = 0;
- return id;
-}
-
-status_t HWComposer::freeDisplayId(int32_t id) {
- if (id < NUM_BUILTIN_DISPLAYS) {
- // cannot free the reserved IDs
- return BAD_VALUE;
- }
- if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
- return BAD_INDEX;
- }
- mAllocatedDisplayIDs.clearBit(id);
- mDisplayData[id].connected = false;
- return NO_ERROR;
-}
-
-nsecs_t HWComposer::getRefreshTimestamp(int disp) const {
- // this returns the last refresh timestamp.
- // if the last one is not available, we estimate it based on
- // the refresh period and whatever closest timestamp we have.
- Mutex::Autolock _l(mLock);
- nsecs_t now = systemTime(CLOCK_MONOTONIC);
- size_t configId = mDisplayData[disp].currentConfig;
- return now - ((now - mLastHwVSync[disp]) %
- mDisplayData[disp].configs[configId].refresh);
-}
-
-sp<Fence> HWComposer::getDisplayFence(int disp) const {
- return mDisplayData[disp].lastDisplayFence;
-}
-
-uint32_t HWComposer::getFormat(int disp) const {
- if (static_cast<uint32_t>(disp) >= MAX_HWC_DISPLAYS || !mAllocatedDisplayIDs.hasBit(disp)) {
- return HAL_PIXEL_FORMAT_RGBA_8888;
- } else {
- return mDisplayData[disp].format;
- }
-}
-
-bool HWComposer::isConnected(int disp) const {
- return mDisplayData[disp].connected;
-}
-
-uint32_t HWComposer::getWidth(int disp) const {
- size_t currentConfig = mDisplayData[disp].currentConfig;
- return mDisplayData[disp].configs[currentConfig].width;
-}
-
-uint32_t HWComposer::getHeight(int disp) const {
- size_t currentConfig = mDisplayData[disp].currentConfig;
- return mDisplayData[disp].configs[currentConfig].height;
-}
-
-float HWComposer::getDpiX(int disp) const {
- size_t currentConfig = mDisplayData[disp].currentConfig;
- return mDisplayData[disp].configs[currentConfig].xdpi;
-}
-
-float HWComposer::getDpiY(int disp) const {
- size_t currentConfig = mDisplayData[disp].currentConfig;
- return mDisplayData[disp].configs[currentConfig].ydpi;
-}
-
-nsecs_t HWComposer::getRefreshPeriod(int disp) const {
- size_t currentConfig = mDisplayData[disp].currentConfig;
- return mDisplayData[disp].configs[currentConfig].refresh;
-}
-
-android_color_mode_t HWComposer::getColorMode(int disp) const {
- size_t currentConfig = mDisplayData[disp].currentConfig;
- return mDisplayData[disp].configs[currentConfig].colorMode;
-}
-
-const Vector<HWComposer::DisplayConfig>& HWComposer::getConfigs(int disp) const {
- return mDisplayData[disp].configs;
-}
-
-size_t HWComposer::getCurrentConfig(int disp) const {
- return mDisplayData[disp].currentConfig;
-}
-
-void HWComposer::eventControl(int disp, int event, int enabled) {
- if (uint32_t(disp)>31 || !mAllocatedDisplayIDs.hasBit(disp)) {
- ALOGD("eventControl ignoring event %d on unallocated disp %d (en=%d)",
- event, disp, enabled);
- return;
- }
- if (event != EVENT_VSYNC) {
- ALOGW("eventControl got unexpected event %d (disp=%d en=%d)",
- event, disp, enabled);
- return;
- }
- status_t err = NO_ERROR;
- if (mHwc && !mDebugForceFakeVSync) {
- // NOTE: we use our own internal lock here because we have to call
- // into the HWC with the lock held, and we want to make sure
- // that even if HWC blocks (which it shouldn't), it won't
- // affect other threads.
- Mutex::Autolock _l(mEventControlLock);
- const int32_t eventBit = 1UL << event;
- const int32_t newValue = enabled ? eventBit : 0;
- const int32_t oldValue = mDisplayData[disp].events & eventBit;
- if (newValue != oldValue) {
- ATRACE_CALL();
- err = mHwc->eventControl(mHwc, disp, event, enabled);
- if (!err) {
- int32_t& events(mDisplayData[disp].events);
- events = (events & ~eventBit) | newValue;
-
- char tag[16];
- snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp);
- ATRACE_INT(tag, enabled);
- }
- }
- // error here should not happen -- not sure what we should
- // do if it does.
- ALOGE_IF(err, "eventControl(%d, %d) failed %s",
- event, enabled, strerror(-err));
- }
-
- if (err == NO_ERROR && mVSyncThread != NULL) {
- mVSyncThread->setEnabled(enabled);
- }
-}
-
-status_t HWComposer::createWorkList(int32_t id, size_t numLayers) {
- if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
- return BAD_INDEX;
- }
-
- if (mHwc) {
- DisplayData& disp(mDisplayData[id]);
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- // we need space for the HWC_FRAMEBUFFER_TARGET
- numLayers++;
- }
- if (disp.capacity < numLayers || disp.list == NULL) {
- size_t size = sizeof(hwc_display_contents_1_t)
- + numLayers * sizeof(hwc_layer_1_t);
- free(disp.list);
- disp.list = (hwc_display_contents_1_t*)malloc(size);
- disp.capacity = numLayers;
- }
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1];
- memset(disp.framebufferTarget, 0, sizeof(hwc_layer_1_t));
- const DisplayConfig& currentConfig =
- disp.configs[disp.currentConfig];
- const hwc_rect_t r = { 0, 0,
- (int) currentConfig.width, (int) currentConfig.height };
- disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;
- disp.framebufferTarget->hints = 0;
- disp.framebufferTarget->flags = 0;
- disp.framebufferTarget->handle = disp.fbTargetHandle;
- disp.framebufferTarget->transform = 0;
- disp.framebufferTarget->blending = HWC_BLENDING_PREMULT;
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
- disp.framebufferTarget->sourceCropf.left = 0;
- disp.framebufferTarget->sourceCropf.top = 0;
- disp.framebufferTarget->sourceCropf.right =
- currentConfig.width;
- disp.framebufferTarget->sourceCropf.bottom =
- currentConfig.height;
- } else {
- disp.framebufferTarget->sourceCrop = r;
- }
- disp.framebufferTarget->displayFrame = r;
- disp.framebufferTarget->visibleRegionScreen.numRects = 1;
- disp.framebufferTarget->visibleRegionScreen.rects =
- &disp.framebufferTarget->displayFrame;
- disp.framebufferTarget->acquireFenceFd = -1;
- disp.framebufferTarget->releaseFenceFd = -1;
- disp.framebufferTarget->planeAlpha = 0xFF;
- }
- disp.list->retireFenceFd = -1;
- disp.list->flags = HWC_GEOMETRY_CHANGED;
- disp.list->numHwLayers = numLayers;
- }
- return NO_ERROR;
-}
-
-status_t HWComposer::setFramebufferTarget(int32_t id,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf) {
- if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
- return BAD_INDEX;
- }
- DisplayData& disp(mDisplayData[id]);
- if (!disp.framebufferTarget) {
- // this should never happen, but apparently eglCreateWindowSurface()
- // triggers a Surface::queueBuffer() on some
- // devices (!?) -- log and ignore.
- ALOGE("HWComposer: framebufferTarget is null");
- return NO_ERROR;
- }
-
- int acquireFenceFd = -1;
- if (acquireFence->isValid()) {
- acquireFenceFd = acquireFence->dup();
- }
-
- // ALOGD("fbPost: handle=%p, fence=%d", buf->handle, acquireFenceFd);
- disp.fbTargetHandle = buf->handle;
- disp.framebufferTarget->handle = disp.fbTargetHandle;
- disp.framebufferTarget->acquireFenceFd = acquireFenceFd;
- return NO_ERROR;
-}
-
-status_t HWComposer::prepare() {
- Mutex::Autolock _l(mDisplayLock);
- for (size_t i=0 ; i<mNumDisplays ; i++) {
- DisplayData& disp(mDisplayData[i]);
- if (disp.framebufferTarget) {
- // make sure to reset the type to HWC_FRAMEBUFFER_TARGET
- // DO NOT reset the handle field to NULL, because it's possible
- // that we have nothing to redraw (eg: eglSwapBuffers() not called)
- // in which case, we should continue to use the same buffer.
- LOG_FATAL_IF(disp.list == NULL);
- disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;
- }
- if (!disp.connected && disp.list != NULL) {
- ALOGW("WARNING: disp %zu: connected, non-null list, layers=%zu",
- i, disp.list->numHwLayers);
- }
- mLists[i] = disp.list;
- if (mLists[i]) {
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
- mLists[i]->outbuf = disp.outbufHandle;
- mLists[i]->outbufAcquireFenceFd = -1;
- } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- // garbage data to catch improper use
- mLists[i]->dpy = (hwc_display_t)0xDEADBEEF;
- mLists[i]->sur = (hwc_surface_t)0xDEADBEEF;
- } else {
- mLists[i]->dpy = EGL_NO_DISPLAY;
- mLists[i]->sur = EGL_NO_SURFACE;
- }
- }
- }
-
- int err = mHwc->prepare(mHwc, mNumDisplays, mLists);
- ALOGE_IF(err, "HWComposer: prepare failed (%s)", strerror(-err));
-
- if (err == NO_ERROR) {
- // here we're just making sure that "skip" layers are set
- // to HWC_FRAMEBUFFER and we're also counting how many layers
- // we have of each type.
- //
- // If there are no window layers, we treat the display has having FB
- // composition, because SurfaceFlinger will use GLES to draw the
- // wormhole region.
- for (size_t i=0 ; i<mNumDisplays ; i++) {
- DisplayData& disp(mDisplayData[i]);
- disp.hasFbComp = false;
- disp.hasOvComp = false;
- if (disp.list) {
- for (size_t i=0 ; i<disp.list->numHwLayers ; i++) {
- hwc_layer_1_t& l = disp.list->hwLayers[i];
-
- //ALOGD("prepare: %d, type=%d, handle=%p",
- // i, l.compositionType, l.handle);
-
- if (l.flags & HWC_SKIP_LAYER) {
- l.compositionType = HWC_FRAMEBUFFER;
- }
- if (l.compositionType == HWC_FRAMEBUFFER) {
- disp.hasFbComp = true;
- }
- if (l.compositionType == HWC_OVERLAY) {
- disp.hasOvComp = true;
- }
- if (l.compositionType == HWC_CURSOR_OVERLAY) {
- disp.hasOvComp = true;
- }
- }
- if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) {
- disp.hasFbComp = true;
- }
- } else {
- disp.hasFbComp = true;
- }
- }
- }
- return (status_t)err;
-}
-
-bool HWComposer::hasHwcComposition(int32_t id) const {
- if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
- return false;
- return mDisplayData[id].hasOvComp;
-}
-
-bool HWComposer::hasGlesComposition(int32_t id) const {
- if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
- return true;
- return mDisplayData[id].hasFbComp;
-}
-
-sp<Fence> HWComposer::getAndResetReleaseFence(int32_t id) {
- if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
- return Fence::NO_FENCE;
-
- int fd = INVALID_OPERATION;
- if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- const DisplayData& disp(mDisplayData[id]);
- if (disp.framebufferTarget) {
- fd = disp.framebufferTarget->releaseFenceFd;
- disp.framebufferTarget->acquireFenceFd = -1;
- disp.framebufferTarget->releaseFenceFd = -1;
- }
- }
- return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE;
-}
-
-status_t HWComposer::commit() {
- int err = NO_ERROR;
- if (mHwc) {
- if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- // On version 1.0, the OpenGL ES target surface is communicated
- // by the (dpy, sur) fields and we are guaranteed to have only
- // a single display.
- mLists[0]->dpy = eglGetCurrentDisplay();
- mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW);
- }
-
- for (size_t i=VIRTUAL_DISPLAY_ID_BASE; i<mNumDisplays; i++) {
- DisplayData& disp(mDisplayData[i]);
- if (disp.outbufHandle) {
- mLists[i]->outbuf = disp.outbufHandle;
- mLists[i]->outbufAcquireFenceFd =
- disp.outbufAcquireFence->dup();
- }
- }
-
- err = mHwc->set(mHwc, mNumDisplays, mLists);
-
- for (size_t i=0 ; i<mNumDisplays ; i++) {
- DisplayData& disp(mDisplayData[i]);
- disp.lastDisplayFence = disp.lastRetireFence;
- disp.lastRetireFence = Fence::NO_FENCE;
- if (disp.list) {
- if (disp.list->retireFenceFd != -1) {
- disp.lastRetireFence = new Fence(disp.list->retireFenceFd);
- disp.list->retireFenceFd = -1;
- }
- disp.list->flags &= ~HWC_GEOMETRY_CHANGED;
- }
- }
- }
- return (status_t)err;
-}
-
-status_t HWComposer::setPowerMode(int disp, int mode) {
- LOG_FATAL_IF(disp >= VIRTUAL_DISPLAY_ID_BASE);
- if (mHwc) {
- if (mode == HWC_POWER_MODE_OFF) {
- eventControl(disp, HWC_EVENT_VSYNC, 0);
- }
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) {
- return (status_t)mHwc->setPowerMode(mHwc, disp, mode);
- } else {
- return (status_t)mHwc->blank(mHwc, disp,
- mode == HWC_POWER_MODE_OFF ? 1 : 0);
- }
- }
- return NO_ERROR;
-}
-
-status_t HWComposer::setActiveConfig(int disp, int mode) {
- LOG_FATAL_IF(disp >= VIRTUAL_DISPLAY_ID_BASE);
- DisplayData& dd(mDisplayData[disp]);
- dd.currentConfig = mode;
- if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) {
- return (status_t)mHwc->setActiveConfig(mHwc, disp, mode);
- } else {
- LOG_FATAL_IF(mode != 0);
- }
- return NO_ERROR;
-}
-
-void HWComposer::disconnectDisplay(int disp) {
- LOG_ALWAYS_FATAL_IF(disp < 0 || disp == HWC_DISPLAY_PRIMARY);
- DisplayData& dd(mDisplayData[disp]);
- free(dd.list);
- dd.list = NULL;
- dd.framebufferTarget = NULL; // points into dd.list
- dd.fbTargetHandle = NULL;
- dd.outbufHandle = NULL;
- dd.lastRetireFence = Fence::NO_FENCE;
- dd.lastDisplayFence = Fence::NO_FENCE;
- dd.outbufAcquireFence = Fence::NO_FENCE;
- // clear all the previous configs and repopulate when a new
- // device is added
- dd.configs.clear();
-}
-
-int HWComposer::getVisualID() const {
- if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- // FIXME: temporary hack until HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED
- // is supported by the implementation. we can only be in this case
- // if we have HWC 1.1
- return HAL_PIXEL_FORMAT_RGBA_8888;
- //return HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- } else {
- return mFbDev->format;
- }
-}
-
-bool HWComposer::supportsFramebufferTarget() const {
- return (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1));
-}
-
-int HWComposer::fbPost(int32_t id,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) {
- if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- return setFramebufferTarget(id, acquireFence, buffer);
- } else {
- acquireFence->waitForever("HWComposer::fbPost");
- return mFbDev->post(mFbDev, buffer->handle);
- }
-}
-
-int HWComposer::fbCompositionComplete() {
- if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
- return NO_ERROR;
-
- if (mFbDev->compositionComplete) {
- return mFbDev->compositionComplete(mFbDev);
- } else {
- return INVALID_OPERATION;
- }
-}
-
-void HWComposer::fbDump(String8& result) {
- if (mFbDev && mFbDev->common.version >= 1 && mFbDev->dump) {
- const size_t SIZE = 4096;
- char buffer[SIZE];
- mFbDev->dump(mFbDev, buffer, SIZE);
- result.append(buffer);
- }
-}
-
-status_t HWComposer::setOutputBuffer(int32_t id, const sp<Fence>& acquireFence,
- const sp<GraphicBuffer>& buf) {
- if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
- return BAD_INDEX;
- if (id < VIRTUAL_DISPLAY_ID_BASE)
- return INVALID_OPERATION;
-
- DisplayData& disp(mDisplayData[id]);
- disp.outbufHandle = buf->handle;
- disp.outbufAcquireFence = acquireFence;
- return NO_ERROR;
-}
-
-sp<Fence> HWComposer::getLastRetireFence(int32_t id) const {
- if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
- return Fence::NO_FENCE;
- return mDisplayData[id].lastRetireFence;
-}
-
-status_t HWComposer::setCursorPositionAsync(int32_t id, const Rect& pos)
-{
- if (mHwc->setCursorPositionAsync) {
- return (status_t)mHwc->setCursorPositionAsync(mHwc, id, pos.left, pos.top);
- }
- else {
- return NO_ERROR;
- }
-}
-
-/*
- * Helper template to implement a concrete HWCLayer
- * This holds the pointer to the concrete hwc layer type
- * and implements the "iterable" side of HWCLayer.
- */
-template<typename CONCRETE, typename HWCTYPE>
-class Iterable : public HWComposer::HWCLayer {
-protected:
- HWCTYPE* const mLayerList;
- HWCTYPE* mCurrentLayer;
- Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
- mIndex(0) { }
- inline HWCTYPE const * getLayer() const { return mCurrentLayer; }
- inline HWCTYPE* getLayer() { return mCurrentLayer; }
- virtual ~Iterable() { }
- size_t mIndex;
-private:
- // returns a copy of ourselves
- virtual HWComposer::HWCLayer* dup() {
- return new CONCRETE( static_cast<const CONCRETE&>(*this) );
- }
- virtual status_t setLayer(size_t index) {
- mIndex = index;
- mCurrentLayer = &mLayerList[index];
- return NO_ERROR;
- }
-};
-
-/*
- * Concrete implementation of HWCLayer for HWC_DEVICE_API_VERSION_1_0.
- * This implements the HWCLayer side of HWCIterableLayer.
- */
-class HWCLayerVersion1 : public Iterable<HWCLayerVersion1, hwc_layer_1_t> {
- struct hwc_composer_device_1* mHwc;
-public:
- HWCLayerVersion1(struct hwc_composer_device_1* hwc, hwc_layer_1_t* layer,
- Vector<Region>* visibleRegions,
- Vector<Region>* surfaceDamageRegions)
- : Iterable<HWCLayerVersion1, hwc_layer_1_t>(layer), mHwc(hwc),
- mVisibleRegions(visibleRegions),
- mSurfaceDamageRegions(surfaceDamageRegions) {}
-
- virtual int32_t getCompositionType() const {
- return getLayer()->compositionType;
- }
- virtual uint32_t getHints() const {
- return getLayer()->hints;
- }
- virtual sp<Fence> getAndResetReleaseFence() {
- int fd = getLayer()->releaseFenceFd;
- getLayer()->releaseFenceFd = -1;
- return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE;
- }
- virtual void setAcquireFenceFd(int fenceFd) {
- getLayer()->acquireFenceFd = fenceFd;
- }
- virtual void setPerFrameDefaultState() {
- //getLayer()->compositionType = HWC_FRAMEBUFFER;
- }
- virtual void setPlaneAlpha(uint8_t alpha) {
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_2)) {
- getLayer()->planeAlpha = alpha;
- } else {
- if (alpha < 0xFF) {
- getLayer()->flags |= HWC_SKIP_LAYER;
- }
- }
- }
- virtual void setDefaultState() {
- hwc_layer_1_t* const l = getLayer();
- l->compositionType = HWC_FRAMEBUFFER;
- l->hints = 0;
- l->flags = HWC_SKIP_LAYER;
- l->handle = 0;
- l->transform = 0;
- l->blending = HWC_BLENDING_NONE;
- l->visibleRegionScreen.numRects = 0;
- l->visibleRegionScreen.rects = NULL;
- l->acquireFenceFd = -1;
- l->releaseFenceFd = -1;
- l->planeAlpha = 0xFF;
- }
- virtual void setSkip(bool skip) {
- if (skip) {
- getLayer()->flags |= HWC_SKIP_LAYER;
- } else {
- getLayer()->flags &= ~HWC_SKIP_LAYER;
- }
- }
- virtual void setIsCursorLayerHint(bool isCursor) {
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) {
- if (isCursor) {
- getLayer()->flags |= HWC_IS_CURSOR_LAYER;
- }
- else {
- getLayer()->flags &= ~HWC_IS_CURSOR_LAYER;
- }
- }
- }
- virtual void setBlending(uint32_t blending) {
- getLayer()->blending = blending;
- }
- virtual void setTransform(uint32_t transform) {
- getLayer()->transform = transform;
- }
- virtual void setFrame(const Rect& frame) {
- getLayer()->displayFrame = reinterpret_cast<hwc_rect_t const&>(frame);
- }
- virtual void setCrop(const FloatRect& crop) {
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
- getLayer()->sourceCropf = reinterpret_cast<hwc_frect_t const&>(crop);
- } else {
- /*
- * Since h/w composer didn't support a flot crop rect before version 1.3,
- * using integer coordinates instead produces a different output from the GL code in
- * Layer::drawWithOpenGL(). The difference can be large if the buffer crop to
- * window size ratio is large and a window crop is defined
- * (i.e.: if we scale the buffer a lot and we also crop it with a window crop).
- */
- hwc_rect_t& r = getLayer()->sourceCrop;
- r.left = int(ceilf(crop.left));
- r.top = int(ceilf(crop.top));
- r.right = int(floorf(crop.right));
- r.bottom= int(floorf(crop.bottom));
- }
- }
- virtual void setVisibleRegionScreen(const Region& reg) {
- hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen;
- mVisibleRegions->editItemAt(mIndex) = reg;
- visibleRegion.rects = reinterpret_cast<hwc_rect_t const *>(
- mVisibleRegions->itemAt(mIndex).getArray(
- &visibleRegion.numRects));
- }
- virtual void setSurfaceDamage(const Region& reg) {
- if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_5)) {
- return;
- }
- hwc_region_t& surfaceDamage = getLayer()->surfaceDamage;
- // We encode default full-screen damage as INVALID_RECT upstream, but as
- // 0 rects for HWComposer
- if (reg.isRect() && reg.getBounds() == Rect::INVALID_RECT) {
- surfaceDamage.numRects = 0;
- surfaceDamage.rects = NULL;
- return;
- }
- mSurfaceDamageRegions->editItemAt(mIndex) = reg;
- surfaceDamage.rects = reinterpret_cast<hwc_rect_t const *>(
- mSurfaceDamageRegions->itemAt(mIndex).getArray(
- &surfaceDamage.numRects));
- }
- virtual void setSidebandStream(const sp<NativeHandle>& stream) {
- ALOG_ASSERT(stream->handle() != NULL);
- getLayer()->compositionType = HWC_SIDEBAND;
- getLayer()->sidebandStream = stream->handle();
- }
- virtual void setBuffer(const sp<GraphicBuffer>& buffer) {
- if (buffer == 0 || buffer->handle == 0) {
- getLayer()->compositionType = HWC_FRAMEBUFFER;
- getLayer()->flags |= HWC_SKIP_LAYER;
- getLayer()->handle = 0;
- } else {
- if (getLayer()->compositionType == HWC_SIDEBAND) {
- // If this was a sideband layer but the stream was removed, reset
- // it to FRAMEBUFFER. The HWC can change it to OVERLAY in prepare.
- getLayer()->compositionType = HWC_FRAMEBUFFER;
- }
- getLayer()->handle = buffer->handle;
- }
- }
- virtual void onDisplayed() {
- getLayer()->acquireFenceFd = -1;
- }
-
-protected:
- // Pointers to the vectors of Region backing-memory held in DisplayData.
- // Only the Region at mIndex corresponds to this Layer.
- Vector<Region>* mVisibleRegions;
- Vector<Region>* mSurfaceDamageRegions;
-};
-
-/*
- * returns an iterator initialized at a given index in the layer list
- */
-HWComposer::LayerListIterator HWComposer::getLayerIterator(int32_t id, size_t index) {
- if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
- return LayerListIterator();
- }
- DisplayData& disp(mDisplayData[id]);
- if (!mHwc || !disp.list || index > disp.list->numHwLayers) {
- return LayerListIterator();
- }
- if (disp.visibleRegions.size() < disp.list->numHwLayers) {
- disp.visibleRegions.resize(disp.list->numHwLayers);
- }
- if (disp.surfaceDamageRegions.size() < disp.list->numHwLayers) {
- disp.surfaceDamageRegions.resize(disp.list->numHwLayers);
- }
- return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers,
- &disp.visibleRegions, &disp.surfaceDamageRegions), index);
-}
-
-/*
- * returns an iterator on the beginning of the layer list
- */
-HWComposer::LayerListIterator HWComposer::begin(int32_t id) {
- return getLayerIterator(id, 0);
-}
-
-/*
- * returns an iterator on the end of the layer list
- */
-HWComposer::LayerListIterator HWComposer::end(int32_t id) {
- size_t numLayers = 0;
- if (uint32_t(id) <= 31 && mAllocatedDisplayIDs.hasBit(id)) {
- const DisplayData& disp(mDisplayData[id]);
- if (mHwc && disp.list) {
- numLayers = disp.list->numHwLayers;
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
- // with HWC 1.1, the last layer is always the HWC_FRAMEBUFFER_TARGET,
- // which we ignore when iterating through the layer list.
- ALOGE_IF(!numLayers, "mDisplayData[%d].list->numHwLayers is 0", id);
- if (numLayers) {
- numLayers--;
- }
- }
- }
- }
- return getLayerIterator(id, numLayers);
-}
-
-// Converts a PixelFormat to a human-readable string. Max 11 chars.
-// (Could use a table of prefab String8 objects.)
-static String8 getFormatStr(PixelFormat format) {
- switch (format) {
- case PIXEL_FORMAT_RGBA_8888: return String8("RGBA_8888");
- case PIXEL_FORMAT_RGBX_8888: return String8("RGBx_8888");
- case PIXEL_FORMAT_RGB_888: return String8("RGB_888");
- case PIXEL_FORMAT_RGB_565: return String8("RGB_565");
- case PIXEL_FORMAT_BGRA_8888: return String8("BGRA_8888");
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
- return String8("ImplDef");
- default:
- String8 result;
- result.appendFormat("? %08x", format);
- return result;
- }
-}
-
-void HWComposer::dump(String8& result) const {
- Mutex::Autolock _l(mDisplayLock);
- if (mHwc) {
- result.appendFormat("Hardware Composer state (version %08x):\n", hwcApiVersion(mHwc));
- result.appendFormat(" mDebugForceFakeVSync=%d\n", mDebugForceFakeVSync);
- for (size_t i=0 ; i<mNumDisplays ; i++) {
- const DisplayData& disp(mDisplayData[i]);
- if (!disp.connected)
- continue;
-
- const Vector< sp<Layer> >& visibleLayersSortedByZ =
- mFlinger->getLayerSortedByZForHwcDisplay(i);
-
-
- result.appendFormat(" Display[%zd] configurations (* current):\n", i);
- for (size_t c = 0; c < disp.configs.size(); ++c) {
- const DisplayConfig& config(disp.configs[c]);
- result.appendFormat(" %s%zd: %ux%u, xdpi=%f, ydpi=%f"
- ", refresh=%" PRId64 ", colorMode=%d\n",
- c == disp.currentConfig ? "* " : "", c,
- config.width, config.height, config.xdpi, config.ydpi,
- config.refresh, config.colorMode);
- }
-
- if (disp.list) {
- result.appendFormat(
- " numHwLayers=%zu, flags=%08x\n",
- disp.list->numHwLayers, disp.list->flags);
-
- result.append(
- " type | handle | hint | flag | tr | blnd | format | source crop (l,t,r,b) | frame | name \n"
- "-----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------\n");
- // " _________ | ________ | ____ | ____ | __ | ____ | ___________ |_____._,_____._,_____._,_____._ |_____,_____,_____,_____ | ___...
- for (size_t i=0 ; i<disp.list->numHwLayers ; i++) {
- const hwc_layer_1_t&l = disp.list->hwLayers[i];
- int32_t format = -1;
- String8 name("unknown");
-
- if (i < visibleLayersSortedByZ.size()) {
- const sp<Layer>& layer(visibleLayersSortedByZ[i]);
- const sp<GraphicBuffer>& buffer(
- layer->getActiveBuffer());
- if (buffer != NULL) {
- format = buffer->getPixelFormat();
- }
- name = layer->getName();
- }
-
- int type = l.compositionType;
- if (type == HWC_FRAMEBUFFER_TARGET) {
- name = "HWC_FRAMEBUFFER_TARGET";
- format = disp.format;
- }
-
- static char const* compositionTypeName[] = {
- "GLES",
- "HWC",
- "BKGND",
- "FB TARGET",
- "SIDEBAND",
- "HWC_CURSOR",
- "UNKNOWN"};
- if (type >= NELEM(compositionTypeName))
- type = NELEM(compositionTypeName) - 1;
-
- String8 formatStr = getFormatStr(format);
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
- result.appendFormat(
- " %9s | %08" PRIxPTR " | %04x | %04x | %02x | %04x | %-11s |%7.1f,%7.1f,%7.1f,%7.1f |%5d,%5d,%5d,%5d | %s\n",
- compositionTypeName[type],
- intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, formatStr.string(),
- l.sourceCropf.left, l.sourceCropf.top, l.sourceCropf.right, l.sourceCropf.bottom,
- l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom,
- name.string());
- } else {
- result.appendFormat(
- " %9s | %08" PRIxPTR " | %04x | %04x | %02x | %04x | %-11s |%7d,%7d,%7d,%7d |%5d,%5d,%5d,%5d | %s\n",
- compositionTypeName[type],
- intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, formatStr.string(),
- l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom,
- l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom,
- name.string());
- }
- }
- }
- }
- }
-
- if (mHwc && mHwc->dump) {
- const size_t SIZE = 4096;
- char buffer[SIZE];
- mHwc->dump(mHwc, buffer, SIZE);
- result.append(buffer);
- }
-}
-
-// ---------------------------------------------------------------------------
-
-HWComposer::VSyncThread::VSyncThread(HWComposer& hwc)
- : mHwc(hwc), mEnabled(false),
- mNextFakeVSync(0),
- mRefreshPeriod(hwc.getRefreshPeriod(HWC_DISPLAY_PRIMARY))
-{
-}
-
-void HWComposer::VSyncThread::setEnabled(bool enabled) {
- Mutex::Autolock _l(mLock);
- if (mEnabled != enabled) {
- mEnabled = enabled;
- mCondition.signal();
- }
-}
-
-void HWComposer::VSyncThread::onFirstRef() {
- run("VSyncThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
-}
-
-bool HWComposer::VSyncThread::threadLoop() {
- { // scope for lock
- Mutex::Autolock _l(mLock);
- while (!mEnabled) {
- mCondition.wait(mLock);
- }
- }
-
- const nsecs_t period = mRefreshPeriod;
- const nsecs_t now = systemTime(CLOCK_MONOTONIC);
- nsecs_t next_vsync = mNextFakeVSync;
- nsecs_t sleep = next_vsync - now;
- if (sleep < 0) {
- // we missed, find where the next vsync should be
- sleep = (period - ((now - next_vsync) % period));
- next_vsync = now + sleep;
- }
- mNextFakeVSync = next_vsync + period;
-
- struct timespec spec;
- spec.tv_sec = next_vsync / 1000000000;
- spec.tv_nsec = next_vsync % 1000000000;
-
- int err;
- do {
- err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
- } while (err<0 && errno == EINTR);
-
- if (err == 0) {
- mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
- }
-
- return true;
-}
-
-HWComposer::DisplayData::DisplayData()
-: configs(),
- currentConfig(0),
- format(HAL_PIXEL_FORMAT_RGBA_8888),
- connected(false),
- hasFbComp(false), hasOvComp(false),
- capacity(0), list(NULL),
- framebufferTarget(NULL), fbTargetHandle(0),
- lastRetireFence(Fence::NO_FENCE), lastDisplayFence(Fence::NO_FENCE),
- outbufHandle(NULL), outbufAcquireFence(Fence::NO_FENCE),
- events(0)
-{}
-
-HWComposer::DisplayData::~DisplayData() {
- free(list);
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
deleted file mode 100644
index 170e382..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_HWCOMPOSER_HWC1_H
-#define ANDROID_SF_HWCOMPOSER_HWC1_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <hardware/hwcomposer_defs.h>
-
-#include <system/graphics.h>
-
-#include <ui/Fence.h>
-
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
- const struct timespec *request,
- struct timespec *remain);
-
-struct hwc_composer_device_1;
-struct hwc_display_contents_1;
-struct hwc_layer_1;
-struct hwc_procs;
-struct framebuffer_device_t;
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class Fence;
-class FloatRect;
-class GraphicBuffer;
-class NativeHandle;
-class Region;
-class String8;
-class SurfaceFlinger;
-
-class HWComposer
-{
-public:
- class EventHandler {
- friend class HWComposer;
- virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0;
- virtual void onHotplugReceived(int disp, bool connected) = 0;
- protected:
- virtual ~EventHandler() {}
- };
-
- enum {
- NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
- MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES,
- VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL,
- };
-
- HWComposer(
- const sp<SurfaceFlinger>& flinger,
- EventHandler& handler);
-
- ~HWComposer();
-
- status_t initCheck() const;
-
- // Returns a display ID starting at VIRTUAL_DISPLAY_ID_BASE, this ID is to
- // be used with createWorkList (and all other methods requiring an ID
- // below).
- // IDs below NUM_BUILTIN_DISPLAYS are pre-defined and therefore are
- // always valid.
- // Returns -1 if an ID cannot be allocated
- int32_t allocateDisplayId();
-
- // Recycles the given virtual display ID and frees the associated worklist.
- // IDs below NUM_BUILTIN_DISPLAYS are not recycled.
- status_t freeDisplayId(int32_t id);
-
-
- // Asks the HAL what it can do
- status_t prepare();
-
- // commits the list
- status_t commit();
-
- // set power mode
- status_t setPowerMode(int disp, int mode);
-
- // set active config
- status_t setActiveConfig(int disp, int mode);
-
- // reset state when an external, non-virtual display is disconnected
- void disconnectDisplay(int disp);
-
- // create a work list for numLayers layer. sets HWC_GEOMETRY_CHANGED.
- status_t createWorkList(int32_t id, size_t numLayers);
-
- bool supportsFramebufferTarget() const;
-
- // does this display have layers handled by HWC
- bool hasHwcComposition(int32_t id) const;
-
- // does this display have layers handled by GLES
- bool hasGlesComposition(int32_t id) const;
-
- // get the releaseFence file descriptor for a display's framebuffer layer.
- // the release fence is only valid after commit()
- sp<Fence> getAndResetReleaseFence(int32_t id);
-
- // needed forward declarations
- class LayerListIterator;
-
- // return the visual id to be used to find a suitable EGLConfig for
- // *ALL* displays.
- int getVisualID() const;
-
- // Forwarding to FB HAL for pre-HWC-1.1 code (see FramebufferSurface).
- int fbPost(int32_t id, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf);
- int fbCompositionComplete();
- void fbDump(String8& result);
-
- // Set the output buffer and acquire fence for a virtual display.
- // Returns INVALID_OPERATION if id is not a virtual display.
- status_t setOutputBuffer(int32_t id, const sp<Fence>& acquireFence,
- const sp<GraphicBuffer>& buf);
-
- // Get the retire fence for the last committed frame. This fence will
- // signal when the h/w composer is completely finished with the frame.
- // For physical displays, it is no longer being displayed. For virtual
- // displays, writes to the output buffer are complete.
- sp<Fence> getLastRetireFence(int32_t id) const;
-
- status_t setCursorPositionAsync(int32_t id, const Rect &pos);
-
- /*
- * Interface to hardware composer's layers functionality.
- * This abstracts the HAL interface to layers which can evolve in
- * incompatible ways from one release to another.
- * The idea is that we could extend this interface as we add
- * features to h/w composer.
- */
- class HWCLayerInterface {
- protected:
- virtual ~HWCLayerInterface() { }
- public:
- virtual int32_t getCompositionType() const = 0;
- virtual uint32_t getHints() const = 0;
- virtual sp<Fence> getAndResetReleaseFence() = 0;
- virtual void setDefaultState() = 0;
- virtual void setSkip(bool skip) = 0;
- virtual void setIsCursorLayerHint(bool isCursor = true) = 0;
- virtual void setBlending(uint32_t blending) = 0;
- virtual void setTransform(uint32_t transform) = 0;
- virtual void setFrame(const Rect& frame) = 0;
- virtual void setCrop(const FloatRect& crop) = 0;
- virtual void setVisibleRegionScreen(const Region& reg) = 0;
- virtual void setSurfaceDamage(const Region& reg) = 0;
- virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0;
- virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0;
- virtual void setAcquireFenceFd(int fenceFd) = 0;
- virtual void setPlaneAlpha(uint8_t alpha) = 0;
- virtual void onDisplayed() = 0;
- };
-
- /*
- * Interface used to implement an iterator to a list
- * of HWCLayer.
- */
- class HWCLayer : public HWCLayerInterface {
- friend class LayerListIterator;
- // select the layer at the given index
- virtual status_t setLayer(size_t index) = 0;
- virtual HWCLayer* dup() = 0;
- static HWCLayer* copy(HWCLayer *rhs) {
- return rhs ? rhs->dup() : NULL;
- }
- protected:
- virtual ~HWCLayer() { }
- };
-
- /*
- * Iterator through a HWCLayer list.
- * This behaves more or less like a forward iterator.
- */
- class LayerListIterator {
- friend class HWComposer;
- HWCLayer* const mLayerList;
- size_t mIndex;
-
- LayerListIterator() : mLayerList(NULL), mIndex(0) { }
-
- LayerListIterator(HWCLayer* layer, size_t index)
- : mLayerList(layer), mIndex(index) { }
-
- // we don't allow assignment, because we don't need it for now
- LayerListIterator& operator = (const LayerListIterator& rhs);
-
- public:
- // copy operators
- LayerListIterator(const LayerListIterator& rhs)
- : mLayerList(HWCLayer::copy(rhs.mLayerList)), mIndex(rhs.mIndex) {
- }
-
- ~LayerListIterator() { delete mLayerList; }
-
- // pre-increment
- LayerListIterator& operator++() {
- mLayerList->setLayer(++mIndex);
- return *this;
- }
-
- // dereference
- HWCLayerInterface& operator * () { return *mLayerList; }
- HWCLayerInterface* operator -> () { return mLayerList; }
-
- // comparison
- bool operator == (const LayerListIterator& rhs) const {
- return mIndex == rhs.mIndex;
- }
- bool operator != (const LayerListIterator& rhs) const {
- return !operator==(rhs);
- }
- };
-
- // Returns an iterator to the beginning of the layer list
- LayerListIterator begin(int32_t id);
-
- // Returns an iterator to the end of the layer list
- LayerListIterator end(int32_t id);
-
-
- // Events handling ---------------------------------------------------------
-
- enum {
- EVENT_VSYNC = HWC_EVENT_VSYNC
- };
-
- void eventControl(int disp, int event, int enabled);
-
- struct DisplayConfig {
- uint32_t width;
- uint32_t height;
- float xdpi;
- float ydpi;
- nsecs_t refresh;
- android_color_mode_t colorMode;
- bool operator==(const DisplayConfig& rhs) const {
- return width == rhs.width &&
- height == rhs.height &&
- xdpi == rhs.xdpi &&
- ydpi == rhs.ydpi &&
- refresh == rhs.refresh &&
- colorMode == rhs.colorMode;
- }
- };
-
- // Query display parameters. Pass in a display index (e.g.
- // HWC_DISPLAY_PRIMARY).
- nsecs_t getRefreshTimestamp(int disp) const;
- sp<Fence> getDisplayFence(int disp) const;
- uint32_t getFormat(int disp) const;
- bool isConnected(int disp) const;
-
- // These return the values for the current config of a given display index.
- // To get the values for all configs, use getConfigs below.
- uint32_t getWidth(int disp) const;
- uint32_t getHeight(int disp) const;
- float getDpiX(int disp) const;
- float getDpiY(int disp) const;
- nsecs_t getRefreshPeriod(int disp) const;
- android_color_mode_t getColorMode(int disp) const;
-
- const Vector<DisplayConfig>& getConfigs(int disp) const;
- size_t getCurrentConfig(int disp) const;
-
- status_t setVirtualDisplayProperties(int32_t id, uint32_t w, uint32_t h,
- uint32_t format);
-
- // this class is only used to fake the VSync event on systems that don't
- // have it.
- class VSyncThread : public Thread {
- HWComposer& mHwc;
- mutable Mutex mLock;
- Condition mCondition;
- bool mEnabled;
- mutable nsecs_t mNextFakeVSync;
- nsecs_t mRefreshPeriod;
- virtual void onFirstRef();
- virtual bool threadLoop();
- public:
- VSyncThread(HWComposer& hwc);
- void setEnabled(bool enabled);
- };
-
- friend class VSyncThread;
-
- // for debugging ----------------------------------------------------------
- void dump(String8& out) const;
-
-private:
- void loadHwcModule();
- int loadFbHalModule();
-
- LayerListIterator getLayerIterator(int32_t id, size_t index);
-
- struct cb_context;
-
- static void hook_invalidate(const struct hwc_procs* procs);
- static void hook_vsync(const struct hwc_procs* procs, int disp,
- int64_t timestamp);
- static void hook_hotplug(const struct hwc_procs* procs, int disp,
- int connected);
-
- inline void invalidate();
- inline void vsync(int disp, int64_t timestamp);
- inline void hotplug(int disp, int connected);
-
- status_t queryDisplayProperties(int disp);
-
- status_t setFramebufferTarget(int32_t id,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf);
-
- struct DisplayData {
- DisplayData();
- ~DisplayData();
- Vector<DisplayConfig> configs;
- size_t currentConfig;
- uint32_t format; // pixel format from FB hal, for pre-hwc-1.1
- bool connected;
- bool hasFbComp;
- bool hasOvComp;
- size_t capacity;
- hwc_display_contents_1* list;
- hwc_layer_1* framebufferTarget;
- buffer_handle_t fbTargetHandle;
- sp<Fence> lastRetireFence; // signals when the last set op retires
- sp<Fence> lastDisplayFence; // signals when the last set op takes
- // effect on screen
- buffer_handle_t outbufHandle;
- sp<Fence> outbufAcquireFence;
-
- // protected by mEventControlLock
- int32_t events;
-
- // We need to hold "copies" of these for memory management purposes. The
- // actual hwc_layer_1_t holds pointers to the memory within. Vector<>
- // internally doesn't copy the memory unless one of the copies is
- // modified.
- Vector<Region> visibleRegions;
- Vector<Region> surfaceDamageRegions;
- };
-
- sp<SurfaceFlinger> mFlinger;
- framebuffer_device_t* mFbDev;
- struct hwc_composer_device_1* mHwc;
- // invariant: mLists[0] != NULL iff mHwc != NULL
- // mLists[i>0] can be NULL. that display is to be ignored
- struct hwc_display_contents_1* mLists[MAX_HWC_DISPLAYS];
- DisplayData mDisplayData[MAX_HWC_DISPLAYS];
- // protect mDisplayData from races between prepare and dump
- mutable Mutex mDisplayLock;
- size_t mNumDisplays;
-
- cb_context* mCBContext;
- EventHandler& mEventHandler;
- size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES];
- sp<VSyncThread> mVSyncThread;
- bool mDebugForceFakeVSync;
- BitSet32 mAllocatedDisplayIDs;
-
- // protected by mLock
- mutable Mutex mLock;
- mutable nsecs_t mLastHwVSync[HWC_NUM_PHYSICAL_DISPLAY_TYPES];
-
- // thread-safe
- mutable Mutex mEventControlLock;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 2190466..a9271c2 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -175,12 +175,6 @@
return NO_ERROR;
}
-#ifndef USE_HWC2
-status_t VirtualDisplaySurface::compositionComplete() {
- return NO_ERROR;
-}
-#endif
-
status_t VirtualDisplaySurface::advanceFrame() {
if (mDisplayId < 0)
return NO_ERROR;
@@ -220,13 +214,9 @@
status_t result = NO_ERROR;
if (fbBuffer != NULL) {
-#ifdef USE_HWC2
// TODO: Correctly propagate the dataspace from GL composition
result = mHwc.setClientTarget(mDisplayId, mFbFence, fbBuffer,
HAL_DATASPACE_UNKNOWN);
-#else
- result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
-#endif
}
return result;
@@ -240,22 +230,14 @@
"Unexpected onFrameCommitted() in %s state", dbgStateStr());
mDbgState = DBG_STATE_IDLE;
-#ifdef USE_HWC2
sp<Fence> retireFence = mHwc.getRetireFence(mDisplayId);
-#else
- sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
-#endif
if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
// release the scratch buffer back to the pool
Mutex::Autolock lock(mMutex);
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
-#ifdef USE_HWC2
addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot],
retireFence);
-#else
- addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence);
-#endif
releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot],
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
}
@@ -263,9 +245,6 @@
if (mOutputProducerSlot >= 0) {
int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
QueueBufferOutput qbo;
-#ifndef USE_HWC2
- sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId);
-#endif
VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
if (mMustRecompose) {
status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
@@ -274,11 +253,7 @@
HAL_DATASPACE_UNKNOWN,
Rect(mSinkBufferWidth, mSinkBufferHeight),
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */,
-#ifdef USE_HWC2
retireFence),
-#else
- outFence),
-#endif
&qbo);
if (result == NO_ERROR) {
updateQueueBufferOutput(qbo);
@@ -288,11 +263,7 @@
// through the motions of updating the display to keep our state
// machine happy. We cancel the buffer to avoid triggering another
// re-composition and causing an infinite loop.
-#ifdef USE_HWC2
mSource[SOURCE_SINK]->cancelBuffer(sslot, retireFence);
-#else
- mSource[SOURCE_SINK]->cancelBuffer(sslot, outFence);
-#endif
}
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 70f717f..884460d 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -83,9 +83,6 @@
//
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
-#ifndef USE_HWC2
- virtual status_t compositionComplete();
-#endif
virtual status_t advanceFrame();
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/EventControlThread.cpp
index ee6e886..26822c8 100644
--- a/services/surfaceflinger/EventControlThread.cpp
+++ b/services/surfaceflinger/EventControlThread.cpp
@@ -35,12 +35,7 @@
bool vsyncEnabled = mVsyncEnabled;
-#ifdef USE_HWC2
mFlinger->setVsyncEnabled(HWC_DISPLAY_PRIMARY, mVsyncEnabled);
-#else
- mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC,
- mVsyncEnabled);
-#endif
while (true) {
status_t err = mCond.wait(mMutex);
@@ -51,12 +46,7 @@
}
if (vsyncEnabled != mVsyncEnabled) {
-#ifdef USE_HWC2
mFlinger->setVsyncEnabled(HWC_DISPLAY_PRIMARY, mVsyncEnabled);
-#else
- mFlinger->eventControl(HWC_DISPLAY_PRIMARY,
- SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled);
-#endif
vsyncEnabled = mVsyncEnabled;
}
}
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/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
index 0e18a93..a3aaf42 100644
--- a/services/surfaceflinger/FenceTracker.cpp
+++ b/services/surfaceflinger/FenceTracker.cpp
@@ -141,7 +141,6 @@
layers[i]->getFenceData(&name, &frameNumber, &glesComposition,
&postedTime, &acquireFence, &prevReleaseFence);
-#ifdef USE_HWC2
if (glesComposition) {
frame.layers.emplace(std::piecewise_construct,
std::forward_as_tuple(layerId),
@@ -158,16 +157,6 @@
prevLayer->second.releaseFence = prevReleaseFence;
}
}
-#else
- frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(layerId),
- std::forward_as_tuple(name, frameNumber, glesComposition,
- postedTime, 0, 0, acquireFence,
- glesComposition ? Fence::NO_FENCE : prevReleaseFence));
- if (glesComposition) {
- wasGlesCompositionDone = true;
- }
-#endif
frame.layers.emplace(std::piecewise_construct,
std::forward_as_tuple(layerId),
std::forward_as_tuple(name, frameNumber, glesComposition,
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..9350fa5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -85,9 +85,6 @@
mFiltering(false),
mNeedsFiltering(false),
mMesh(Mesh::TRIANGLE_FAN, 4, 2, 2),
-#ifndef USE_HWC2
- mIsGlesComposition(false),
-#endif
mProtectedByApp(false),
mHasSurface(false),
mClientRef(client),
@@ -100,9 +97,7 @@
mAutoRefresh(false),
mFreezePositionUpdates(false)
{
-#ifdef USE_HWC2
ALOGV("Creating Layer %s", name.string());
-#endif
mCurrentCrop.makeInvalid();
mFlinger->getRenderEngine().genTextures(1, &mTextureName);
@@ -127,11 +122,7 @@
mCurrentState.crop.makeInvalid();
mCurrentState.finalCrop.makeInvalid();
mCurrentState.z = 0;
-#ifdef USE_HWC2
mCurrentState.alpha = 1.0f;
-#else
- mCurrentState.alpha = 0xFF;
-#endif
mCurrentState.layerStack = 0;
mCurrentState.flags = layerFlags;
mCurrentState.sequence = 0;
@@ -140,14 +131,9 @@
// drawing state & current state are identical
mDrawingState = mCurrentState;
-#ifdef USE_HWC2
const auto& hwc = flinger->getHwComposer();
const auto& activeConfig = hwc.getActiveConfig(HWC_DISPLAY_PRIMARY);
nsecs_t displayPeriod = activeConfig->getVsyncPeriod();
-#else
- nsecs_t displayPeriod =
- flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
-#endif
mFrameTracker.setDisplayRefreshPeriod(displayPeriod);
}
@@ -155,10 +141,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);
@@ -191,28 +176,19 @@
// callbacks
// ---------------------------------------------------------------------------
-#ifdef USE_HWC2
void Layer::onLayerDisplayed(const sp<Fence>& releaseFence) {
if (mHwcLayers.empty()) {
return;
}
mSurfaceFlingerConsumer->setReleaseFence(releaseFence);
}
-#else
-void Layer::onLayerDisplayed(const sp<const DisplayDevice>& /* hw */,
- HWComposer::HWCLayerInterface* layer) {
- if (layer) {
- layer->onDisplayed();
- mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFence());
- }
-}
-#endif
void Layer::onFrameAvailable(const BufferItem& item) {
// 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 +288,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);
@@ -493,23 +453,12 @@
return crop;
}
-#ifdef USE_HWC2
void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice)
-#else
-void Layer::setGeometry(
- const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer)
-#endif
{
-#ifdef USE_HWC2
const auto hwcId = displayDevice->getHwcDisplayId();
auto& hwcInfo = mHwcLayers[hwcId];
-#else
- layer.setDefaultState();
-#endif
// enable this layer
-#ifdef USE_HWC2
hwcInfo.forceClientComposition = false;
if (isSecure() && !displayDevice->isSecure()) {
@@ -517,17 +466,9 @@
}
auto& hwcLayer = hwcInfo.layer;
-#else
- layer.setSkip(false);
-
- if (isSecure() && !hw->isSecure()) {
- layer.setSkip(true);
- }
-#endif
// this gives us only the "orientation" component of the transform
const State& s(getDrawingState());
-#ifdef USE_HWC2
if (!isOpaque(s) || s.alpha != 1.0f) {
auto blendMode = mPremultipliedAlpha ?
HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
@@ -536,13 +477,6 @@
" %s (%d)", mName.string(), to_string(blendMode).c_str(),
to_string(error).c_str(), static_cast<int32_t>(error));
}
-#else
- if (!isOpaque(s) || s.alpha != 0xFF) {
- layer.setBlending(mPremultipliedAlpha ?
- HWC_BLENDING_PREMULT :
- HWC_BLENDING_COVERAGE);
- }
-#endif
// apply the layer's transform, followed by the display's global transform
// here we're guaranteed that the layer's transform preserves rects
@@ -550,11 +484,7 @@
if (!s.crop.isEmpty()) {
Rect activeCrop(s.crop);
activeCrop = s.active.transform.transform(activeCrop);
-#ifdef USE_HWC2
if(!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) {
-#else
- if(!activeCrop.intersect(hw->getViewport(), &activeCrop)) {
-#endif
activeCrop.clear();
}
activeCrop = s.active.transform.inverse().transform(activeCrop, true);
@@ -582,7 +512,6 @@
frame.clear();
}
}
-#ifdef USE_HWC2
if (!frame.intersect(displayDevice->getViewport(), &frame)) {
frame.clear();
}
@@ -618,15 +547,6 @@
ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)",
mName.string(), s.z, to_string(error).c_str(),
static_cast<int32_t>(error));
-#else
- if (!frame.intersect(hw->getViewport(), &frame)) {
- frame.clear();
- }
- const Transform& tr(hw->getTransform());
- layer.setFrame(tr.transform(frame));
- layer.setCrop(computeCrop(hw));
- layer.setPlaneAlpha(s.alpha);
-#endif
/*
* Transformations are applied in this order:
@@ -657,7 +577,6 @@
// this gives us only the "orientation" component of the transform
const uint32_t orientation = transform.getOrientation();
-#ifdef USE_HWC2
if (orientation & Transform::ROT_INVALID) {
// we can only handle simple transformation
hwcInfo.forceClientComposition = true;
@@ -668,17 +587,8 @@
"%s (%d)", mName.string(), to_string(transform).c_str(),
to_string(error).c_str(), static_cast<int32_t>(error));
}
-#else
- if (orientation & Transform::ROT_INVALID) {
- // we can only handle simple transformation
- layer.setSkip(true);
- } else {
- layer.setTransform(orientation);
- }
-#endif
}
-#ifdef USE_HWC2
void Layer::forceClientComposition(int32_t hwcId) {
if (mHwcLayers.count(hwcId) == 0) {
ALOGE("forceClientComposition: no HWC layer found (%d)", hwcId);
@@ -687,9 +597,7 @@
mHwcLayers[hwcId].forceClientComposition = true;
}
-#endif
-#ifdef USE_HWC2
void Layer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
// Apply this display's projection's viewport to the visible region
// before giving it to the HWC HAL.
@@ -772,31 +680,7 @@
static_cast<int32_t>(error));
}
}
-#else
-void Layer::setPerFrameData(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer) {
- // we have to set the visible region on every frame because
- // we currently free it during onLayerDisplayed(), which is called
- // after HWComposer::commit() -- every frame.
- // Apply this display's projection's viewport to the visible region
- // before giving it to the HWC HAL.
- const Transform& tr = hw->getTransform();
- Region visible = tr.transform(visibleRegion.intersect(hw->getViewport()));
- layer.setVisibleRegionScreen(visible);
- layer.setSurfaceDamage(surfaceDamageRegion);
- mIsGlesComposition = (layer.getCompositionType() == HWC_FRAMEBUFFER);
- if (mSidebandStream.get()) {
- layer.setSidebandStream(mSidebandStream);
- } else {
- // NOTE: buffer can be NULL if the client never drew into this
- // layer yet, or if we ran out of memory
- layer.setBuffer(mActiveBuffer);
- }
-}
-#endif
-
-#ifdef USE_HWC2
void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) {
auto hwcId = displayDevice->getHwcDisplayId();
if (mHwcLayers.count(hwcId) == 0 ||
@@ -830,49 +714,6 @@
position.top, to_string(error).c_str(),
static_cast<int32_t>(error));
}
-#else
-void Layer::setAcquireFence(const sp<const DisplayDevice>& /* hw */,
- HWComposer::HWCLayerInterface& layer) {
- int fenceFd = -1;
-
- // TODO: there is a possible optimization here: we only need to set the
- // acquire fence the first time a new buffer is acquired on EACH display.
-
- if (layer.getCompositionType() == HWC_OVERLAY || layer.getCompositionType() == HWC_CURSOR_OVERLAY) {
- sp<Fence> fence = mSurfaceFlingerConsumer->getCurrentFence();
- if (fence->isValid()) {
- fenceFd = fence->dup();
- if (fenceFd == -1) {
- ALOGW("failed to dup layer fence, skipping sync: %d", errno);
- }
- }
- }
- layer.setAcquireFenceFd(fenceFd);
-}
-
-Rect Layer::getPosition(
- const sp<const DisplayDevice>& hw)
-{
- // this gives us only the "orientation" component of the transform
- const State& s(getCurrentState());
-
- // apply the layer's transform, followed by the display's global transform
- // here we're guaranteed that the layer's transform preserves rects
- Rect win(s.active.w, s.active.h);
- if (!s.crop.isEmpty()) {
- win.intersect(s.crop, &win);
- }
- // subtract the transparent region and snap to the bounds
- Rect bounds = reduce(win, s.activeTransparentRegion);
- Rect frame(s.active.transform.transform(bounds));
- frame.intersect(hw->getViewport(), &frame);
- if (!s.finalCrop.isEmpty()) {
- frame.intersect(s.finalCrop, &frame);
- }
- const Transform& tr(hw->getTransform());
- return Rect(tr.transform(frame));
-}
-#endif
// ---------------------------------------------------------------------------
// drawing...
@@ -1057,7 +898,6 @@
engine.disableBlending();
}
-#ifdef USE_HWC2
void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type,
bool callIntoHwc) {
if (mHwcLayers.count(hwcId) == 0) {
@@ -1109,7 +949,6 @@
}
return mHwcLayers.at(hwcId).clearClientTarget;
}
-#endif
uint32_t Layer::getProducerStickyTransform() const {
int producerStickyTransform = 0;
@@ -1146,7 +985,6 @@
}
bool Layer::headFenceHasSignaled() const {
-#ifdef USE_HWC2
if (latchUnsignaledBuffers()) {
return true;
}
@@ -1163,9 +1001,6 @@
return true;
}
return mQueueItems[0].mFence->getSignalTime() != INT64_MAX;
-#else
- return true;
-#endif
}
bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
@@ -1605,11 +1440,7 @@
setTransactionFlags(eTransactionNeeded);
return true;
}
-#ifdef USE_HWC2
bool Layer::setAlpha(float alpha) {
-#else
-bool Layer::setAlpha(uint8_t alpha) {
-#endif
if (mCurrentState.alpha == alpha)
return false;
mCurrentState.sequence++;
@@ -1764,11 +1595,7 @@
}
const HWComposer& hwc = mFlinger->getHwComposer();
-#ifdef USE_HWC2
sp<Fence> presentFence = hwc.getRetireFence(HWC_DISPLAY_PRIMARY);
-#else
- sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
-#endif
if (presentFence->isValid()) {
mFrameTracker.setActualPresentFence(presentFence);
} else {
@@ -1784,21 +1611,14 @@
return frameLatencyNeeded;
}
-#ifdef USE_HWC2
void Layer::releasePendingBuffer() {
mSurfaceFlingerConsumer->releasePendingBuffer();
}
-#endif
bool Layer::isVisible() const {
const Layer::State& s(mDrawingState);
-#ifdef USE_HWC2
return !(s.flags & layer_state_t::eLayerHidden) && s.alpha > 0.0f
&& (mActiveBuffer != NULL || mSidebandStream != NULL);
-#else
- return !(s.flags & layer_state_t::eLayerHidden) && s.alpha
- && (mActiveBuffer != NULL || mSidebandStream != NULL);
-#endif
}
Region Layer::latchBuffer(bool& recomputeVisibleRegions)
@@ -2202,11 +2022,7 @@
"layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), "
"crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), "
"isOpaque=%1d, invalidate=%1d, "
-#ifdef USE_HWC2
"alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#else
- "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#endif
" client=%p\n",
s.layerStack, s.z, s.active.transform.tx(), s.active.transform.ty(), s.active.w, s.active.h,
s.crop.left, s.crop.top,
@@ -2239,7 +2055,6 @@
}
}
-#ifdef USE_HWC2
void Layer::miniDumpHeader(String8& result) {
result.append("----------------------------------------");
result.append("---------------------------------------\n");
@@ -2285,7 +2100,6 @@
result.append("- - - - - - - - - - - - - - - - - - - - ");
result.append("- - - - - - - - - - - - - - - - - - - -\n");
}
-#endif
void Layer::dumpFrameStats(String8& result) const {
mFrameTracker.dumpStats(result);
@@ -2309,13 +2123,9 @@
*outName = mName;
*outFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-#ifdef USE_HWC2
*outIsGlesComposition = mHwcLayers.count(HWC_DISPLAY_PRIMARY) ?
mHwcLayers.at(HWC_DISPLAY_PRIMARY).compositionType ==
HWC2::Composition::Client : true;
-#else
- *outIsGlesComposition = mIsGlesComposition;
-#endif
*outPostedTime = mSurfaceFlingerConsumer->getTimestamp();
*outAcquireFence = mSurfaceFlingerConsumer->getCurrentFence();
*outPrevReleaseFence = mSurfaceFlingerConsumer->getPrevReleaseFence();
@@ -2340,17 +2150,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..59129c7 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -110,11 +110,7 @@
Geometry requested;
uint32_t z;
uint32_t layerStack;
-#ifdef USE_HWC2
float alpha;
-#else
- uint8_t alpha;
-#endif
uint8_t flags;
uint8_t mask;
uint8_t reserved[2];
@@ -152,11 +148,7 @@
bool setPosition(float x, float y, bool immediate);
bool setLayer(uint32_t z);
bool setSize(uint32_t w, uint32_t h);
-#ifdef USE_HWC2
bool setAlpha(float alpha);
-#else
- bool setAlpha(uint8_t alpha);
-#endif
bool setMatrix(const layer_state_t::matrix22_t& matrix);
bool setTransparentRegionHint(const Region& transparent);
bool setFlags(uint8_t flags, uint8_t mask);
@@ -180,11 +172,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; }
// -----------------------------------------------------------------------
@@ -233,7 +220,6 @@
public:
// -----------------------------------------------------------------------
-#ifdef USE_HWC2
void setGeometry(const sp<const DisplayDevice>& displayDevice);
void forceClientComposition(int32_t hwcId);
void setPerFrameData(const sp<const DisplayDevice>& displayDevice);
@@ -248,26 +234,11 @@
bool getClearClientTarget(int32_t hwcId) const;
void updateCursorPosition(const sp<const DisplayDevice>& hw);
-#else
- void setGeometry(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer);
- void setPerFrameData(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer);
- void setAcquireFence(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer);
-
- Rect getPosition(const sp<const DisplayDevice>& hw);
-#endif
/*
* called after page-flip
*/
-#ifdef USE_HWC2
void onLayerDisplayed(const sp<Fence>& releaseFence);
-#else
- void onLayerDisplayed(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface* layer);
-#endif
bool shouldPresentNow(const DispSync& dispSync) const;
@@ -283,10 +254,8 @@
*/
bool onPostComposition();
-#ifdef USE_HWC2
// If a buffer was replaced this frame, release the former buffer
void releasePendingBuffer();
-#endif
/*
* draw - performs some global clipping optimizations
@@ -355,7 +324,6 @@
bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
mSidebandStreamChanged || mAutoRefresh; }
-#ifdef USE_HWC2
// -----------------------------------------------------------------------
bool hasHwcLayer(int32_t hwcId) {
@@ -385,7 +353,6 @@
}
}
-#endif
// -----------------------------------------------------------------------
void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip) const;
@@ -402,10 +369,8 @@
/* always call base class first */
void dump(String8& result, Colorizer& colorizer) const;
-#ifdef USE_HWC2
static void miniDumpHeader(String8& result);
void miniDump(String8& result, int32_t hwcId) const;
-#endif
void dumpFrameStats(String8& result) const;
void clearFrameStats();
void logFrameStats();
@@ -427,9 +392,6 @@
protected:
// constant
sp<SurfaceFlinger> mFlinger;
-
- virtual void onFirstRef();
-
/*
* Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
* is called.
@@ -438,13 +400,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 +504,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:
@@ -579,7 +571,6 @@
// The texture used to draw the layer in GLES composition mode
mutable Texture mTexture;
-#ifdef USE_HWC2
// HWC items, accessed from the main thread
struct HWCInfo {
HWCInfo()
@@ -596,9 +587,6 @@
FloatRect sourceCrop;
};
std::unordered_map<int32_t, HWCInfo> mHwcLayers;
-#else
- bool mIsGlesComposition;
-#endif
// page-flip thread (currently main thread)
bool mProtectedByApp; // application requires protected path to external sink
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
index 579affb..00b1cd2 100644
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
@@ -28,43 +28,25 @@
}
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);
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
index 61abd6a..c416408 100644
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
@@ -30,12 +30,8 @@
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
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
index 847cdb3..a6d66f7 100644
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
@@ -107,33 +107,20 @@
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
@@ -165,11 +152,7 @@
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);
@@ -178,28 +161,16 @@
}
}
-#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) {
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
index 4cd968d..fdaa533 100644
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
@@ -53,15 +53,9 @@
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) ;
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 406e611..2503b27 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -117,25 +117,14 @@
mVpHeight = vph;
}
-#ifdef USE_HWC2
void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha,
bool opaque, float alpha) {
-#else
-void GLES20RenderEngine::setupLayerBlending(
- bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
mState.setPremultipliedAlpha(premultipliedAlpha);
mState.setOpaque(opaque);
-#ifdef USE_HWC2
mState.setPlaneAlpha(alpha);
if (alpha < 1.0f || !opaque) {
-#else
- mState.setPlaneAlpha(alpha / 255.0f);
-
- if (alpha < 0xFF || !opaque) {
-#endif
glEnable(GL_BLEND);
glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
@@ -143,26 +132,14 @@
}
}
-#ifdef USE_HWC2
void GLES20RenderEngine::setupDimLayerBlending(float alpha) {
-#else
-void GLES20RenderEngine::setupDimLayerBlending(int alpha) {
-#endif
mState.setPlaneAlpha(1.0f);
mState.setPremultipliedAlpha(true);
mState.setOpaque(false);
-#ifdef USE_HWC2
mState.setColor(0, 0, 0, alpha);
-#else
- mState.setColor(0, 0, 0, alpha/255.0f);
-#endif
mState.disableTexture();
-#ifdef USE_HWC2
if (alpha == 1.0f) {
-#else
- if (alpha == 0xFF) {
-#endif
glDisable(GL_BLEND);
} else {
glEnable(GL_BLEND);
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
index 7c3f9b5..735e174 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
@@ -68,15 +68,9 @@
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);
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index d6a032f..ed6f6cd 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -317,7 +317,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 +338,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/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 0259881..bd45bae 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -95,13 +95,8 @@
virtual void checkErrors() const;
virtual void setViewportAndProjection(size_t vpw, size_t vph,
Rect sourceCrop, size_t hwh, bool yswap, Transform::orientation_flags rotation) = 0;
-#ifdef USE_HWC2
virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, float alpha) = 0;
virtual void setupDimLayerBlending(float alpha) = 0;
-#else
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha) = 0;
- virtual void setupDimLayerBlending(int alpha) = 0;
-#endif
virtual void setupLayerTexturing(const Texture& texture) = 0;
virtual void setupLayerBlackedOut() = 0;
virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
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..9c37223 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;
// ---------------------------------------------------------------------------
@@ -126,11 +129,7 @@
// enable/disable h/w composer event
// TODO: this should be made accessible only to EventThread
-#ifdef USE_HWC2
void setVsyncEnabled(int disp, int enabled);
-#else
- void eventControl(int disp, int event, int enabled);
-#endif
// called on the main thread by MessageQueue when an internal message
// is received
@@ -148,6 +147,7 @@
private:
friend class Client;
friend class DisplayEventConnection;
+ friend class EventThread;
friend class Layer;
friend class MonitoredProducer;
@@ -171,23 +171,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 +217,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
@@ -391,10 +377,6 @@
// region of all screens presenting this layer stack.
void invalidateLayerStack(uint32_t layerStack, const Region& dirty);
-#ifndef USE_HWC2
- int32_t allocateHwcDisplayId(DisplayDevice::DisplayType type);
-#endif
-
/* ------------------------------------------------------------------------
* H/W composer
*/
@@ -489,6 +471,8 @@
bool mGpuToCpuSupported;
sp<EventThread> mEventThread;
sp<EventThread> mSFEventThread;
+ sp<EventThread> mInjectorEventThread;
+ sp<InjectVSyncSource> mVSyncInjector;
sp<EventControlThread> mEventControlThread;
EGLContext mEGLContext;
EGLDisplay mEGLDisplay;
@@ -498,17 +482,11 @@
// don't need synchronization
State mDrawingState;
bool mVisibleRegionsDirty;
-#ifndef USE_HWC2
- bool mHwWorkListDirty;
-#else
bool mGeometryInvalid;
-#endif
bool mAnimCompositionPending;
-#ifdef USE_HWC2
std::vector<sp<Layer>> mLayersWithQueuedFrames;
sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
bool mHadClientComposition = false;
-#endif
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
@@ -526,9 +504,8 @@
bool mBootFinished;
bool mForceFullDamage;
FenceTracker mFenceTracker;
-#ifdef USE_HWC2
bool mPropagateBackpressure = true;
-#endif
+ SurfaceInterceptor mInterceptor;
bool mUseHwcVirtualDisplays = true;
// these are thread safe
@@ -549,10 +526,9 @@
* Feature prototyping
*/
+ bool mInjectVSyncs;
+
Daltonizer mDaltonizer;
-#ifndef USE_HWC2
- bool mDaltonize;
-#endif
mat4 mPreviousColorMatrix;
mat4 mColorMatrix;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index e0e4c61..eb78b67 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -88,11 +88,7 @@
}
// Release the previous buffer.
-#ifdef USE_HWC2
err = updateAndReleaseLocked(item, &mPendingRelease);
-#else
- err = updateAndReleaseLocked(item);
-#endif
if (err != NO_ERROR) {
return err;
}
@@ -189,7 +185,6 @@
return nextRefresh + extraPadding;
}
-#ifdef USE_HWC2
void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence)
{
mPrevReleaseFence = fence;
@@ -222,12 +217,6 @@
strerror(-result), result);
mPendingRelease = PendingRelease();
}
-#else
-void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence) {
- mPrevReleaseFence = fence;
- GLConsumer::setReleaseFence(fence);
-}
-#endif
sp<Fence> SurfaceFlingerConsumer::getPrevReleaseFence() const {
return mPrevReleaseFence;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 4271039..0f9a0b4 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -81,9 +81,7 @@
virtual void setReleaseFence(const sp<Fence>& fence) override;
sp<Fence> getPrevReleaseFence() const;
-#ifdef USE_HWC2
void releasePendingBuffer();
-#endif
virtual bool getFrameTimestamps(uint64_t frameNumber,
FrameTimestamps* outTimestamps) const override;
@@ -101,11 +99,9 @@
// The portion of this surface that has changed since the previous frame
Region mSurfaceDamage;
-#ifdef USE_HWC2
// A release that is pending on the receipt of a new release fence from
// presentDisplay
PendingRelease mPendingRelease;
-#endif
// The release fence of the already displayed buffer (previous frame).
sp<Fence> mPrevReleaseFence;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
deleted file mode 100644
index b0f418c..0000000
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ /dev/null
@@ -1,3751 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <math.h>
-#include <dlfcn.h>
-#include <inttypes.h>
-#include <stdatomic.h>
-
-#include <EGL/egl.h>
-
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/PermissionCache.h>
-
-#include <ui/DisplayInfo.h>
-#include <ui/DisplayStatInfo.h>
-
-#include <gui/BitTube.h>
-#include <gui/BufferQueue.h>
-#include <gui/GuiConfig.h>
-#include <gui/IDisplayEventConnection.h>
-#include <gui/Surface.h>
-#include <gui/GraphicBufferAlloc.h>
-
-#include <ui/GraphicBufferAllocator.h>
-#include <ui/HdrCapabilities.h>
-#include <ui/PixelFormat.h>
-#include <ui/UiConfig.h>
-
-#include <utils/misc.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-#include <utils/StopWatch.h>
-#include <utils/Timers.h>
-#include <utils/Trace.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/gui/SyncFeatures.h>
-
-#include <set>
-
-#include "Client.h"
-#include "clz.h"
-#include "Colorizer.h"
-#include "DdmConnection.h"
-#include "DisplayDevice.h"
-#include "DispSync.h"
-#include "EventControlThread.h"
-#include "EventThread.h"
-#include "Layer.h"
-#include "LayerDim.h"
-#include "SurfaceFlinger.h"
-
-#include "DisplayHardware/FramebufferSurface.h"
-#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/VirtualDisplaySurface.h"
-
-#include "Effects/Daltonizer.h"
-
-#include "RenderEngine/RenderEngine.h"
-#include <cutils/compiler.h>
-
-#define DISPLAY_COUNT 1
-
-/*
- * DEBUG_SCREENSHOTS: set to true to check that screenshots are not all
- * black pixels.
- */
-#define DEBUG_SCREENSHOTS false
-
-EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
-namespace android {
-
-// This is the phase offset in nanoseconds of the software vsync event
-// relative to the vsync event reported by HWComposer. The software vsync
-// event is when SurfaceFlinger and Choreographer-based applications run each
-// frame.
-//
-// This phase offset allows adjustment of the minimum latency from application
-// wake-up (by Choregographer) time to the time at which the resulting window
-// image is displayed. This value may be either positive (after the HW vsync)
-// or negative (before the HW vsync). Setting it to 0 will result in a
-// minimum latency of two vsync periods because the app and SurfaceFlinger
-// will run just after the HW vsync. Setting it to a positive number will
-// result in the minimum latency being:
-//
-// (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-//
-// Note that reducing this latency makes it more likely for the applications
-// to not have their window content image ready in time. When this happens
-// the latency will end up being an additional vsync period, and animations
-// will hiccup. Therefore, this latency should be tuned somewhat
-// conservatively (or at least with awareness of the trade-off being made).
-static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
-
-// This is the phase offset at which SurfaceFlinger's composition runs.
-static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
-
-// ---------------------------------------------------------------------------
-
-const String16 sHardwareTest("android.permission.HARDWARE_TEST");
-const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
-const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
-const String16 sDump("android.permission.DUMP");
-
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::SurfaceFlinger()
- : BnSurfaceComposer(),
- mTransactionFlags(0),
- mTransactionPending(false),
- mAnimTransactionPending(false),
- mLayersRemoved(false),
- mRepaintEverything(0),
- mRenderEngine(NULL),
- mBootTime(systemTime()),
- mVisibleRegionsDirty(false),
- mHwWorkListDirty(false),
- mAnimCompositionPending(false),
- mDebugRegion(0),
- mDebugDDMS(0),
- mDebugDisableHWC(0),
- mDebugDisableTransformHint(0),
- mDebugInSwapBuffers(0),
- mLastSwapBufferTime(0),
- mDebugInTransaction(0),
- mLastTransactionTime(0),
- mBootFinished(false),
- mForceFullDamage(false),
- mPrimaryDispSync("PrimaryDispSync"),
- mPrimaryHWVsyncEnabled(false),
- mHWVsyncAvailable(false),
- mDaltonize(false),
- mHasColorMatrix(false),
- mHasPoweredOff(false),
- mFrameBuckets(),
- mTotalTime(0),
- mLastSwapTime(0)
-{
- ALOGI("SurfaceFlinger is starting");
-
- // debugging stuff...
- char value[PROPERTY_VALUE_MAX];
-
- property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
- mGpuToCpuSupported = !atoi(value);
-
- property_get("debug.sf.showupdates", value, "0");
- mDebugRegion = atoi(value);
-
- property_get("debug.sf.ddms", value, "0");
- mDebugDDMS = atoi(value);
- if (mDebugDDMS) {
- if (!startDdmConnection()) {
- // start failed, and DDMS debugging not enabled
- mDebugDDMS = 0;
- }
- }
- ALOGI_IF(mDebugRegion, "showupdates enabled");
- ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
-
- property_get("debug.sf.disable_hwc_vds", value, "0");
- mUseHwcVirtualDisplays = !atoi(value);
- ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
-}
-
-void SurfaceFlinger::onFirstRef()
-{
- mEventQueue.init(this);
-}
-
-SurfaceFlinger::~SurfaceFlinger()
-{
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglTerminate(display);
-}
-
-void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
-{
- // the window manager died on us. prepare its eulogy.
-
- // restore initial conditions (default device unblank, etc)
- initializeDisplays();
-
- // restart the boot-animation
- startBootAnim();
-}
-
-sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
-{
- sp<ISurfaceComposerClient> bclient;
- sp<Client> client(new Client(this));
- status_t err = client->initCheck();
- if (err == NO_ERROR) {
- bclient = client;
- }
- return bclient;
-}
-
-sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName,
- bool secure)
-{
- class DisplayToken : public BBinder {
- sp<SurfaceFlinger> flinger;
- virtual ~DisplayToken() {
- // no more references, this display must be terminated
- Mutex::Autolock _l(flinger->mStateLock);
- flinger->mCurrentState.displays.removeItem(this);
- flinger->setTransactionFlags(eDisplayTransactionNeeded);
- }
- public:
- DisplayToken(const sp<SurfaceFlinger>& flinger)
- : flinger(flinger) {
- }
- };
-
- sp<BBinder> token = new DisplayToken(this);
-
- Mutex::Autolock _l(mStateLock);
- DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
- info.displayName = displayName;
- mCurrentState.displays.add(token, info);
-
- return token;
-}
-
-void SurfaceFlinger::destroyDisplay(const sp<IBinder>& display) {
- Mutex::Autolock _l(mStateLock);
-
- ssize_t idx = mCurrentState.displays.indexOfKey(display);
- if (idx < 0) {
- ALOGW("destroyDisplay: invalid display token");
- return;
- }
-
- const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
- if (!info.isVirtualDisplay()) {
- ALOGE("destroyDisplay called for non-virtual display");
- return;
- }
-
- mCurrentState.displays.removeItemsAt(idx);
- setTransactionFlags(eDisplayTransactionNeeded);
-}
-
-void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) {
- ALOGW_IF(mBuiltinDisplays[type],
- "Overwriting display token for display type %d", type);
- mBuiltinDisplays[type] = new BBinder();
- // All non-virtual displays are currently considered secure.
- DisplayDeviceState info(type, true);
- mCurrentState.displays.add(mBuiltinDisplays[type], info);
-}
-
-sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
- if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id);
- return NULL;
- }
- return mBuiltinDisplays[id];
-}
-
-sp<IGraphicBufferAlloc> SurfaceFlinger::createGraphicBufferAlloc()
-{
- sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc());
- return gba;
-}
-
-void SurfaceFlinger::bootFinished()
-{
- const nsecs_t now = systemTime();
- const nsecs_t duration = now - mBootTime;
- ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
- mBootFinished = true;
-
- // wait patiently for the window manager death
- const String16 name("window");
- sp<IBinder> window(defaultServiceManager()->getService(name));
- if (window != 0) {
- window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
- }
-
- // stop boot animation
- // formerly we would just kill the process, but we now ask it to exit so it
- // can choose where to stop the animation.
- property_set("service.bootanim.exit", "1");
-
- const int LOGTAG_SF_STOP_BOOTANIM = 60110;
- LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
- ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
-}
-
-void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
- class MessageDestroyGLTexture : public MessageBase {
- RenderEngine& engine;
- uint32_t texture;
- public:
- MessageDestroyGLTexture(RenderEngine& engine, uint32_t texture)
- : engine(engine), texture(texture) {
- }
- virtual bool handler() {
- engine.deleteTextures(1, &texture);
- return true;
- }
- };
- postMessageAsync(new MessageDestroyGLTexture(getRenderEngine(), texture));
-}
-
-class DispSyncSource : public VSyncSource, private DispSync::Callback {
-public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
- const char* name) :
- mName(name),
- mValue(0),
- mTraceVsync(traceVsync),
- mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
- mVsyncEventLabel(String8::format("VSYNC-%s", name)),
- mDispSync(dispSync),
- mCallbackMutex(),
- mCallback(),
- mVsyncMutex(),
- mPhaseOffset(phaseOffset),
- mEnabled(false) {}
-
- virtual ~DispSyncSource() {}
-
- virtual void setVSyncEnabled(bool enable) {
- Mutex::Autolock lock(mVsyncMutex);
- if (enable) {
- status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
- static_cast<DispSync::Callback*>(this));
- if (err != NO_ERROR) {
- ALOGE("error registering vsync callback: %s (%d)",
- strerror(-err), err);
- }
- //ATRACE_INT(mVsyncOnLabel.string(), 1);
- } else {
- status_t err = mDispSync->removeEventListener(
- static_cast<DispSync::Callback*>(this));
- if (err != NO_ERROR) {
- ALOGE("error unregistering vsync callback: %s (%d)",
- strerror(-err), err);
- }
- //ATRACE_INT(mVsyncOnLabel.string(), 0);
- }
- mEnabled = enable;
- }
-
- virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
- Mutex::Autolock lock(mCallbackMutex);
- mCallback = callback;
- }
-
- virtual void setPhaseOffset(nsecs_t phaseOffset) {
- Mutex::Autolock lock(mVsyncMutex);
-
- // Normalize phaseOffset to [0, period)
- auto period = mDispSync->getPeriod();
- phaseOffset %= period;
- if (phaseOffset < 0) {
- // If we're here, then phaseOffset is in (-period, 0). After this
- // operation, it will be in (0, period)
- phaseOffset += period;
- }
- mPhaseOffset = phaseOffset;
-
- // If we're not enabled, we don't need to mess with the listeners
- if (!mEnabled) {
- return;
- }
-
- // Remove the listener with the old offset
- status_t err = mDispSync->removeEventListener(
- static_cast<DispSync::Callback*>(this));
- if (err != NO_ERROR) {
- ALOGE("error unregistering vsync callback: %s (%d)",
- strerror(-err), err);
- }
-
- // Add a listener with the new offset
- err = mDispSync->addEventListener(mName, mPhaseOffset,
- static_cast<DispSync::Callback*>(this));
- if (err != NO_ERROR) {
- ALOGE("error registering vsync callback: %s (%d)",
- strerror(-err), err);
- }
- }
-
-private:
- virtual void onDispSyncEvent(nsecs_t when) {
- sp<VSyncSource::Callback> callback;
- {
- Mutex::Autolock lock(mCallbackMutex);
- callback = mCallback;
-
- if (mTraceVsync) {
- mValue = (mValue + 1) % 2;
- ATRACE_INT(mVsyncEventLabel.string(), mValue);
- }
- }
-
- if (callback != NULL) {
- callback->onVSyncEvent(when);
- }
- }
-
- const char* const mName;
-
- int mValue;
-
- const bool mTraceVsync;
- const String8 mVsyncOnLabel;
- const String8 mVsyncEventLabel;
-
- DispSync* mDispSync;
-
- Mutex mCallbackMutex; // Protects the following
- sp<VSyncSource::Callback> mCallback;
-
- Mutex mVsyncMutex; // Protects the following
- nsecs_t mPhaseOffset;
- bool mEnabled;
-};
-
-void SurfaceFlinger::init() {
- ALOGI( "SurfaceFlinger's main thread ready to run. "
- "Initializing graphics H/W...");
-
- Mutex::Autolock _l(mStateLock);
-
- // initialize EGL for the default display
- mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- eglInitialize(mEGLDisplay, NULL, NULL);
-
- // start the EventThread
- sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- vsyncPhaseOffsetNs, true, "app");
- mEventThread = new EventThread(vsyncSrc, *this);
- sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = new EventThread(sfVsyncSrc, *this);
- mEventQueue.setEventThread(mSFEventThread);
-
- // set SFEventThread to SCHED_FIFO to minimize jitter
- struct sched_param param = {0};
- param.sched_priority = 2;
- if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) {
- ALOGE("Couldn't set SCHED_FIFO for SFEventThread");
- }
-
-
- // Initialize the H/W composer object. There may or may not be an
- // actual hardware composer underneath.
- mHwc = new HWComposer(this,
- *static_cast<HWComposer::EventHandler *>(this));
-
- // get a RenderEngine for the given display / config (can't fail)
- mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc->getVisualID());
-
- // retrieve the EGL context that was selected/created
- mEGLContext = mRenderEngine->getEGLContext();
-
- LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
- "couldn't create EGLContext");
-
- // initialize our non-virtual displays
- for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
- DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
- // set-up the displays that are already connected
- if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
- // All non-virtual displays are currently considered secure.
- bool isSecure = true;
- createBuiltinDisplayLocked(type);
- wp<IBinder> token = mBuiltinDisplays[i];
-
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer,
- new GraphicBufferAlloc());
-
- sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
- consumer);
- int32_t hwcId = allocateHwcDisplayId(type);
- sp<DisplayDevice> hw = new DisplayDevice(this,
- type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
- fbs, producer,
- mRenderEngine->getEGLConfig());
- if (i > DisplayDevice::DISPLAY_PRIMARY) {
- // FIXME: currently we don't get blank/unblank requests
- // for displays other than the main display, so we always
- // assume a connected display is unblanked.
- ALOGD("marking display %zu as acquired/unblanked", i);
- hw->setPowerMode(HWC_POWER_MODE_NORMAL);
- }
- mDisplays.add(token, hw);
- }
- }
-
- // make the GLContext current so that we can create textures when creating Layers
- // (which may happens before we render something)
- getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
-
- mEventControlThread = new EventControlThread(this);
- mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
-
- // set a fake vsync period if there is no HWComposer
- if (mHwc->initCheck() != NO_ERROR) {
- mPrimaryDispSync.setPeriod(16666667);
- }
-
- // initialize our drawing state
- mDrawingState = mCurrentState;
-
- // set initial conditions (e.g. unblank default device)
- initializeDisplays();
-
- mRenderEngine->primeCache();
-
- // start boot animation
- startBootAnim();
-}
-
-int32_t SurfaceFlinger::allocateHwcDisplayId(DisplayDevice::DisplayType type) {
- return (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) ?
- type : mHwc->allocateDisplayId();
-}
-
-void SurfaceFlinger::startBootAnim() {
- // start boot animation
- property_set("service.bootanim.exit", "0");
- property_set("ctl.start", "bootanim");
-}
-
-size_t SurfaceFlinger::getMaxTextureSize() const {
- return mRenderEngine->getMaxTextureSize();
-}
-
-size_t SurfaceFlinger::getMaxViewportDims() const {
- return mRenderEngine->getMaxViewportDims();
-}
-
-// ----------------------------------------------------------------------------
-
-bool SurfaceFlinger::authenticateSurfaceTexture(
- const sp<IGraphicBufferProducer>& bufferProducer) const {
- Mutex::Autolock _l(mStateLock);
- sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
- return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
-}
-
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs) {
- if ((configs == NULL) || (display.get() == NULL)) {
- return BAD_VALUE;
- }
-
- int32_t type = getDisplayType(display);
- if (type < 0) return type;
-
- // TODO: Not sure if display density should handled by SF any longer
- class Density {
- static int getDensityFromProperty(char const* propName) {
- char property[PROPERTY_VALUE_MAX];
- int density = 0;
- if (property_get(propName, property, NULL) > 0) {
- density = atoi(property);
- }
- return density;
- }
- public:
- static int getEmuDensity() {
- return getDensityFromProperty("qemu.sf.lcd_density"); }
- static int getBuildDensity() {
- return getDensityFromProperty("ro.sf.lcd_density"); }
- };
-
- configs->clear();
-
- const Vector<HWComposer::DisplayConfig>& hwConfigs =
- getHwComposer().getConfigs(type);
- for (size_t c = 0; c < hwConfigs.size(); ++c) {
- const HWComposer::DisplayConfig& hwConfig = hwConfigs[c];
- DisplayInfo info = DisplayInfo();
-
- float xdpi = hwConfig.xdpi;
- float ydpi = hwConfig.ydpi;
-
- if (type == DisplayDevice::DISPLAY_PRIMARY) {
- // The density of the device is provided by a build property
- float density = Density::getBuildDensity() / 160.0f;
- if (density == 0) {
- // the build doesn't provide a density -- this is wrong!
- // use xdpi instead
- ALOGE("ro.sf.lcd_density must be defined as a build property");
- density = xdpi / 160.0f;
- }
- if (Density::getEmuDensity()) {
- // if "qemu.sf.lcd_density" is specified, it overrides everything
- xdpi = ydpi = density = Density::getEmuDensity();
- density /= 160.0f;
- }
- info.density = density;
-
- // TODO: this needs to go away (currently needed only by webkit)
- sp<const DisplayDevice> hw(getDefaultDisplayDevice());
- info.orientation = hw->getOrientation();
- } else {
- // TODO: where should this value come from?
- static const int TV_DENSITY = 213;
- info.density = TV_DENSITY / 160.0f;
- info.orientation = 0;
- }
-
- info.w = hwConfig.width;
- info.h = hwConfig.height;
- info.xdpi = xdpi;
- info.ydpi = ydpi;
- info.fps = float(1e9 / hwConfig.refresh);
- info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
-
- // This is how far in advance a buffer must be queued for
- // presentation at a given time. If you want a buffer to appear
- // on the screen at time N, you must submit the buffer before
- // (N - presentationDeadline).
- //
- // Normally it's one full refresh period (to give SF a chance to
- // latch the buffer), but this can be reduced by configuring a
- // DispSync offset. Any additional delays introduced by the hardware
- // composer or panel must be accounted for here.
- //
- // We add an additional 1ms to allow for processing time and
- // differences between the ideal and actual refresh rate.
- info.presentationDeadline =
- hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
-
- // All non-virtual displays are currently considered secure.
- info.secure = true;
-
- configs->push_back(info);
- }
-
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */,
- DisplayStatInfo* stats) {
- if (stats == NULL) {
- return BAD_VALUE;
- }
-
- // FIXME for now we always return stats for the primary display
- memset(stats, 0, sizeof(*stats));
- stats->vsyncTime = mPrimaryDispSync.computeNextRefresh(0);
- stats->vsyncPeriod = mPrimaryDispSync.getPeriod();
- return NO_ERROR;
-}
-
-int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
- sp<DisplayDevice> device(getDisplayDevice(display));
- if (device != NULL) {
- return device->getActiveConfig();
- }
- return BAD_VALUE;
-}
-
-void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode) {
- ALOGD("Set active config mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
- this);
- int32_t type = hw->getDisplayType();
- int currentMode = hw->getActiveConfig();
-
- if (mode == currentMode) {
- ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode);
- return;
- }
-
- if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGW("Trying to set config for virtual display");
- return;
- }
-
- hw->setActiveConfig(mode);
- getHwComposer().setActiveConfig(type, mode);
-}
-
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) {
- class MessageSetActiveConfig: public MessageBase {
- SurfaceFlinger& mFlinger;
- sp<IBinder> mDisplay;
- int mMode;
- public:
- MessageSetActiveConfig(SurfaceFlinger& flinger, const sp<IBinder>& disp,
- int mode) :
- mFlinger(flinger), mDisplay(disp) { mMode = mode; }
- virtual bool handler() {
- Vector<DisplayInfo> configs;
- mFlinger.getDisplayConfigs(mDisplay, &configs);
- if (mMode < 0 || mMode >= static_cast<int>(configs.size())) {
- ALOGE("Attempt to set active config = %d for display with %zu configs",
- mMode, configs.size());
- }
- sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
- if (hw == NULL) {
- ALOGE("Attempt to set active config = %d for null display %p",
- mMode, mDisplay.get());
- } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
- ALOGW("Attempt to set active config = %d for virtual display",
- mMode);
- } else {
- mFlinger.setActiveConfigInternal(hw, mMode);
- }
- return true;
- }
- };
- sp<MessageBase> msg = new MessageSetActiveConfig(*this, display, mode);
- postMessageSync(msg);
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display,
- Vector<android_color_mode_t>* outColorModes) {
- if (outColorModes == nullptr || display.get() == nullptr) {
- return BAD_VALUE;
- }
-
- int32_t type = getDisplayType(display);
- if (type < 0) return type;
-
- std::set<android_color_mode_t> colorModes;
- for (const HWComposer::DisplayConfig& hwConfig : getHwComposer().getConfigs(type)) {
- colorModes.insert(hwConfig.colorMode);
- }
-
- outColorModes->clear();
- std::copy(colorModes.cbegin(), colorModes.cend(), std::back_inserter(*outColorModes));
-
- return NO_ERROR;
-}
-
-android_color_mode_t SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) {
- if (display.get() == nullptr) return static_cast<android_color_mode_t>(BAD_VALUE);
-
- int32_t type = getDisplayType(display);
- if (type < 0) return static_cast<android_color_mode_t>(type);
-
- return getHwComposer().getColorMode(type);
-}
-
-status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display,
- android_color_mode_t colorMode) {
- if (display.get() == nullptr || colorMode < 0) {
- return BAD_VALUE;
- }
-
- int32_t type = getDisplayType(display);
- if (type < 0) return type;
- const Vector<HWComposer::DisplayConfig>& hwConfigs = getHwComposer().getConfigs(type);
- HWComposer::DisplayConfig desiredConfig = hwConfigs[getHwComposer().getCurrentConfig(type)];
- desiredConfig.colorMode = colorMode;
- for (size_t c = 0; c < hwConfigs.size(); ++c) {
- const HWComposer::DisplayConfig config = hwConfigs[c];
- if (config == desiredConfig) {
- return setActiveConfig(display, c);
- }
- }
- return BAD_VALUE;
-}
-
-status_t SurfaceFlinger::clearAnimationFrameStats() {
- Mutex::Autolock _l(mStateLock);
- mAnimFrameTracker.clearStats();
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const {
- Mutex::Autolock _l(mStateLock);
- mAnimFrameTracker.getStats(outStats);
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& /*display*/,
- HdrCapabilities* outCapabilities) const {
- // HWC1 does not provide HDR capabilities
- *outCapabilities = HdrCapabilities();
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
- return mEventThread->createEventConnection();
-}
-
-// ----------------------------------------------------------------------------
-
-void SurfaceFlinger::waitForEvent() {
- mEventQueue.waitMessage();
-}
-
-void SurfaceFlinger::signalTransaction() {
- mEventQueue.invalidate();
-}
-
-void SurfaceFlinger::signalLayerUpdate() {
- mEventQueue.invalidate();
-}
-
-void SurfaceFlinger::signalRefresh() {
- mEventQueue.refresh();
-}
-
-status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t /* flags */) {
- return mEventQueue.postMessage(msg, reltime);
-}
-
-status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t /* flags */) {
- status_t res = mEventQueue.postMessage(msg, reltime);
- if (res == NO_ERROR) {
- msg->wait();
- }
- return res;
-}
-
-void SurfaceFlinger::run() {
- do {
- waitForEvent();
- } while (true);
-}
-
-void SurfaceFlinger::enableHardwareVsync() {
- Mutex::Autolock _l(mHWVsyncLock);
- if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- mPrimaryDispSync.beginResync();
- //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
- mEventControlThread->setVsyncEnabled(true);
- mPrimaryHWVsyncEnabled = true;
- }
-}
-
-void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
- Mutex::Autolock _l(mHWVsyncLock);
-
- if (makeAvailable) {
- mHWVsyncAvailable = true;
- } else if (!mHWVsyncAvailable) {
- // Hardware vsync is not currently available, so abort the resync
- // attempt for now
- return;
- }
-
- const nsecs_t period =
- getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
-
- mPrimaryDispSync.reset();
- mPrimaryDispSync.setPeriod(period);
-
- if (!mPrimaryHWVsyncEnabled) {
- mPrimaryDispSync.beginResync();
- //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
- mEventControlThread->setVsyncEnabled(true);
- mPrimaryHWVsyncEnabled = true;
- }
-}
-
-void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) {
- Mutex::Autolock _l(mHWVsyncLock);
- if (mPrimaryHWVsyncEnabled) {
- //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false);
- mEventControlThread->setVsyncEnabled(false);
- mPrimaryDispSync.endResync();
- mPrimaryHWVsyncEnabled = false;
- }
- if (makeUnavailable) {
- mHWVsyncAvailable = false;
- }
-}
-
-void SurfaceFlinger::resyncWithRateLimit() {
- static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
- if (systemTime() - mLastSwapTime > kIgnoreDelay) {
- resyncToHardwareVsync(false);
- }
-}
-
-void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
- bool needsHwVsync = false;
-
- { // Scope for the lock
- Mutex::Autolock _l(mHWVsyncLock);
- if (type == 0 && mPrimaryHWVsyncEnabled) {
- needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
- }
- }
-
- if (needsHwVsync) {
- enableHardwareVsync();
- } else {
- disableHardwareVsync(false);
- }
-}
-
-void SurfaceFlinger::onHotplugReceived(int type, bool connected) {
- if (mEventThread == NULL) {
- // This is a temporary workaround for b/7145521. A non-null pointer
- // does not mean EventThread has finished initializing, so this
- // is not a correct fix.
- ALOGW("WARNING: EventThread not started, ignoring hotplug");
- return;
- }
-
- if (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- Mutex::Autolock _l(mStateLock);
- if (connected) {
- createBuiltinDisplayLocked((DisplayDevice::DisplayType)type);
- } else {
- mCurrentState.displays.removeItem(mBuiltinDisplays[type]);
- mBuiltinDisplays[type].clear();
- }
- setTransactionFlags(eDisplayTransactionNeeded);
-
- // Defer EventThread notification until SF has updated mDisplays.
- }
-}
-
-void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
- ATRACE_CALL();
- getHwComposer().eventControl(disp, event, enabled);
-}
-
-void SurfaceFlinger::onMessageReceived(int32_t what) {
- ATRACE_CALL();
- switch (what) {
- case MessageQueue::INVALIDATE: {
- bool refreshNeeded = handleMessageTransaction();
- refreshNeeded |= handleMessageInvalidate();
- refreshNeeded |= mRepaintEverything;
- if (refreshNeeded) {
- // Signal a refresh if a transaction modified the window state,
- // a new buffer was latched, or if HWC has requested a full
- // repaint
- signalRefresh();
- }
- break;
- }
- case MessageQueue::REFRESH: {
- handleMessageRefresh();
- break;
- }
- }
-}
-
-bool SurfaceFlinger::handleMessageTransaction() {
- uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
- if (transactionFlags) {
- handleTransaction(transactionFlags);
- return true;
- }
- return false;
-}
-
-bool SurfaceFlinger::handleMessageInvalidate() {
- ATRACE_CALL();
- return handlePageFlip();
-}
-
-void SurfaceFlinger::handleMessageRefresh() {
- ATRACE_CALL();
-
- nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
- preComposition();
- rebuildLayerStacks();
- setUpHWComposer();
- doDebugFlashRegions();
- doComposition();
- postComposition(refreshStartTime);
-}
-
-void SurfaceFlinger::doDebugFlashRegions()
-{
- // is debugging enabled
- if (CC_LIKELY(!mDebugRegion))
- return;
-
- const bool repaintEverything = mRepaintEverything;
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<DisplayDevice>& hw(mDisplays[dpy]);
- if (hw->isDisplayOn()) {
- // transform the dirty region into this screen's coordinate space
- const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
- if (!dirtyRegion.isEmpty()) {
- // redraw the whole screen
- doComposeSurfaces(hw, Region(hw->bounds()));
-
- // and draw the dirty region
- const int32_t height = hw->getHeight();
- RenderEngine& engine(getRenderEngine());
- engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1);
-
- hw->compositionComplete();
- hw->swapBuffers(getHwComposer());
- }
- }
- }
-
- postFramebuffer();
-
- if (mDebugRegion > 1) {
- usleep(mDebugRegion * 1000);
- }
-
- HWComposer& hwc(getHwComposer());
- if (hwc.initCheck() == NO_ERROR) {
- status_t err = hwc.prepare();
- ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
- }
-}
-
-void SurfaceFlinger::preComposition()
-{
- bool needExtraInvalidate = false;
- const LayerVector& layers(mDrawingState.layersSortedByZ);
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; i++) {
- if (layers[i]->onPreComposition()) {
- needExtraInvalidate = true;
- }
- }
- if (needExtraInvalidate) {
- signalLayerUpdate();
- }
-}
-
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-{
- const LayerVector& layers(mDrawingState.layersSortedByZ);
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; i++) {
- bool frameLatched = layers[i]->onPostComposition();
- if (frameLatched) {
- recordBufferingStats(layers[i]->getName().string(),
- layers[i]->getOccupancyHistory(false));
- }
- }
-
- const HWComposer& hwc = getHwComposer();
- sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
-
- if (presentFence->isValid()) {
- if (mPrimaryDispSync.addPresentFence(presentFence)) {
- enableHardwareVsync();
- } else {
- disableHardwareVsync(false);
- }
- }
-
- const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
- if (kIgnorePresentFences) {
- if (hw->isDisplayOn()) {
- enableHardwareVsync();
- }
- }
-
- mFenceTracker.addFrame(refreshStartTime, presentFence,
- hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
- if (mAnimCompositionPending) {
- mAnimCompositionPending = false;
-
- if (presentFence->isValid()) {
- mAnimFrameTracker.setActualPresentFence(presentFence);
- } else {
- // The HWC doesn't support present fences, so use the refresh
- // timestamp instead.
- nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
- mAnimFrameTracker.setActualPresentTime(presentTime);
- }
- mAnimFrameTracker.advanceFrame();
- }
-
- if (hw->getPowerMode() == HWC_POWER_MODE_OFF) {
- return;
- }
-
- nsecs_t currentTime = systemTime();
- if (mHasPoweredOff) {
- mHasPoweredOff = false;
- } else {
- nsecs_t period = mPrimaryDispSync.getPeriod();
- nsecs_t elapsedTime = currentTime - mLastSwapTime;
- size_t numPeriods = static_cast<size_t>(elapsedTime / period);
- if (numPeriods < NUM_BUCKETS - 1) {
- mFrameBuckets[numPeriods] += elapsedTime;
- } else {
- mFrameBuckets[NUM_BUCKETS - 1] += elapsedTime;
- }
- mTotalTime += elapsedTime;
- }
- mLastSwapTime = currentTime;
-}
-
-void SurfaceFlinger::rebuildLayerStacks() {
- // rebuild the visible layer list per screen
- if (CC_UNLIKELY(mVisibleRegionsDirty)) {
- ATRACE_CALL();
- mVisibleRegionsDirty = false;
- invalidateHwcGeometry();
-
- const LayerVector& layers(mDrawingState.layersSortedByZ);
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- Region opaqueRegion;
- Region dirtyRegion;
- Vector< sp<Layer> > layersSortedByZ;
- const sp<DisplayDevice>& hw(mDisplays[dpy]);
- const Transform& tr(hw->getTransform());
- const Rect bounds(hw->getBounds());
- if (hw->isDisplayOn()) {
- SurfaceFlinger::computeVisibleRegions(layers,
- hw->getLayerStack(), dirtyRegion, opaqueRegion);
-
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(layers[i]);
- const Layer::State& s(layer->getDrawingState());
- if (s.layerStack == hw->getLayerStack()) {
- Region drawRegion(tr.transform(
- layer->visibleNonTransparentRegion));
- drawRegion.andSelf(bounds);
- if (!drawRegion.isEmpty()) {
- layersSortedByZ.add(layer);
- }
- }
- }
- }
- hw->setVisibleLayersSortedByZ(layersSortedByZ);
- hw->undefinedRegion.set(bounds);
- hw->undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
- hw->dirtyRegion.orSelf(dirtyRegion);
- }
- }
-}
-
-void SurfaceFlinger::setUpHWComposer() {
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- bool dirty = !mDisplays[dpy]->getDirtyRegion(false).isEmpty();
- bool empty = mDisplays[dpy]->getVisibleLayersSortedByZ().size() == 0;
- bool wasEmpty = !mDisplays[dpy]->lastCompositionHadVisibleLayers;
-
- // If nothing has changed (!dirty), don't recompose.
- // If something changed, but we don't currently have any visible layers,
- // and didn't when we last did a composition, then skip it this time.
- // The second rule does two things:
- // - When all layers are removed from a display, we'll emit one black
- // frame, then nothing more until we get new layers.
- // - When a display is created with a private layer stack, we won't
- // emit any black frames until a layer is added to the layer stack.
- bool mustRecompose = dirty && !(empty && wasEmpty);
-
- ALOGV_IF(mDisplays[dpy]->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL,
- "dpy[%zu]: %s composition (%sdirty %sempty %swasEmpty)", dpy,
- mustRecompose ? "doing" : "skipping",
- dirty ? "+" : "-",
- empty ? "+" : "-",
- wasEmpty ? "+" : "-");
-
- mDisplays[dpy]->beginFrame(mustRecompose);
-
- if (mustRecompose) {
- mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty;
- }
- }
-
- HWComposer& hwc(getHwComposer());
- if (hwc.initCheck() == NO_ERROR) {
- // build the h/w work list
- if (CC_UNLIKELY(mHwWorkListDirty)) {
- mHwWorkListDirty = false;
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- const int32_t id = hw->getHwcDisplayId();
- if (id >= 0) {
- const Vector< sp<Layer> >& currentLayers(
- hw->getVisibleLayersSortedByZ());
- const size_t count = currentLayers.size();
- if (hwc.createWorkList(id, count) == NO_ERROR) {
- HWComposer::LayerListIterator cur = hwc.begin(id);
- const HWComposer::LayerListIterator end = hwc.end(id);
- for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
- const sp<Layer>& layer(currentLayers[i]);
- layer->setGeometry(hw, *cur);
- if (mDebugDisableHWC || mDebugRegion || mDaltonize || mHasColorMatrix) {
- cur->setSkip(true);
- }
- }
- }
- }
- }
- }
-
- // set the per-frame data
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- const int32_t id = hw->getHwcDisplayId();
- if (id >= 0) {
- const Vector< sp<Layer> >& currentLayers(
- hw->getVisibleLayersSortedByZ());
- const size_t count = currentLayers.size();
- HWComposer::LayerListIterator cur = hwc.begin(id);
- const HWComposer::LayerListIterator end = hwc.end(id);
- for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
- /*
- * update the per-frame h/w composer data for each layer
- * and build the transparent region of the FB
- */
- const sp<Layer>& layer(currentLayers[i]);
- layer->setPerFrameData(hw, *cur);
- }
- }
- }
-
- // If possible, attempt to use the cursor overlay on each display.
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- const int32_t id = hw->getHwcDisplayId();
- if (id >= 0) {
- const Vector< sp<Layer> >& currentLayers(
- hw->getVisibleLayersSortedByZ());
- const size_t count = currentLayers.size();
- HWComposer::LayerListIterator cur = hwc.begin(id);
- const HWComposer::LayerListIterator end = hwc.end(id);
- for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
- const sp<Layer>& layer(currentLayers[i]);
- if (layer->isPotentialCursor()) {
- cur->setIsCursorLayerHint();
- break;
- }
- }
- }
- }
-
- status_t err = hwc.prepare();
- ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
-
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- hw->prepareFrame(hwc);
- }
- }
-}
-
-void SurfaceFlinger::doComposition() {
- ATRACE_CALL();
- const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<DisplayDevice>& hw(mDisplays[dpy]);
- if (hw->isDisplayOn()) {
- // transform the dirty region into this screen's coordinate space
- const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
-
- // repaint the framebuffer (if needed)
- doDisplayComposition(hw, dirtyRegion);
-
- hw->dirtyRegion.clear();
- hw->flip(hw->swapRegion);
- hw->swapRegion.clear();
- }
- // inform the h/w that we're done compositing
- hw->compositionComplete();
- }
- postFramebuffer();
-}
-
-void SurfaceFlinger::postFramebuffer()
-{
- ATRACE_CALL();
-
- const nsecs_t now = systemTime();
- mDebugInSwapBuffers = now;
-
- HWComposer& hwc(getHwComposer());
- if (hwc.initCheck() == NO_ERROR) {
- if (!hwc.supportsFramebufferTarget()) {
- // EGL spec says:
- // "surface must be bound to the calling thread's current context,
- // for the current rendering API."
- getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
- }
- hwc.commit();
- }
-
- // 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 (and does in some case) call
- // dequeueBuffer().
- getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
-
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- const Vector< sp<Layer> >& currentLayers(hw->getVisibleLayersSortedByZ());
- hw->onSwapBuffersCompleted(hwc);
- const size_t count = currentLayers.size();
- int32_t id = hw->getHwcDisplayId();
- if (id >=0 && hwc.initCheck() == NO_ERROR) {
- HWComposer::LayerListIterator cur = hwc.begin(id);
- const HWComposer::LayerListIterator end = hwc.end(id);
- for (size_t i = 0; cur != end && i < count; ++i, ++cur) {
- currentLayers[i]->onLayerDisplayed(hw, &*cur);
- }
- } else {
- for (size_t i = 0; i < count; i++) {
- currentLayers[i]->onLayerDisplayed(hw, NULL);
- }
- }
- }
-
- mLastSwapBufferTime = systemTime() - now;
- mDebugInSwapBuffers = 0;
-
- uint32_t flipCount = getDefaultDisplayDevice()->getPageFlipCount();
- if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
- logFrameStats();
- }
-}
-
-void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
-{
- ATRACE_CALL();
-
- // here we keep a copy of the drawing state (that is the state that's
- // going to be overwritten by handleTransactionLocked()) outside of
- // mStateLock so that the side-effects of the State assignment
- // don't happen with mStateLock held (which can cause deadlocks).
- State drawingState(mDrawingState);
-
- Mutex::Autolock _l(mStateLock);
- const nsecs_t now = systemTime();
- mDebugInTransaction = now;
-
- // Here we're guaranteed that some transaction flags are set
- // so we can call handleTransactionLocked() unconditionally.
- // We call getTransactionFlags(), which will also clear the flags,
- // with mStateLock held to guarantee that mCurrentState won't change
- // until the transaction is committed.
-
- transactionFlags = getTransactionFlags(eTransactionMask);
- handleTransactionLocked(transactionFlags);
-
- mLastTransactionTime = systemTime() - now;
- mDebugInTransaction = 0;
- invalidateHwcGeometry();
- // here the transaction has been committed
-}
-
-void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
-{
- const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
- const size_t count = currentLayers.size();
-
- // Notify all layers of available frames
- for (size_t i = 0; i < count; ++i) {
- currentLayers[i]->notifyAvailableFrames();
- }
-
- /*
- * Traversal of the children
- * (perform the transaction for each of them if needed)
- */
-
- if (transactionFlags & eTraversalNeeded) {
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(currentLayers[i]);
- uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
- if (!trFlags) continue;
-
- const uint32_t flags = layer->doTransaction(0);
- if (flags & Layer::eVisibleRegion)
- mVisibleRegionsDirty = true;
- }
- }
-
- /*
- * Perform display own transactions if needed
- */
-
- if (transactionFlags & eDisplayTransactionNeeded) {
- // here we take advantage of Vector's copy-on-write semantics to
- // improve performance by skipping the transaction entirely when
- // know that the lists are identical
- const KeyedVector< wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
- const KeyedVector< wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
- if (!curr.isIdenticalTo(draw)) {
- mVisibleRegionsDirty = true;
- const size_t cc = curr.size();
- size_t dc = draw.size();
-
- // find the displays that were removed
- // (ie: in drawing state but not in current state)
- // also handle displays that changed
- // (ie: displays that are in both lists)
- for (size_t i=0 ; i<dc ; i++) {
- const ssize_t j = curr.indexOfKey(draw.keyAt(i));
- if (j < 0) {
- // in drawing state but not in current state
- if (!draw[i].isMainDisplay()) {
- // Call makeCurrent() on the primary display so we can
- // be sure that nothing associated with this display
- // is current.
- const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDevice());
- defaultDisplay->makeCurrent(mEGLDisplay, mEGLContext);
- sp<DisplayDevice> hw(getDisplayDevice(draw.keyAt(i)));
- if (hw != NULL)
- hw->disconnect(getHwComposer());
- if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
- mEventThread->onHotplugReceived(draw[i].type, false);
- mDisplays.removeItem(draw.keyAt(i));
- } else {
- ALOGW("trying to remove the main display");
- }
- } else {
- // this display is in both lists. see if something changed.
- const DisplayDeviceState& state(curr[j]);
- const wp<IBinder>& display(curr.keyAt(j));
- const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
- const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
- if (state_binder != draw_binder) {
- // changing the surface is like destroying and
- // recreating the DisplayDevice, so we just remove it
- // from the drawing state, so that it get re-added
- // below.
- sp<DisplayDevice> hw(getDisplayDevice(display));
- if (hw != NULL)
- hw->disconnect(getHwComposer());
- mDisplays.removeItem(display);
- mDrawingState.displays.removeItemsAt(i);
- dc--; i--;
- // at this point we must loop to the next item
- continue;
- }
-
- const sp<DisplayDevice> disp(getDisplayDevice(display));
- if (disp != NULL) {
- if (state.layerStack != draw[i].layerStack) {
- disp->setLayerStack(state.layerStack);
- }
- if ((state.orientation != draw[i].orientation)
- || (state.viewport != draw[i].viewport)
- || (state.frame != draw[i].frame))
- {
- disp->setProjection(state.orientation,
- state.viewport, state.frame);
- }
- if (state.width != draw[i].width || state.height != draw[i].height) {
- disp->setDisplaySize(state.width, state.height);
- }
- }
- }
- }
-
- // find displays that were added
- // (ie: in current state but not in drawing state)
- for (size_t i=0 ; i<cc ; i++) {
- if (draw.indexOfKey(curr.keyAt(i)) < 0) {
- const DisplayDeviceState& state(curr[i]);
-
- sp<DisplaySurface> dispSurface;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferProducer> bqProducer;
- sp<IGraphicBufferConsumer> bqConsumer;
- BufferQueue::createBufferQueue(&bqProducer, &bqConsumer,
- new GraphicBufferAlloc());
-
- int32_t hwcDisplayId = -1;
- if (state.isVirtualDisplay()) {
- // Virtual displays without a surface are dormant:
- // they have external state (layer stack, projection,
- // etc.) but no internal state (i.e. a DisplayDevice).
- if (state.surface != NULL) {
-
- int width = 0;
- int status = state.surface->query(
- NATIVE_WINDOW_WIDTH, &width);
- ALOGE_IF(status != NO_ERROR,
- "Unable to query width (%d)", status);
- int height = 0;
- status = state.surface->query(
- NATIVE_WINDOW_HEIGHT, &height);
- ALOGE_IF(status != NO_ERROR,
- "Unable to query height (%d)", status);
- if (mUseHwcVirtualDisplays &&
- (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 ||
- (width <= MAX_VIRTUAL_DISPLAY_DIMENSION &&
- height <= MAX_VIRTUAL_DISPLAY_DIMENSION))) {
- hwcDisplayId = allocateHwcDisplayId(state.type);
- }
-
- sp<VirtualDisplaySurface> vds = new VirtualDisplaySurface(
- *mHwc, hwcDisplayId, state.surface,
- bqProducer, bqConsumer, state.displayName);
-
- dispSurface = vds;
- producer = vds;
- }
- } else {
- ALOGE_IF(state.surface!=NULL,
- "adding a supported display, but rendering "
- "surface is provided (%p), ignoring it",
- state.surface.get());
- hwcDisplayId = allocateHwcDisplayId(state.type);
- // for supported (by hwc) displays we provide our
- // own rendering surface
- dispSurface = new FramebufferSurface(*mHwc, state.type,
- bqConsumer);
- producer = bqProducer;
- }
-
- const wp<IBinder>& display(curr.keyAt(i));
- if (dispSurface != NULL) {
- sp<DisplayDevice> hw = new DisplayDevice(this,
- state.type, hwcDisplayId,
- mHwc->getFormat(hwcDisplayId), state.isSecure,
- display, dispSurface, producer,
- mRenderEngine->getEGLConfig());
- hw->setLayerStack(state.layerStack);
- hw->setProjection(state.orientation,
- state.viewport, state.frame);
- hw->setDisplayName(state.displayName);
- mDisplays.add(display, hw);
- if (state.isVirtualDisplay()) {
- if (hwcDisplayId >= 0) {
- mHwc->setVirtualDisplayProperties(hwcDisplayId,
- hw->getWidth(), hw->getHeight(),
- hw->getFormat());
- }
- } else {
- mEventThread->onHotplugReceived(state.type, true);
- }
- }
- }
- }
- }
- }
-
- if (transactionFlags & (eTraversalNeeded|eDisplayTransactionNeeded)) {
- // The transform hint might have changed for some layers
- // (either because a display has changed, or because a layer
- // as changed).
- //
- // Walk through all the layers in currentLayers,
- // and update their transform hint.
- //
- // If a layer is visible only on a single display, then that
- // display is used to calculate the hint, otherwise we use the
- // default display.
- //
- // NOTE: we do this here, rather than in rebuildLayerStacks() so that
- // the hint is set before we acquire a buffer from the surface texture.
- //
- // NOTE: layer transactions have taken place already, so we use their
- // drawing state. However, SurfaceFlinger's own transaction has not
- // happened yet, so we must use the current state layer list
- // (soon to become the drawing state list).
- //
- sp<const DisplayDevice> disp;
- uint32_t currentlayerStack = 0;
- for (size_t i=0; i<count; i++) {
- // NOTE: we rely on the fact that layers are sorted by
- // layerStack first (so we don't have to traverse the list
- // of displays for every layer).
- const sp<Layer>& layer(currentLayers[i]);
- uint32_t layerStack = layer->getDrawingState().layerStack;
- if (i==0 || currentlayerStack != layerStack) {
- currentlayerStack = layerStack;
- // figure out if this layerstack is mirrored
- // (more than one display) if so, pick the default display,
- // if not, pick the only display it's on.
- disp.clear();
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- if (hw->getLayerStack() == currentlayerStack) {
- if (disp == NULL) {
- disp = hw;
- } else {
- disp = NULL;
- break;
- }
- }
- }
- }
- if (disp == NULL) {
- // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
- // redraw after transform hint changes. See bug 8508397.
-
- // could be null when this layer is using a layerStack
- // that is not visible on any display. Also can occur at
- // screen off/on times.
- disp = getDefaultDisplayDevice();
- }
- layer->updateTransformHint(disp);
- }
- }
-
-
- /*
- * Perform our own transaction if needed
- */
-
- const LayerVector& layers(mDrawingState.layersSortedByZ);
- if (currentLayers.size() > layers.size()) {
- // layers have been added
- mVisibleRegionsDirty = true;
- }
-
- // some layers might have been removed, so
- // we need to update the regions they're exposing.
- if (mLayersRemoved) {
- mLayersRemoved = false;
- mVisibleRegionsDirty = true;
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(layers[i]);
- if (currentLayers.indexOf(layer) < 0) {
- // this layer is not visible anymore
- // TODO: we could traverse the tree from front to back and
- // compute the actual visible region
- // TODO: we could cache the transformed region
- const Layer::State& s(layer->getDrawingState());
- Region visibleReg = s.active.transform.transform(
- Region(Rect(s.active.w, s.active.h)));
- invalidateLayerStack(s.layerStack, visibleReg);
- }
- }
- }
-
- commitTransaction();
-
- updateCursorAsync();
-}
-
-void SurfaceFlinger::updateCursorAsync()
-{
- HWComposer& hwc(getHwComposer());
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- const int32_t id = hw->getHwcDisplayId();
- if (id < 0) {
- continue;
- }
- const Vector< sp<Layer> >& currentLayers(
- hw->getVisibleLayersSortedByZ());
- const size_t count = currentLayers.size();
- HWComposer::LayerListIterator cur = hwc.begin(id);
- const HWComposer::LayerListIterator end = hwc.end(id);
- for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
- if (cur->getCompositionType() != HWC_CURSOR_OVERLAY) {
- continue;
- }
- const sp<Layer>& layer(currentLayers[i]);
- Rect cursorPos = layer->getPosition(hw);
- hwc.setCursorPositionAsync(id, cursorPos);
- break;
- }
- }
-}
-
-void SurfaceFlinger::commitTransaction()
-{
- if (!mLayersPendingRemoval.isEmpty()) {
- // Notify removed layers now that they can't be drawn from
- for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
- recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
- mLayersPendingRemoval[i]->getOccupancyHistory(true));
- mLayersPendingRemoval[i]->onRemoved();
- }
- mLayersPendingRemoval.clear();
- }
-
- // If this transaction is part of a window animation then the next frame
- // we composite should be considered an animation as well.
- mAnimCompositionPending = mAnimTransactionPending;
-
- mDrawingState = mCurrentState;
- mTransactionPending = false;
- mAnimTransactionPending = false;
- mTransactionCV.broadcast();
-}
-
-void SurfaceFlinger::computeVisibleRegions(
- const LayerVector& currentLayers, uint32_t layerStack,
- Region& outDirtyRegion, Region& outOpaqueRegion)
-{
- ATRACE_CALL();
-
- Region aboveOpaqueLayers;
- Region aboveCoveredLayers;
- Region dirty;
-
- outDirtyRegion.clear();
-
- size_t i = currentLayers.size();
- while (i--) {
- const sp<Layer>& layer = currentLayers[i];
-
- // start with the whole surface at its current location
- const Layer::State& s(layer->getDrawingState());
-
- // only consider the layers on the given layer stack
- if (s.layerStack != layerStack)
- continue;
-
- /*
- * opaqueRegion: area of a surface that is fully opaque.
- */
- Region opaqueRegion;
-
- /*
- * visibleRegion: area of a surface that is visible on screen
- * and not fully transparent. This is essentially the layer's
- * footprint minus the opaque regions above it.
- * Areas covered by a translucent surface are considered visible.
- */
- Region visibleRegion;
-
- /*
- * coveredRegion: area of a surface that is covered by all
- * visible regions above it (which includes the translucent areas).
- */
- Region coveredRegion;
-
- /*
- * transparentRegion: area of a surface that is hinted to be completely
- * transparent. This is only used to tell when the layer has no visible
- * non-transparent regions and can be removed from the layer list. It
- * does not affect the visibleRegion of this layer or any layers
- * beneath it. The hint may not be correct if apps don't respect the
- * SurfaceView restrictions (which, sadly, some don't).
- */
- Region transparentRegion;
-
-
- // handle hidden surfaces by setting the visible region to empty
- if (CC_LIKELY(layer->isVisible())) {
- const bool translucent = !layer->isOpaque(s);
- Rect bounds(s.active.transform.transform(layer->computeBounds()));
- visibleRegion.set(bounds);
- if (!visibleRegion.isEmpty()) {
- // Remove the transparent area from the visible region
- if (translucent) {
- const Transform tr(s.active.transform);
- if (tr.preserveRects()) {
- // transform the transparent region
- transparentRegion = tr.transform(s.activeTransparentRegion);
- } else {
- // transformation too complex, can't do the
- // transparent region optimization.
- transparentRegion.clear();
- }
- }
-
- // compute the opaque region
- const int32_t layerOrientation = s.active.transform.getOrientation();
- if (s.alpha==255 && !translucent &&
- ((layerOrientation & Transform::ROT_INVALID) == false)) {
- // the opaque region is the layer's footprint
- opaqueRegion = visibleRegion;
- }
- }
- }
-
- // Clip the covered region to the visible region
- coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
-
- // Update aboveCoveredLayers for next (lower) layer
- aboveCoveredLayers.orSelf(visibleRegion);
-
- // subtract the opaque region covered by the layers above us
- visibleRegion.subtractSelf(aboveOpaqueLayers);
-
- // compute this layer's dirty region
- if (layer->contentDirty) {
- // we need to invalidate the whole region
- dirty = visibleRegion;
- // as well, as the old visible region
- dirty.orSelf(layer->visibleRegion);
- layer->contentDirty = false;
- } else {
- /* compute the exposed region:
- * the exposed region consists of two components:
- * 1) what's VISIBLE now and was COVERED before
- * 2) what's EXPOSED now less what was EXPOSED before
- *
- * note that (1) is conservative, we start with the whole
- * visible region but only keep what used to be covered by
- * something -- which mean it may have been exposed.
- *
- * (2) handles areas that were not covered by anything but got
- * exposed because of a resize.
- */
- const Region newExposed = visibleRegion - coveredRegion;
- const Region oldVisibleRegion = layer->visibleRegion;
- const Region oldCoveredRegion = layer->coveredRegion;
- const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
- dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
- }
- dirty.subtractSelf(aboveOpaqueLayers);
-
- // accumulate to the screen dirty region
- outDirtyRegion.orSelf(dirty);
-
- // Update aboveOpaqueLayers for next (lower) layer
- aboveOpaqueLayers.orSelf(opaqueRegion);
-
- // Store the visible region in screen space
- layer->setVisibleRegion(visibleRegion);
- layer->setCoveredRegion(coveredRegion);
- layer->setVisibleNonTransparentRegion(
- visibleRegion.subtract(transparentRegion));
- }
-
- outOpaqueRegion = aboveOpaqueLayers;
-}
-
-void SurfaceFlinger::invalidateLayerStack(uint32_t layerStack,
- const Region& dirty) {
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<DisplayDevice>& hw(mDisplays[dpy]);
- if (hw->getLayerStack() == layerStack) {
- hw->dirtyRegion.orSelf(dirty);
- }
- }
-}
-
-bool SurfaceFlinger::handlePageFlip()
-{
- Region dirtyRegion;
-
- bool visibleRegions = false;
- const LayerVector& layers(mDrawingState.layersSortedByZ);
- bool frameQueued = false;
-
- // Store the set of layers that need updates. This set must not change as
- // buffers are being latched, as this could result in a deadlock.
- // Example: Two producers share the same command stream and:
- // 1.) Layer 0 is latched
- // 2.) Layer 0 gets a new frame
- // 2.) Layer 1 gets a new frame
- // 3.) Layer 1 is latched.
- // Display is now waiting on Layer 1's frame, which is behind layer 0's
- // second frame. But layer 0's second frame could be waiting on display.
- Vector<Layer*> layersWithQueuedFrames;
- for (size_t i = 0, count = layers.size(); i<count ; i++) {
- const sp<Layer>& layer(layers[i]);
- if (layer->hasQueuedFrame()) {
- frameQueued = true;
- if (layer->shouldPresentNow(mPrimaryDispSync)) {
- layersWithQueuedFrames.push_back(layer.get());
- } else {
- layer->useEmptyDamage();
- }
- } else {
- layer->useEmptyDamage();
- }
- }
- for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) {
- Layer* layer = layersWithQueuedFrames[i];
- const Region dirty(layer->latchBuffer(visibleRegions));
- layer->useSurfaceDamage();
- const Layer::State& s(layer->getDrawingState());
- invalidateLayerStack(s.layerStack, dirty);
- }
-
- mVisibleRegionsDirty |= visibleRegions;
-
- // If we will need to wake up at some time in the future to deal with a
- // queued frame that shouldn't be displayed during this vsync period, wake
- // up during the next vsync period to check again.
- if (frameQueued && layersWithQueuedFrames.empty()) {
- signalLayerUpdate();
- }
-
- // Only continue with the refresh if there is actually new work to do
- return !layersWithQueuedFrames.empty();
-}
-
-void SurfaceFlinger::invalidateHwcGeometry()
-{
- mHwWorkListDirty = true;
-}
-
-
-void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw,
- const Region& inDirtyRegion)
-{
- // We only need to actually compose the display if:
- // 1) It is being handled by hardware composer, which may need this to
- // keep its virtual display state machine in sync, or
- // 2) There is work to be done (the dirty region isn't empty)
- bool isHwcDisplay = hw->getHwcDisplayId() >= 0;
- if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
- return;
- }
-
- Region dirtyRegion(inDirtyRegion);
-
- // compute the invalid region
- hw->swapRegion.orSelf(dirtyRegion);
-
- uint32_t flags = hw->getFlags();
- if (flags & DisplayDevice::SWAP_RECTANGLE) {
- // we can redraw only what's dirty, but since SWAP_RECTANGLE only
- // takes a rectangle, we must make sure to update that whole
- // rectangle in that case
- dirtyRegion.set(hw->swapRegion.bounds());
- } else {
- if (flags & DisplayDevice::PARTIAL_UPDATES) {
- // We need to redraw the rectangle that will be updated
- // (pushed to the framebuffer).
- // This is needed because PARTIAL_UPDATES only takes one
- // rectangle instead of a region (see DisplayDevice::flip())
- dirtyRegion.set(hw->swapRegion.bounds());
- } else {
- // we need to redraw everything (the whole screen)
- dirtyRegion.set(hw->bounds());
- hw->swapRegion = dirtyRegion;
- }
- }
-
- if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) {
- if (!doComposeSurfaces(hw, dirtyRegion)) return;
- } else {
- RenderEngine& engine(getRenderEngine());
- mat4 colorMatrix = mColorMatrix;
- if (mDaltonize) {
- colorMatrix = colorMatrix * mDaltonizer();
- }
- mat4 oldMatrix = engine.setupColorTransform(colorMatrix);
- doComposeSurfaces(hw, dirtyRegion);
- engine.setupColorTransform(oldMatrix);
- }
-
- // update the swap region and clear the dirty region
- hw->swapRegion.orSelf(dirtyRegion);
-
- // swap buffers (presentation)
- hw->swapBuffers(getHwComposer());
-}
-
-bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty)
-{
- RenderEngine& engine(getRenderEngine());
- const int32_t id = hw->getHwcDisplayId();
- HWComposer& hwc(getHwComposer());
- HWComposer::LayerListIterator cur = hwc.begin(id);
- const HWComposer::LayerListIterator end = hwc.end(id);
-
- bool hasGlesComposition = hwc.hasGlesComposition(id);
- if (hasGlesComposition) {
- if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) {
- ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
- hw->getDisplayName().string());
- eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- if(!getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext)) {
- ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting.");
- }
- return false;
- }
-
- // Never touch the framebuffer if we don't have any framebuffer layers
- const bool hasHwcComposition = hwc.hasHwcComposition(id);
- if (hasHwcComposition) {
- // when using overlays, we assume a fully transparent framebuffer
- // NOTE: we could reduce how much we need to clear, for instance
- // remove where there are opaque FB layers. however, on some
- // GPUs doing a "clean slate" clear might be more efficient.
- // We'll revisit later if needed.
- engine.clearWithColor(0, 0, 0, 0);
- } else {
- // we start with the whole screen area
- const Region bounds(hw->getBounds());
-
- // we remove the scissor part
- // we're left with the letterbox region
- // (common case is that letterbox ends-up being empty)
- const Region letterbox(bounds.subtract(hw->getScissor()));
-
- // compute the area to clear
- Region region(hw->undefinedRegion.merge(letterbox));
-
- // but limit it to the dirty region
- region.andSelf(dirty);
-
- // screen is already cleared here
- if (!region.isEmpty()) {
- // can happen with SurfaceView
- drawWormhole(hw, region);
- }
- }
-
- if (hw->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) {
- // just to be on the safe side, we don't set the
- // scissor on the main display. It should never be needed
- // anyways (though in theory it could since the API allows it).
- const Rect& bounds(hw->getBounds());
- const Rect& scissor(hw->getScissor());
- if (scissor != bounds) {
- // scissor doesn't match the screen's dimensions, so we
- // need to clear everything outside of it and enable
- // the GL scissor so we don't draw anything where we shouldn't
-
- // enable scissor for this frame
- const uint32_t height = hw->getHeight();
- engine.setScissor(scissor.left, height - scissor.bottom,
- scissor.getWidth(), scissor.getHeight());
- }
- }
- }
-
- /*
- * and then, render the layers targeted at the framebuffer
- */
-
- const Vector< sp<Layer> >& layers(hw->getVisibleLayersSortedByZ());
- const size_t count = layers.size();
- const Transform& tr = hw->getTransform();
- if (cur != end) {
- // we're using h/w composer
- for (size_t i=0 ; i<count && cur!=end ; ++i, ++cur) {
- const sp<Layer>& layer(layers[i]);
- const Region clip(dirty.intersect(tr.transform(layer->visibleRegion)));
- if (!clip.isEmpty()) {
- switch (cur->getCompositionType()) {
- case HWC_CURSOR_OVERLAY:
- case HWC_OVERLAY: {
- const Layer::State& state(layer->getDrawingState());
- if ((cur->getHints() & HWC_HINT_CLEAR_FB)
- && i
- && layer->isOpaque(state) && (state.alpha == 0xFF)
- && hasGlesComposition) {
- // never clear the very first layer since we're
- // guaranteed the FB is already cleared
- layer->clearWithOpenGL(hw, clip);
- }
- break;
- }
- case HWC_FRAMEBUFFER: {
- layer->draw(hw, clip);
- break;
- }
- case HWC_FRAMEBUFFER_TARGET: {
- // this should not happen as the iterator shouldn't
- // let us get there.
- ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%zu)", i);
- break;
- }
- }
- }
- layer->setAcquireFence(hw, *cur);
- }
- } else {
- // we're not using h/w composer
- for (size_t i=0 ; i<count ; ++i) {
- const sp<Layer>& layer(layers[i]);
- const Region clip(dirty.intersect(
- tr.transform(layer->visibleRegion)));
- if (!clip.isEmpty()) {
- layer->draw(hw, clip);
- }
- }
- }
-
- // disable scissor at the end of the frame
- engine.disableScissor();
- return true;
-}
-
-void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const {
- const int32_t height = hw->getHeight();
- RenderEngine& engine(getRenderEngine());
- engine.fillRegionWithColor(region, height, 0, 0, 0, 0);
-}
-
-status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
- const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbc,
- const sp<Layer>& lbc)
-{
- // add this layer to the current state list
- {
- Mutex::Autolock _l(mStateLock);
- if (mCurrentState.layersSortedByZ.size() >= MAX_LAYERS) {
- return NO_MEMORY;
- }
- mCurrentState.layersSortedByZ.add(lbc);
- mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
- }
-
- // attach this layer to the client
- client->attachLayer(handle, lbc);
-
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) {
- Mutex::Autolock _l(mStateLock);
- sp<Layer> layer = weakLayer.promote();
- if (layer == nullptr) {
- // The layer has already been removed, carry on
- return NO_ERROR;
- }
-
- ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
- if (index >= 0) {
- mLayersPendingRemoval.push(layer);
- mLayersRemoved = true;
- setTransactionFlags(eTransactionNeeded);
- return NO_ERROR;
- }
- return status_t(index);
-}
-
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
- return android_atomic_release_load(&mTransactionFlags);
-}
-
-uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) {
- return android_atomic_and(~flags, &mTransactionFlags) & flags;
-}
-
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- uint32_t old = android_atomic_or(flags, &mTransactionFlags);
- if ((old & flags)==0) { // wake the server up
- signalTransaction();
- }
- return old;
-}
-
-void SurfaceFlinger::setTransactionState(
- const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays,
- uint32_t flags)
-{
- ATRACE_CALL();
- Mutex::Autolock _l(mStateLock);
- uint32_t transactionFlags = 0;
-
- if (flags & eAnimation) {
- // For window updates that are part of an animation we must wait for
- // previous animation "frames" to be handled.
- while (mAnimTransactionPending) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- // just in case something goes wrong in SF, return to the
- // caller after a few seconds.
- ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out "
- "waiting for previous animation frame");
- mAnimTransactionPending = false;
- break;
- }
- }
- }
-
- size_t count = displays.size();
- for (size_t i=0 ; i<count ; i++) {
- const DisplayState& s(displays[i]);
- transactionFlags |= setDisplayStateLocked(s);
- }
-
- count = state.size();
- for (size_t i=0 ; i<count ; i++) {
- const ComposerState& s(state[i]);
- // Here we need to check that the interface we're given is indeed
- // one of our own. A malicious client could give us a NULL
- // IInterface, or one of its own or even one of our own but a
- // different type. All these situations would cause us to crash.
- //
- // NOTE: it would be better to use RTTI as we could directly check
- // that we have a Client*. however, RTTI is disabled in Android.
- if (s.client != NULL) {
- sp<IBinder> binder = IInterface::asBinder(s.client);
- if (binder != NULL) {
- String16 desc(binder->getInterfaceDescriptor());
- if (desc == ISurfaceComposerClient::descriptor) {
- sp<Client> client( static_cast<Client *>(s.client.get()) );
- transactionFlags |= setClientStateLocked(client, s.state);
- }
- }
- }
- }
-
- // If a synchronous transaction is explicitly requested without any changes,
- // force a transaction anyway. This can be used as a flush mechanism for
- // previous async transactions.
- if (transactionFlags == 0 && (flags & eSynchronous)) {
- transactionFlags = eTransactionNeeded;
- }
-
- if (transactionFlags) {
- // this triggers the transaction
- setTransactionFlags(transactionFlags);
-
- // if this is a synchronous transaction, wait for it to take effect
- // before returning.
- if (flags & eSynchronous) {
- mTransactionPending = true;
- }
- if (flags & eAnimation) {
- mAnimTransactionPending = true;
- }
- while (mTransactionPending) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- // just in case something goes wrong in SF, return to the
- // called after a few seconds.
- ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
- mTransactionPending = false;
- break;
- }
- }
- }
-}
-
-uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s)
-{
- ssize_t dpyIdx = mCurrentState.displays.indexOfKey(s.token);
- if (dpyIdx < 0)
- return 0;
-
- uint32_t flags = 0;
- DisplayDeviceState& disp(mCurrentState.displays.editValueAt(dpyIdx));
- if (disp.isValid()) {
- const uint32_t what = s.what;
- if (what & DisplayState::eSurfaceChanged) {
- if (IInterface::asBinder(disp.surface) != IInterface::asBinder(s.surface)) {
- disp.surface = s.surface;
- flags |= eDisplayTransactionNeeded;
- }
- }
- if (what & DisplayState::eLayerStackChanged) {
- if (disp.layerStack != s.layerStack) {
- disp.layerStack = s.layerStack;
- flags |= eDisplayTransactionNeeded;
- }
- }
- if (what & DisplayState::eDisplayProjectionChanged) {
- if (disp.orientation != s.orientation) {
- disp.orientation = s.orientation;
- flags |= eDisplayTransactionNeeded;
- }
- if (disp.frame != s.frame) {
- disp.frame = s.frame;
- flags |= eDisplayTransactionNeeded;
- }
- if (disp.viewport != s.viewport) {
- disp.viewport = s.viewport;
- flags |= eDisplayTransactionNeeded;
- }
- }
- if (what & DisplayState::eDisplaySizeChanged) {
- if (disp.width != s.width) {
- disp.width = s.width;
- flags |= eDisplayTransactionNeeded;
- }
- if (disp.height != s.height) {
- disp.height = s.height;
- flags |= eDisplayTransactionNeeded;
- }
- }
- }
- return flags;
-}
-
-uint32_t SurfaceFlinger::setClientStateLocked(
- const sp<Client>& client,
- const layer_state_t& s)
-{
- uint32_t flags = 0;
- sp<Layer> layer(client->getLayerUser(s.surface));
- if (layer != 0) {
- const uint32_t what = s.what;
- bool geometryAppliesWithResize =
- what & layer_state_t::eGeometryAppliesWithResize;
- if (what & layer_state_t::ePositionChanged) {
- if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eLayerChanged) {
- // NOTE: index needs to be calculated before we update the state
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setLayer(s.z) && idx >= 0) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eSizeChanged) {
- if (layer->setSize(s.w, s.h)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eAlphaChanged) {
- if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eMatrixChanged) {
- if (layer->setMatrix(s.matrix))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eTransparentRegionChanged) {
- if (layer->setTransparentRegionHint(s.transparentRegion))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eFlagsChanged) {
- if (layer->setFlags(s.flags, s.mask))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eCropChanged) {
- if (layer->setCrop(s.crop, !geometryAppliesWithResize))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eFinalCropChanged) {
- if (layer->setFinalCrop(s.finalCrop))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eLayerStackChanged) {
- // NOTE: index needs to be calculated before we update the state
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setLayerStack(s.layerStack) && idx >= 0) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDeferTransaction) {
- layer->deferTransactionUntil(s.handle, s.frameNumber);
- // We don't trigger a traversal here because if no other state is
- // changed, we don't want this to cause any more work
- }
- if (what & layer_state_t::eOverrideScalingModeChanged) {
- layer->setOverrideScalingMode(s.overrideScalingMode);
- // We don't trigger a traversal here because if no other state is
- // changed, we don't want this to cause any more work
- }
- }
- return flags;
-}
-
-status_t SurfaceFlinger::createLayer(
- const String8& name,
- const sp<Client>& client,
- 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));
- return BAD_VALUE;
- }
-
- status_t result = NO_ERROR;
-
- sp<Layer> layer;
-
- switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
- case ISurfaceComposerClient::eFXSurfaceNormal:
- result = createNormalLayer(client,
- name, w, h, flags, format,
- handle, gbp, &layer);
- break;
- case ISurfaceComposerClient::eFXSurfaceDim:
- result = createDimLayer(client,
- name, w, h, flags,
- handle, gbp, &layer);
- break;
- default:
- result = BAD_VALUE;
- break;
- }
-
- if (result != NO_ERROR) {
- return result;
- }
-
- result = addClientLayer(client, *handle, *gbp, layer);
- if (result != NO_ERROR) {
- return result;
- }
-
- setTransactionFlags(eTransactionNeeded);
- return result;
-}
-
-status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
- const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
-{
- // initialize the surfaces
- switch (format) {
- case PIXEL_FORMAT_TRANSPARENT:
- case PIXEL_FORMAT_TRANSLUCENT:
- format = PIXEL_FORMAT_RGBA_8888;
- break;
- case PIXEL_FORMAT_OPAQUE:
- format = PIXEL_FORMAT_RGBX_8888;
- break;
- }
-
- *outLayer = new Layer(this, client, name, w, h, flags);
- status_t err = (*outLayer)->setBuffers(w, h, format, flags);
- if (err == NO_ERROR) {
- *handle = (*outLayer)->getHandle();
- *gbp = (*outLayer)->getProducer();
- }
-
- ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err));
- return err;
-}
-
-status_t SurfaceFlinger::createDimLayer(const sp<Client>& client,
- const String8& name, uint32_t w, uint32_t h, uint32_t flags,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
-{
- *outLayer = new LayerDim(this, client, name, w, h, flags);
- *handle = (*outLayer)->getHandle();
- *gbp = (*outLayer)->getProducer();
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
-{
- // called by the window manager when it wants to remove a Layer
- status_t err = NO_ERROR;
- sp<Layer> l(client->getLayerUser(handle));
- if (l != NULL) {
- err = removeLayer(l);
- ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
- "error removing layer=%p (%s)", l.get(), strerror(-err));
- }
- return err;
-}
-
-status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer)
-{
- // called by ~LayerCleaner() when all references to the IBinder (handle)
- // are gone
- return removeLayer(layer);
-}
-
-// ---------------------------------------------------------------------------
-
-void SurfaceFlinger::onInitializeDisplays() {
- // reset screen orientation and use primary layer stack
- Vector<ComposerState> state;
- Vector<DisplayState> displays;
- DisplayState d;
- d.what = DisplayState::eDisplayProjectionChanged |
- DisplayState::eLayerStackChanged;
- d.token = mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY];
- d.layerStack = 0;
- d.orientation = DisplayState::eOrientationDefault;
- d.frame.makeInvalid();
- d.viewport.makeInvalid();
- d.width = 0;
- d.height = 0;
- displays.add(d);
- setTransactionState(state, displays, 0);
- setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL);
-
- const nsecs_t period =
- getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
- mAnimFrameTracker.setDisplayRefreshPeriod(period);
-}
-
-void SurfaceFlinger::initializeDisplays() {
- class MessageScreenInitialized : public MessageBase {
- SurfaceFlinger* flinger;
- public:
- MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
- virtual bool handler() {
- flinger->onInitializeDisplays();
- return true;
- }
- };
- sp<MessageBase> msg = new MessageScreenInitialized(this);
- postMessageAsync(msg); // we may be called from main thread, use async message
-}
-
-void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw,
- int mode) {
- ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
- this);
- int32_t type = hw->getDisplayType();
- int currentMode = hw->getPowerMode();
-
- if (mode == currentMode) {
- ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode);
- return;
- }
-
- hw->setPowerMode(mode);
- if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGW("Trying to set power mode for virtual display");
- return;
- }
-
- if (currentMode == HWC_POWER_MODE_OFF) {
- // Turn on the display
- getHwComposer().setPowerMode(type, mode);
- if (type == DisplayDevice::DISPLAY_PRIMARY) {
- // FIXME: eventthread only knows about the main display right now
- mEventThread->onScreenAcquired();
- resyncToHardwareVsync(true);
- }
-
- mVisibleRegionsDirty = true;
- mHasPoweredOff = true;
- repaintEverything();
-
- struct sched_param param = {0};
- param.sched_priority = 1;
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
- ALOGW("Couldn't set SCHED_FIFO on display on");
- }
- } else if (mode == HWC_POWER_MODE_OFF) {
- // Turn off the display
- struct sched_param param = {0};
- if (sched_setscheduler(0, SCHED_OTHER, ¶m) != 0) {
- ALOGW("Couldn't set SCHED_OTHER on display off");
- }
-
- if (type == DisplayDevice::DISPLAY_PRIMARY) {
- disableHardwareVsync(true); // also cancels any in-progress resync
-
- // FIXME: eventthread only knows about the main display right now
- mEventThread->onScreenReleased();
- }
-
- getHwComposer().setPowerMode(type, mode);
- mVisibleRegionsDirty = true;
- // from this point on, SF will stop drawing on this display
- } else {
- getHwComposer().setPowerMode(type, mode);
- }
-}
-
-void SurfaceFlinger::setPowerMode(const sp<IBinder>& display, int mode) {
- class MessageSetPowerMode: public MessageBase {
- SurfaceFlinger& mFlinger;
- sp<IBinder> mDisplay;
- int mMode;
- public:
- MessageSetPowerMode(SurfaceFlinger& flinger,
- const sp<IBinder>& disp, int mode) : mFlinger(flinger),
- mDisplay(disp) { mMode = mode; }
- virtual bool handler() {
- sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
- if (hw == NULL) {
- ALOGE("Attempt to set power mode = %d for null display %p",
- mMode, mDisplay.get());
- } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
- ALOGW("Attempt to set power mode = %d for virtual display",
- mMode);
- } else {
- mFlinger.setPowerModeInternal(hw, mMode);
- }
- return true;
- }
- };
- sp<MessageBase> msg = new MessageSetPowerMode(*this, display, mode);
- postMessageSync(msg);
-}
-
-// ---------------------------------------------------------------------------
-
-status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
-{
- String8 result;
-
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- if ((uid != AID_SHELL) &&
- !PermissionCache::checkPermission(sDump, pid, uid)) {
- result.appendFormat("Permission Denial: "
- "can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid);
- } else {
- // Try to get the main lock, but give up after one second
- // (this would indicate SF is stuck, but we want to be able to
- // print something in dumpsys).
- status_t err = mStateLock.timedLock(s2ns(1));
- bool locked = (err == NO_ERROR);
- if (!locked) {
- result.appendFormat(
- "SurfaceFlinger appears to be unresponsive (%s [%d]), "
- "dumping anyways (no locks held)\n", strerror(-err), err);
- }
-
- bool dumpAll = true;
- size_t index = 0;
- size_t numArgs = args.size();
- if (numArgs) {
- if ((index < numArgs) &&
- (args[index] == String16("--list"))) {
- index++;
- listLayersLocked(args, index, result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--latency"))) {
- index++;
- dumpStatsLocked(args, index, result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--latency-clear"))) {
- index++;
- clearStatsLocked(args, index, result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--dispsync"))) {
- index++;
- mPrimaryDispSync.dump(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--static-screen"))) {
- index++;
- dumpStaticScreenStats(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--fences"))) {
- index++;
- mFenceTracker.dump(&result);
- dumpAll = false;
- }
- }
-
- if (dumpAll) {
- dumpAllLocked(args, index, result);
- }
-
- if (locked) {
- mStateLock.unlock();
- }
- }
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */,
- size_t& /* index */, String8& result) const
-{
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(currentLayers[i]);
- result.appendFormat("%s\n", layer->getName().string());
- }
-}
-
-void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
- String8& result) const
-{
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
- }
-
- const nsecs_t period =
- getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
- result.appendFormat("%" PRId64 "\n", period);
-
- if (name.isEmpty()) {
- mAnimFrameTracker.dumpStats(result);
- } else {
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(currentLayers[i]);
- if (name == layer->getName()) {
- layer->dumpFrameStats(result);
- }
- }
- }
-}
-
-void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
- String8& /* result */)
-{
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
- }
-
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(currentLayers[i]);
- if (name.isEmpty() || (name == layer->getName())) {
- layer->clearFrameStats();
- }
- }
-
- mAnimFrameTracker.clearStats();
-}
-
-// This should only be called from the main thread. Otherwise it would need
-// the lock and should use mCurrentState rather than mDrawingState.
-void SurfaceFlinger::logFrameStats() {
- const LayerVector& drawingLayers = mDrawingState.layersSortedByZ;
- const size_t count = drawingLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(drawingLayers[i]);
- layer->logFrameStats();
- }
-
- mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
-}
-
-/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
-{
- static const char* config =
- " [sf"
-#ifdef HAS_CONTEXT_PRIORITY
- " HAS_CONTEXT_PRIORITY"
-#endif
-#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
- " NEVER_DEFAULT_TO_ASYNC_MODE"
-#endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
- " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
- "]";
- result.append(config);
-}
-
-void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
-{
- result.appendFormat("Static screen stats:\n");
- for (size_t b = 0; b < NUM_BUCKETS - 1; ++b) {
- float bucketTimeSec = mFrameBuckets[b] / 1e9;
- float percent = 100.0f *
- static_cast<float>(mFrameBuckets[b]) / mTotalTime;
- result.appendFormat(" < %zd frames: %.3f s (%.1f%%)\n",
- b + 1, bucketTimeSec, percent);
- }
- float bucketTimeSec = mFrameBuckets[NUM_BUCKETS - 1] / 1e9;
- float percent = 100.0f *
- static_cast<float>(mFrameBuckets[NUM_BUCKETS - 1]) / mTotalTime;
- result.appendFormat(" %zd+ frames: %.3f s (%.1f%%)\n",
- NUM_BUCKETS - 1, bucketTimeSec, percent);
-}
-
-void SurfaceFlinger::recordBufferingStats(const char* layerName,
- std::vector<OccupancyTracker::Segment>&& history) {
- Mutex::Autolock lock(mBufferingStatsMutex);
- auto& stats = mBufferingStats[layerName];
- for (const auto& segment : history) {
- if (!segment.usedThirdBuffer) {
- stats.twoBufferTime += segment.totalTime;
- }
- if (segment.occupancyAverage < 1.0f) {
- stats.doubleBufferedTime += segment.totalTime;
- } else if (segment.occupancyAverage < 2.0f) {
- stats.tripleBufferedTime += segment.totalTime;
- }
- ++stats.numSegments;
- stats.totalTime += segment.totalTime;
- }
-}
-
-void SurfaceFlinger::dumpBufferingStats(String8& result) const {
- result.append("Buffering stats:\n");
- result.append(" [Layer name] <Active time> <Two buffer> "
- "<Double buffered> <Triple buffered>\n");
- Mutex::Autolock lock(mBufferingStatsMutex);
- typedef std::tuple<std::string, float, float, float> BufferTuple;
- std::map<float, BufferTuple, std::greater<float>> sorted;
- for (const auto& statsPair : mBufferingStats) {
- const char* name = statsPair.first.c_str();
- const BufferingStats& stats = statsPair.second;
- if (stats.numSegments == 0) {
- continue;
- }
- float activeTime = ns2ms(stats.totalTime) / 1000.0f;
- float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
- stats.totalTime;
- float doubleBufferRatio = static_cast<float>(
- stats.doubleBufferedTime) / stats.totalTime;
- float tripleBufferRatio = static_cast<float>(
- stats.tripleBufferedTime) / stats.totalTime;
- sorted.insert({activeTime, {name, twoBufferRatio,
- doubleBufferRatio, tripleBufferRatio}});
- }
- for (const auto& sortedPair : sorted) {
- float activeTime = sortedPair.first;
- const BufferTuple& values = sortedPair.second;
- result.appendFormat(" [%s] %.2f %.3f %.3f %.3f\n",
- std::get<0>(values).c_str(), activeTime,
- std::get<1>(values), std::get<2>(values),
- std::get<3>(values));
- }
- result.append("\n");
-}
-
-void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
- String8& result) const
-{
- bool colorize = false;
- if (index < args.size()
- && (args[index] == String16("--color"))) {
- colorize = true;
- index++;
- }
-
- Colorizer colorizer(colorize);
-
- // figure out if we're stuck somewhere
- const nsecs_t now = systemTime();
- const nsecs_t inSwapBuffers(mDebugInSwapBuffers);
- const nsecs_t inTransaction(mDebugInTransaction);
- nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0;
- nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
-
- /*
- * Dump library configuration.
- */
-
- colorizer.bold(result);
- result.append("Build configuration:");
- colorizer.reset(result);
- appendSfConfigString(result);
- appendUiConfigString(result);
- appendGuiConfigString(result);
- result.append("\n");
-
- colorizer.bold(result);
- result.append("Sync configuration: ");
- colorizer.reset(result);
- result.append(SyncFeatures::getInstance().toString());
- result.append("\n");
-
- colorizer.bold(result);
- result.append("DispSync configuration: ");
- colorizer.reset(result);
- result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, "
- "present offset %d ns (refresh %" PRId64 " ns)",
- vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, PRESENT_TIME_OFFSET_FROM_VSYNC_NS,
- mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY));
- result.append("\n");
-
- // Dump static screen stats
- result.append("\n");
- dumpStaticScreenStats(result);
- result.append("\n");
-
- dumpBufferingStats(result);
-
- /*
- * Dump the visible layer list
- */
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- colorizer.bold(result);
- result.appendFormat("Visible layers (count = %zu)\n", count);
- colorizer.reset(result);
- for (size_t i=0 ; i<count ; i++) {
- const sp<Layer>& layer(currentLayers[i]);
- layer->dump(result, colorizer);
- }
-
- /*
- * Dump Display state
- */
-
- colorizer.bold(result);
- result.appendFormat("Displays (%zu entries)\n", mDisplays.size());
- colorizer.reset(result);
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<const DisplayDevice>& hw(mDisplays[dpy]);
- hw->dump(result);
- }
-
- /*
- * Dump SurfaceFlinger global state
- */
-
- colorizer.bold(result);
- result.append("SurfaceFlinger global state:\n");
- colorizer.reset(result);
-
- HWComposer& hwc(getHwComposer());
- sp<const DisplayDevice> hw(getDefaultDisplayDevice());
-
- colorizer.bold(result);
- result.appendFormat("EGL implementation : %s\n",
- eglQueryStringImplementationANDROID(mEGLDisplay, EGL_VERSION));
- colorizer.reset(result);
- result.appendFormat("%s\n",
- eglQueryStringImplementationANDROID(mEGLDisplay, EGL_EXTENSIONS));
-
- mRenderEngine->dump(result);
-
- hw->undefinedRegion.dump(result, "undefinedRegion");
- result.appendFormat(" orientation=%d, isDisplayOn=%d\n",
- hw->getOrientation(), hw->isDisplayOn());
- result.appendFormat(
- " last eglSwapBuffers() time: %f us\n"
- " last transaction time : %f us\n"
- " transaction-flags : %08x\n"
- " refresh-rate : %f fps\n"
- " x-dpi : %f\n"
- " y-dpi : %f\n"
- " gpu_to_cpu_unsupported : %d\n"
- ,
- mLastSwapBufferTime/1000.0,
- mLastTransactionTime/1000.0,
- mTransactionFlags,
- 1e9 / hwc.getRefreshPeriod(HWC_DISPLAY_PRIMARY),
- hwc.getDpiX(HWC_DISPLAY_PRIMARY),
- hwc.getDpiY(HWC_DISPLAY_PRIMARY),
- !mGpuToCpuSupported);
-
- result.appendFormat(" eglSwapBuffers time: %f us\n",
- inSwapBuffersDuration/1000.0);
-
- result.appendFormat(" transaction time: %f us\n",
- inTransactionDuration/1000.0);
-
- /*
- * VSYNC state
- */
- mEventThread->dump(result);
-
- /*
- * Dump HWComposer state
- */
- colorizer.bold(result);
- result.append("h/w composer state:\n");
- colorizer.reset(result);
- result.appendFormat(" h/w composer %s and %s\n",
- hwc.initCheck()==NO_ERROR ? "present" : "not present",
- (mDebugDisableHWC || mDebugRegion || mDaltonize
- || mHasColorMatrix) ? "disabled" : "enabled");
- hwc.dump(result);
-
- /*
- * Dump gralloc state
- */
- const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
- alloc.dump(result);
-}
-
-const Vector< sp<Layer> >&
-SurfaceFlinger::getLayerSortedByZForHwcDisplay(int id) {
- // Note: mStateLock is held here
- wp<IBinder> dpy;
- for (size_t i=0 ; i<mDisplays.size() ; i++) {
- if (mDisplays.valueAt(i)->getHwcDisplayId() == id) {
- dpy = mDisplays.keyAt(i);
- break;
- }
- }
- if (dpy == NULL) {
- ALOGE("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id);
- // Just use the primary display so we have something to return
- dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY);
- }
- return getDisplayDevice(dpy)->getVisibleLayersSortedByZ();
-}
-
-bool SurfaceFlinger::startDdmConnection()
-{
- void* libddmconnection_dso =
- dlopen("libsurfaceflinger_ddmconnection.so", RTLD_NOW);
- if (!libddmconnection_dso) {
- return false;
- }
- void (*DdmConnection_start)(const char* name);
- DdmConnection_start =
- (decltype(DdmConnection_start))dlsym(libddmconnection_dso, "DdmConnection_start");
- if (!DdmConnection_start) {
- dlclose(libddmconnection_dso);
- return false;
- }
- (*DdmConnection_start)(getServiceName());
- return true;
-}
-
-status_t SurfaceFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case CREATE_CONNECTION:
- case CREATE_DISPLAY:
- case SET_TRANSACTION_STATE:
- case BOOT_FINISHED:
- case CLEAR_ANIMATION_FRAME_STATS:
- case GET_ANIMATION_FRAME_STATS:
- case SET_POWER_MODE:
- case GET_HDR_CAPABILITIES:
- {
- // codes that require permission check
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&
- !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
- ALOGE("Permission Denial: "
- "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- break;
- }
- case CAPTURE_SCREEN:
- {
- // codes that require permission check
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS) &&
- !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
- ALOGE("Permission Denial: "
- "can't read framebuffer pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- break;
- }
- }
-
- status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
- if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- if (CC_UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) {
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- ALOGE("Permission Denial: "
- "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- int n;
- switch (code) {
- case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
- case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
- return NO_ERROR;
- case 1002: // SHOW_UPDATES
- n = data.readInt32();
- mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
- invalidateHwcGeometry();
- repaintEverything();
- return NO_ERROR;
- case 1004:{ // repaint everything
- repaintEverything();
- return NO_ERROR;
- }
- case 1005:{ // force transaction
- setTransactionFlags(
- eTransactionNeeded|
- eDisplayTransactionNeeded|
- eTraversalNeeded);
- return NO_ERROR;
- }
- case 1006:{ // send empty update
- signalRefresh();
- return NO_ERROR;
- }
- case 1008: // toggle use of hw composer
- n = data.readInt32();
- mDebugDisableHWC = n ? 1 : 0;
- invalidateHwcGeometry();
- repaintEverything();
- return NO_ERROR;
- case 1009: // toggle use of transform hint
- n = data.readInt32();
- mDebugDisableTransformHint = n ? 1 : 0;
- invalidateHwcGeometry();
- repaintEverything();
- return NO_ERROR;
- case 1010: // interrogate.
- reply->writeInt32(0);
- reply->writeInt32(0);
- reply->writeInt32(mDebugRegion);
- reply->writeInt32(0);
- reply->writeInt32(mDebugDisableHWC);
- return NO_ERROR;
- case 1013: {
- Mutex::Autolock _l(mStateLock);
- sp<const DisplayDevice> hw(getDefaultDisplayDevice());
- reply->writeInt32(hw->getPageFlipCount());
- return NO_ERROR;
- }
- case 1014: {
- // daltonize
- n = data.readInt32();
- switch (n % 10) {
- case 1:
- mDaltonizer.setType(ColorBlindnessType::Protanomaly);
- break;
- case 2:
- mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
- break;
- case 3:
- mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
- break;
- }
- if (n >= 10) {
- mDaltonizer.setMode(ColorBlindnessMode::Correction);
- } else {
- mDaltonizer.setMode(ColorBlindnessMode::Simulation);
- }
- mDaltonize = n > 0;
- invalidateHwcGeometry();
- repaintEverything();
- return NO_ERROR;
- }
- case 1015: {
- // apply a color matrix
- n = data.readInt32();
- mHasColorMatrix = n ? 1 : 0;
- if (n) {
- // color matrix is sent as mat3 matrix followed by vec3
- // offset, then packed into a mat4 where the last row is
- // the offset and extra values are 0
- for (size_t i = 0 ; i < 4; i++) {
- for (size_t j = 0; j < 4; j++) {
- mColorMatrix[i][j] = data.readFloat();
- }
- }
- } else {
- mColorMatrix = mat4();
- }
- invalidateHwcGeometry();
- repaintEverything();
- return NO_ERROR;
- }
- // This is an experimental interface
- // Needs to be shifted to proper binder interface when we productize
- case 1016: {
- n = data.readInt32();
- mPrimaryDispSync.setRefreshSkipCount(n);
- return NO_ERROR;
- }
- case 1017: {
- n = data.readInt32();
- mForceFullDamage = static_cast<bool>(n);
- return NO_ERROR;
- }
- case 1018: { // Modify Choreographer's phase offset
- n = data.readInt32();
- mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
- return NO_ERROR;
- }
- case 1019: { // Modify SurfaceFlinger's phase offset
- n = data.readInt32();
- mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
- return NO_ERROR;
- }
- case 1021: { // Disable HWC virtual displays
- n = data.readInt32();
- mUseHwcVirtualDisplays = !n;
- return NO_ERROR;
- }
- }
- }
- return err;
-}
-
-void SurfaceFlinger::repaintEverything() {
- android_atomic_or(1, &mRepaintEverything);
- signalTransaction();
-}
-
-// ---------------------------------------------------------------------------
-// Capture screen into an IGraphiBufferProducer
-// ---------------------------------------------------------------------------
-
-/* The code below is here to handle b/8734824
- *
- * We create a IGraphicBufferProducer wrapper that forwards all calls
- * from the surfaceflinger thread to the calling binder thread, where they
- * are executed. This allows the calling thread in the calling process to be
- * reused and not depend on having "enough" binder threads to handle the
- * requests.
- */
-class GraphicProducerWrapper : public BBinder, public MessageHandler {
- /* Parts of GraphicProducerWrapper are run on two different threads,
- * communicating by sending messages via Looper but also by shared member
- * data. Coherence maintenance is subtle and in places implicit (ugh).
- *
- * Don't rely on Looper's sendMessage/handleMessage providing
- * release/acquire semantics for any data not actually in the Message.
- * Data going from surfaceflinger to binder threads needs to be
- * synchronized explicitly.
- *
- * Barrier open/wait do provide release/acquire semantics. This provides
- * implicit synchronization for data coming back from binder to
- * surfaceflinger threads.
- */
-
- sp<IGraphicBufferProducer> impl;
- sp<Looper> looper;
- status_t result;
- bool exitPending;
- bool exitRequested;
- Barrier barrier;
- uint32_t code;
- Parcel const* data;
- Parcel* reply;
-
- enum {
- MSG_API_CALL,
- MSG_EXIT
- };
-
- /*
- * Called on surfaceflinger thread. This is called by our "fake"
- * BpGraphicBufferProducer. We package the data and reply Parcel and
- * forward them to the binder thread.
- */
- virtual status_t transact(uint32_t code,
- const Parcel& data, Parcel* reply, uint32_t /* flags */) {
- this->code = code;
- this->data = &data;
- this->reply = reply;
- if (exitPending) {
- // if we've exited, we run the message synchronously right here.
- // note (JH): as far as I can tell from looking at the code, this
- // never actually happens. if it does, i'm not sure if it happens
- // on the surfaceflinger or binder thread.
- handleMessage(Message(MSG_API_CALL));
- } else {
- barrier.close();
- // Prevent stores to this->{code, data, reply} from being
- // reordered later than the construction of Message.
- atomic_thread_fence(memory_order_release);
- looper->sendMessage(this, Message(MSG_API_CALL));
- barrier.wait();
- }
- return result;
- }
-
- /*
- * here we run on the binder thread. All we've got to do is
- * call the real BpGraphicBufferProducer.
- */
- virtual void handleMessage(const Message& message) {
- int what = message.what;
- // Prevent reads below from happening before the read from Message
- atomic_thread_fence(memory_order_acquire);
- if (what == MSG_API_CALL) {
- result = IInterface::asBinder(impl)->transact(code, data[0], reply);
- barrier.open();
- } else if (what == MSG_EXIT) {
- exitRequested = true;
- }
- }
-
-public:
- GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
- : impl(impl),
- looper(new Looper(true)),
- result(NO_ERROR),
- exitPending(false),
- exitRequested(false),
- code(0),
- data(NULL),
- reply(NULL)
- {}
-
- // Binder thread
- status_t waitForResponse() {
- do {
- looper->pollOnce(-1);
- } while (!exitRequested);
- return result;
- }
-
- // Client thread
- void exit(status_t result) {
- this->result = result;
- exitPending = true;
- // Ensure this->result is visible to the binder thread before it
- // handles the message.
- atomic_thread_fence(memory_order_release);
- looper->sendMessage(this, Message(MSG_EXIT));
- }
-};
-
-
-status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
- bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
-
- if (CC_UNLIKELY(display == 0))
- return BAD_VALUE;
-
- if (CC_UNLIKELY(producer == 0))
- return BAD_VALUE;
-
- // if we have secure windows on this display, never allow the screen capture
- // unless the producer interface is local (i.e.: we can take a screenshot for
- // ourselves).
- bool isLocalScreenshot = IInterface::asBinder(producer)->localBinder();
-
- // Convert to surfaceflinger's internal rotation type.
- Transform::orientation_flags rotationFlags;
- switch (rotation) {
- case ISurfaceComposer::eRotateNone:
- rotationFlags = Transform::ROT_0;
- break;
- case ISurfaceComposer::eRotate90:
- rotationFlags = Transform::ROT_90;
- break;
- case ISurfaceComposer::eRotate180:
- rotationFlags = Transform::ROT_180;
- break;
- case ISurfaceComposer::eRotate270:
- rotationFlags = Transform::ROT_270;
- break;
- default:
- rotationFlags = Transform::ROT_0;
- ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
- break;
- }
-
- class MessageCaptureScreen : public MessageBase {
- SurfaceFlinger* flinger;
- sp<IBinder> display;
- sp<IGraphicBufferProducer> producer;
- Rect sourceCrop;
- uint32_t reqWidth, reqHeight;
- uint32_t minLayerZ,maxLayerZ;
- bool useIdentityTransform;
- Transform::orientation_flags rotation;
- status_t result;
- bool isLocalScreenshot;
- public:
- MessageCaptureScreen(SurfaceFlinger* flinger,
- const sp<IBinder>& display,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
- bool useIdentityTransform,
- Transform::orientation_flags rotation,
- bool isLocalScreenshot)
- : flinger(flinger), display(display), producer(producer),
- sourceCrop(sourceCrop), reqWidth(reqWidth), reqHeight(reqHeight),
- minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
- useIdentityTransform(useIdentityTransform),
- rotation(rotation), result(PERMISSION_DENIED),
- isLocalScreenshot(isLocalScreenshot)
- {
- }
- status_t getResult() const {
- return result;
- }
- virtual bool handler() {
- Mutex::Autolock _l(flinger->mStateLock);
- sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
- result = flinger->captureScreenImplLocked(hw, producer,
- sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
- useIdentityTransform, rotation, isLocalScreenshot);
- static_cast<GraphicProducerWrapper*>(IInterface::asBinder(producer).get())->exit(result);
- return true;
- }
- };
-
- // this creates a "fake" BBinder which will serve as a "fake" remote
- // binder to receive the marshaled calls and forward them to the
- // real remote (a BpGraphicBufferProducer)
- sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer);
-
- // the asInterface() call below creates our "fake" BpGraphicBufferProducer
- // which does the marshaling work forwards to our "fake remote" above.
- sp<MessageBase> msg = new MessageCaptureScreen(this,
- display, IGraphicBufferProducer::asInterface( wrapper ),
- sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
- useIdentityTransform, rotationFlags, isLocalScreenshot);
-
- status_t res = postMessageAsync(msg);
- if (res == NO_ERROR) {
- res = wrapper->waitForResponse();
- }
- return res;
-}
-
-
-void SurfaceFlinger::renderScreenImplLocked(
- const sp<const DisplayDevice>& hw,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
- bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation)
-{
- ATRACE_CALL();
- RenderEngine& engine(getRenderEngine());
-
- // get screen geometry
- const int32_t hw_w = hw->getWidth();
- const int32_t hw_h = hw->getHeight();
- const bool filtering = static_cast<int32_t>(reqWidth) != hw_w ||
- static_cast<int32_t>(reqHeight) != hw_h;
-
- // if a default or invalid sourceCrop is passed in, set reasonable values
- if (sourceCrop.width() == 0 || sourceCrop.height() == 0 ||
- !sourceCrop.isValid()) {
- sourceCrop.setLeftTop(Point(0, 0));
- sourceCrop.setRightBottom(Point(hw_w, hw_h));
- }
-
- // ensure that sourceCrop is inside screen
- if (sourceCrop.left < 0) {
- ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left);
- }
- if (sourceCrop.right > hw_w) {
- ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, hw_w);
- }
- if (sourceCrop.top < 0) {
- ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top);
- }
- if (sourceCrop.bottom > hw_h) {
- ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, hw_h);
- }
-
- // make sure to clear all GL error flags
- engine.checkErrors();
-
- // set-up our viewport
- engine.setViewportAndProjection(
- reqWidth, reqHeight, sourceCrop, hw_h, yswap, rotation);
- engine.disableTexturing();
-
- // redraw the screen entirely...
- engine.clearWithColor(0, 0, 0, 1);
-
- const LayerVector& layers( mDrawingState.layersSortedByZ );
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<Layer>& layer(layers[i]);
- const Layer::State& state(layer->getDrawingState());
- if (state.layerStack == hw->getLayerStack()) {
- if (state.z >= minLayerZ && state.z <= maxLayerZ) {
- if (layer->isVisible()) {
- if (filtering) layer->setFiltering(true);
- layer->draw(hw, useIdentityTransform);
- if (filtering) layer->setFiltering(false);
- }
- }
- }
- }
-
- // compositionComplete is needed for older driver
- hw->compositionComplete();
- hw->setViewportAndProjection();
-}
-
-
-status_t SurfaceFlinger::captureScreenImplLocked(
- const sp<const DisplayDevice>& hw,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
- bool useIdentityTransform, Transform::orientation_flags rotation,
- bool isLocalScreenshot)
-{
- ATRACE_CALL();
-
- // get screen geometry
- uint32_t hw_w = hw->getWidth();
- uint32_t hw_h = hw->getHeight();
-
- if (rotation & Transform::ROT_90) {
- std::swap(hw_w, hw_h);
- }
-
- if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
- ALOGE("size mismatch (%d, %d) > (%d, %d)",
- reqWidth, reqHeight, hw_w, hw_h);
- return BAD_VALUE;
- }
-
- reqWidth = (!reqWidth) ? hw_w : reqWidth;
- reqHeight = (!reqHeight) ? hw_h : reqHeight;
-
- bool secureLayerIsVisible = false;
- const LayerVector& layers(mDrawingState.layersSortedByZ);
- const size_t count = layers.size();
- for (size_t i = 0 ; i < count ; ++i) {
- const sp<Layer>& layer(layers[i]);
- const Layer::State& state(layer->getDrawingState());
- if (state.layerStack == hw->getLayerStack() && state.z >= minLayerZ &&
- state.z <= maxLayerZ && layer->isVisible() &&
- layer->isSecure()) {
- secureLayerIsVisible = true;
- }
- }
-
- if (!isLocalScreenshot && secureLayerIsVisible) {
- ALOGW("FB is protected: PERMISSION_DENIED");
- return PERMISSION_DENIED;
- }
-
- // create a surface (because we're a producer, and we need to
- // dequeue/queue a buffer)
- sp<Surface> sur = new Surface(producer, false);
- ANativeWindow* window = sur.get();
-
- status_t result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
- if (result == NO_ERROR) {
- uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-
- int err = 0;
- err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
- err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
- err |= native_window_set_usage(window, usage);
-
- if (err == NO_ERROR) {
- ANativeWindowBuffer* buffer;
- /* TODO: Once we have the sync framework everywhere this can use
- * server-side waits on the fence that dequeueBuffer returns.
- */
- result = native_window_dequeue_buffer_and_wait(window, &buffer);
- if (result == NO_ERROR) {
- int syncFd = -1;
- // create an EGLImage from the buffer so we can later
- // turn it into a texture
- EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT,
- EGL_NATIVE_BUFFER_ANDROID, buffer, NULL);
- if (image != EGL_NO_IMAGE_KHR) {
- // this binds the given EGLImage as a framebuffer for the
- // duration of this scope.
- RenderEngine::BindImageAsFramebuffer imageBond(getRenderEngine(), image);
- if (imageBond.getStatus() == NO_ERROR) {
- // this will in fact render into our dequeued buffer
- // via an FBO, which means we didn't have to create
- // an EGLSurface and therefore we're not
- // dependent on the context's EGLConfig.
- renderScreenImplLocked(
- hw, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, true,
- useIdentityTransform, rotation);
-
- // Attempt to create a sync khr object that can produce a sync point. If that
- // isn't available, create a non-dupable sync object in the fallback path and
- // wait on it directly.
- EGLSyncKHR sync;
- if (!DEBUG_SCREENSHOTS) {
- sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
- // native fence fd will not be populated until flush() is done.
- getRenderEngine().flush();
- } else {
- sync = EGL_NO_SYNC_KHR;
- }
- if (sync != EGL_NO_SYNC_KHR) {
- // get the sync fd
- syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync);
- if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- ALOGW("captureScreen: failed to dup sync khr object");
- syncFd = -1;
- }
- eglDestroySyncKHR(mEGLDisplay, sync);
- } else {
- // fallback path
- sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
- if (sync != EGL_NO_SYNC_KHR) {
- EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync,
- EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 2000000000 /*2 sec*/);
- EGLint eglErr = eglGetError();
- if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGW("captureScreen: fence wait timed out");
- } else {
- ALOGW_IF(eglErr != EGL_SUCCESS,
- "captureScreen: error waiting on EGL fence: %#x", eglErr);
- }
- eglDestroySyncKHR(mEGLDisplay, sync);
- } else {
- ALOGW("captureScreen: error creating EGL fence: %#x", eglGetError());
- }
- }
- if (DEBUG_SCREENSHOTS) {
- uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
- getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
- checkScreenshot(reqWidth, reqHeight, reqWidth, pixels,
- hw, minLayerZ, maxLayerZ);
- delete [] pixels;
- }
-
- } else {
- ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
- result = INVALID_OPERATION;
- window->cancelBuffer(window, buffer, syncFd);
- buffer = NULL;
- }
- // destroy our image
- eglDestroyImageKHR(mEGLDisplay, image);
- } else {
- result = BAD_VALUE;
- }
- if (buffer) {
- // queueBuffer takes ownership of syncFd
- result = window->queueBuffer(window, buffer, syncFd);
- }
- }
- } else {
- result = BAD_VALUE;
- }
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- }
-
- return result;
-}
-
-bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
- uint64_t frameNumber, FrameTimestamps* outTimestamps) {
- return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
-}
-
-void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
- const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
- if (DEBUG_SCREENSHOTS) {
- for (size_t y=0 ; y<h ; y++) {
- uint32_t const * p = (uint32_t const *)vaddr + y*s;
- for (size_t x=0 ; x<w ; x++) {
- if (p[x] != 0xFF000000) return;
- }
- }
- ALOGE("*** we just took a black screenshot ***\n"
- "requested minz=%d, maxz=%d, layerStack=%d",
- minLayerZ, maxLayerZ, hw->getLayerStack());
- const LayerVector& layers( mDrawingState.layersSortedByZ );
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<Layer>& layer(layers[i]);
- const Layer::State& state(layer->getDrawingState());
- const bool visible = (state.layerStack == hw->getLayerStack())
- && (state.z >= minLayerZ && state.z <= maxLayerZ)
- && (layer->isVisible());
- ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
- visible ? '+' : '-',
- i, layer->getName().string(), state.layerStack, state.z,
- layer->isVisible(), state.flags, state.alpha);
- }
- }
-}
-
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::LayerVector::LayerVector() {
-}
-
-SurfaceFlinger::LayerVector::LayerVector(const LayerVector& rhs)
- : SortedVector<sp<Layer> >(rhs) {
-}
-
-int SurfaceFlinger::LayerVector::do_compare(const void* lhs,
- const void* rhs) const
-{
- // sort layers per layer-stack, then by z-order and finally by sequence
- const sp<Layer>& l(*reinterpret_cast<const sp<Layer>*>(lhs));
- const sp<Layer>& r(*reinterpret_cast<const sp<Layer>*>(rhs));
-
- uint32_t ls = l->getCurrentState().layerStack;
- uint32_t rs = r->getCurrentState().layerStack;
- if (ls != rs)
- return ls - rs;
-
- uint32_t lz = l->getCurrentState().z;
- uint32_t rz = r->getCurrentState().z;
- if (lz != rz)
- return lz - rz;
-
- 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
-
-
-#if defined(__gl_h_)
-#error "don't include gl/gl.h in this file"
-#endif
-
-#if defined(__gl2_h_)
-#error "don't include gl2/gl2.h in this file"
-#endif
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..e5dffe5 100644
--- a/services/surfaceflinger/tests/Android.mk
+++ b/services/surfaceflinger/tests/Android.mk
@@ -3,21 +3,29 @@
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CFLAGS := -std=c++14
+
LOCAL_MODULE := SurfaceFlinger_test
LOCAL_MODULE_TAGS := tests
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);