Merge changes Ic746ea89,Iebaa8063 am: 950e3f5696 am: f1d87ca9b8 am: 4c00ce0e6a
am: d06bc6a970
Change-Id: I6849956cd847b61b062e4f01cc7221e2be8dfbd1
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 320d11d..0fc03e6 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -121,6 +121,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" },
@@ -370,7 +381,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;
@@ -1027,7 +1038,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"
@@ -1202,7 +1213,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 747cc69..54ba5ca 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -54,6 +54,15 @@
chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_lock/enable
chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_locked/enable
chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_write/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_result/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_reply/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_read/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_write/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_result/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_reply/enable
# Tracing disabled by default
write /sys/kernel/debug/tracing/tracing_on 0
diff --git a/cmds/cmd/Android.mk b/cmds/cmd/Android.mk
index ac2f4c0..d565e57 100644
--- a/cmds/cmd/Android.mk
+++ b/cmds/cmd/Android.mk
@@ -7,8 +7,11 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
liblog \
+ libselinux \
libbinder
-
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE)
ifeq ($(TARGET_OS),linux)
LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index ed740d3..73d274f 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -21,7 +21,10 @@
#include <binder/ProcessState.h>
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
#include <binder/TextOutput.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
#include <utils/Vector.h>
#include <getopt.h>
@@ -29,7 +32,16 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <sys/time.h>
+#include <errno.h>
+
+#include "selinux/selinux.h"
+#include "selinux/android.h"
+
+#include <UniquePtr.h>
+
+#define DEBUG 0
using namespace android;
@@ -38,10 +50,72 @@
return lhs->compare(*rhs);
}
+struct SecurityContext_Delete {
+ void operator()(security_context_t p) const {
+ freecon(p);
+ }
+};
+typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
+
+class MyShellCallback : public BnShellCallback
+{
+public:
+ bool mActive = true;
+
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+ String8 path8(path);
+ char cwd[256];
+ getcwd(cwd, 256);
+ String8 fullPath(cwd);
+ fullPath.appendPath(path8);
+ if (!mActive) {
+ aerr << "Open attempt after active for: " << fullPath << endl;
+ return -EPERM;
+ }
+ int fd = open(fullPath.string(), O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG);
+ if (fd < 0) {
+ return fd;
+ }
+ if (is_selinux_enabled() && seLinuxContext.size() > 0) {
+ String8 seLinuxContext8(seLinuxContext);
+ security_context_t tmp = NULL;
+ int ret = getfilecon(fullPath.string(), &tmp);
+ Unique_SecurityContext context(tmp);
+ int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(),
+ "file", "write", NULL);
+ if (accessGranted != 0) {
+ close(fd);
+ aerr << "System server has no access to file context " << context.get()
+ << " (from path " << fullPath.string() << ", context "
+ << seLinuxContext8.string() << ")" << endl;
+ return -EPERM;
+ }
+ }
+ return fd;
+ }
+};
+
class MyResultReceiver : public BnResultReceiver
{
public:
- virtual void send(int32_t /*resultCode*/) {
+ Mutex mMutex;
+ Condition mCondition;
+ bool mHaveResult = false;
+ int32_t mResult = 0;
+
+ virtual void send(int32_t resultCode) {
+ AutoMutex _l(mMutex);
+ mResult = resultCode;
+ mHaveResult = true;
+ mCondition.signal();
+ }
+
+ int32_t waitForResult() {
+ AutoMutex _l(mMutex);
+ while (!mHaveResult) {
+ mCondition.wait(mMutex);
+ }
+ return mResult;
}
};
@@ -54,13 +128,13 @@
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == NULL) {
- ALOGE("Unable to get default service manager!");
+ ALOGW("Unable to get default service manager!");
aerr << "cmd: Unable to get default service manager!" << endl;
return 20;
}
if (argc == 1) {
- aout << "cmd: no service specified; use -l to list all services" << endl;
+ aerr << "cmd: No service specified; use -l to list all services" << endl;
return 20;
}
@@ -85,12 +159,41 @@
String16 cmd = String16(argv[1]);
sp<IBinder> service = sm->checkService(cmd);
if (service == NULL) {
- aerr << "Can't find service: " << argv[1] << endl;
+ ALOGW("Can't find service %s", argv[1]);
+ aerr << "cmd: Can't find service: " << argv[1] << endl;
return 20;
}
+ sp<MyShellCallback> cb = new MyShellCallback();
+ sp<MyResultReceiver> result = new MyResultReceiver();
+
+#if DEBUG
+ ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", argv[1], STDIN_FILENO, STDOUT_FILENO,
+ STDERR_FILENO);
+#endif
+
// TODO: block until a result is returned to MyResultReceiver.
- IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
- new MyResultReceiver());
- return 0;
+ status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
+ cb, result);
+ if (err < 0) {
+ const char* errstr;
+ switch (err) {
+ case BAD_TYPE: errstr = "Bad type"; break;
+ case FAILED_TRANSACTION: errstr = "Failed transaction"; break;
+ case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;
+ case UNEXPECTED_NULL: errstr = "Unexpected null"; break;
+ default: errstr = strerror(-err); break;
+ }
+ ALOGW("Failure calling service %s: %s (%d)", argv[1], errstr, -err);
+ aout << "cmd: Failure calling service " << argv[1] << ": " << errstr << " ("
+ << (-err) << ")" << endl;
+ return err;
+ }
+
+ cb->mActive = false;
+ status_t res = result->waitForResult();
+#if DEBUG
+ ALOGD("result=%d", (int)res);
+#endif
+ return res;
}
diff --git a/cmds/dumpstate/.clang-format b/cmds/dumpstate/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/dumpstate/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
deleted file mode 100644
index 3db41c2..0000000
--- a/cmds/dumpstate/Android.bp
+++ /dev/null
@@ -1,4 +0,0 @@
-cc_library_static {
- name: "libdumpstate.default",
- srcs: ["libdumpstate_default.cpp"],
-}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 0433f31..99024c3 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,20 +1,181 @@
LOCAL_PATH:= $(call my-dir)
+# ================#
+# Common settings #
+# ================#
+# ZipArchive support, the order matters here to get all symbols.
+COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto
+
+# TODO: ideally the tests should depend on a shared dumpstate library, but currently libdumpstate
+# is used to define the device-specific HAL library. Instead, both dumpstate and dumpstate_test
+# shares a lot of common settings
+COMMON_LOCAL_CFLAGS := \
+ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+COMMON_SRC_FILES := \
+ DumpstateInternal.cpp \
+ utils.cpp
+COMMON_SHARED_LIBRARIES := \
+ android.hardware.dumpstate@1.0 \
+ libhidlbase \
+ libbase \
+ libbinder \
+ libcutils \
+ libdumpstateaidl \
+ libdumpstateutil \
+ liblog \
+ libselinux \
+ libutils \
+ $(COMMON_ZIP_LIBRARIES)
+
+# ====================#
+# libdumpstateutil #
+# ====================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdumpstateutil
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_SRC_FILES := \
+ DumpstateInternal.cpp \
+ DumpstateUtil.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ====================#
+# libdumpstateheaders #
+# ====================#
+# TODO: this module is necessary so the device-specific libdumpstate implementations do not
+# need to add any other dependency (like libbase). Should go away once dumpstate HAL changes.
+include $(CLEAR_VARS)
+
+LOCAL_EXPORT_C_INCLUDE_DIRS = $(LOCAL_PATH)
+LOCAL_MODULE := libdumpstateheaders
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+ $(COMMON_SHARED_LIBRARIES)
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
+ $(COMMON_STATIC_LIBRARIES)
+# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
+LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
+LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ================ #
+# libdumpstateaidl #
+# =================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdumpstateaidl
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libutils
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/binder
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_SRC_FILES := \
+ binder/android/os/IDumpstate.aidl \
+ binder/android/os/IDumpstateListener.aidl \
+ binder/android/os/IDumpstateToken.aidl
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ==========#
+# dumpstate #
+# ==========#
include $(CLEAR_VARS)
ifdef BOARD_WLAN_DEVICE
LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
endif
-LOCAL_SRC_FILES := dumpstate.cpp utils.cpp
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+ DumpstateService.cpp \
+ dumpstate.cpp
LOCAL_MODULE := dumpstate
-LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux libbase
-# ZipArchive support, the order matters here to get all symbols.
-LOCAL_STATIC_LIBRARIES := libziparchive libz libcrypto_static
-LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES) \
+ android.hardware.vibrator@1.0
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES)
+
+LOCAL_CFLAGS += $(COMMON_LOCAL_CFLAGS)
+
LOCAL_INIT_RC := dumpstate.rc
include $(BUILD_EXECUTABLE)
+
+# ===============#
+# dumpstate_test #
+# ===============#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpstate_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+ DumpstateService.cpp \
+ tests/dumpstate_test.cpp
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES) \
+ libgmock
+
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
+
+include $(BUILD_NATIVE_TEST)
+
+# =======================#
+# dumpstate_test_fixture #
+# =======================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpstate_test_fixture
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_SRC_FILES := \
+ tests/dumpstate_test_fixture.cpp
+
+LOCAL_MODULE_CLASS := NATIVE_TESTS
+
+dumpstate_tests_intermediates := $(local-intermediates-dir)/DATA
+dumpstate_tests_subpath_from_data := nativetest/dumpstate_test_fixture
+dumpstate_tests_root_in_device := /data/$(dumpstate_tests_subpath_from_data)
+dumpstate_tests_root_for_test_zip := $(dumpstate_tests_intermediates)/$(dumpstate_tests_subpath_from_data)
+testdata_files := $(call find-subdir-files, testdata/*)
+
+# Copy test data files to intermediates/DATA for use with LOCAL_PICKUP_FILES
+GEN := $(addprefix $(dumpstate_tests_root_for_test_zip)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(dumpstate_tests_root_for_test_zip)/testdata/% : $(LOCAL_PATH)/testdata/%
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+# Copy test data files again to $OUT/data so the tests can be run with adb sync
+# TODO: the build system should do this automatically
+GEN := $(addprefix $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/testdata/% : $(LOCAL_PATH)/testdata/%
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+LOCAL_PICKUP_FILES := $(dumpstate_tests_intermediates)
+
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
new file mode 100644
index 0000000..0343277
--- /dev/null
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateInternal.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+
+uint64_t Nanotime() {
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec);
+}
+
+// Switches to non-root user and group.
+bool DropRootUser() {
+ if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
+ MYLOGD("drop_root_user(): already running as Shell\n");
+ return true;
+ }
+ /* ensure we will keep capabilities when we drop root */
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ gid_t groups[] = {AID_LOG, AID_SDCARD_R, AID_SDCARD_RW, AID_MOUNT,
+ AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_BLUETOOTH};
+ if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
+ MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
+ return false;
+ }
+ if (setgid(AID_SHELL) != 0) {
+ MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+ return false;
+ }
+ if (setuid(AID_SHELL) != 0) {
+ MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+ return false;
+ }
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ MYLOGE("capset failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
+ bool dry_run) {
+ const char* path = path_string.c_str();
+ if (!title.empty()) {
+ dprintf(out_fd, "------ %s (%s", title.c_str(), path);
+
+ struct stat st;
+ // Only show the modification time of non-device files.
+ size_t path_len = strlen(path);
+ if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
+ (path_len < 5 || memcmp(path, "/sys/", 5)) &&
+ (path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) {
+ char stamp[80];
+ time_t mtime = st.st_mtime;
+ strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
+ dprintf(out_fd, ": %s", stamp);
+ }
+ dprintf(out_fd, ") ------\n");
+ fsync(out_fd);
+ }
+ if (dry_run) {
+ if (out_fd != STDOUT_FILENO) {
+ // There is no title, but we should still print a dry-run message
+ dprintf(out_fd, "%s: skipped on dry run\n", path);
+ } else if (!title.empty()) {
+ dprintf(out_fd, "\t(skipped on dry run)\n");
+ }
+ fsync(out_fd);
+ return 0;
+ }
+ bool newline = false;
+ fd_set read_set;
+ timeval tm;
+ while (true) {
+ FD_ZERO(&read_set);
+ FD_SET(fd, &read_set);
+ /* Timeout if no data is read for 30 seconds. */
+ tm.tv_sec = 30;
+ tm.tv_usec = 0;
+ uint64_t elapsed = Nanotime();
+ int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, nullptr, nullptr, &tm));
+ if (ret == -1) {
+ dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
+ newline = true;
+ break;
+ } else if (ret == 0) {
+ elapsed = Nanotime() - elapsed;
+ dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
+ newline = true;
+ break;
+ } else {
+ char buffer[65536];
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
+ if (bytes_read > 0) {
+ android::base::WriteFully(out_fd, buffer, bytes_read);
+ newline = (buffer[bytes_read - 1] == '\n');
+ } else {
+ if (bytes_read == -1) {
+ dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
+ newline = true;
+ }
+ break;
+ }
+ }
+ }
+ close(fd);
+
+ if (!newline) dprintf(out_fd, "\n");
+ if (!title.empty()) dprintf(out_fd, "\n");
+ return 0;
+}
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
new file mode 100644
index 0000000..2f7704d
--- /dev/null
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
+
+#include <cstdint>
+#include <string>
+
+// TODO: rename macros to DUMPSTATE_LOGXXX
+#ifndef MYLOGD
+#define MYLOGD(...) \
+ fprintf(stderr, __VA_ARGS__); \
+ ALOGD(__VA_ARGS__);
+#endif
+
+#ifndef MYLOGI
+#define MYLOGI(...) \
+ fprintf(stderr, __VA_ARGS__); \
+ ALOGI(__VA_ARGS__);
+#endif
+
+#ifndef MYLOGE
+#define MYLOGE(...) \
+ fprintf(stderr, __VA_ARGS__); \
+ ALOGE(__VA_ARGS__);
+#endif
+
+// Internal functions used by .cpp files on multiple build targets.
+// TODO: move to android::os::dumpstate::internal namespace
+
+// TODO: use functions from <chrono> instead
+const uint64_t NANOS_PER_SEC = 1000000000;
+uint64_t Nanotime();
+
+// Switches to non-root user and group.
+bool DropRootUser();
+
+// TODO: move to .cpp as static once is not used by utils.cpp anymore.
+int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
+ bool dry_run = false);
+
+#endif // FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
new file mode 100644
index 0000000..5430956
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateService.h"
+
+#include <android-base/stringprintf.h>
+
+#include "android/os/BnDumpstate.h"
+
+#include "DumpstateInternal.h"
+
+namespace android {
+namespace os {
+
+namespace {
+class DumpstateToken : public BnDumpstateToken {};
+}
+
+DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
+}
+
+char const* DumpstateService::getServiceName() {
+ return "dumpstate";
+}
+
+status_t DumpstateService::Start() {
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ status_t ret = BinderService<DumpstateService>::publish();
+ if (ret != android::OK) {
+ return ret;
+ }
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ return android::OK;
+}
+
+binder::Status DumpstateService::setListener(const std::string& name,
+ const sp<IDumpstateListener>& listener,
+ sp<IDumpstateToken>* returned_token) {
+ *returned_token = nullptr;
+ if (name.empty()) {
+ MYLOGE("setListener(): name not set\n");
+ return binder::Status::ok();
+ }
+ if (listener == nullptr) {
+ MYLOGE("setListener(): listener not set\n");
+ return binder::Status::ok();
+ }
+ std::lock_guard<std::mutex> lock(lock_);
+ if (ds_.listener_ != nullptr) {
+ MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str());
+ return binder::Status::ok();
+ }
+
+ ds_.listener_name_ = name;
+ ds_.listener_ = listener;
+ *returned_token = new DumpstateToken();
+
+ return binder::Status::ok();
+}
+
+status_t DumpstateService::dump(int fd, const Vector<String16>&) {
+ dprintf(fd, "id: %d\n", ds_.id_);
+ dprintf(fd, "pid: %d\n", ds_.pid_);
+ dprintf(fd, "update_progress: %s\n", ds_.update_progress_ ? "true" : "false");
+ dprintf(fd, "update_progress_threshold: %d\n", ds_.update_progress_threshold_);
+ dprintf(fd, "last_updated_progress: %d\n", ds_.last_updated_progress_);
+ dprintf(fd, "progress:\n");
+ ds_.progress_->Dump(fd, " ");
+ dprintf(fd, "args: %s\n", ds_.args_.c_str());
+ dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
+ dprintf(fd, "version: %s\n", ds_.version_.c_str());
+ dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str());
+ dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str());
+ dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str());
+ dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str());
+ dprintf(fd, "path: %s\n", ds_.path_.c_str());
+ dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
+ dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str());
+ dprintf(fd, "name: %s\n", ds_.name_.c_str());
+ dprintf(fd, "now: %ld\n", ds_.now_);
+ dprintf(fd, "is_zipping: %s\n", ds_.IsZipping() ? "true" : "false");
+ dprintf(fd, "listener: %s\n", ds_.listener_name_.c_str());
+
+ return NO_ERROR;
+}
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
new file mode 100644
index 0000000..4352d3d
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.h
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_DUMPSTATE_H_
+#define ANDROID_OS_DUMPSTATE_H_
+
+#include <mutex>
+#include <vector>
+
+#include <binder/BinderService.h>
+
+#include "android/os/BnDumpstate.h"
+#include "android/os/BnDumpstateToken.h"
+#include "dumpstate.h"
+
+namespace android {
+namespace os {
+
+class DumpstateService : public BinderService<DumpstateService>, public BnDumpstate {
+ public:
+ DumpstateService();
+
+ static status_t Start();
+ static char const* getServiceName();
+
+ status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
+ sp<IDumpstateToken>* returned_token) override;
+
+ private:
+ Dumpstate& ds_;
+ std::mutex lock_;
+};
+
+} // namespace os
+} // namespace android
+
+#endif // ANDROID_OS_DUMPSTATE_H_
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
new file mode 100644
index 0000000..26702c4
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateUtil.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/log.h>
+
+#include "DumpstateInternal.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+namespace {
+
+static constexpr const char* kSuPath = "/system/xbin/su";
+
+static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
+ sigset_t child_mask, old_mask;
+ sigemptyset(&child_mask);
+ sigaddset(&child_mask, SIGCHLD);
+
+ if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
+ printf("*** sigprocmask failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ timespec ts;
+ ts.tv_sec = timeout_seconds;
+ ts.tv_nsec = 0;
+ int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
+ int saved_errno = errno;
+ // Set the signals back the way they were.
+ if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
+ printf("*** sigprocmask failed: %s\n", strerror(errno));
+ if (ret == 0) {
+ return false;
+ }
+ }
+ if (ret == -1) {
+ errno = saved_errno;
+ if (errno == EAGAIN) {
+ errno = ETIMEDOUT;
+ } else {
+ printf("*** sigtimedwait failed: %s\n", strerror(errno));
+ }
+ return false;
+ }
+
+ pid_t child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid != pid) {
+ if (child_pid != -1) {
+ printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+ } else {
+ printf("*** waitpid failed: %s\n", strerror(errno));
+ }
+ return false;
+ }
+ return true;
+}
+} // unnamed namespace
+
+CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
+CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build();
+
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
+ values.always_ = true;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
+ values.account_mode_ = SU_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
+ values.account_mode_ = DROP_ROOT;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
+ values.output_mode_ = REDIRECT_TO_STDERR;
+ return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
+ const std::string& message) {
+ values.logging_message_ = message;
+ return *this;
+}
+
+CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
+ return CommandOptions(values);
+}
+
+CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
+ : timeout_(timeout),
+ always_(false),
+ account_mode_(DONT_DROP_ROOT),
+ output_mode_(NORMAL_OUTPUT),
+ logging_message_("") {
+}
+
+CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
+}
+
+int64_t CommandOptions::Timeout() const {
+ return values.timeout_;
+}
+
+bool CommandOptions::Always() const {
+ return values.always_;
+}
+
+PrivilegeMode CommandOptions::PrivilegeMode() const {
+ return values.account_mode_;
+}
+
+OutputMode CommandOptions::OutputMode() const {
+ return values.output_mode_;
+}
+
+std::string CommandOptions::LoggingMessage() const {
+ return values.logging_message_;
+}
+
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
+ return CommandOptions::CommandOptionsBuilder(timeout);
+}
+
+std::string PropertiesHelper::build_type_ = "";
+int PropertiesHelper::dry_run_ = -1;
+
+bool PropertiesHelper::IsUserBuild() {
+ if (build_type_.empty()) {
+ build_type_ = android::base::GetProperty("ro.build.type", "user");
+ }
+ return "user" == build_type_;
+}
+
+bool PropertiesHelper::IsDryRun() {
+ if (dry_run_ == -1) {
+ dry_run_ = android::base::GetBoolProperty("dumpstate.dry_run", false) ? 1 : 0;
+ }
+ return dry_run_ == 1;
+}
+
+int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd < 0) {
+ int err = errno;
+ if (title.empty()) {
+ dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
+ } else {
+ dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
+ strerror(err));
+ }
+ fsync(out_fd);
+ return -1;
+ }
+ return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
+}
+
+int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options) {
+ if (full_command.empty()) {
+ MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str());
+ return -1;
+ }
+
+ int size = full_command.size() + 1; // null terminated
+ int starting_index = 0;
+ if (options.PrivilegeMode() == SU_ROOT) {
+ starting_index = 2; // "su" "root"
+ size += starting_index;
+ }
+
+ std::vector<const char*> args;
+ args.resize(size);
+
+ std::string command_string;
+ if (options.PrivilegeMode() == SU_ROOT) {
+ args[0] = kSuPath;
+ command_string += kSuPath;
+ args[1] = "root";
+ command_string += " root ";
+ }
+ for (size_t i = 0; i < full_command.size(); i++) {
+ args[i + starting_index] = full_command[i].data();
+ command_string += args[i + starting_index];
+ if (i != full_command.size() - 1) {
+ command_string += " ";
+ }
+ }
+ args[size - 1] = nullptr;
+
+ const char* command = command_string.c_str();
+
+ if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) {
+ dprintf(fd, "Skipping '%s' on user build.\n", command);
+ return 0;
+ }
+
+ if (!title.empty()) {
+ dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
+ fsync(fd);
+ }
+
+ const std::string& logging_message = options.LoggingMessage();
+ if (!logging_message.empty()) {
+ MYLOGI(logging_message.c_str(), command_string.c_str());
+ }
+
+ bool silent = (options.OutputMode() == REDIRECT_TO_STDERR);
+ bool redirecting_to_fd = STDOUT_FILENO != fd;
+
+ if (PropertiesHelper::IsDryRun() && !options.Always()) {
+ if (!title.empty()) {
+ dprintf(fd, "\t(skipped on dry run)\n");
+ } else if (redirecting_to_fd) {
+ // There is no title, but we should still print a dry-run message
+ dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
+ }
+ fsync(fd);
+ return 0;
+ }
+
+ const char* path = args[0];
+
+ uint64_t start = Nanotime();
+ pid_t pid = fork();
+
+ /* handle error case */
+ if (pid < 0) {
+ if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno));
+ MYLOGE("*** fork: %s\n", strerror(errno));
+ return pid;
+ }
+
+ /* handle child case */
+ if (pid == 0) {
+ if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) {
+ if (!silent) {
+ dprintf(fd, "*** failed to drop root before running %s: %s\n", command,
+ strerror(errno));
+ }
+ MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
+ return -1;
+ }
+
+ if (silent) {
+ // Redirects stdout to stderr
+ TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
+ } else if (redirecting_to_fd) {
+ // Redirect stdout to fd
+ TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
+ close(fd);
+ }
+
+ /* make sure the child dies when dumpstate dies */
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+
+ /* just ignore SIGPIPE, will go down with parent's */
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sigact, NULL);
+
+ execvp(path, (char**)args.data());
+ // execvp's result will be handled after waitpid_with_timeout() below, but
+ // if it failed, it's safer to exit dumpstate.
+ MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
+ // Must call _exit (instead of exit), otherwise it will corrupt the zip
+ // file.
+ _exit(EXIT_FAILURE);
+ }
+
+ /* handle parent case */
+ int status;
+ bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
+ fsync(fd);
+
+ uint64_t elapsed = Nanotime() - start;
+ if (!ret) {
+ if (errno == ETIMEDOUT) {
+ if (!silent)
+ dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ } else {
+ if (!silent)
+ dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
+ static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+ }
+ kill(pid, SIGTERM);
+ if (!waitpid_with_timeout(pid, 5, nullptr)) {
+ kill(pid, SIGKILL);
+ if (!waitpid_with_timeout(pid, 5, nullptr)) {
+ if (!silent)
+ dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n",
+ command, pid);
+ MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
+ }
+ }
+ return -1;
+ }
+
+ if (WIFSIGNALED(status)) {
+ if (!silent)
+ dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
+ MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
+ } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
+ status = WEXITSTATUS(status);
+ if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status);
+ MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
+ }
+
+ return status;
+}
+
+int GetPidByName(const std::string& ps_name) {
+ DIR* proc_dir;
+ struct dirent* ps;
+ unsigned int pid;
+ std::string cmdline;
+
+ if (!(proc_dir = opendir("/proc"))) {
+ MYLOGE("Can't open /proc\n");
+ return -1;
+ }
+
+ while ((ps = readdir(proc_dir))) {
+ if (!(pid = atoi(ps->d_name))) {
+ continue;
+ }
+ android::base::ReadFileToString("/proc/" + std::string(ps->d_name) + "/cmdline", &cmdline);
+ if (cmdline.find(ps_name) == std::string::npos) {
+ continue;
+ } else {
+ closedir(proc_dir);
+ return pid;
+ }
+ }
+ MYLOGE("can't find the pid\n");
+ closedir(proc_dir);
+ return -1;
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
new file mode 100644
index 0000000..5a8ce5b
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_OS_DUMPSTATE_UTIL_H_
+#define ANDROID_OS_DUMPSTATE_UTIL_H_
+
+#include <cstdint>
+#include <string>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+/*
+ * Defines the Linux account that should be executing a command.
+ */
+enum PrivilegeMode {
+ /* Explicitly change the `uid` and `gid` to be `shell`.*/
+ DROP_ROOT,
+ /* Don't change the `uid` and `gid`. */
+ DONT_DROP_ROOT,
+ /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */
+ SU_ROOT
+};
+
+/*
+ * Defines what should happen with the main output stream (`stdout` or fd) of a command.
+ */
+enum OutputMode {
+ /* Don't change main output. */
+ NORMAL_OUTPUT,
+ /* Redirect main output to `stderr`. */
+ REDIRECT_TO_STDERR
+};
+
+/*
+ * Value object used to set command options.
+ *
+ * Typically constructed using a builder with chained setters. Examples:
+ *
+ * CommandOptions::WithTimeout(20).AsRoot().Build();
+ * CommandOptions::WithTimeout(10).Always().RedirectStderr().Build();
+ *
+ * Although the builder could be used to dynamically set values. Example:
+ *
+ * CommandOptions::CommandOptionsBuilder options =
+ * CommandOptions::WithTimeout(10);
+ * if (!is_user_build()) {
+ * options.AsRoot();
+ * }
+ * RunCommand("command", {"args"}, options.Build());
+ */
+class CommandOptions {
+ private:
+ class CommandOptionsValues {
+ private:
+ CommandOptionsValues(int64_t timeout);
+
+ int64_t timeout_;
+ bool always_;
+ PrivilegeMode account_mode_;
+ OutputMode output_mode_;
+ std::string logging_message_;
+
+ friend class CommandOptions;
+ friend class CommandOptionsBuilder;
+ };
+
+ CommandOptions(const CommandOptionsValues& values);
+
+ const CommandOptionsValues values;
+
+ public:
+ class CommandOptionsBuilder {
+ public:
+ /* Sets the command to always run, even on `dry-run` mode. */
+ CommandOptionsBuilder& Always();
+ /* Sets the command's PrivilegeMode as `SU_ROOT` */
+ CommandOptionsBuilder& AsRoot();
+ /* Sets the command's PrivilegeMode as `DROP_ROOT` */
+ CommandOptionsBuilder& DropRoot();
+ /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */
+ CommandOptionsBuilder& RedirectStderr();
+ /* When not empty, logs a message before executing the command.
+ * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
+ CommandOptionsBuilder& Log(const std::string& message);
+ /* Builds the command options. */
+ CommandOptions Build();
+
+ private:
+ CommandOptionsBuilder(int64_t timeout);
+ CommandOptionsValues values;
+ friend class CommandOptions;
+ };
+
+ /** Gets the command timeout, in seconds. */
+ int64_t Timeout() const;
+ /* Checks whether the command should always be run, even on dry-run mode. */
+ bool Always() const;
+ /** Gets the PrivilegeMode of the command. */
+ PrivilegeMode PrivilegeMode() const;
+ /** Gets the OutputMode of the command. */
+ OutputMode OutputMode() const;
+ /** Gets the logging message header, it any. */
+ std::string LoggingMessage() const;
+
+ /** Creates a builder with the requied timeout. */
+ static CommandOptionsBuilder WithTimeout(int64_t timeout);
+
+ // Common options.
+ static CommandOptions DEFAULT;
+ static CommandOptions AS_ROOT;
+};
+
+/*
+ * System properties helper.
+ */
+class PropertiesHelper {
+ friend class DumpstateBaseTest;
+
+ public:
+ /*
+ * Gets whether device is running a `user` build.
+ */
+ static bool IsUserBuild();
+
+ /*
+ * When running in dry-run mode, skips the real dumps and just print the section headers.
+ *
+ * Useful when debugging dumpstate or other bugreport-related activities.
+ *
+ * Dry-run mode is enabled by setting the system property `dumpstate.dry_run` to true.
+ */
+ static bool IsDryRun();
+
+ private:
+ static std::string build_type_;
+ static int dry_run_;
+};
+
+/*
+ * Forks a command, waits for it to finish, and returns its status.
+ *
+ * |fd| file descriptor that receives the command's 'stdout'.
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |full_command| array containing the command (first entry) and its arguments.
+ * Must contain at least one element.
+ * |options| optional argument defining the command's behavior.
+ */
+int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT);
+
+/*
+ * Dumps the contents of a file into a file descriptor.
+ *
+ * |fd| file descriptor where the file is dumped into.
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |path| location of the file to be dumped.
+ */
+int DumpFileToFd(int fd, const std::string& title, const std::string& path);
+
+/*
+ * Finds the process id by process name.
+ * |ps_name| the process name we want to search for
+ */
+int GetPidByName(const std::string& ps_name);
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif // ANDROID_OS_DUMPSTATE_UTIL_H_
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
new file mode 100644
index 0000000..0302ea5
--- /dev/null
+++ b/cmds/dumpstate/README.md
@@ -0,0 +1,103 @@
+# `dumpstate` development tips
+
+## To build `dumpstate`
+
+Do a full build first:
+
+```
+m -j dumpstate
+```
+
+Then incremental ones:
+
+```
+mmm -j frameworks/native/cmds/dumpstate
+```
+
+If you're working on device-specific code, you might need to build them as well. Example:
+
+```
+mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ hardware/interfaces/dumpstate
+```
+
+## To build, deploy, and take a bugreport
+
+```
+mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb shell am bug-report
+```
+
+## To build, deploy, and run unit tests
+
+First create `/data/nativetest`:
+
+```
+adb shell mkdir /data/nativetest
+```
+
+Then run:
+
+```
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test
+```
+
+And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`):
+
+```
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs
+```
+
+## To take quick bugreports
+
+```
+adb shell setprop dumpstate.dry_run true
+```
+
+## To change the `dumpstate` version
+
+```
+adb shell setprop dumpstate.version VERSION_NAME
+```
+
+Example:
+
+```
+adb shell setprop dumpstate.version split-dumpsys && adb shell dumpstate -v
+```
+
+
+Then to restore the default version:
+
+```
+adb shell setprop dumpstate.version default
+```
+
+## Code style and formatting
+
+Use the style defined at the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
+and make sure to run the following command prior to `repo upload`:
+
+```
+git clang-format --style=file HEAD~
+```
+
+## Useful Bash tricks
+
+```
+export BR_DIR=/bugreports
+
+alias br='adb shell cmd activity bug-report'
+alias ls_bugs='adb shell ls -l ${BR_DIR}/'
+
+unzip_bug() {
+ adb pull ${BR_DIR}/$1 && emacs $1 && mv $1 /tmp
+}
+
+less_bug() {
+ adb pull ${BR_DIR}/$1 && less $1 && mv $1 /tmp
+}
+
+rm_bugs() {
+ if [ -z "${BR_DIR}" ] ; then echo "Variable BR_DIR not set"; else adb shell rm -rf ${BR_DIR}/*; fi
+}
+
+```
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
new file mode 100644
index 0000000..4becccf
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IDumpstateListener;
+import android.os.IDumpstateToken;
+
+/**
+ * Binder interface for the currently running dumpstate process.
+ * {@hide}
+ */
+interface IDumpstate {
+
+ /*
+ * Sets the listener for this dumpstate progress.
+ *
+ * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
+ * set (the listener behaves like a Highlander: There Can be Only One).
+ */
+ IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener);
+}
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
similarity index 61%
copy from cmds/dumpstate/libdumpstate_default.cpp
copy to cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index fd840df..32717f4 100644
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
+/**
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,9 +14,14 @@
* limitations under the License.
*/
-#include "dumpstate.h"
+package android.os;
-void dumpstate_board(void)
-{
+/**
+ * Listener for dumpstate events.
+ *
+ * {@hide}
+ */
+interface IDumpstateListener {
+ void onProgressUpdated(int progress);
+ void onMaxProgressUpdated(int maxProgress);
}
-
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
similarity index 66%
rename from cmds/dumpstate/libdumpstate_default.cpp
rename to cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
index fd840df..7f74ceb 100644
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
+/**
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-#include "dumpstate.h"
+package android.os;
-void dumpstate_board(void)
-{
+/**
+ * Token used by the IDumpstateListener to watch for dumpstate death.
+ * {@hide}
+ */
+interface IDumpstateToken {
}
-
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index ca7d574..b995b80 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -22,7 +22,7 @@
file as the `ACTION_SEND_MULTIPLE` attachment.
## Version 1.0 (Android N)
-On _Android N (TBD)_, `dumpstate` generates a zip file directly (unless there
+On _Android N (Nougat)_, `dumpstate` generates a zip file directly (unless there
is a failure, in which case it reverts to the flat file that is zipped by
**Shell** and hence the end result is the _v0_ format).
@@ -55,6 +55,10 @@
- `title.txt`: whose value is a single-line summary of the problem.
- `description.txt`: whose value is a multi-line, detailed description of the problem.
+## Android O versions
+On _Android O (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made:
+- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`).
+
## Intermediate versions
During development, the versions will be suffixed with _-devX_ or
_-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
@@ -63,8 +67,8 @@
For example, the initial version during _Android N_ development was
**1.0-dev1**. When `dumpsys` was split in 2 sections but not all tools were
ready to parse that format, the version was named **1.0-dev2**,
-which had to be passed do `dumpsys` explicitly (i.e., trhough a
-`-V 1.0-dev2` argument). Once that format became stable and tools
+which had to be passed to `dumpsys` explicitly (by setting the `dumpstate.version` system property).
+Once that format became stable and tools
knew how to parse it, the default version became **1.0-dev2**.
Similarly, if changes in the file format are made after the initial release of
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a2a7def..7d45f86 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#define LOG_TAG "dumpstate"
#include <dirent.h>
@@ -36,36 +37,40 @@
#include <unistd.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <android/hardware/vibrator/1.0/IVibrator.h>
+#include <cutils/native_handle.h>
#include <cutils/properties.h>
-
+#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
#include "dumpstate.h"
-#include "ziparchive/zip_writer.h"
-#include <openssl/sha.h>
+using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using ::android::hardware::vibrator::V1_0::IVibrator;
+using VibratorStatus = ::android::hardware::vibrator::V1_0::Status;
-using android::base::StringPrintf;
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::GetPidByName;
/* read before root is shed */
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = NULL;
-// TODO: variables below should be part of dumpstate object
-static unsigned long id;
-static char build_type[PROPERTY_VALUE_MAX];
-static time_t now;
-static std::unique_ptr<ZipWriter> zip_writer;
+// TODO: variables and functions below should be part of dumpstate object
+
static std::set<std::string> mount_points;
void add_mountinfo();
-int control_socket_fd = -1;
-/* suffix of the bugreport files - it's typically the date (when invoked with -d),
- * although it could be changed by the user using a system property */
-static std::string suffix;
#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
@@ -90,41 +95,54 @@
static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
-const std::string ZIP_ROOT_DIR = "FS";
-std::string bugreport_dir;
-
-/*
- * List of supported zip format versions.
- *
- * See bugreport-format.txt for more info.
- */
-static std::string VERSION_DEFAULT = "1.0";
-
-bool is_user_build() {
- return 0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1);
+// TODO: temporary variables and functions used during C++ refactoring
+static Dumpstate& ds = Dumpstate::GetInstance();
+static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ return ds.RunCommand(title, fullCommand, options);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
+ long dumpsysTimeout = 0) {
+ return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout);
+}
+static int DumpFile(const std::string& title, const std::string& path) {
+ return ds.DumpFile(title, path);
}
-/* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
- * otherwise gets just those modified in the last half an hour. */
+// Relative directory (inside the zip) for all files copied as-is into the bugreport.
+static const std::string ZIP_ROOT_DIR = "FS";
+
+// Must be hardcoded because dumpstate HAL implementation need SELinux access to it
+static const std::string kDumpstateBoardPath = "/bugreports/dumpstate_board.txt";
+
+static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
+static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
+
+static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
+
+/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
+ * otherwise, gets just those modified in the last half an hour. */
static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
- time_t thirty_minutes_ago = now - 60*30;
+ time_t thirty_minutes_ago = ds.now_ - 60 * 30;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
int fd = TEMP_FAILURE_RETRY(open(data[i].name,
O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
struct stat st;
- if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
- (zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) {
- data[i].fd = fd;
+ if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
+ (ds.IsZipping() || st.st_mtime >= thirty_minutes_ago)) {
+ data[i].fd = fd;
} else {
- close(fd);
+ close(fd);
data[i].fd = -1;
}
}
}
// for_each_pid() callback to get mount info about a process.
-void do_mountinfo(int pid, const char *name) {
+void do_mountinfo(int pid, const char* name __attribute__((unused))) {
char path[PATH_MAX];
// Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
@@ -141,7 +159,7 @@
if (mount_points.find(linkname) == mount_points.end()) {
// First time this mount point was found: add it
snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid);
- if (add_zip_entry(ZIP_ROOT_DIR + path, path)) {
+ if (ds.AddZipEntry(ZIP_ROOT_DIR + path, path)) {
mount_points.insert(linkname);
} else {
MYLOGE("Unable to add mountinfo %s to zip file\n", path);
@@ -150,12 +168,12 @@
}
void add_mountinfo() {
- if (!is_zipping()) return;
- const char *title = "MOUNT INFO";
+ if (!ds.IsZipping()) return;
+ std::string title = "MOUNT INFO";
mount_points.clear();
- DurationReporter duration_reporter(title, NULL);
- for_each_pid(do_mountinfo, NULL);
- MYLOGD("%s: %d entries added to zip file\n", title, (int) mount_points.size());
+ DurationReporter duration_reporter(title, true);
+ for_each_pid(do_mountinfo, nullptr);
+ MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size());
}
static void dump_dev_files(const char *title, const char *driverpath, const char *filename)
@@ -174,40 +192,13 @@
continue;
}
snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename);
- dump_file(title, path);
+ DumpFile(title, path);
}
closedir(d);
}
-// return pid of a userspace process. If not found or error, return 0.
-static unsigned int pid_of_process(const char* ps_name) {
- DIR *proc_dir;
- struct dirent *ps;
- unsigned int pid;
- std::string cmdline;
- if (!(proc_dir = opendir("/proc"))) {
- MYLOGE("Can't open /proc\n");
- return 0;
- }
-
- while ((ps = readdir(proc_dir))) {
- if (!(pid = atoi(ps->d_name))) {
- continue;
- }
- android::base::ReadFileToString("/proc/"
- + std::string(ps->d_name) + "/cmdline", &cmdline);
- if (cmdline.find(ps_name) == std::string::npos) {
- continue;
- } else {
- closedir(proc_dir);
- return pid;
- }
- }
- closedir(proc_dir);
- return 0;
-}
// dump anrd's trace and add to the zip file.
// 1. check if anrd is running on this device.
@@ -224,13 +215,13 @@
long long cur_size = 0;
const char *trace_path = "/data/misc/anrd/";
- if (!zip_writer) {
- MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
+ if (!ds.IsZipping()) {
+ MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n");
return false;
}
// find anrd's pid if it is running.
- pid = pid_of_process("/system/xbin/anrd");
+ pid = GetPidByName("/system/xbin/anrd");
if (pid > 0) {
if (stat(trace_path, &st) == 0) {
@@ -242,7 +233,8 @@
// send SIGUSR1 to the anrd to generate a trace.
sprintf(buf, "%u", pid);
- if (run_command("ANRD_DUMP", 1, "kill", "-SIGUSR1", buf, NULL)) {
+ if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf},
+ CommandOptions::WithTimeout(1).Build())) {
MYLOGE("anrd signal timed out. Please manually collect trace\n");
return false;
}
@@ -295,7 +287,7 @@
}
}
// Add to the zip file.
- if (!add_zip_entry("anrd_trace.txt", path)) {
+ if (!ds.AddZipEntry("anrd_trace.txt", path)) {
MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
} else {
if (remove(path)) {
@@ -311,11 +303,11 @@
}
static void dump_systrace() {
- if (!is_zipping()) {
- MYLOGD("Not dumping systrace because dumpstate is not zipping\n");
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping systrace because it's not a zipped bugreport\n");
return;
}
- std::string systrace_path = bugreport_dir + "/systrace-" + suffix + ".txt";
+ std::string systrace_path = ds.GetPath("-systrace.txt");
if (systrace_path.empty()) {
MYLOGE("Not dumping systrace because path is empty\n");
return;
@@ -332,17 +324,17 @@
MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
systrace_path.c_str());
- if (run_command("SYSTRACE", 120, "/system/bin/atrace", "--async_dump", "-o",
- systrace_path.c_str(), NULL)) {
+ if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path},
+ CommandOptions::WithTimeout(120).Build())) {
MYLOGE("systrace timed out, its zip entry will be incomplete\n");
- // TODO: run_command tries to kill the process, but atrace doesn't die peacefully; ideally,
- // we should call strace to stop itself, but there is no such option yet (just a
- // --async_stop, which stops and dump
- // if (run_command("SYSTRACE", 10, "/system/bin/atrace", "--kill", NULL)) {
- // MYLOGE("could not stop systrace ");
- // }
+ // TODO: RunCommand tries to kill the process, but atrace doesn't die
+ // peacefully; ideally, we should call strace to stop itself, but there is no such option
+ // yet (just a --async_stop, which stops and dump
+ // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) {
+ // MYLOGE("could not stop systrace ");
+ // }
}
- if (!add_zip_entry("systrace.txt", systrace_path)) {
+ if (!ds.AddZipEntry("systrace.txt", systrace_path)) {
MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
} else {
if (remove(systrace_path.c_str())) {
@@ -352,13 +344,13 @@
}
static void dump_raft() {
- if (is_user_build()) {
+ if (PropertiesHelper::IsUserBuild()) {
return;
}
- std::string raft_log_path = bugreport_dir + "/raft_log.txt";
- if (raft_log_path.empty()) {
- MYLOGD("raft_log_path is empty\n");
+ std::string raft_path = ds.GetPath("-raft_log.txt");
+ if (raft_path.empty()) {
+ MYLOGD("raft_path is empty\n");
return;
}
@@ -368,29 +360,30 @@
return;
}
- if (!is_zipping()) {
- // Write compressed and encoded raft logs to stdout if not zip_writer.
- run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+ CommandOptions options = CommandOptions::WithTimeout(600).Build();
+ if (!ds.IsZipping()) {
+ // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport.
+ RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
return;
}
- run_command("RAFT LOGS", 600, "logcompressor", "-n", "-r", RAFT_DIR,
- "-o", raft_log_path.c_str(), NULL);
- if (!add_zip_entry("raft_log.txt", raft_log_path)) {
- MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+ RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options);
+ if (!ds.AddZipEntry("raft_log.txt", raft_path)) {
+ MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str());
} else {
- if (remove(raft_log_path.c_str())) {
- MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+ if (remove(raft_path.c_str())) {
+ MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno));
}
}
}
/**
- * Finds the last modified file in the directory dir whose name starts with file_prefix
+ * Finds the last modified file in the directory dir whose name starts with file_prefix.
+ *
* Function returns empty string when it does not find a file
*/
-static std::string get_last_modified_file_matching_prefix(const std::string& dir,
- const std::string& file_prefix) {
+static std::string GetLastModifiedFileWithPrefix(const std::string& dir,
+ const std::string& file_prefix) {
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dir.c_str()), closedir);
if (d == nullptr) {
MYLOGD("Error %d opening %s\n", errno, dir.c_str());
@@ -399,7 +392,7 @@
// Find the newest file matching the file_prefix in dir
struct dirent *de;
- time_t last_modified = 0;
+ time_t last_modified_time = 0;
std::string last_modified_file = "";
struct stat s;
@@ -411,39 +404,39 @@
file = dir + "/" + file;
int ret = stat(file.c_str(), &s);
- if ((ret == 0) && (s.st_mtime > last_modified)) {
+ if ((ret == 0) && (s.st_mtime > last_modified_time)) {
last_modified_file = file;
- last_modified = s.st_mtime;
+ last_modified_time = s.st_mtime;
}
}
return last_modified_file;
}
-void dump_modem_logs() {
- DurationReporter duration_reporter("dump_modem_logs");
- if (is_user_build()) {
+static void DumpModemLogs() {
+ DurationReporter durationReporter("DUMP MODEM LOGS");
+ if (PropertiesHelper::IsUserBuild()) {
return;
}
- if (!is_zipping()) {
+ if (!ds.IsZipping()) {
MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
return;
}
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.radio.log_prefix", property, "");
- std::string file_prefix = std::string(property);
+ std::string file_prefix = android::base::GetProperty("ro.radio.log_prefix", "");
+
if(file_prefix.empty()) {
MYLOGD("No modem log : file_prefix is empty\n");
return;
}
- MYLOGD("dump_modem_logs: directory is %s and file_prefix is %s\n",
+ // TODO: use bugreport_dir_ directly when this function is moved to Dumpstate class
+ std::string bugreport_dir = dirname(ds.GetPath("").c_str());
+ MYLOGD("DumpModemLogs: directory is %s and file_prefix is %s\n",
bugreport_dir.c_str(), file_prefix.c_str());
- std::string modem_log_file =
- get_last_modified_file_matching_prefix(bugreport_dir, file_prefix);
+ std::string modem_log_file = GetLastModifiedFileWithPrefix(bugreport_dir, file_prefix);
struct stat s;
if (modem_log_file.empty() || stat(modem_log_file.c_str(), &s) != 0) {
@@ -452,7 +445,7 @@
}
std::string filename = basename(modem_log_file.c_str());
- if (!add_zip_entry(filename, modem_log_file)) {
+ if (!ds.AddZipEntry(filename, modem_log_file)) {
MYLOGE("Unable to add modem log %s to zip file\n", modem_log_file.c_str());
} else {
MYLOGD("Modem Log %s is added to zip\n", modem_log_file.c_str());
@@ -471,7 +464,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;
}
@@ -630,11 +623,10 @@
/ fields[__STAT_IO_TICKS];
if (!write_perf && !write_ios) {
- printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n",
- path, read_perf, read_ios, queue);
+ printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n", path, read_perf, read_ios, queue);
} else {
- printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n",
- path, read_perf, read_ios, write_perf, write_ios, queue);
+ printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n", path, read_perf,
+ read_ios, write_perf, write_ios, queue);
}
/* bugreport timeout factor adjustment */
@@ -653,36 +645,35 @@
return 10 * (property_size + worst_write_perf) / worst_write_perf;
}
-/* dumps the current system state to stdout */
-static void print_header(std::string version) {
- char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
- char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
- char network[PROPERTY_VALUE_MAX], date[80];
+void Dumpstate::PrintHeader() const {
+ std::string build, fingerprint, radio, bootloader, network;
+ char date[80];
- property_get("ro.build.display.id", build, "(unknown)");
- property_get("ro.build.fingerprint", fingerprint, "(unknown)");
- property_get("ro.build.type", build_type, "(unknown)");
- property_get("gsm.version.baseband", radio, "(unknown)");
- property_get("ro.bootloader", bootloader, "(unknown)");
- property_get("gsm.operator.alpha", network, "(unknown)");
- strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
+ build = android::base::GetProperty("ro.build.display.id", "(unknown)");
+ fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
+ radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
+ bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
+ network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
+ strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
printf("========================================================\n");
printf("== dumpstate: %s\n", date);
printf("========================================================\n");
printf("\n");
- printf("Build: %s\n", build);
- printf("Build fingerprint: '%s'\n", fingerprint); /* format is important for other tools */
- printf("Bootloader: %s\n", bootloader);
- printf("Radio: %s\n", radio);
- printf("Network: %s\n", network);
+ printf("Build: %s\n", build.c_str());
+ // NOTE: fingerprint entry format is important for other tools.
+ printf("Build fingerprint: '%s'\n", fingerprint.c_str());
+ printf("Bootloader: %s\n", bootloader.c_str());
+ printf("Radio: %s\n", radio.c_str());
+ printf("Network: %s\n", network.c_str());
printf("Kernel: ");
- dump_file(NULL, "/proc/version");
+ DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
- printf("Bugreport format version: %s\n", version.c_str());
- printf("Dumpstate info: id=%lu pid=%d\n", id, getpid());
+ printf("Bugreport format version: %s\n", version_.c_str());
+ printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
+ PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
printf("\n");
}
@@ -694,10 +685,10 @@
".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
};
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
- if (!is_zipping()) {
- MYLOGD("Not adding entry %s from fd because dumpstate is not zipping\n",
- entry_name.c_str());
+bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
+ entry_name.c_str());
return false;
}
std::string valid_name = entry_name;
@@ -715,253 +706,286 @@
// Logging statement below is useful to time how long each entry takes, but it's too verbose.
// MYLOGD("Adding zip entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(),
- ZipWriter::kCompress, get_mtime(fd, now));
- if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
+ get_mtime(fd, ds.now_));
+ if (err != 0) {
+ MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
std::vector<uint8_t> buffer(65536);
while (1) {
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), sizeof(buffer)));
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
return false;
}
- err = zip_writer->WriteBytes(buffer.data(), bytes_read);
+ err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
if (err) {
- MYLOGE("zip_writer->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
+ MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
}
- err = zip_writer->FinishEntry();
- if (err) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->FinishEntry();
+ if (err != 0) {
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
return true;
}
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK
- | O_CLOEXEC)));
+bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd == -1) {
MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
return false;
}
- return add_zip_entry_from_fd(entry_name, fd.get());
+ return AddZipEntryFromFd(entry_name, fd.get());
}
/* adds a file to the existing zipped bugreport */
-static int _add_file_from_fd(const char *title, const char *path, int fd) {
- return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
+static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
+ return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}
-// TODO: move to util.cpp
-void add_dir(const char *dir, bool recursive) {
- if (!is_zipping()) {
- MYLOGD("Not adding dir %s because dumpstate is not zipping\n", dir);
+void Dumpstate::AddDir(const std::string& dir, bool recursive) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str());
return;
}
- MYLOGD("Adding dir %s (recursive: %d)\n", dir, recursive);
- DurationReporter duration_reporter(dir, NULL);
- dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd);
+ MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
+ DurationReporter duration_reporter(dir, true);
+ dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
}
-bool is_zipping() {
- return zip_writer != nullptr;
-}
-
-/* adds a text entry entry to the existing zip file. */
-static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
- if (!is_zipping()) {
- MYLOGD("Not adding text entry %s because dumpstate is not zipping\n", entry_name.c_str());
+bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n",
+ entry_name.c_str());
return false;
}
MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, now);
- if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
+ if (err != 0) {
+ MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
- err = zip_writer->WriteBytes(content.c_str(), content.length());
- if (err) {
- MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->WriteBytes(content.c_str(), content.length());
+ if (err != 0) {
+ MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
- err = zip_writer->FinishEntry();
- if (err) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->FinishEntry();
+ if (err != 0) {
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
return true;
}
-static void dump_iptables() {
- run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL);
- run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL);
- run_command("IPTABLES NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
- /* no ip6 nat */
- run_command("IPTABLES MANGLE", 10, "iptables", "-t", "mangle", "-L", "-nvx", NULL);
- run_command("IP6TABLES MANGLE", 10, "ip6tables", "-t", "mangle", "-L", "-nvx", NULL);
- run_command("IPTABLES RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
- run_command("IP6TABLES RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
-}
-
-static void do_kmsg() {
+static void DoKmsg() {
struct stat st;
if (!stat(PSTORE_LAST_KMSG, &st)) {
/* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
- dump_file("LAST KMSG", PSTORE_LAST_KMSG);
+ DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
} else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
- dump_file("LAST KMSG", ALT_PSTORE_LAST_KMSG);
+ DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG);
} else {
/* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
- dump_file("LAST KMSG", "/proc/last_kmsg");
+ DumpFile("LAST KMSG", "/proc/last_kmsg");
}
}
-static void do_logcat() {
+static void DoLogcat() {
unsigned long timeout;
- // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
+ // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
// calculate timeout
timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
if (timeout < 20000) {
timeout = 20000;
}
- run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime",
- "-v", "printable",
- "-d",
- "*:v", NULL);
+ RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-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"});
/* 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"});
}
-static void dumpstate(const std::string& screenshot_path, const std::string& version) {
+static void DumpIpTables() {
+ RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
+ RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
+ RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
+ /* no ip6 nat */
+ RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
+ RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
+ RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
+ RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
+}
+
+static void AddAnrTraceFiles() {
+ bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+ std::string dump_traces_dir;
+
+ /* show the traces we collected in main(), if that was done */
+ if (dump_traces_path != nullptr) {
+ if (add_to_zip) {
+ dump_traces_dir = dirname(dump_traces_path);
+ MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
+ ds.AddDir(dump_traces_dir, true);
+ } else {
+ MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+ dump_traces_path);
+ ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+ }
+ }
+
+ std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ std::string anr_traces_dir = dirname(anr_traces_path.c_str());
+
+ // Make sure directory is not added twice.
+ // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
+ // generated by dump_traces() - and anr_traces_path - which is retrieved from a system
+ // property - but in reality they're the same path (although the former could be nullptr).
+ // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
+ // be revisited.
+ bool already_dumped = anr_traces_dir == dump_traces_dir;
+
+ MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+ dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
+
+ if (anr_traces_path.empty()) {
+ printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+ } else {
+ int fd = TEMP_FAILURE_RETRY(
+ open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+ if (fd < 0) {
+ printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
+ strerror(errno));
+ } else {
+ if (add_to_zip) {
+ if (!already_dumped) {
+ MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+ anr_traces_dir.c_str());
+ ds.AddDir(anr_traces_dir, true);
+ already_dumped = true;
+ }
+ } else {
+ MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+ anr_traces_path.c_str());
+ dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+ }
+ }
+ }
+
+ if (add_to_zip && already_dumped) {
+ MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+ return;
+ }
+
+ /* slow traces for slow operations */
+ struct stat st;
+ if (!anr_traces_path.empty()) {
+ int tail = anr_traces_path.size() - 1;
+ while (tail > 0 && anr_traces_path.at(tail) != '/') {
+ tail--;
+ }
+ int i = 0;
+ while (1) {
+ anr_traces_path = anr_traces_path.substr(0, tail + 1) +
+ android::base::StringPrintf("slow%02d.txt", i);
+ if (stat(anr_traces_path.c_str(), &st)) {
+ // No traces file at this index, done with the files.
+ break;
+ }
+ ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+ i++;
+ }
+ }
+}
+
+static void dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
- run_command("UPTIME", 10, "uptime", NULL);
+ RunCommand("UPTIME", {"uptime"});
dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
- dump_file("MEMORY INFO", "/proc/meminfo");
- run_command("CPU INFO", 10, "top", "-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"}, 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);
- run_command("PRINTENV", 10, "printenv", NULL);
- run_command("NETSTAT", 10, "netstat", "-nW", NULL);
- run_command("LSMOD", 10, "lsmod", NULL);
+ RunCommand("PRINTENV", {"printenv"});
+ RunCommand("NETSTAT", {"netstat", "-nW"});
+ struct stat s;
+ if (stat("/proc/modules", &s) != 0) {
+ MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
+ } else {
+ RunCommand("LSMOD", {"lsmod"});
+ }
do_dmesg();
- run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
+ RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
/* Dump Bluetooth HCI logs */
- add_dir("/data/misc/bluetooth/logs", true);
+ ds.AddDir("/data/misc/bluetooth/logs", true);
- if (!screenshot_path.empty()) {
+ if (!ds.do_early_screenshot_) {
MYLOGI("taking late screenshot\n");
- take_screenshot(screenshot_path);
- MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
+ ds.TakeScreenshot();
}
- do_logcat();
+ DoLogcat();
- /* show the traces we collected in main(), if that was done */
- if (dump_traces_path != NULL) {
- dump_file("VM TRACES JUST NOW", dump_traces_path);
- }
-
- /* only show ANR traces if they're less than 15 minutes old */
- struct stat st;
- char anr_traces_path[PATH_MAX];
- property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
- if (!anr_traces_path[0]) {
- printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
- } else {
- int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
- O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
- if (fd < 0) {
- printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
- } else {
- dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
- }
- }
-
- /* slow traces for slow operations */
- if (anr_traces_path[0] != 0) {
- int tail = strlen(anr_traces_path)-1;
- while (tail > 0 && anr_traces_path[tail] != '/') {
- tail--;
- }
- int i = 0;
- while (1) {
- sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
- if (stat(anr_traces_path, &st)) {
- // No traces file at this index, done with the files.
- break;
- }
- dump_file("VM TRACES WHEN SLOW", anr_traces_path);
- i++;
- }
- }
+ AddAnrTraceFiles();
int dumped = 0;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -969,8 +993,8 @@
const char *name = tombstone_data[i].name;
int fd = tombstone_data[i].fd;
dumped = 1;
- if (zip_writer) {
- if (!add_zip_entry_from_fd(ZIP_ROOT_DIR + name, fd)) {
+ if (ds.IsZipping()) {
+ if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
MYLOGE("Unable to add tombstone %s to zip file\n", name);
}
} else {
@@ -984,214 +1008,291 @@
printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
}
- dump_file("NETWORK DEV INFO", "/proc/net/dev");
- dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
- dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
- dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
- dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
+ DumpFile("NETWORK DEV INFO", "/proc/net/dev");
+ DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
+ DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
+ DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
+ DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
- do_kmsg();
+ DoKmsg();
/* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
- run_command("NETWORK INTERFACES", 10, "ip", "link", NULL);
+ RunCommand("NETWORK INTERFACES", {"ip", "link"});
- run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL);
- run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL);
+ RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
+ RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
- run_command("IP RULES", 10, "ip", "rule", "show", NULL);
- run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
+ RunCommand("IP RULES", {"ip", "rule", "show"});
+ RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
dump_route_tables();
- run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
- run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
- run_command("MULTICAST ADDRESSES", 10, "ip", "maddr", NULL);
- run_command("WIFI NETWORKS", 20, "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
+ RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
+ RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
+ RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
+ RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
+ CommandOptions::WithTimeout(20).Build());
#ifdef FWDUMP_bcmdhd
- run_command("ND OFFLOAD TABLE", 5,
- SU_PATH, "root", WLUTIL, "nd_hostip", NULL);
+ RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT);
- run_command("DUMP WIFI INTERNAL COUNTERS (1)", 20,
- SU_PATH, "root", WLUTIL, "counters", NULL);
+ RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, 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);
#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"}, 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"}, 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);
#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("STORAGED TASKIOINFO", {"storaged", "-d"}, CommandOptions::WithTimeout(10).Build());
- run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
+ RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
+
+ RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
printf("------ BACKLIGHTS ------\n");
printf("LCD brightness=");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
+ DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
printf("Button brightness=");
- dump_file(NULL, "/sys/class/leds/button-backlight/brightness");
+ DumpFile("", "/sys/class/leds/button-backlight/brightness");
printf("Keyboard brightness=");
- dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness");
+ DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
printf("ALS mode=");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/als");
+ DumpFile("", "/sys/class/leds/lcd-backlight/als");
printf("LCD driver registers:\n");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
+ DumpFile("", "/sys/class/leds/lcd-backlight/registers");
printf("\n");
/* Binder state is expensive to look at as it uses a lot of memory. */
- dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
- dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
- dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
- dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
- dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
+ DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
+ DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
+ DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
+ DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
+ DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
- printf("========================================================\n");
- printf("== Board\n");
- printf("========================================================\n");
+ ds.DumpstateBoard();
- dumpstate_board();
- printf("\n");
-
- /* Migrate the ril_dumpstate to a dumpstate_board()? */
- char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
- property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30");
- if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) {
- if (is_user_build()) {
- // su does not exist on user builds, so try running without it.
- // This way any implementations of vril-dump that do not require
- // root can run on user builds.
- run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
- "vril-dump", NULL);
- } else {
- run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
- SU_PATH, "root", "vril-dump", NULL);
+ /* Migrate the ril_dumpstate to a device specific dumpstate? */
+ int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
+ if (rilDumpstateTimeout > 0) {
+ // su does not exist on user builds, so try running without it.
+ // This way any implementations of vril-dump that do not require
+ // root can run on user builds.
+ CommandOptions::CommandOptionsBuilder options =
+ CommandOptions::WithTimeout(rilDumpstateTimeout);
+ if (!PropertiesHelper::IsUserBuild()) {
+ options.AsRoot();
}
+ RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
}
printf("========================================================\n");
printf("== Android Framework Services\n");
printf("========================================================\n");
- run_command("DUMPSYS", 60, "dumpsys", "-t", "60", "--skip", "meminfo", "cpuinfo", NULL);
+ RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(),
+ 10);
printf("========================================================\n");
printf("== Checkins\n");
printf("========================================================\n");
- run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "-t", "30", "batterystats", "-c", NULL);
- run_command("CHECKIN MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "--checkin", NULL);
- run_command("CHECKIN NETSTATS", 30, "dumpsys", "-t", "30", "netstats", "--checkin", NULL);
- run_command("CHECKIN PROCSTATS", 30, "dumpsys", "-t", "30", "procstats", "-c", NULL);
- run_command("CHECKIN USAGESTATS", 30, "dumpsys", "-t", "30", "usagestats", "-c", NULL);
- run_command("CHECKIN PACKAGE", 30, "dumpsys", "-t", "30", "package", "--checkin", NULL);
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+ RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
+ RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
+ RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
+ RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
+ RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
printf("========================================================\n");
printf("== Running Application Activities\n");
printf("========================================================\n");
- run_command("APP ACTIVITIES", 30, "dumpsys", "-t", "30", "activity", "all", NULL);
+ RunDumpsys("APP ACTIVITIES", {"activity", "all"});
printf("========================================================\n");
printf("== Running Application Services\n");
printf("========================================================\n");
- run_command("APP SERVICES", 30, "dumpsys", "-t", "30", "activity", "service", "all", NULL);
+ RunDumpsys("APP SERVICES", {"activity", "service", "all"});
printf("========================================================\n");
printf("== Running Application Providers\n");
printf("========================================================\n");
- run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
+ RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
- // dump_modem_logs adds the modem logs if available to the bugreport.
+ // DumpModemLogs adds the modem logs if available to the bugreport.
// Do this at the end to allow for sufficient time for the modem logs to be
// collected.
- dump_modem_logs();
+ DumpModemLogs();
printf("========================================================\n");
- printf("== Final progress (pid %d): %d/%d (originally %d)\n",
- getpid(), progress, weight_total, WEIGHT_TOTAL);
+ printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+ ds.progress_->GetMax(), ds.progress_->GetInitialMax());
printf("========================================================\n");
- printf("== dumpstate: done\n");
+ printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
}
-static void usage() {
- fprintf(stderr,
- "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-t]"
- "[-z] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
- " -h: display this help message\n"
- " -b: play sound file instead of vibrate, at beginning of job\n"
- " -e: play sound file instead of vibrate, at end of job\n"
- " -o: write to file (instead of stdout)\n"
- " -d: append date to filename (requires -o)\n"
- " -p: capture screenshot to filename.png (requires -o)\n"
- " -t: only captures telephony sections\n"
- " -z: generate zipped file (requires -o)\n"
- " -s: write output to control socket (for init)\n"
- " -S: write file location to control socket (for init; requires -o and -z)"
- " -q: disable vibrate\n"
- " -B: send broadcast when finished (requires -o)\n"
- " -P: send broadcast when started and update system properties on "
- "progress (requires -o and -B)\n"
- " -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
- "shouldn't be used with -P)\n"
- " -V: sets the bugreport format version (valid values: %s)\n",
- VERSION_DEFAULT.c_str());
+void Dumpstate::DumpstateBoard() {
+ DurationReporter duration_reporter("dumpstate_board()");
+ printf("========================================================\n");
+ printf("== Board\n");
+ printf("========================================================\n");
+
+ ::android::sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService("dumpstate"));
+ if (dumpstate_device == nullptr) {
+ MYLOGE("No IDumpstateDevice implementation\n");
+ return;
+ }
+
+ if (!IsZipping()) {
+ MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
+ return;
+ }
+
+ std::string path = kDumpstateBoardPath;
+ MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path.c_str());
+
+ int fd =
+ TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+ if (fd < 0) {
+ MYLOGE("Could not open file %s: %s\n", path.c_str(), strerror(errno));
+ return;
+ }
+
+ native_handle_t* handle = native_handle_create(1, 0);
+ if (handle == nullptr) {
+ MYLOGE("Could not create native_handle\n");
+ return;
+ }
+ handle->data[0] = fd;
+
+ // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call.
+ android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle);
+ if (!status.isOk()) {
+ MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ return;
+ }
+
+ AddZipEntry("dumpstate-board.txt", path);
+ printf("*** See dumpstate-board.txt entry ***\n");
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+
+ if (remove(path.c_str()) != 0) {
+ MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno));
+ }
}
-static void sigpipe_handler(int n) {
- // don't complain to stderr or stdout
+static void ShowUsageAndExit(int exitCode = 1) {
+ fprintf(stderr,
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] "
+ "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+ " -h: display this help message\n"
+ " -b: play sound file instead of vibrate, at beginning of job\n"
+ " -e: play sound file instead of vibrate, at end of job\n"
+ " -o: write to file (instead of stdout)\n"
+ " -d: append date to filename (requires -o)\n"
+ " -p: capture screenshot to filename.png (requires -o)\n"
+ " -z: generate zipped file (requires -o)\n"
+ " -s: write output to control socket (for init)\n"
+ " -S: write file location to control socket (for init; requires -o and -z)"
+ " -q: disable vibrate\n"
+ " -B: send broadcast when finished (requires -o)\n"
+ " -P: send broadcast when started and update system properties on "
+ "progress (requires -o and -B)\n"
+ " -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
+ "shouldn't be used with -P)\n"
+ " -v: prints the dumpstate header and exit\n");
+ exit(exitCode);
+}
+
+static void ExitOnInvalidArgs() {
+ fprintf(stderr, "invalid combination of args\n");
+ ShowUsageAndExit();
+}
+
+static void sig_handler(int) {
_exit(EXIT_FAILURE);
}
-/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
- temporary file.
- */
-static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
- time_t now) {
- if (!add_zip_entry(bugreport_name, bugreport_path)) {
+static void register_sig_handler() {
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = sig_handler;
+ sigaction(SIGPIPE, &sa, NULL); // broken pipe
+ sigaction(SIGSEGV, &sa, NULL); // segment fault
+ sigaction(SIGINT, &sa, NULL); // ctrl-c
+ sigaction(SIGTERM, &sa, NULL); // killed
+ sigaction(SIGQUIT, &sa, NULL); // quit
+}
+
+bool Dumpstate::FinishZipFile() {
+ std::string entry_name = base_name_ + "-" + name_ + ".txt";
+ MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
+ tmp_path_.c_str());
+ // Final timestamp
+ char date[80];
+ time_t the_real_now_please_stand_up = time(nullptr);
+ strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
+ MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date,
+ the_real_now_please_stand_up - ds.now_);
+
+ if (!ds.AddZipEntry(entry_name, tmp_path_)) {
MYLOGE("Failed to add text entry to .zip file\n");
return false;
}
- if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+ if (!AddTextZipEntry("main_entry.txt", entry_name)) {
MYLOGE("Failed to add main_entry.txt to .zip file\n");
return false;
}
- int32_t err = zip_writer->Finish();
- if (err) {
- MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+ // Add log file (which contains stderr output) to zip...
+ fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
+ if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
+ MYLOGE("Failed to add dumpstate log to .zip file\n");
+ return false;
+ }
+ // ... and re-opens it for further logging.
+ redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+ fprintf(stderr, "\n");
+
+ int32_t err = zip_writer_->Finish();
+ if (err != 0) {
+ MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
- if (is_user_build()) {
- MYLOGD("Removing temporary file %s\n", bugreport_path.c_str())
- if (remove(bugreport_path.c_str())) {
- ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno));
- }
- } else {
- MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
+ // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
+ ds.zip_file.reset(nullptr);
+
+ MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+ if (remove(tmp_path_.c_str()) != 0) {
+ MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
}
return true;
@@ -1233,7 +1334,6 @@
}
int main(int argc, char *argv[]) {
- struct sigaction sigact;
int do_add_date = 0;
int do_zip_file = 0;
int do_vibrate = 1;
@@ -1242,33 +1342,15 @@
int use_control_socket = 0;
int do_fb = 0;
int do_broadcast = 0;
- int do_early_screenshot = 0;
int is_remote_mode = 0;
+ bool show_header_only = false;
+ bool do_start_service = false;
bool telephony_only = false;
- std::string version = VERSION_DEFAULT;
-
- now = time(NULL);
-
- MYLOGI("begin\n");
-
- /* gets the sequential id */
- char last_id[PROPERTY_VALUE_MAX];
- property_get("dumpstate.last_id", last_id, "0");
- id = strtoul(last_id, NULL, 10) + 1;
- snprintf(last_id, sizeof(last_id), "%lu", id);
- property_set("dumpstate.last_id", last_id);
- MYLOGI("dumpstate id: %lu\n", id);
-
- /* clear SIGPIPE handler */
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_handler = sigpipe_handler;
- sigaction(SIGPIPE, &sigact, NULL);
-
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
- FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
+ FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
if (oom_adj) {
fputs("-1000", oom_adj);
fclose(oom_adj);
@@ -1282,60 +1364,132 @@
}
/* parse arguments */
- std::string args;
- format_args(argc, const_cast<const char **>(argv), &args);
- MYLOGD("Dumpstate command line: %s\n", args.c_str());
int c;
- while ((c = getopt(argc, argv, "dho:svqzptPBRSV:")) != -1) {
+ while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
switch (c) {
- case 'd': do_add_date = 1; break;
- case 't': telephony_only = true; break;
- case 'z': do_zip_file = 1; break;
- case 'o': use_outfile = optarg; break;
- case 's': use_socket = 1; break;
- case 'S': use_control_socket = 1; break;
- case 'v': break; // compatibility no-op
- case 'q': do_vibrate = 0; break;
- case 'p': do_fb = 1; break;
- case 'P': do_update_progress = 1; break;
- case 'R': is_remote_mode = 1; break;
- case 'B': do_broadcast = 1; break;
- case 'V': version = optarg; break;
- case '?': printf("\n");
+ // clang-format off
+ case 'd': do_add_date = 1; break;
+ case 'z': do_zip_file = 1; break;
+ case 'o': use_outfile = optarg; break;
+ case 's': use_socket = 1; break;
+ case 'S': use_control_socket = 1; break;
+ case 'v': show_header_only = true; break;
+ case 'q': do_vibrate = 0; break;
+ case 'p': do_fb = 1; break;
+ case 'P': ds.update_progress_ = true; break;
+ case 'R': is_remote_mode = 1; break;
+ case 'B': do_broadcast = 1; break;
+ case 'V': break; // compatibility no-op
case 'h':
- usage();
- exit(1);
+ ShowUsageAndExit(0);
+ break;
+ default:
+ fprintf(stderr, "Invalid option: %c\n", c);
+ ShowUsageAndExit();
+ // clang-format on
}
}
- if ((do_zip_file || do_add_date || do_update_progress || do_broadcast) && !use_outfile) {
- usage();
- exit(1);
+ // TODO: use helper function to convert argv into a string
+ for (int i = 0; i < argc; i++) {
+ ds.args_ += argv[i];
+ if (i < argc - 1) {
+ ds.args_ += " ";
+ }
+ }
+
+ ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ if (!ds.extra_options_.empty()) {
+ // Framework uses a system property to override some command-line args.
+ // Currently, it contains the type of the requested bugreport.
+ if (ds.extra_options_ == "bugreportplus") {
+ // Currently, the dumpstate binder is only used by Shell to update progress.
+ do_start_service = true;
+ ds.update_progress_ = true;
+ do_fb = 0;
+ } else if (ds.extra_options_ == "bugreportremote") {
+ do_vibrate = 0;
+ is_remote_mode = 1;
+ do_fb = 0;
+ } else if (ds.extra_options_ == "bugreportwear") {
+ ds.update_progress_ = true;
+ } else if (ds.extra_options_ == "bugreporttelephony") {
+ telephony_only = true;
+ } else {
+ MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
+ }
+ // Reset the property
+ android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ }
+
+ if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) {
+ ExitOnInvalidArgs();
}
if (use_control_socket && !do_zip_file) {
- usage();
+ ExitOnInvalidArgs();
+ }
+
+ if (ds.update_progress_ && !do_broadcast) {
+ ExitOnInvalidArgs();
+ }
+
+ if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
+ ExitOnInvalidArgs();
+ }
+
+ if (ds.version_ == VERSION_DEFAULT) {
+ ds.version_ = VERSION_CURRENT;
+ }
+
+ if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
+ MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
+ ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
+ VERSION_SPLIT_ANR.c_str());
exit(1);
}
- if (do_update_progress && !do_broadcast) {
- usage();
- exit(1);
+ if (show_header_only) {
+ ds.PrintHeader();
+ exit(0);
}
- if (is_remote_mode && (do_update_progress || !do_broadcast || !do_zip_file || !do_add_date)) {
- usage();
- exit(1);
+ /* redirect output if needed */
+ bool is_redirecting = !use_socket && use_outfile;
+
+ // TODO: temporarily set progress until it's part of the Dumpstate constructor
+ std::string stats_path =
+ is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", dirname(use_outfile))
+ : "";
+ ds.progress_.reset(new Progress(stats_path));
+
+ /* gets the sequential id */
+ uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+ ds.id_ = ++last_id;
+ android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
+
+ MYLOGI("begin\n");
+
+ register_sig_handler();
+
+ if (do_start_service) {
+ MYLOGI("Starting 'dumpstate' service\n");
+ android::status_t ret;
+ if ((ret = android::os::DumpstateService::Start()) != android::OK) {
+ MYLOGE("Unable to start DumpstateService: %d\n", ret);
+ }
}
- if (version != VERSION_DEFAULT) {
- usage();
- exit(1);
+ if (PropertiesHelper::IsDryRun()) {
+ MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
- MYLOGI("bugreport format version: %s\n", version.c_str());
+ MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
+ ds.extra_options_.c_str());
- do_early_screenshot = do_update_progress;
+ MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
+
+ ds.do_early_screenshot_ = ds.update_progress_;
// If we are going to use a socket, do it as early as possible
// to avoid timeouts from bugreport.
@@ -1345,98 +1499,72 @@
if (use_control_socket) {
MYLOGD("Opening control socket\n");
- control_socket_fd = open_socket("dumpstate");
- do_update_progress = 1;
+ ds.control_socket_fd_ = open_socket("dumpstate");
+ ds.update_progress_ = 1;
}
- /* full path of the temporary file containing the bugreport */
- std::string tmp_path;
-
- /* full path of the file containing the dumpstate logs*/
- std::string log_path;
-
- /* full path of the systrace file, when enabled */
- std::string systrace_path;
-
- /* full path of the temporary file containing the screenshot (when requested) */
- std::string screenshot_path;
-
- /* base name (without suffix or extensions) of the bugreport files */
- std::string base_name;
-
- /* pointer to the actual path, be it zip or text */
- std::string path;
-
- /* pointer to the zipped file */
- std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose);
-
- /* redirect output if needed */
- bool is_redirecting = !use_socket && use_outfile;
-
if (is_redirecting) {
- bugreport_dir = dirname(use_outfile);
- base_name = basename(use_outfile);
+ ds.bugreport_dir_ = dirname(use_outfile);
+ ds.base_name_ = basename(use_outfile);
if (do_add_date) {
char date[80];
- strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&now));
- suffix = date;
+ strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
+ ds.name_ = date;
} else {
- suffix = "undated";
+ ds.name_ = "undated";
}
- char build_id[PROPERTY_VALUE_MAX];
- property_get("ro.build.id", build_id, "UNKNOWN_BUILD");
- base_name = base_name + "-" + build_id;
- if (telephony_only) {
- base_name = base_name + "-telephony";
- }
- if (do_fb) {
- // TODO: if dumpstate was an object, the paths could be internal variables and then
- // we could have a function to calculate the derived values, such as:
- // screenshot_path = GetPath(".png");
- screenshot_path = bugreport_dir + "/" + base_name + "-" + suffix + ".png";
- }
- tmp_path = bugreport_dir + "/" + base_name + "-" + suffix + ".tmp";
- log_path = bugreport_dir + "/dumpstate_log-" + suffix + "-"
- + std::to_string(getpid()) + ".txt";
+ std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
- MYLOGD("Bugreport dir: %s\n"
- "Base name: %s\n"
- "Suffix: %s\n"
- "Log path: %s\n"
- "Temporary path: %s\n"
- "Screenshot path: %s\n",
- bugreport_dir.c_str(), base_name.c_str(), suffix.c_str(),
- log_path.c_str(), tmp_path.c_str(), screenshot_path.c_str());
+ if (telephony_only) {
+ ds.base_name_ += "-telephony";
+ }
+
+ ds.base_name_ += "-" + buildId;
+ if (do_fb) {
+ ds.screenshot_path_ = ds.GetPath(".png");
+ }
+ ds.tmp_path_ = ds.GetPath(".tmp");
+ ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
+
+ MYLOGD(
+ "Bugreport dir: %s\n"
+ "Base name: %s\n"
+ "Suffix: %s\n"
+ "Log path: %s\n"
+ "Temporary path: %s\n"
+ "Screenshot path: %s\n",
+ ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(),
+ ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
if (do_zip_file) {
- path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
- MYLOGD("Creating initial .zip file (%s)\n", path.c_str());
- create_parent_dirs(path.c_str());
- zip_file.reset(fopen(path.c_str(), "wb"));
- if (!zip_file) {
- MYLOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
+ ds.path_ = ds.GetPath(".zip");
+ MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
+ create_parent_dirs(ds.path_.c_str());
+ ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
+ if (ds.zip_file == nullptr) {
+ MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
do_zip_file = 0;
} else {
- zip_writer.reset(new ZipWriter(zip_file.get()));
+ ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
}
- add_text_zip_entry("version.txt", version);
+ ds.AddTextZipEntry("version.txt", ds.version_);
}
- if (do_update_progress) {
+ if (ds.update_progress_) {
if (do_broadcast) {
// clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
- "--es", "android.intent.extra.NAME", suffix,
- "--ei", "android.intent.extra.ID", std::to_string(id),
- "--ei", "android.intent.extra.PID", std::to_string(getpid()),
- "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
+ "--es", "android.intent.extra.NAME", ds.name_,
+ "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
+ "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
+ "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
};
// clang-format on
send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
}
if (use_control_socket) {
- dprintf(control_socket_fd, "BEGIN:%s\n", path.c_str());
+ dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
}
}
}
@@ -1448,66 +1576,75 @@
fclose(cmdline);
}
- /* open the vibrator before dropping root */
- std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
+ ::android::sp<IVibrator> vibrator = nullptr;
if (do_vibrate) {
- vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we"));
- if (vibrator) {
- vibrate(vibrator.get(), 150);
- }
- }
+ vibrator = IVibrator::getService("vibrator");
- if (do_fb && do_early_screenshot) {
- if (screenshot_path.empty()) {
- // should not have happened
- MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
- } else {
- MYLOGI("taking early screenshot\n");
- take_screenshot(screenshot_path);
- MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
- if (chown(screenshot_path.c_str(), AID_SHELL, AID_SHELL)) {
- MYLOGE("Unable to change ownership of screenshot file %s: %s\n",
- screenshot_path.c_str(), strerror(errno));
+ if (vibrator != nullptr) {
+ // cancel previous vibration if any
+ ::android::hardware::Return<VibratorStatus> offStatus = vibrator->off();
+ if (!offStatus.isOk() || offStatus != VibratorStatus::OK) {
+ MYLOGE("Vibrator off failed.");
+ } else {
+ ::android::hardware::Return<VibratorStatus> onStatus = vibrator->on(150);
+ if (!onStatus.isOk() || onStatus != VibratorStatus::OK) {
+ MYLOGE("Vibrator on failed.");
+ }
}
}
}
+ if (do_fb && ds.do_early_screenshot_) {
+ if (ds.screenshot_path_.empty()) {
+ // should not have happened
+ MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
+ } else {
+ MYLOGI("taking early screenshot\n");
+ ds.TakeScreenshot();
+ }
+ }
+
if (do_zip_file) {
- if (chown(path.c_str(), AID_SHELL, AID_SHELL)) {
- MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno));
+ if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
+ MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
+ strerror(errno));
}
}
if (is_redirecting) {
- redirect_to_file(stderr, const_cast<char*>(log_path.c_str()));
- if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) {
+ redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+ if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
- log_path.c_str(), strerror(errno));
+ ds.log_path_.c_str(), strerror(errno));
}
/* TODO: rather than generating a text file now and zipping it later,
it would be more efficient to redirect stdout to the zip entry
directly, but the libziparchive doesn't support that option yet. */
- redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
- if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) {
+ redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
+ if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
- tmp_path.c_str(), strerror(errno));
+ ds.tmp_path_.c_str(), strerror(errno));
}
}
+
+ // Don't buffer stdout
+ setvbuf(stdout, nullptr, _IONBF, 0);
+
// NOTE: there should be no stdout output until now, otherwise it would break the header.
// In particular, DurationReport objects should be created passing 'title, NULL', so their
// duration is logged into MYLOG instead.
- print_header(version);
+ ds.PrintHeader();
if (telephony_only) {
- dump_iptables();
- if (!drop_root_user()) {
+ DumpIpTables();
+ if (!DropRootUser()) {
return -1;
}
do_dmesg();
- do_logcat();
- do_kmsg();
- dumpstate_board();
- dump_modem_logs();
+ DoLogcat();
+ DoKmsg();
+ ds.DumpstateBoard();
+ DumpModemLogs();
} else {
// Dumps systrace right away, otherwise it will be filled with unnecessary events.
// First try to dump anrd trace if the daemon is running. Otherwise, dump
@@ -1516,40 +1653,44 @@
dump_systrace();
}
- // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
- dump_raft();
-
// Invoking the following dumpsys calls before dump_traces() to try and
// keep the system stats as close to its initial state as possible.
- run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
- run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "-t", "10", "cpuinfo", "-a", NULL);
+ RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
+ CommandOptions::WithTimeout(90).DropRoot().Build());
+ RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
+ CommandOptions::WithTimeout(10).DropRoot().Build());
+
+ // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
+ dump_raft();
/* collect stack traces from Dalvik and native processes (needs root) */
dump_traces_path = dump_traces();
/* Run some operations that require root. */
get_tombstone_fds(tombstone_data);
- add_dir(RECOVERY_DIR, true);
- add_dir(RECOVERY_DATA_DIR, true);
- add_dir(LOGPERSIST_DATA_DIR, false);
- if (!is_user_build()) {
- add_dir(PROFILE_DATA_DIR_CUR, true);
- add_dir(PROFILE_DATA_DIR_REF, true);
+ ds.AddDir(RECOVERY_DIR, true);
+ ds.AddDir(RECOVERY_DATA_DIR, true);
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+ if (!PropertiesHelper::IsUserBuild()) {
+ ds.AddDir(PROFILE_DATA_DIR_CUR, true);
+ ds.AddDir(PROFILE_DATA_DIR_REF, true);
}
add_mountinfo();
- dump_iptables();
+ DumpIpTables();
// Capture any IPSec policies in play. No keys are exposed here.
- run_command("IP XFRM POLICY", 10, "ip", "xfrm", "policy", nullptr);
+ RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
+ CommandOptions::WithTimeout(10).Build());
// Run ss as root so we can see socket marks.
- run_command("DETAILED SOCKET STATE", 10, "ss", "-eionptu", NULL);
+ RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"},
+ CommandOptions::WithTimeout(10).Build());
- if (!drop_root_user()) {
+ if (!DropRootUser()) {
return -1;
}
- dumpstate(do_early_screenshot ? "": screenshot_path, version);
+ dumpstate();
}
/* close output if needed */
@@ -1561,106 +1702,115 @@
if (use_outfile) {
/* check if user changed the suffix using system properties */
- char key[PROPERTY_KEY_MAX];
- char value[PROPERTY_VALUE_MAX];
- snprintf(key, sizeof(key), "dumpstate.%d.name", getpid());
- property_get(key, value, "");
+ std::string name = android::base::GetProperty(
+ android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
bool change_suffix= false;
- if (value[0]) {
+ if (!name.empty()) {
/* must whitelist which characters are allowed, otherwise it could cross directories */
std::regex valid_regex("^[-_a-zA-Z0-9]+$");
- if (std::regex_match(value, valid_regex)) {
+ if (std::regex_match(name.c_str(), valid_regex)) {
change_suffix = true;
} else {
- MYLOGE("invalid suffix provided by user: %s\n", value);
+ MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
}
}
if (change_suffix) {
- MYLOGI("changing suffix from %s to %s\n", suffix.c_str(), value);
- suffix = value;
- if (!screenshot_path.empty()) {
- std::string new_screenshot_path =
- bugreport_dir + "/" + base_name + "-" + suffix + ".png";
- if (rename(screenshot_path.c_str(), new_screenshot_path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", screenshot_path.c_str(),
- new_screenshot_path.c_str(), strerror(errno));
+ MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
+ ds.name_ = name;
+ if (!ds.screenshot_path_.empty()) {
+ std::string new_screenshot_path = ds.GetPath(".png");
+ if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
+ new_screenshot_path.c_str(), strerror(errno));
} else {
- screenshot_path = new_screenshot_path;
+ ds.screenshot_path_ = new_screenshot_path;
}
}
}
bool do_text_file = true;
if (do_zip_file) {
- std::string entry_name = base_name + "-" + suffix + ".txt";
- MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
- if (!finish_zip_file(entry_name, tmp_path, now)) {
+ if (!ds.FinishZipFile()) {
MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
do_text_file = true;
} else {
do_text_file = false;
// Since zip file is already created, it needs to be renamed.
- std::string new_path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
- if (path != new_path) {
- MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str());
- if (rename(path.c_str(), new_path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", path.c_str(),
- new_path.c_str(), strerror(errno));
+ std::string new_path = ds.GetPath(".zip");
+ if (ds.path_ != new_path) {
+ MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+ if (rename(ds.path_.c_str(), new_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+ strerror(errno));
} else {
- path = new_path;
+ ds.path_ = new_path;
}
}
}
}
if (do_text_file) {
- path = bugreport_dir + "/" + base_name + "-" + suffix + ".txt";
- MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
- if (rename(tmp_path.c_str(), path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
- path.clear();
+ ds.path_ = ds.GetPath(".txt");
+ MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
+ ds.tmp_path_.c_str());
+ if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
+ strerror(errno));
+ ds.path_.clear();
}
}
if (use_control_socket) {
if (do_text_file) {
- dprintf(control_socket_fd, "FAIL:could not create zip file, check %s "
- "for more details\n", log_path.c_str());
+ dprintf(ds.control_socket_fd_,
+ "FAIL:could not create zip file, check %s "
+ "for more details\n",
+ ds.log_path_.c_str());
} else {
- dprintf(control_socket_fd, "OK:%s\n", path.c_str());
+ dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
}
}
}
/* vibrate a few but shortly times to let user know it's finished */
- if (vibrator) {
- for (int i = 0; i < 3; i++) {
- vibrate(vibrator.get(), 75);
- usleep((75 + 50) * 1000);
+ if (vibrator != nullptr) {
+ // in case dumpstate magically completes before the above vibration
+ ::android::hardware::Return<VibratorStatus> offStatus = vibrator->off();
+ if (!offStatus.isOk() || offStatus != VibratorStatus::OK) {
+ MYLOGE("Vibrator off failed.");
+ } else {
+ for (int i = 0; i < 3; i++) {
+ ::android::hardware::Return<VibratorStatus> onStatus = vibrator->on(75);
+ if (!onStatus.isOk() || onStatus != VibratorStatus::OK) {
+ MYLOGE("Vibrator on failed.");
+ break;
+ }
+ usleep((75 + 50) * 1000);
+ }
}
}
/* tell activity manager we're done */
if (do_broadcast) {
- if (!path.empty()) {
- MYLOGI("Final bugreport path: %s\n", path.c_str());
+ if (!ds.path_.empty()) {
+ MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
// clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
- "--ei", "android.intent.extra.ID", std::to_string(id),
- "--ei", "android.intent.extra.PID", std::to_string(getpid()),
- "--ei", "android.intent.extra.MAX", std::to_string(weight_total),
- "--es", "android.intent.extra.BUGREPORT", path,
- "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
+ "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
+ "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
+ "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
+ "--es", "android.intent.extra.BUGREPORT", ds.path_,
+ "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
};
// clang-format on
if (do_fb) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.SCREENSHOT");
- am_args.push_back(screenshot_path);
+ am_args.push_back(ds.screenshot_path_);
}
if (is_remote_mode) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
- am_args.push_back(SHA256_file_hash(path));
+ am_args.push_back(SHA256_file_hash(ds.path_));
send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
} else {
send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
@@ -1670,16 +1820,18 @@
}
}
- MYLOGD("Final progress: %d/%d (originally %d)\n", progress, weight_total, WEIGHT_TOTAL);
- MYLOGI("done\n");
+ MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(),
+ ds.progress_->GetInitialMax());
+ ds.progress_->Save();
+ MYLOGI("done (id %d)\n", ds.id_);
if (is_redirecting) {
fclose(stderr);
}
- if (use_control_socket && control_socket_fd != -1) {
- MYLOGD("Closing control socket\n");
- close(control_socket_fd);
+ if (use_control_socket && ds.control_socket_fd_ != -1) {
+ MYLOGD("Closing control socket\n");
+ close(ds.control_socket_fd_);
}
return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 1b28c49..b2cd241 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -14,93 +14,337 @@
* limitations under the License.
*/
-#ifndef _DUMPSTATE_H_
-#define _DUMPSTATE_H_
-
-/* When defined, skips the real dumps and just print the section headers.
- Useful when debugging dumpstate itself. */
-//#define _DUMPSTATE_DRY_RUN_
-
-#ifdef _DUMPSTATE_DRY_RUN_
-#define ON_DRY_RUN_RETURN(X) return X
-#define ON_DRY_RUN(code) code
-#else
-#define ON_DRY_RUN_RETURN(X)
-#define ON_DRY_RUN(code)
-#endif
-
-#ifndef MYLOGD
-#define MYLOGD(...) fprintf(stderr, __VA_ARGS__); ALOGD(__VA_ARGS__);
-#endif
-
-#ifndef MYLOGI
-#define MYLOGI(...) fprintf(stderr, __VA_ARGS__); ALOGI(__VA_ARGS__);
-#endif
-
-#ifndef MYLOGE
-#define MYLOGE(...) fprintf(stderr, __VA_ARGS__); ALOGE(__VA_ARGS__);
-#endif
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
#include <time.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
+
+#include <string>
#include <vector>
-#define SU_PATH "/system/xbin/su"
+#include <android-base/macros.h>
+#include <ziparchive/zip_writer.h>
+#include "DumpstateUtil.h"
+#include "android/os/BnDumpstate.h"
+
+// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
+// std::vector<std::string>
+// TODO: remove once not used
+#define MAX_ARGS_ARRAY_SIZE 1000
+
+// TODO: move everything under this namespace
+// TODO: and then remove explicitly android::os::dumpstate:: prefixes
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpstateTest;
+class ProgressTest;
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+// TODO: remove once moved to HAL
#ifdef __cplusplus
extern "C" {
#endif
-typedef void (for_each_pid_func)(int, const char *);
-typedef void (for_each_tid_func)(int, int, const char *);
-
-/* Estimated total weight of bugreport generation.
+/*
+ * Helper class used to report how long it takes for a section to finish.
*
- * Each section contributes to the total weight by an individual weight, so the overall progress
- * can be calculated by dividing the all completed weight by the total weight.
+ * Typical usage:
*
- * This value is defined empirically and it need to be adjusted as more sections are added.
+ * DurationReporter duration_reporter(title);
*
- * It does not need to match the exact sum of all sections, but ideally it should to be slight more
- * than such sum: a value too high will cause the bugreport to finish before the user expected (for
- * example, jumping from 70% to 100%), while a value too low will cause the progress to get stuck
- * at an almost-finished value (like 99%) for a while.
*/
-static const int WEIGHT_TOTAL = 6500;
+class DurationReporter {
+ public:
+ DurationReporter(const std::string& title, bool log_only = false);
-/* Most simple commands have 10 as timeout, so 5 is a good estimate */
-static const int WEIGHT_FILE = 5;
+ ~DurationReporter();
+
+ private:
+ std::string title_;
+ bool log_only_;
+ uint64_t started_;
+
+ DISALLOW_COPY_AND_ASSIGN(DurationReporter);
+};
/*
- * TODO: the dumpstate internal state is getting fragile; for example, this variable is defined
- * here, declared at utils.cpp, and used on utils.cpp and dumpstate.cpp.
- * It would be better to take advantage of the C++ migration and encapsulate the state in an object,
- * but that will be better handled in a major C++ refactoring, which would also get rid of other C
- * idioms (like using std::string instead of char*, removing varargs, etc...) */
-extern int do_update_progress, progress, weight_total, control_socket_fd;
+ * Keeps track of current progress and estimated max, saving stats on file to tune up future runs.
+ *
+ * Each `dumpstate` section contributes to the total weight by an individual weight, so the overall
+ * progress can be calculated by dividing the estimate max progress by the current progress.
+ *
+ * The estimated max progress is initially set to a value (`kDefaultMax) defined empirically, but
+ * it's adjusted after each dumpstate run by storing the average duration in a file.
+ *
+ */
+class Progress {
+ friend class android::os::dumpstate::ProgressTest;
+ friend class android::os::dumpstate::DumpstateTest;
-/* full path of the directory where the bugreport files will be written */
-extern std::string bugreport_dir;
+ public:
+ /*
+ * Default estimation of the max duration of a bugreport generation.
+ *
+ * It does not need to match the exact sum of all sections, but ideally it should to be slight
+ * more than such sum: a value too high will cause the bugreport to finish before the user
+ * expected (for example, jumping from 70% to 100%), while a value too low will cause the
+ * progress to get stuck at an almost-finished value (like 99%) for a while.
+ *
+ * This constant is only used when the average duration from previous runs cannot be used.
+ */
+ static const int kDefaultMax;
-/* root dir for all files copied as-is into the bugreport. */
-extern const std::string ZIP_ROOT_DIR;
+ Progress(const std::string& path = "");
-/* Checkes whether dumpstate is generating a zipped bugreport. */
-bool is_zipping();
+ // Gets the current progress.
+ int32_t Get() const;
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
+ // Gets the current estimated max progress.
+ int32_t GetMax() const;
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
+ // Gets the initial estimated max progress.
+ int32_t GetInitialMax() const;
-/* adds all files from a directory to the zipped bugreport file */
-void add_dir(const char *dir, bool recursive);
+ // Increments progress (ignored if not positive).
+ // Returns `true` if the max progress increased as well.
+ bool Inc(int32_t delta);
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path);
+ // Persist the stats.
+ void Save();
+
+ void Dump(int fd, const std::string& prefix) const;
+
+ private:
+ Progress(int32_t initial_max, float growth_factor,
+ const std::string& path = ""); // Used by test cases.
+ Progress(int32_t initial_max, int32_t progress, float growth_factor); // Used by test cases.
+ void Load();
+ int32_t initial_max_;
+ int32_t progress_;
+ int32_t max_;
+ float growth_factor_;
+ int32_t n_runs_;
+ int32_t average_max_;
+ const std::string& path_;
+};
+
+/*
+ * List of supported zip format versions.
+ *
+ * See bugreport-format.md for more info.
+ */
+static std::string VERSION_CURRENT = "1.0";
+
+/*
+ * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version
+ * will be bumped to 2.0-dev-1.
+ */
+static std::string VERSION_SPLIT_ANR = "2.0-dev-1";
+
+/*
+ * "Alias" for the current version.
+ */
+static std::string VERSION_DEFAULT = "default";
+
+/*
+ * Main class driving a bugreport generation.
+ *
+ * Currently, it only contains variables that are accessed externally, but gradually the functions
+ * that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
+ */
+class Dumpstate {
+ friend class DumpstateTest;
+
+ public:
+ static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
+
+ static Dumpstate& GetInstance();
+
+ /* Checkes whether dumpstate is generating a zipped bugreport. */
+ bool IsZipping() const;
+
+ /*
+ * Forks a command, waits for it to finish, and returns its status.
+ *
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |full_command| array containing the command (first entry) and its arguments.
+ * Must contain at least one element.
+ * |options| optional argument defining the command's behavior.
+ */
+ int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+ const android::os::dumpstate::CommandOptions& options =
+ android::os::dumpstate::CommandOptions::DEFAULT);
+
+ /*
+ * Runs `dumpsys` with the given arguments, automatically setting its timeout
+ * (`-t` argument)
+ * according to the command options.
+ *
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |dumpsys_args| `dumpsys` arguments (except `-t`).
+ * |options| optional argument defining the command's behavior.
+ * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the
+ * timeout from `options`)
+ */
+ void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
+ const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
+ long dumpsys_timeout = 0);
+
+ /*
+ * Prints the contents of a file.
+ *
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |path| location of the file to be dumped.
+ */
+ int DumpFile(const std::string& title, const std::string& path);
+
+ /*
+ * Adds a new entry to the existing zip file.
+ * */
+ bool AddZipEntry(const std::string& entry_name, const std::string& entry_path);
+
+ /*
+ * Adds a new entry to the existing zip file.
+ */
+ bool AddZipEntryFromFd(const std::string& entry_name, int fd);
+
+ /*
+ * Adds a text entry entry to the existing zip file.
+ */
+ bool AddTextZipEntry(const std::string& entry_name, const std::string& content);
+
+ /*
+ * Adds all files from a directory to the zipped bugreport file.
+ */
+ void AddDir(const std::string& dir, bool recursive);
+
+ /*
+ * Takes a screenshot and save it to the given `path`.
+ *
+ * If `path` is empty, uses a standard path based on the bugreport name.
+ */
+ void TakeScreenshot(const std::string& path = "");
+
+ /////////////////////////////////////////////////////////////////////
+ // TODO: members below should be private once refactor is finished //
+ /////////////////////////////////////////////////////////////////////
+
+ // TODO: temporary method until Dumpstate object is properly set
+ void SetProgress(std::unique_ptr<Progress> progress);
+
+ void DumpstateBoard();
+
+ /*
+ * Updates the overall progress of the bugreport generation by the given weight increment.
+ */
+ void UpdateProgress(int32_t delta);
+
+ /* Prints the dumpstate header on `stdout`. */
+ void PrintHeader() const;
+
+ /*
+ * Adds the temporary report to the existing .zip file, closes the .zip file, and removes the
+ * temporary file.
+ */
+ bool FinishZipFile();
+
+ /* Gets the path of a bugreport file with the given suffix. */
+ std::string GetPath(const std::string& suffix) const;
+
+ // TODO: initialize fields on constructor
+
+ // dumpstate id - unique after each device reboot.
+ uint32_t id_;
+
+ // dumpstate pid
+ pid_t pid_;
+
+ // Whether progress updates should be published.
+ bool update_progress_ = false;
+
+ // How frequently the progess should be updated;the listener will only be notificated when the
+ // delta from the previous update is more than the threshold.
+ int32_t update_progress_threshold_ = 100;
+
+ // Last progress that triggered a listener updated
+ int32_t last_updated_progress_;
+
+ // Whether it should take an screenshot earlier in the process.
+ bool do_early_screenshot_ = false;
+
+ std::unique_ptr<Progress> progress_;
+
+ // When set, defines a socket file-descriptor use to report progress to bugreportz.
+ int control_socket_fd_ = -1;
+
+ // Bugreport format version;
+ std::string version_ = VERSION_CURRENT;
+
+ // Command-line arguments as string
+ std::string args_;
+
+ // Extra options passed as system property.
+ std::string extra_options_;
+
+ // Full path of the directory where the bugreport files will be written.
+ std::string bugreport_dir_;
+
+ // Full path of the temporary file containing the screenshot (when requested).
+ std::string screenshot_path_;
+
+ time_t now_;
+
+ // Base name (without suffix or extensions) of the bugreport files, typically
+ // `bugreport-BUILD_ID`.
+ std::string base_name_;
+
+ // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
+ // `-d`), but it could be changed by the user..
+ std::string name_;
+
+ // Full path of the temporary file containing the bugreport.
+ std::string tmp_path_;
+
+ // Full path of the file containing the dumpstate logs.
+ std::string log_path_;
+
+ // Pointer to the actual path, be it zip or text.
+ std::string path_;
+
+ // Pointer to the zipped file.
+ std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose};
+
+ // Pointer to the zip structure.
+ std::unique_ptr<ZipWriter> zip_writer_;
+
+ // Binder object listing to progress.
+ android::sp<android::os::IDumpstateListener> listener_;
+ std::string listener_name_;
+
+ private:
+ // Used by GetInstance() only.
+ Dumpstate(const std::string& version = VERSION_CURRENT);
+
+ DISALLOW_COPY_AND_ASSIGN(Dumpstate);
+};
+
+// for_each_pid_func = void (*)(int, const char*);
+// for_each_tid_func = void (*)(int, int, const char*);
+
+typedef void(for_each_pid_func)(int, const char*);
+typedef void(for_each_tid_func)(int, int, const char*);
/* saves the the contents of a file as a long */
int read_file_as_long(const char *path, long int *output);
@@ -116,35 +360,12 @@
* to false when set to NULL. dump_from_fd will always be
* called with title NULL.
*/
-int dump_files(const char *title, const char *dir,
- bool (*skip)(const char *path),
- int (*dump_from_fd)(const char *title, const char *path, int fd));
-
-// TODO: need to refactor all those run_command variations; there shold be just one, receiving an
-// optional CommandOptions objects with values such as run_always, drop_root, etc...
-
-/* forks a command and waits for it to finish -- terminate args with NULL */
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...);
-int run_command(const char *title, int timeout_seconds, const char *command, ...);
-
-enum RootMode { DROP_ROOT, DONT_DROP_ROOT };
-enum StdoutMode { NORMAL_STDOUT, REDIRECT_TO_STDERR };
-
-/* forks a command and waits for it to finish
- first element of args is the command, and last must be NULL.
- command is always ran, even when _DUMPSTATE_DRY_RUN_ is defined. */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
- int timeout_seconds, const char *args[]);
-
-/* switch to non-root user and group */
-bool drop_root_user();
+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));
/* sends a broadcast using Activity Manager */
void send_broadcast(const std::string& action, const std::vector<std::string>& args);
-/* updates the overall progress of dumpstate by the given weight increment */
-void update_progress(int weight);
-
/* prints all the system properties */
void print_properties();
@@ -154,9 +375,12 @@
/* redirect output to a service control socket */
void redirect_to_socket(FILE *redirect, const char *service);
-/* redirect output to a file */
+/* redirect output to a new file */
void redirect_to_file(FILE *redirect, char *path);
+/* redirect output to an existing file */
+void redirect_to_existing_file(FILE *redirect, char *path);
+
/* create leading directories, if necessary */
void create_parent_dirs(const char *path);
@@ -187,15 +411,6 @@
/* Play a sound via Stagefright */
void play_sound(const char *path);
-/* Implemented by libdumpstate_board to dump board-specific info */
-void dumpstate_board();
-
-/* Takes a screenshot and save it to the given file */
-void take_screenshot(const std::string& path);
-
-/* Vibrates for a given durating (in milliseconds). */
-void vibrate(FILE* vibrator, int ms);
-
/* Checks if a given path is a directory. */
bool is_dir(const char* pathname);
@@ -208,34 +423,8 @@
/** Gets command-line arguments. */
void format_args(int argc, const char *argv[], std::string *args);
-/** Tells if the device is running a user build. */
-bool is_user_build();
-
-/*
- * Helper class used to report how long it takes for a section to finish.
- *
- * Typical usage:
- *
- * DurationReporter duration_reporter(title);
- *
- */
-class DurationReporter {
-public:
- 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 336db9f..2e72574 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,40 +17,3 @@
class main
disabled
oneshot
-
-# bugreportplus is an enhanced version of bugreport that provides a better
-# user interface (like displaying progress and allowing user to enter details).
-# It's typically triggered by the power button or developer settings.
-service bugreportplus /system/bin/dumpstate -d -B -P -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
- class main
- disabled
- oneshot
-
-# bugreportremote is an altered version of bugreport that is supposed to be
-# called not by human user of the device, but by DevicePolicyManagerService only when the
-# Device Owner explicitly requests it, and shared with the Device Policy Controller (DPC) app only
-# if the user consents
-# it will disable vibrations, screenshot taking and will not track progress or
-# allow user to enter any details
-service bugreportremote /system/bin/dumpstate -d -q -B -R -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/remote/bugreport
- class main
- disabled
- oneshot
-
-# bugreportwear is a wearable version of bugreport that displays progress and takes early
-# screenshot.
-service bugreportwear /system/bin/dumpstate -d -B -P -p -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
- class main
- disabled
- oneshot
-
-# bugreportelefony is a lightweight version of bugreport that only includes a few, urgent
-# sections used to report telephony bugs.
-service bugreportelefony /system/bin/dumpstate -t -d -B -z \
- -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
- class main
- disabled
- oneshot
diff --git a/cmds/dumpstate/testdata/empty-file.txt b/cmds/dumpstate/testdata/empty-file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/dumpstate/testdata/empty-file.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
new file mode 100644
index 0000000..7b7a187
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/testdata/multiple-lines.txt
new file mode 100644
index 0000000..bead103
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/testdata/single-line-with-newline.txt
new file mode 100644
index 0000000..cb48c82
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line-with-newline.txt
@@ -0,0 +1 @@
+I AM LINE1
diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/testdata/single-line.txt
new file mode 100644
index 0000000..2f64046
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line.txt
@@ -0,0 +1 @@
+I AM LINE1
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
new file mode 100644
index 0000000..dad9fe8
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
@@ -0,0 +1 @@
+SIX_SIX_SIX 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
new file mode 100644
index 0000000..4facef9
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
@@ -0,0 +1 @@
+-666 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
new file mode 100644
index 0000000..42508f1
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
@@ -0,0 +1 @@
+4815162342 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
new file mode 100644
index 0000000..a23ba2c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
@@ -0,0 +1 @@
+666 FORTY_TWO
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
new file mode 100644
index 0000000..dd529b4
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
@@ -0,0 +1 @@
+666 -42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
new file mode 100644
index 0000000..b148b46
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
@@ -0,0 +1 @@
+666 4815162342
diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
new file mode 100644
index 0000000..4a9466d
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
@@ -0,0 +1 @@
+N_RUNS AVERAGE
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
new file mode 100644
index 0000000..0aef60c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
@@ -0,0 +1 @@
+1 10
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/testdata/stats-two-runs.txt
new file mode 100644
index 0000000..9af1233
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-two-runs.txt
@@ -0,0 +1 @@
+2 15
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
new file mode 100644
index 0000000..1c19268
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -0,0 +1,1157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
+#include "android/os/BnDumpstate.h"
+#include "dumpstate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+using ::testing::EndsWith;
+using ::testing::HasSubstr;
+using ::testing::IsNull;
+using ::testing::IsEmpty;
+using ::testing::NotNull;
+using ::testing::StrEq;
+using ::testing::StartsWith;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class DumpstateListenerMock : public IDumpstateListener {
+ public:
+ MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
+ MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
+
+ protected:
+ MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
+static int calls_;
+
+// Base class for all tests in this file
+class DumpstateBaseTest : public Test {
+ public:
+ virtual void SetUp() override {
+ calls_++;
+ SetDryRun(false);
+ }
+
+ void SetDryRun(bool dry_run) const {
+ PropertiesHelper::dry_run_ = dry_run;
+ }
+
+ void SetBuildType(const std::string& build_type) const {
+ PropertiesHelper::build_type_ = build_type;
+ }
+
+ bool IsStandalone() const {
+ return calls_ == 1;
+ }
+
+ void DropRoot() const {
+ DropRootUser();
+ uid_t uid = getuid();
+ ASSERT_EQ(2000, (int)uid);
+ }
+
+ protected:
+ const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str());
+ const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/";
+ const std::string kTestDataPath = kFixturesPath + "/testdata/";
+ const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture";
+ const std::string kEchoCommand = "/system/bin/echo";
+
+ /*
+ * Copies a text file fixture to a temporary file, returning it's path.
+ *
+ * Useful in cases where the test case changes the content of the tile.
+ */
+ std::string CopyTextFileFixture(const std::string& relative_name) {
+ std::string from = kTestDataPath + relative_name;
+ // Not using TemporaryFile because it's deleted at the end, and it's useful to keep it
+ // around for poking when the test fails.
+ std::string to = kTestDataPath + relative_name + ".tmp";
+ ALOGD("CopyTextFileFixture: from %s to %s\n", from.c_str(), to.c_str());
+ android::base::RemoveFileIfExists(to);
+ CopyTextFile(from, to);
+ return to.c_str();
+ }
+
+ // Need functions that returns void to use assertions -
+ // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#assertion-placement
+ void ReadFileToString(const std::string& path, std::string* content) {
+ ASSERT_TRUE(android::base::ReadFileToString(path, content))
+ << "could not read contents from " << path;
+ }
+ void WriteStringToFile(const std::string& content, const std::string& path) {
+ ASSERT_TRUE(android::base::WriteStringToFile(content, path))
+ << "could not write contents to " << path;
+ }
+
+ private:
+ void CopyTextFile(const std::string& from, const std::string& to) {
+ std::string content;
+ ReadFileToString(from, &content);
+ WriteStringToFile(content, to);
+ }
+};
+
+class DumpstateTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ SetDryRun(false);
+ SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
+ ds.progress_.reset(new Progress());
+ ds.update_progress_ = false;
+ ds.update_progress_threshold_ = 0;
+ }
+
+ // Runs a command and capture `stdout` and `stderr`.
+ int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ CaptureStdout();
+ CaptureStderr();
+ int status = ds.RunCommand(title, full_command, options);
+ out = GetCapturedStdout();
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ // Dumps a file and capture `stdout` and `stderr`.
+ int DumpFile(const std::string& title, const std::string& path) {
+ CaptureStdout();
+ CaptureStderr();
+ int status = ds.DumpFile(title, path);
+ out = GetCapturedStdout();
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ void SetProgress(long progress, long initial_max, long threshold = 0) {
+ ds.update_progress_ = true;
+ ds.update_progress_threshold_ = threshold;
+ ds.last_updated_progress_ = 0;
+ ds.progress_.reset(new Progress(initial_max, progress, 1.2));
+ }
+
+ std::string GetProgressMessage(const std::string& listener_name, int progress, int max,
+ int old_max = 0, bool update_progress = true) {
+ EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
+ EXPECT_EQ(max, ds.progress_->GetMax()) << "invalid max";
+
+ bool max_increased = old_max > 0;
+
+ std::string message = "";
+ if (max_increased) {
+ message =
+ android::base::StringPrintf("Adjusting max progress from %d to %d\n", old_max, max);
+ }
+
+ if (update_progress) {
+ message += android::base::StringPrintf("Setting progress (%s): %d/%d\n",
+ listener_name.c_str(), progress, max);
+ }
+
+ return message;
+ }
+
+ // `stdout` and `stderr` from the last command ran.
+ std::string out, err;
+
+ Dumpstate& ds = Dumpstate::GetInstance();
+};
+
+TEST_F(DumpstateTest, RunCommandNoArgs) {
+ EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateTest, RunCommandNoTitle) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithTitle) {
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out,
+ StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
+ EXPECT_EQ(
+ 0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandRedirectStderr) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithOneArg) {
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandDryRun) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand +
+ ") ------\n\t(skipped on dry run)\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunAlways) {
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandNotFound) {
+ EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+ EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+ EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateTest, RunCommandFails) {
+ EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+ EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+ EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandCrashes) {
+ EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+ // We don't know the exit code, so check just the prefix.
+ EXPECT_THAT(
+ out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+ EXPECT_THAT(
+ err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateTest, RunCommandTimesout) {
+ EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+ CommandOptions::WithTimeout(1).Build()));
+ EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+ EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateTest, RunCommandIsKilled) {
+ CaptureStdout();
+ CaptureStderr();
+
+ std::thread t([=]() {
+ EXPECT_EQ(SIGTERM, ds.RunCommand("", {kSimpleCommand, "--pid", "--sleep", "20"},
+ CommandOptions::WithTimeout(100).Always().Build()));
+ });
+
+ // Capture pid and pre-sleep output.
+ sleep(1); // Wait a little bit to make sure pid and 1st line were printed.
+ std::string err = GetCapturedStderr();
+ EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+ std::string out = GetCapturedStdout();
+ std::vector<std::string> lines = android::base::Split(out, "\n");
+ ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+ int pid = atoi(lines[0].c_str());
+ EXPECT_THAT(lines[1], StrEq("stdout line1"));
+ EXPECT_THAT(lines[2], IsEmpty()); // \n
+
+ // Then kill the process.
+ CaptureStdout();
+ CaptureStderr();
+ ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+ t.join();
+
+ // Finally, check output after murder.
+ out = GetCapturedStdout();
+ err = GetCapturedStderr();
+
+ EXPECT_THAT(out, StrEq("*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+ EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandProgress) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+ SetProgress(0, 30);
+
+ EXPECT_CALL(*listener, onProgressUpdated(20));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build()));
+ std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ EXPECT_CALL(*listener, onProgressUpdated(30));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Run a command that will increase maximum timeout.
+ EXPECT_CALL(*listener, onProgressUpdated(31));
+ EXPECT_CALL(*listener, onMaxProgressUpdated(37));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30); // 20% increase
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Make sure command ran while in dry_run is counted.
+ SetDryRun(true);
+ EXPECT_CALL(*listener, onProgressUpdated(35));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 35, 37);
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq(progress_message));
+
+ ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+ SetProgress(0, 8, 5); // 8 max, 5 threshold
+
+ // First update should always be sent.
+ EXPECT_CALL(*listener, onProgressUpdated(1));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+ std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5).
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+
+ // Third update should be sent because it reaches threshold (6 - 1 = 5).
+ EXPECT_CALL(*listener, onProgressUpdated(6));
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 6, 8);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5).
+ // But max update should be sent.
+ EXPECT_CALL(*listener, onMaxProgressUpdated(10)); // 9 * 120% = 10.8 = 10
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandDropRoot) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandDropRoot() on test suite\n")
+ return;
+ }
+ // First check root case - only available when running with 'adb root'.
+ uid_t uid = getuid();
+ if (uid == 0) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ return;
+ }
+ // Then run dropping root.
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).DropRoot().Build()));
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandAsRootUserBuild() on test suite\n")
+ return;
+ }
+ if (!PropertiesHelper::IsUserBuild()) {
+ // Emulates user build if necessarily.
+ SetBuildType("user");
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ // We don't know the exact path of su, so we just check for the 'root ...' commands
+ EXPECT_THAT(out, StartsWith("Skipping"));
+ EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandAsRootNonUserBuild() on test suite\n")
+ return;
+ }
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+ return;
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
+ EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(out,
+ StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundWithTitle) {
+ EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(err, IsEmpty());
+ // We don't know the exact duration, so we check the prefix and suffix
+ EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No "
+ "such file or directory\n"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLine) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLines) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) {
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) {
+ SetDryRun(true);
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRun) {
+ SetDryRun(true);
+ EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(
+ out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
+ EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n------"));
+ EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+ SetProgress(0, 30);
+
+ EXPECT_CALL(*listener, onProgressUpdated(5));
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+
+ std::string progress_message =
+ GetProgressMessage(ds.listener_name_, 5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
+ EXPECT_THAT(err, StrEq(progress_message));
+ EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
+
+ ds.listener_.clear();
+}
+
+class DumpstateServiceTest : public DumpstateBaseTest {
+ public:
+ DumpstateService dss;
+};
+
+TEST_F(DumpstateServiceTest, SetListenerNoName) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ sp<IDumpstateToken> token;
+ EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+ ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
+ sp<IDumpstateToken> token;
+ EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+ ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerTwice) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ sp<IDumpstateToken> token;
+ EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+ ASSERT_THAT(token, NotNull());
+ EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+
+ token.clear();
+ EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+ ASSERT_THAT(token, IsNull());
+ EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+}
+
+class ProgressTest : public DumpstateBaseTest {
+ public:
+ Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
+ return Progress(max, growth_factor, path);
+ }
+
+ void AssertStats(const std::string& path, int32_t expected_runs, int32_t expected_average) {
+ std::string expected_content =
+ android::base::StringPrintf("%d %d\n", expected_runs, expected_average);
+ std::string actual_content;
+ ReadFileToString(path, &actual_content);
+ ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path;
+ }
+};
+
+TEST_F(ProgressTest, SimpleTest) {
+ Progress progress;
+ EXPECT_EQ(0, progress.Get());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+
+ bool max_increased = progress.Inc(1);
+ EXPECT_EQ(1, progress.Get());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Ignore negative increase.
+ max_increased = progress.Inc(-1);
+ EXPECT_EQ(1, progress.Get());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsInsideNewRange) {
+ Progress progress = GetInstance(10, 1.2); // 20% growth factor
+ EXPECT_EQ(0, progress.Get());
+ EXPECT_EQ(10, progress.GetInitialMax());
+ EXPECT_EQ(10, progress.GetMax());
+
+ // No increase
+ bool max_increased = progress.Inc(10);
+ EXPECT_EQ(10, progress.Get());
+ EXPECT_EQ(10, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Increase, with new value < max*20%
+ max_increased = progress.Inc(1);
+ EXPECT_EQ(11, progress.Get());
+ EXPECT_EQ(13, progress.GetMax()); // 11 average * 20% growth = 13.2 = 13
+ EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsOutsideNewRange) {
+ Progress progress = GetInstance(10, 1.2); // 20% growth factor
+ EXPECT_EQ(0, progress.Get());
+ EXPECT_EQ(10, progress.GetInitialMax());
+ EXPECT_EQ(10, progress.GetMax());
+
+ // No increase
+ bool max_increased = progress.Inc(10);
+ EXPECT_EQ(10, progress.Get());
+ EXPECT_EQ(10, progress.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Increase, with new value > max*20%
+ max_increased = progress.Inc(5);
+ EXPECT_EQ(15, progress.Get());
+ EXPECT_EQ(18, progress.GetMax()); // 15 average * 20% growth = 18
+ EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, InvalidPath) {
+ Progress progress("/devil/null");
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, EmptyFile) {
+ Progress progress(CopyTextFileFixture("empty-file.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNAN) {
+ Progress progress(CopyTextFileFixture("stats-invalid-1st-NAN.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNAN) {
+ Progress progress(CopyTextFileFixture("stats-invalid-2nd-NAN.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLineBothNAN) {
+ Progress progress(CopyTextFileFixture("stats-invalid-both-NAN.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNegative) {
+ Progress progress(CopyTextFileFixture("stats-invalid-1st-negative.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNegative) {
+ Progress progress(CopyTextFileFixture("stats-invalid-2nd-negative.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryTooBig) {
+ Progress progress(CopyTextFileFixture("stats-invalid-1st-too-big.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryTooBig) {
+ Progress progress(CopyTextFileFixture("stats-invalid-2nd-too-big.txt"));
+ EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+// Tests stats are properly saved when the file does not exists.
+TEST_F(ProgressTest, FirstTime) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it's failing when running as suite
+ MYLOGE("Skipping ProgressTest.FirstTime() on test suite\n")
+ return;
+ }
+
+ std::string path = kTestDataPath + "FirstTime.txt";
+ android::base::RemoveFileIfExists(path);
+
+ Progress run1(path);
+ EXPECT_EQ(0, run1.Get());
+ EXPECT_EQ(Progress::kDefaultMax, run1.GetInitialMax());
+ EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+
+ bool max_increased = run1.Inc(20);
+ EXPECT_EQ(20, run1.Get());
+ EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ run1.Save();
+ AssertStats(path, 1, 20);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 1 run.
+// Data on file is 1 run and 109 average.
+TEST_F(ProgressTest, SecondTime) {
+ std::string path = CopyTextFileFixture("stats-one-run-no-newline.txt");
+
+ Progress run1 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run1.Get());
+ EXPECT_EQ(10, run1.GetInitialMax());
+ EXPECT_EQ(10, run1.GetMax());
+
+ bool max_increased = run1.Inc(20);
+ EXPECT_EQ(20, run1.Get());
+ EXPECT_EQ(24, run1.GetMax());
+ EXPECT_TRUE(max_increased);
+
+ // Average now is 2 runs and (10 + 20)/ 2 = 15
+ run1.Save();
+ AssertStats(path, 2, 15);
+
+ Progress run2 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run2.Get());
+ EXPECT_EQ(15, run2.GetInitialMax());
+ EXPECT_EQ(15, run2.GetMax());
+
+ max_increased = run2.Inc(25);
+ EXPECT_EQ(25, run2.Get());
+ EXPECT_EQ(30, run2.GetMax());
+ EXPECT_TRUE(max_increased);
+
+ // Average now is 3 runs and (15 * 2 + 25)/ 3 = 18.33 = 18
+ run2.Save();
+ AssertStats(path, 3, 18);
+
+ Progress run3 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run3.Get());
+ EXPECT_EQ(18, run3.GetInitialMax());
+ EXPECT_EQ(18, run3.GetMax());
+
+ // Make sure average decreases as well
+ max_increased = run3.Inc(5);
+ EXPECT_EQ(5, run3.Get());
+ EXPECT_EQ(18, run3.GetMax());
+ EXPECT_FALSE(max_increased);
+
+ // Average now is 4 runs and (18 * 3 + 5)/ 4 = 14.75 = 14
+ run3.Save();
+ AssertStats(path, 4, 14);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 2 runs.
+// Data on file is 2 runs and 15 average.
+TEST_F(ProgressTest, ThirdTime) {
+ std::string path = CopyTextFileFixture("stats-two-runs.txt");
+ AssertStats(path, 2, 15); // Sanity check
+
+ Progress run1 = GetInstance(-42, 1.2, path);
+ EXPECT_EQ(0, run1.Get());
+ EXPECT_EQ(15, run1.GetInitialMax());
+ EXPECT_EQ(15, run1.GetMax());
+
+ bool max_increased = run1.Inc(20);
+ EXPECT_EQ(20, run1.Get());
+ EXPECT_EQ(24, run1.GetMax());
+ EXPECT_TRUE(max_increased);
+
+ // Average now is 3 runs and (15 * 2 + 20)/ 3 = 16.66 = 16
+ run1.Save();
+ AssertStats(path, 3, 16);
+}
+
+class DumpstateUtilTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ SetDryRun(false);
+ }
+
+ void CaptureFdOut() {
+ ReadFileToString(path_, &out);
+ }
+
+ void CreateFd(const std::string& name) {
+ path_ = kTestDataPath + name;
+ MYLOGD("Creating fd for file %s\n", path_.c_str());
+
+ fd = TEMP_FAILURE_RETRY(open(path_.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+ ASSERT_GE(fd, 0) << "could not create FD for path " << path_;
+ }
+
+ // Runs a command into the `fd` and capture `stderr`.
+ int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ CaptureStderr();
+ int status = RunCommandToFd(fd, title, full_command, options);
+ close(fd);
+
+ CaptureFdOut();
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ // Dumps a file and into the `fd` and `stderr`.
+ int DumpFile(const std::string& title, const std::string& path) {
+ CaptureStderr();
+ int status = DumpFileToFd(fd, title, path);
+ close(fd);
+
+ CaptureFdOut();
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ // Find out the pid of the process_name
+ int FindPidOfProcess(const std::string& process_name) {
+ CaptureStderr();
+ int status = GetPidByName(process_name);
+ err = GetCapturedStderr();
+ return status;
+ }
+
+ int fd;
+
+ // 'fd` output and `stderr` from the last command ran.
+ std::string out, err;
+
+ private:
+ std::string path_;
+};
+
+TEST_F(DumpstateUtilTest, RunCommandNoArgs) {
+ CreateFd("RunCommandNoArgs.txt");
+ EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNoTitle) {
+ CreateFd("RunCommandWithNoArgs.txt");
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithTitle) {
+ CreateFd("RunCommandWithNoArgs.txt");
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithOneArg) {
+ CreateFd("RunCommandWithOneArg.txt");
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithMultipleArgs) {
+ CreateFd("RunCommandWithMultipleArgs.txt");
+ EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithLoggingMessage) {
+ CreateFd("RunCommandWithLoggingMessage.txt");
+ EXPECT_EQ(
+ 0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandRedirectStderr) {
+ CreateFd("RunCommandRedirectStderr.txt");
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+ CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRun) {
+ CreateFd("RunCommandDryRun.txt");
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+ EXPECT_THAT(out, StrEq(android::base::StringPrintf(
+ "------ I AM GROOT (%s) ------\n\t(skipped on dry run)\n",
+ kSimpleCommand.c_str())));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunNoTitle) {
+ CreateFd("RunCommandDryRun.txt");
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+ EXPECT_THAT(
+ out, StrEq(android::base::StringPrintf("%s: skipped on dry run\n", kSimpleCommand.c_str())));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunAlways) {
+ CreateFd("RunCommandDryRunAlways.txt");
+ SetDryRun(true);
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNotFound) {
+ CreateFd("RunCommandNotFound.txt");
+ EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+ EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+ EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandFails) {
+ CreateFd("RunCommandFails.txt");
+ EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+ EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+ EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+ " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandCrashes) {
+ CreateFd("RunCommandCrashes.txt");
+ EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+ // We don't know the exit code, so check just the prefix.
+ EXPECT_THAT(
+ out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+ EXPECT_THAT(
+ err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandTimesout) {
+ CreateFd("RunCommandTimesout.txt");
+ EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+ CommandOptions::WithTimeout(1).Build()));
+ EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+ EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+ " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandIsKilled) {
+ CreateFd("RunCommandIsKilled.txt");
+ CaptureStderr();
+
+ std::thread t([=]() {
+ EXPECT_EQ(SIGTERM, RunCommandToFd(fd, "", {kSimpleCommand, "--pid", "--sleep", "20"},
+ CommandOptions::WithTimeout(100).Always().Build()));
+ });
+
+ // Capture pid and pre-sleep output.
+ sleep(1); // Wait a little bit to make sure pid and 1st line were printed.
+ std::string err = GetCapturedStderr();
+ EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+ CaptureFdOut();
+ std::vector<std::string> lines = android::base::Split(out, "\n");
+ ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+ int pid = atoi(lines[0].c_str());
+ EXPECT_THAT(lines[1], StrEq("stdout line1"));
+ EXPECT_THAT(lines[2], IsEmpty()); // \n
+
+ // Then kill the process.
+ CaptureFdOut();
+ CaptureStderr();
+ ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+ t.join();
+
+ // Finally, check output after murder.
+ CaptureFdOut();
+ err = GetCapturedStderr();
+
+ // out starts with the pid, which is an unknown
+ EXPECT_THAT(out, EndsWith("stdout line1\n*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+ EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+ " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootUserBuild() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandAsRootUserBuild.txt");
+ if (!PropertiesHelper::IsUserBuild()) {
+ // Emulates user build if necessarily.
+ SetBuildType("user");
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ // We don't know the exact path of su, so we just check for the 'root ...' commands
+ EXPECT_THAT(out, StartsWith("Skipping"));
+ EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootNonUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootNonUserBuild() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandAsRootNonUserBuild.txt");
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+ return;
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDropRoot) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandDropRoot() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandDropRoot.txt");
+ // First check root case - only available when running with 'adb root'.
+ uid_t uid = getuid();
+ if (uid == 0) {
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+ return;
+ }
+ // Then run dropping root.
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).DropRoot().Build()));
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundNoTitle) {
+ CreateFd("DumpFileNotFound.txt");
+ EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(out,
+ StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundWithTitle) {
+ CreateFd("DumpFileNotFound.txt");
+ EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+ EXPECT_THAT(out, StrEq("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No such "
+ "file or directory\n"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLine) {
+ CreateFd("DumpFileSingleLine.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLineWithNewLine) {
+ CreateFd("DumpFileSingleLineWithNewLine.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLines) {
+ CreateFd("DumpFileMultipleLines.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLinesWithNewLine) {
+ CreateFd("DumpFileMultipleLinesWithNewLine.txt");
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRunNoTitle) {
+ CreateFd("DumpFileOnDryRun.txt");
+ SetDryRun(true);
+ std::string path = kTestDataPath + "single-line.txt";
+ EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(out, StrEq(path + ": skipped on dry run\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRun) {
+ CreateFd("DumpFileOnDryRun.txt");
+ SetDryRun(true);
+ std::string path = kTestDataPath + "single-line.txt";
+ EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+ EXPECT_THAT(err, IsEmpty());
+ EXPECT_THAT(
+ out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
+ EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
+}
+
+TEST_F(DumpstateUtilTest, FindingPidWithExistingProcess) {
+ // init process always has pid 1.
+ EXPECT_EQ(1, FindPidOfProcess("init"));
+ EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, FindingPidWithNotExistingProcess) {
+ // find the process with abnormal name.
+ EXPECT_EQ(-1, FindPidOfProcess("abcdef12345-543"));
+ EXPECT_THAT(err, StrEq("can't find the pid\n"));
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/tests/dumpstate_test_fixture.cpp b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
new file mode 100644
index 0000000..5be4719
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+void PrintDefaultOutput() {
+ fprintf(stdout, "stdout\n");
+ fflush(stdout);
+ fprintf(stderr, "stderr\n");
+ fflush(stderr);
+}
+
+/*
+ * Binary used to on RunCommand tests.
+ *
+ * Usage:
+ *
+ * - Unless stated otherwise this command:
+ *
+ * 1.Prints `stdout\n` on `stdout` and flushes it.
+ * 2.Prints `stderr\n` on `stderr` and flushes it.
+ * 3.Exit with status 0.
+ *
+ * - If 1st argument is '--pid', it first prints its pid on `stdout`.
+ *
+ * - If 1st argument is '--uid', it first prints its uid on `stdout`.
+ *
+ * - If 1st argument is '--crash', it uses ALOGF to crash and returns 666.
+ *
+ * - With argument '--exit' 'CODE', returns CODE;
+ *
+ * - With argument '--sleep 'TIME':
+ *
+ * 1.Prints `stdout line1\n` on `stdout` and `sleeping TIME s\n` on `stderr`
+ * 2.Sleeps for TIME s
+ * 3.Prints `stdout line2\n` on `stdout` and `woke up\n` on `stderr`
+ */
+int main(int argc, char* const argv[]) {
+ if (argc == 2) {
+ if (strcmp(argv[1], "--crash") == 0) {
+ PrintDefaultOutput();
+ LOG_FATAL("D'OH\n");
+ return 666;
+ }
+ }
+ if (argc == 3) {
+ if (strcmp(argv[1], "--exit") == 0) {
+ PrintDefaultOutput();
+ return atoi(argv[2]);
+ }
+ }
+
+ if (argc > 1) {
+ int index = 1;
+
+ // First check arguments that can shift the index.
+ if (strcmp(argv[1], "--pid") == 0) {
+ index++;
+ fprintf(stdout, "%d\n", getpid());
+ fflush(stdout);
+ } else if (strcmp(argv[1], "--uid") == 0) {
+ index++;
+ fprintf(stdout, "%d\n", getuid());
+ fflush(stdout);
+ }
+
+ // Then the "common" arguments, if any.
+ if (argc > index + 1) {
+ if (strcmp(argv[index], "--sleep") == 0) {
+ int napTime = atoi(argv[index + 1]);
+ fprintf(stdout, "stdout line1\n");
+ fflush(stdout);
+ fprintf(stderr, "sleeping for %ds\n", napTime);
+ fflush(stderr);
+ sleep(napTime);
+ fprintf(stdout, "stdout line2\n");
+ fflush(stdout);
+ fprintf(stderr, "woke up\n");
+ fflush(stderr);
+ return 0;
+ }
+ }
+ }
+
+ PrintDefaultOutput();
+ return 0;
+}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 5b81121..6cc430d 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -16,10 +16,12 @@
#define LOG_TAG "dumpstate"
+#include "dumpstate.h"
+
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
+#include <libgen.h>
+#include <math.h>
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
@@ -40,17 +42,33 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/debugger.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
+#include "DumpstateInternal.h"
-#include "dumpstate.h"
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
-static const int64_t NANOS_PER_SEC = 1000000000;
+static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
+
+/* Most simple commands have 10 as timeout, so 5 is a good estimate */
+static const int32_t WEIGHT_FILE = 5;
+
+// TODO: temporary variables and functions used during C++ refactoring
+static Dumpstate& ds = Dumpstate::GetInstance();
+static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT) {
+ return ds.RunCommand(title, full_command, options);
+}
/* list of native processes to include in the native dumps */
// This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
@@ -68,37 +86,169 @@
NULL,
};
-DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
+// Reasonable value for max stats.
+static const int STATS_MAX_N_RUNS = 1000;
+static const long STATS_MAX_AVERAGE = 100000;
-DurationReporter::DurationReporter(const char *title, FILE *out) {
- title_ = title;
- if (title) {
- started_ = DurationReporter::nanotime();
+CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
+
+Dumpstate::Dumpstate(const std::string& version)
+ : pid_(getpid()), version_(version), now_(time(nullptr)) {
+}
+
+Dumpstate& Dumpstate::GetInstance() {
+ static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT));
+ return singleton_;
+}
+
+DurationReporter::DurationReporter(const std::string& title, bool log_only)
+ : title_(title), log_only_(log_only) {
+ if (!title_.empty()) {
+ started_ = Nanotime();
}
- out_ = out;
}
DurationReporter::~DurationReporter() {
- if (title_) {
- uint64_t elapsed = DurationReporter::nanotime() - started_;
- // Use "Yoda grammar" to make it easier to grep|sort sections.
- if (out_) {
- fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
- (float) elapsed / NANOS_PER_SEC, title_);
+ if (!title_.empty()) {
+ uint64_t elapsed = Nanotime() - started_;
+ if (log_only_) {
+ MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
} else {
- MYLOGD("Duration of '%s': %.3fs\n", title_, (float) elapsed / NANOS_PER_SEC);
+ // Use "Yoda grammar" to make it easier to grep|sort sections.
+ printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC,
+ title_.c_str());
}
}
}
-uint64_t DurationReporter::DurationReporter::nanotime() {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return (uint64_t) ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
+const int32_t Progress::kDefaultMax = 5000;
+
+Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) {
+}
+
+Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor)
+ : Progress(initial_max, growth_factor, "") {
+ progress_ = progress;
+}
+
+Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path)
+ : initial_max_(initial_max),
+ progress_(0),
+ max_(initial_max),
+ growth_factor_(growth_factor),
+ n_runs_(0),
+ average_max_(0),
+ path_(path) {
+ if (!path_.empty()) {
+ Load();
+ }
+}
+
+void Progress::Load() {
+ MYLOGD("Loading stats from %s\n", path_.c_str());
+ std::string content;
+ if (!android::base::ReadFileToString(path_, &content)) {
+ MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_);
+ return;
+ }
+ if (content.empty()) {
+ MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_);
+ return;
+ }
+ std::vector<std::string> lines = android::base::Split(content, "\n");
+
+ if (lines.size() < 1) {
+ MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(),
+ (int)lines.size(), max_);
+ return;
+ }
+ char* ptr;
+ n_runs_ = strtol(lines[0].c_str(), &ptr, 10);
+ average_max_ = strtol(ptr, nullptr, 10);
+ if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS ||
+ average_max_ > STATS_MAX_AVERAGE) {
+ MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str());
+ initial_max_ = Progress::kDefaultMax;
+ } else {
+ initial_max_ = average_max_;
+ }
+ max_ = initial_max_;
+
+ MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_);
+}
+
+void Progress::Save() {
+ int32_t total = n_runs_ * average_max_ + progress_;
+ int32_t runs = n_runs_ + 1;
+ int32_t average = floor(((float)total) / runs);
+ MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average,
+ path_.c_str());
+ if (path_.empty()) {
+ return;
+ }
+
+ std::string content = android::base::StringPrintf("%d %d\n", runs, average);
+ if (!android::base::WriteStringToFile(content, path_)) {
+ MYLOGE("Could not save stats on %s\n", path_.c_str());
+ }
+}
+
+int32_t Progress::Get() const {
+ return progress_;
+}
+
+bool Progress::Inc(int32_t delta) {
+ bool changed = false;
+ if (delta >= 0) {
+ progress_ += delta;
+ if (progress_ > max_) {
+ int32_t old_max = max_;
+ max_ = floor((float)progress_ * growth_factor_);
+ MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_);
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+int32_t Progress::GetMax() const {
+ return max_;
+}
+
+int32_t Progress::GetInitialMax() const {
+ return initial_max_;
+}
+
+void Progress::Dump(int fd, const std::string& prefix) const {
+ const char* pr = prefix.c_str();
+ dprintf(fd, "%sprogress: %d\n", pr, progress_);
+ dprintf(fd, "%smax: %d\n", pr, max_);
+ dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_);
+ dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_);
+ dprintf(fd, "%spath: %s\n", pr, path_.c_str());
+ dprintf(fd, "%sn_runs: %d\n", pr, n_runs_);
+ dprintf(fd, "%saverage_max: %d\n", pr, average_max_);
+}
+
+bool Dumpstate::IsZipping() const {
+ return zip_writer_ != nullptr;
+}
+
+std::string Dumpstate::GetPath(const std::string& suffix) const {
+ return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(),
+ name_.c_str(), suffix.c_str());
+}
+
+void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) {
+ progress_ = std::move(progress);
}
void for_each_userid(void (*func)(int), const char *header) {
- ON_DRY_RUN_RETURN();
+ std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf(
+ "for_each_userid(%s)", header);
+ DurationReporter duration_reporter(title);
+ if (PropertiesHelper::IsDryRun()) return;
+
DIR *d;
struct dirent *de;
@@ -180,7 +330,11 @@
}
void for_each_pid(for_each_pid_func func, const char *header) {
- ON_DRY_RUN_RETURN();
+ std::string title = header == nullptr ? "for_each_pid"
+ : android::base::StringPrintf("for_each_pid(%s)", header);
+ DurationReporter duration_reporter(title);
+ if (PropertiesHelper::IsDryRun()) return;
+
__for_each_pid(for_each_pid_helper, header, (void *) func);
}
@@ -233,12 +387,18 @@
}
void for_each_tid(for_each_tid_func func, const char *header) {
- ON_DRY_RUN_RETURN();
+ std::string title = header == nullptr ? "for_each_tid"
+ : android::base::StringPrintf("for_each_tid(%s)", header);
+ DurationReporter duration_reporter(title);
+
+ if (PropertiesHelper::IsDryRun()) return;
+
__for_each_pid(for_each_tid_helper, header, (void *) func);
}
void show_wchan(int pid, int tid, const char *name) {
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
+
char path[255];
char buffer[255];
int fd, ret, save_errno;
@@ -304,7 +464,8 @@
}
void show_showtime(int pid, const char *name) {
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
+
char path[255];
char buffer[1023];
int fd, ret, save_errno;
@@ -361,7 +522,7 @@
if (iotime) {
snprdec(buffer, sizeof(buffer), 79, permille);
}
- puts(buffer); // adds a trailing newline
+ puts(buffer); // adds a trailing newline
return;
}
@@ -371,7 +532,8 @@
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
+
/* Get size of kernel buffer */
int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (size <= 0) {
@@ -401,84 +563,17 @@
snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name);
snprintf(arg, sizeof(arg), "%d", pid);
- run_command(title, 10, SU_PATH, "root", "showmap", "-q", arg, NULL);
+ RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT);
}
-static int _dump_file_from_fd(const char *title, const char *path, int fd) {
- if (title) {
- printf("------ %s (%s", title, path);
-
- struct stat st;
- // Only show the modification time of non-device files.
- size_t path_len = strlen(path);
- if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
- (path_len < 5 || memcmp(path, "/sys/", 5)) &&
- (path_len < 3 || memcmp(path, "/d/", 3)) &&
- !fstat(fd, &st)) {
- char stamp[80];
- time_t mtime = st.st_mtime;
- strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
- printf(": %s", stamp);
- }
- printf(") ------\n");
- }
- ON_DRY_RUN({ update_progress(WEIGHT_FILE); close(fd); return 0; });
-
- bool newline = false;
- fd_set read_set;
- struct timeval tm;
- while (1) {
- FD_ZERO(&read_set);
- FD_SET(fd, &read_set);
- /* Timeout if no data is read for 30 seconds. */
- tm.tv_sec = 30;
- tm.tv_usec = 0;
- uint64_t elapsed = DurationReporter::nanotime();
- int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
- if (ret == -1) {
- printf("*** %s: select failed: %s\n", path, strerror(errno));
- newline = true;
- break;
- } else if (ret == 0) {
- elapsed = DurationReporter::nanotime() - elapsed;
- printf("*** %s: Timed out after %.3fs\n", path,
- (float) elapsed / NANOS_PER_SEC);
- newline = true;
- break;
- } else {
- char buffer[65536];
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
- if (bytes_read > 0) {
- fwrite(buffer, bytes_read, 1, stdout);
- newline = (buffer[bytes_read-1] == '\n');
- } else {
- if (bytes_read == -1) {
- printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
- newline = true;
- }
- break;
- }
- }
- }
- update_progress(WEIGHT_FILE);
- close(fd);
-
- if (!newline) printf("\n");
- if (title) printf("\n");
- return 0;
-}
-
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path) {
+int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
DurationReporter duration_reporter(title);
- int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
- int err = errno;
- printf("*** %s: %s\n", path, strerror(err));
- if (title) printf("\n");
- return -1;
- }
- return _dump_file_from_fd(title, path, fd);
+
+ int status = DumpFileToFd(STDOUT_FILENO, title, path);
+
+ UpdateProgress(WEIGHT_FILE);
+
+ return status;
}
int read_file_as_long(const char *path, long int *output) {
@@ -508,9 +603,8 @@
* to false when set to NULL. dump_from_fd will always be
* called with title NULL.
*/
-int dump_files(const char *title, const char *dir,
- bool (*skip)(const char *path),
- int (*dump_from_fd)(const char *title, const char *path, int fd)) {
+int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
+ int (*dump_from_fd)(const char* title, const char* path, int fd)) {
DurationReporter duration_reporter(title);
DIR *dirp;
struct dirent *d;
@@ -518,10 +612,10 @@
const char *slash = "/";
int fd, retval = 0;
- if (title) {
- printf("------ %s (%s) ------\n", title, dir);
+ if (!title.empty()) {
+ printf("------ %s (%s) ------\n", title.c_str(), dir);
}
- ON_DRY_RUN_RETURN(0);
+ if (PropertiesHelper::IsDryRun()) return 0;
if (dir[strlen(dir) - 1] == '/') {
++slash;
@@ -552,7 +646,7 @@
continue;
}
if (d->d_type == DT_DIR) {
- int ret = dump_files(NULL, newpath, skip, dump_from_fd);
+ int ret = dump_files("", newpath, skip, dump_from_fd);
if (ret < 0) {
retval = ret;
}
@@ -567,7 +661,7 @@
(*dump_from_fd)(NULL, newpath, fd);
}
closedir(dirp);
- if (title) {
+ if (!title.empty()) {
printf("\n");
}
return retval;
@@ -578,7 +672,8 @@
* stuck.
*/
int dump_file_from_fd(const char *title, const char *path, int fd) {
- ON_DRY_RUN_RETURN(0);
+ if (PropertiesHelper::IsDryRun()) return 0;
+
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
@@ -589,297 +684,43 @@
close(fd);
return -1;
}
- return _dump_file_from_fd(title, path, fd);
+ return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun());
}
-bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
- sigset_t child_mask, old_mask;
- sigemptyset(&child_mask);
- sigaddset(&child_mask, SIGCHLD);
-
- if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
- printf("*** sigprocmask failed: %s\n", strerror(errno));
- return false;
- }
-
- struct timespec ts;
- ts.tv_sec = timeout_seconds;
- ts.tv_nsec = 0;
- int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
- int saved_errno = errno;
- // Set the signals back the way they were.
- if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
- printf("*** sigprocmask failed: %s\n", strerror(errno));
- if (ret == 0) {
- return false;
- }
- }
- if (ret == -1) {
- errno = saved_errno;
- if (errno == EAGAIN) {
- errno = ETIMEDOUT;
- } else {
- printf("*** sigtimedwait failed: %s\n", strerror(errno));
- }
- return false;
- }
-
- pid_t child_pid = waitpid(pid, status, WNOHANG);
- if (child_pid != pid) {
- if (child_pid != -1) {
- printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
- } else {
- printf("*** waitpid failed: %s\n", strerror(errno));
- }
- return false;
- }
- return true;
-}
-
-// TODO: refactor all those commands that convert args
-void format_args(const char* command, const char *args[], std::string *string);
-
-int run_command(const char *title, int timeout_seconds, const char *command, ...) {
+int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+ const CommandOptions& options) {
DurationReporter duration_reporter(title);
- fflush(stdout);
- const char *args[1024] = {command};
- size_t arg;
- va_list ap;
- va_start(ap, command);
- if (title) printf("------ %s (%s", title, command);
- bool null_terminated = false;
- for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
- args[arg] = va_arg(ap, const char *);
- if (args[arg] == nullptr) {
- null_terminated = true;
- break;
- }
- // TODO: null_terminated check is not really working; line below would crash dumpstate if
- // nullptr is missing
- if (title) printf(" %s", args[arg]);
- }
- if (title) printf(") ------\n");
- fflush(stdout);
- if (!null_terminated) {
- // Fail now, otherwise execvp() call on run_command_always() might hang.
- std::string cmd;
- format_args(command, args, &cmd);
- MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
- return -1;
- }
+ int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
- ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
+ /* TODO: for now we're simplifying the progress calculation by using the
+ * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
+ * where its weight should be much higher proportionally to its timeout.
+ * Ideally, it should use a options.EstimatedDuration() instead...*/
+ UpdateProgress(options.Timeout());
- int status = run_command_always(title, DONT_DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
- va_end(ap);
return status;
}
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...) {
- DurationReporter duration_reporter(title);
- fflush(stdout);
-
- const char *args[1024] = {command};
- size_t arg;
- va_list ap;
- va_start(ap, command);
- if (title) printf("------ %s (%s", title, command);
- bool null_terminated = false;
- for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
- args[arg] = va_arg(ap, const char *);
- if (args[arg] == nullptr) {
- null_terminated = true;
- break;
- }
- // TODO: null_terminated check is not really working; line below would crash dumpstate if
- // nullptr is missing
- if (title) printf(" %s", args[arg]);
- }
- if (title) printf(") ------\n");
- fflush(stdout);
- if (!null_terminated) {
- // Fail now, otherwise execvp() call on run_command_always() might hang.
- std::string cmd;
- format_args(command, args, &cmd);
- MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
- return -1;
- }
-
- ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
-
- int status = run_command_always(title, DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
- va_end(ap);
- return status;
-}
-
-/* forks a command and waits for it to finish */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
- int timeout_seconds, const char *args[]) {
- bool silent = (stdout_mode == REDIRECT_TO_STDERR);
- // TODO: need to check if args is null-terminated, otherwise execvp will crash dumpstate
-
- /* TODO: for now we're simplifying the progress calculation by using the timeout as the weight.
- * It's a good approximation for most cases, except when calling dumpsys, where its weight
- * should be much higher proportionally to its timeout. */
- int weight = timeout_seconds;
-
- const char *command = args[0];
- uint64_t start = DurationReporter::nanotime();
- pid_t pid = fork();
-
- /* handle error case */
- if (pid < 0) {
- if (!silent) printf("*** fork: %s\n", strerror(errno));
- MYLOGE("*** fork: %s\n", strerror(errno));
- return pid;
- }
-
- /* handle child case */
- if (pid == 0) {
- if (root_mode == DROP_ROOT && !drop_root_user()) {
- if (!silent) printf("*** fail todrop root before running %s: %s\n", command,
- strerror(errno));
- MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
- return -1;
- }
-
- if (silent) {
- // Redirect stderr to stdout
- dup2(STDERR_FILENO, STDOUT_FILENO);
- }
-
- /* make sure the child dies when dumpstate dies */
- prctl(PR_SET_PDEATHSIG, SIGKILL);
-
- /* just ignore SIGPIPE, will go down with parent's */
- struct sigaction sigact;
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sigact, NULL);
-
- execvp(command, (char**) args);
- // execvp's result will be handled after waitpid_with_timeout() below, but if it failed,
- // it's safer to exit dumpstate.
- MYLOGD("execvp on command '%s' failed (error: %s)", command, strerror(errno));
- fflush(stdout);
- // Must call _exit (instead of exit), otherwise it will corrupt the zip file.
- _exit(EXIT_FAILURE);
- }
-
- /* handle parent case */
- int status;
- bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
- uint64_t elapsed = DurationReporter::nanotime() - start;
- std::string cmd; // used to log command and its args
- if (!ret) {
- if (errno == ETIMEDOUT) {
- format_args(command, args, &cmd);
- if (!silent) printf("*** command '%s' timed out after %.3fs (killing pid %d)\n",
- cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
- MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
- (float) elapsed / NANOS_PER_SEC, pid);
- } else {
- format_args(command, args, &cmd);
- if (!silent) printf("*** command '%s': Error after %.4fs (killing pid %d)\n",
- cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
- MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
- (float) elapsed / NANOS_PER_SEC, pid);
- }
- kill(pid, SIGTERM);
- if (!waitpid_with_timeout(pid, 5, NULL)) {
- kill(pid, SIGKILL);
- if (!waitpid_with_timeout(pid, 5, NULL)) {
- if (!silent) printf("could not kill command '%s' (pid %d) even with SIGKILL.\n",
- command, pid);
- MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
- }
- }
- return -1;
- } else if (status) {
- format_args(command, args, &cmd);
- if (!silent) printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
- MYLOGE("command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
- return -2;
- }
-
- if (WIFSIGNALED(status)) {
- if (!silent) printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
- MYLOGE("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
- } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
- if (!silent) printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
- MYLOGE("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
- }
-
- if (weight > 0) {
- update_progress(weight);
- }
- return status;
-}
-
-bool drop_root_user() {
- if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
- MYLOGD("drop_root_user(): already running as Shell");
- return true;
- }
- /* ensure we will keep capabilities when we drop root */
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
- return false;
- }
-
- gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
- AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_BLUETOOTH };
- if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
- MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
- return false;
- }
- if (setgid(AID_SHELL) != 0) {
- MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
- return false;
- }
- if (setuid(AID_SHELL) != 0) {
- MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
- return false;
- }
-
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
- capdata[0].inheritable = 0;
- capdata[1].inheritable = 0;
-
- if (capset(&capheader, &capdata[0]) < 0) {
- MYLOGE("capset failed: %s\n", strerror(errno));
- return false;
- }
-
- return true;
+void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
+ const CommandOptions& options, long dumpsysTimeout) {
+ long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout();
+ std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)};
+ dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
+ RunCommand(title, dumpsys, options);
}
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;
@@ -903,7 +744,7 @@
const char* title = "SYSTEM PROPERTIES";
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
size_t i;
num_props = 0;
property_list(print_prop, NULL);
@@ -974,11 +815,11 @@
}
}
-/* redirect output to a file */
-void redirect_to_file(FILE *redirect, char *path) {
+void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) {
create_parent_dirs(path);
- int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ int fd = TEMP_FAILURE_RETRY(open(path,
+ O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd < 0) {
MYLOGE("%s: %s\n", path, strerror(errno));
@@ -989,6 +830,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)) {
@@ -1000,35 +849,33 @@
/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
const char *dump_traces() {
- DurationReporter duration_reporter("DUMP TRACES", NULL);
- ON_DRY_RUN_RETURN(NULL);
- const char* result = NULL;
+ DurationReporter duration_reporter("DUMP TRACES");
- char traces_path[PROPERTY_VALUE_MAX] = "";
- property_get("dalvik.vm.stack-trace-file", traces_path, "");
- if (!traces_path[0]) return NULL;
+ const char* result = nullptr;
+
+ std::string traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ if (traces_path.empty()) return nullptr;
/* move the old traces.txt (if any) out of the way temporarily */
- char anr_traces_path[PATH_MAX];
- strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
- strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
- if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
- MYLOGE("rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
- return NULL; // Can't rename old traces.txt -- no permission? -- leave it alone instead
+ std::string anrtraces_path = traces_path + ".anr";
+ if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno));
+ return nullptr; // Can't rename old traces.txt -- no permission? -- leave it alone instead
}
/* create a new, empty traces.txt file to receive stack dumps */
- int fd = TEMP_FAILURE_RETRY(open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
- 0666)); /* -rw-rw-rw- */
+ int fd = TEMP_FAILURE_RETRY(open(traces_path.c_str(),
+ O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
+ 0666)); /* -rw-rw-rw- */
if (fd < 0) {
- MYLOGE("%s: %s\n", traces_path, strerror(errno));
- return NULL;
+ MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno));
+ return nullptr;
}
int chmod_ret = fchmod(fd, 0666);
if (chmod_ret < 0) {
- MYLOGE("fchmod on %s failed: %s\n", traces_path, strerror(errno));
+ MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno));
close(fd);
- return NULL;
+ return nullptr;
}
/* Variables below must be initialized before 'goto' statements */
@@ -1049,9 +896,9 @@
goto error_close_fd;
}
- wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
+ wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE);
if (wfd < 0) {
- MYLOGE("inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
+ MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno));
goto error_close_ifd;
}
@@ -1084,7 +931,7 @@
}
++dalvik_found;
- uint64_t start = DurationReporter::nanotime();
+ uint64_t start = Nanotime();
if (kill(pid, SIGQUIT)) {
MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
continue;
@@ -1092,7 +939,7 @@
/* wait for the writable-close notification from inotify */
struct pollfd pfd = { ifd, POLLIN, 0 };
- int ret = poll(&pfd, 1, 5000); /* 5 sec timeout */
+ int ret = poll(&pfd, 1, TRACE_DUMP_TIMEOUT_MS);
if (ret < 0) {
MYLOGE("poll: %s\n", strerror(errno));
} else if (ret == 0) {
@@ -1105,8 +952,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)(Nanotime() - start) / NANOS_PER_SEC);
}
} else if (should_dump_native_traces(data)) {
/* dump native process if appropriate */
@@ -1114,7 +961,7 @@
MYLOGE("lseek: %s\n", strerror(errno));
} else {
static uint16_t timeout_failures = 0;
- uint64_t start = DurationReporter::nanotime();
+ uint64_t start = Nanotime();
/* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
if (timeout_failures == 3) {
@@ -1125,8 +972,8 @@
} else {
timeout_failures = 0;
}
- dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n",
- pid, (float)(DurationReporter::nanotime() - start) / NANOS_PER_SEC);
+ dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n", pid,
+ (float)(Nanotime() - start) / NANOS_PER_SEC);
}
}
}
@@ -1135,17 +982,17 @@
MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
}
- static char dump_traces_path[PATH_MAX];
- strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
- strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
- if (rename(traces_path, dump_traces_path)) {
- MYLOGE("rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
+ static std::string dumptraces_path = android::base::StringPrintf(
+ "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str()));
+ if (rename(traces_path.c_str(), dumptraces_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(),
+ strerror(errno));
goto error_close_ifd;
}
- result = dump_traces_path;
+ result = dumptraces_path.c_str();
/* replace the saved [ANR] traces.txt file */
- rename(anr_traces_path, traces_path);
+ rename(anrtraces_path.c_str(), traces_path.c_str());
error_close_ifd:
close(ifd);
@@ -1156,9 +1003,9 @@
void dump_route_tables() {
DurationReporter duration_reporter("DUMP ROUTE TABLES");
- ON_DRY_RUN_RETURN();
+ if (PropertiesHelper::IsDryRun()) return;
const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
- dump_file("RT_TABLES", RT_TABLES_PATH);
+ ds.DumpFile("RT_TABLES", RT_TABLES_PATH);
FILE* fp = fopen(RT_TABLES_PATH, "re");
if (!fp) {
printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno));
@@ -1169,72 +1016,67 @@
// need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name.
// Add a fixed max limit so this doesn't go awry.
for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) {
- run_command("ROUTE TABLE IPv4", 10, "ip", "-4", "route", "show", "table", table, NULL);
- run_command("ROUTE TABLE IPv6", 10, "ip", "-6", "route", "show", "table", table, NULL);
+ RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table});
+ RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table});
}
fclose(fp);
}
-/* overall progress */
-int progress = 0;
-int do_update_progress = 0; // Set by dumpstate.cpp
-int weight_total = WEIGHT_TOTAL;
-
// TODO: make this function thread safe if sections are generated in parallel.
-void update_progress(int delta) {
- if (!do_update_progress) return;
+void Dumpstate::UpdateProgress(int32_t delta) {
+ if (progress_ == nullptr) {
+ MYLOGE("UpdateProgress: progress_ not set\n");
+ return;
+ }
- progress += delta;
+ // Always update progess so stats can be tuned...
+ bool max_changed = progress_->Inc(delta);
- char key[PROPERTY_KEY_MAX];
- char value[PROPERTY_VALUE_MAX];
+ // ...but only notifiy listeners when necessary.
+ if (!update_progress_) return;
+
+ int progress = progress_->Get();
+ int max = progress_->GetMax();
// adjusts max on the fly
- if (progress > weight_total) {
- int new_total = weight_total * 1.2;
- MYLOGD("Adjusting total weight from %d to %d\n", weight_total, new_total);
- weight_total = new_total;
- snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
- snprintf(value, sizeof(value), "%d", weight_total);
- int status = property_set(key, value);
- if (status) {
- MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
- key, value, status);
+ if (max_changed && listener_ != nullptr) {
+ listener_->onMaxProgressUpdated(max);
+ }
+
+ int32_t last_update_delta = progress - last_updated_progress_;
+ if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) {
+ return;
+ }
+ last_updated_progress_ = progress;
+
+ if (control_socket_fd_ >= 0) {
+ dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max);
+ fsync(control_socket_fd_);
+ }
+
+ if (listener_ != nullptr) {
+ if (progress % 100 == 0) {
+ // We don't want to spam logcat, so only log multiples of 100.
+ MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
+ } else {
+ // stderr is ignored on normal invocations, but useful when calling
+ // /system/bin/dumpstate directly for debuggging.
+ fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
}
+ listener_->onProgressUpdated(progress);
}
+}
- snprintf(key, sizeof(key), "dumpstate.%d.progress", getpid());
- snprintf(value, sizeof(value), "%d", progress);
-
- if (progress % 100 == 0) {
- // We don't want to spam logcat, so only log multiples of 100.
- MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total);
+void Dumpstate::TakeScreenshot(const std::string& path) {
+ const std::string& real_path = path.empty() ? screenshot_path_ : path;
+ int status =
+ RunCommand("", {"/system/bin/screencap", "-p", real_path},
+ CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+ if (status == 0) {
+ MYLOGD("Screenshot saved on %s\n", real_path.c_str());
} else {
- // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
- // directly for debuggging.
- fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
+ MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
}
-
- if (control_socket_fd >= 0) {
- dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
- fsync(control_socket_fd);
- }
-
- int status = property_set(key, value);
- if (status) {
- MYLOGE("Could not update progress by setting system property %s to %s: %d\n",
- key, value, status);
- }
-}
-
-void take_screenshot(const std::string& path) {
- const char *args[] = { "/system/bin/screencap", "-p", path.c_str(), NULL };
- run_command_always(NULL, DONT_DROP_ROOT, REDIRECT_TO_STDERR, 10, args);
-}
-
-void vibrate(FILE* vibrator, int ms) {
- fprintf(vibrator, "%d\n", ms);
- fflush(vibrator);
}
bool is_dir(const char* pathname) {
@@ -1278,19 +1120,16 @@
int ext_csd_rev = 0;
std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex));
if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) {
- printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n",
- ext_csd_path, sub.c_str());
+ printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
return;
}
static const char *ver_str[] = {
"4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
};
- printf("rev 1.%d (MMC %s)\n",
- ext_csd_rev,
- (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
- ver_str[ext_csd_rev] :
- "Unknown");
+ printf("rev 1.%d (MMC %s)\n", ext_csd_rev,
+ (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev]
+ : "Unknown");
if (ext_csd_rev < 7) {
printf("\n");
return;
@@ -1304,8 +1143,7 @@
int ext_pre_eol_info = 0;
sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex));
if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) {
- printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n",
- ext_csd_path, sub.c_str());
+ printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
return;
}
@@ -1315,11 +1153,10 @@
"Warning (consumed 80% of reserve)",
"Urgent (consumed 90% of reserve)"
};
- printf("PRE_EOL_INFO %d (MMC %s)\n",
- ext_pre_eol_info,
- eol_str[(ext_pre_eol_info < (int)
- (sizeof(eol_str) / sizeof(eol_str[0]))) ?
- ext_pre_eol_info : 0]);
+ printf(
+ "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info,
+ eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info
+ : 0]);
for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A;
lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B;
@@ -1348,48 +1185,18 @@
ext_device_life_time_est = 0;
sub = buffer.substr(lifetime, sizeof(hex));
if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) {
- printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n",
- ext_csd_path,
- (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
- sizeof(hex)) + 'A',
+ printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path,
+ (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
sub.c_str());
continue;
}
printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n",
- (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
- sizeof(hex)) + 'A',
+ (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
ext_device_life_time_est,
- est_str[(ext_device_life_time_est < (int)
- (sizeof(est_str) / sizeof(est_str[0]))) ?
- ext_device_life_time_est : 0]);
+ est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0])))
+ ? ext_device_life_time_est
+ : 0]);
}
printf("\n");
}
-
-// TODO: refactor all those commands that convert args
-void format_args(int argc, const char *argv[], std::string *args) {
- LOG_ALWAYS_FATAL_IF(args == nullptr);
- for (int i = 0; i < argc; i++) {
- args->append(argv[i]);
- if (i < argc -1) {
- args->append(" ");
- }
- }
-}
-void format_args(const char* command, const char *args[], std::string *string) {
- LOG_ALWAYS_FATAL_IF(args == nullptr || command == nullptr);
- string->append(command);
- if (args[0] == nullptr) return;
- string->append(" ");
-
- for (int arg = 1; arg <= 1000; ++arg) {
- if (args[arg] == nullptr) return;
- string->append(args[arg]);
- if (args[arg+1] != nullptr) {
- string->append(" ");
- }
- }
- // TODO: not really working: if NULL is missing, it will crash dumpstate.
- MYLOGE("internal error: missing NULL entry on %s", string->c_str());
-}
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 8bf0b53..f567a10 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.
@@ -35,7 +34,6 @@
libutils \
LOCAL_STATIC_LIBRARIES := libdiskusage
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 102359d..edcb3e9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -67,6 +67,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";
@@ -83,7 +85,6 @@
static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
static constexpr int FLAG_USE_QUOTA = 1 << 12;
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
namespace {
constexpr const char* kDump = "android.permission.DUMP";
diff --git a/cmds/surfacereplayer/proto/Android.mk b/cmds/surfacereplayer/proto/Android.mk
new file mode 100644
index 0000000..3cf1148
--- /dev/null
+++ b/cmds/surfacereplayer/proto/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-proto-files-under, src)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+LOCAL_MODULE := libtrace_proto
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
new file mode 100644
index 0000000..0bc08a9
--- /dev/null
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -0,0 +1,179 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message Trace {
+ repeated Increment increment = 1;
+}
+
+message Increment {
+ required int64 time_stamp = 1;
+
+ oneof increment {
+ Transaction transaction = 2;
+ SurfaceCreation surface_creation = 3;
+ SurfaceDeletion surface_deletion = 4;
+ BufferUpdate buffer_update = 5;
+ VSyncEvent vsync_event = 6;
+ DisplayCreation display_creation = 7;
+ DisplayDeletion display_deletion = 8;
+ PowerModeUpdate power_mode_update = 9;
+ }
+}
+
+message Transaction {
+ repeated SurfaceChange surface_change = 1;
+ repeated DisplayChange display_change = 2;
+
+ required bool synchronous = 3;
+ required bool animation = 4;
+}
+
+message SurfaceChange {
+ required int32 id = 1;
+
+ oneof SurfaceChange {
+ PositionChange position = 2;
+ SizeChange size = 3;
+ AlphaChange alpha = 4;
+ LayerChange layer = 5;
+ CropChange crop = 6;
+ FinalCropChange final_crop = 7;
+ MatrixChange matrix = 8;
+ OverrideScalingModeChange override_scaling_mode = 9;
+ TransparentRegionHintChange transparent_region_hint = 10;
+ LayerStackChange layer_stack = 11;
+ HiddenFlagChange hidden_flag = 12;
+ OpaqueFlagChange opaque_flag = 13;
+ SecureFlagChange secure_flag = 14;
+ DeferredTransactionChange deferred_transaction = 15;
+ }
+}
+
+message PositionChange {
+ required float x = 1;
+ required float y = 2;
+}
+
+message SizeChange {
+ required uint32 w = 1;
+ required uint32 h = 2;
+}
+
+message AlphaChange {
+ required float alpha = 1;
+}
+
+message LayerChange {
+ required uint32 layer = 1;
+}
+
+message CropChange {
+ required Rectangle rectangle = 1;
+}
+
+message FinalCropChange {
+ required Rectangle rectangle = 1;
+}
+
+message MatrixChange {
+ required float dsdx = 1;
+ required float dtdx = 2;
+ required float dsdy = 3;
+ required float dtdy = 4;
+}
+
+message OverrideScalingModeChange {
+ required int32 override_scaling_mode = 1;
+}
+
+message TransparentRegionHintChange {
+ repeated Rectangle region = 1;
+}
+
+message LayerStackChange {
+ required uint32 layer_stack = 1;
+}
+
+message HiddenFlagChange {
+ required bool hidden_flag = 1;
+}
+
+message OpaqueFlagChange {
+ required bool opaque_flag = 1;
+}
+
+message SecureFlagChange {
+ required bool secure_flag = 1;
+}
+
+message DeferredTransactionChange {
+ required int32 layer_id = 1;
+ required uint64 frame_number = 2;
+}
+
+message DisplayChange {
+ required int32 id = 1;
+
+ oneof DisplayChange {
+ DispSurfaceChange surface = 2;
+ LayerStackChange layer_stack = 3;
+ SizeChange size = 4;
+ ProjectionChange projection = 5;
+ }
+}
+
+message DispSurfaceChange {
+ required uint64 buffer_queue_id = 1;
+ required string buffer_queue_name = 2;
+}
+
+message ProjectionChange {
+ required int32 orientation = 1;
+ required Rectangle viewport = 2;
+ required Rectangle frame = 3;
+}
+
+message Rectangle {
+ required int32 left = 1;
+ required int32 top = 2;
+ required int32 right = 3;
+ required int32 bottom = 4;
+}
+
+message SurfaceCreation {
+ required int32 id = 1;
+ required string name = 2;
+ required uint32 w = 3;
+ required uint32 h = 4;
+}
+
+message SurfaceDeletion {
+ required int32 id = 1;
+}
+
+message BufferUpdate {
+ required int32 id = 1;
+ required uint32 w = 2;
+ required uint32 h = 3;
+ required uint64 frame_number = 4;
+}
+
+message VSyncEvent {
+ required int64 when = 1;
+}
+
+message DisplayCreation {
+ required int32 id = 1;
+ required string name = 2;
+ required int32 type = 3;
+ required bool is_secure = 4;
+}
+
+message DisplayDeletion {
+ required int32 id = 1;
+}
+
+message PowerModeUpdate {
+ required int32 id = 1;
+ required int32 mode = 2;
+}
diff --git a/cmds/surfacereplayer/replayer/Android.mk b/cmds/surfacereplayer/replayer/Android.mk
new file mode 100644
index 0000000..3ec3178
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -0,0 +1,73 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_TARGET_DIR := $(TARGET_OUT_DATA)/local/tmp
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call first-makefiles-under, /frameworks/native/cmds/surfacereplayer/proto)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPPFLAGS := -Weverything -Werror
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+LOCAL_CPPFLAGS := -Wno-format
+
+LOCAL_MODULE := libsurfacereplayer
+
+LOCAL_SRC_FILES := \
+ BufferQueueScheduler.cpp \
+ Event.cpp \
+ Replayer.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libEGL \
+ libGLESv2 \
+ libbinder \
+ liblog \
+ libcutils \
+ libgui \
+ libui \
+ libutils \
+ libprotobuf-cpp-lite \
+ libbase \
+
+LOCAL_STATIC_LIBRARIES := \
+ libtrace_proto \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/..
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := surfacereplayer
+
+LOCAL_SRC_FILES := \
+ Main.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libprotobuf-cpp-lite \
+ libsurfacereplayer \
+ libutils \
+
+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..a49da37
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -0,0 +1,704 @@
+/* Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceReplayer"
+
+#include "Replayer.h"
+
+#include <android/native_window.h>
+
+#include <android-base/file.h>
+
+#include <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::string input;
+ if (!android::base::ReadFileToString(filename, &input, true)) {
+ std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl;
+ abort();
+ }
+
+ mLoaded = mTrace.ParseFromString(input);
+ if (!mLoaded) {
+ std::cerr << "Trace did not load." << std::endl;
+ abort();
+ }
+
+ mCurrentTime = mTrace.increment(0).time_stamp();
+
+ sReplayingManually.store(replayManually);
+
+ if (stopHere < 0) {
+ mHasStopped = true;
+ }
+}
+
+Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)
+ : mTrace(t),
+ mLoaded(true),
+ mIncrementIndex(0),
+ mCurrentTime(0),
+ mNumThreads(numThreads),
+ mWaitForTimeStamps(wait),
+ mStopTimeStamp(stopHere) {
+ srand(RAND_COLOR_SEED);
+ mCurrentTime = mTrace.increment(0).time_stamp();
+
+ sReplayingManually.store(replayManually);
+
+ if (stopHere < 0) {
+ mHasStopped = true;
+ }
+}
+
+status_t Replayer::replay() {
+ signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control
+
+ ALOGV("There are %d increments.", mTrace.increment_size());
+
+ status_t status = loadSurfaceComposerClient();
+
+ if (status != NO_ERROR) {
+ ALOGE("Couldn't create SurfaceComposerClient (%d)", status);
+ return status;
+ }
+
+ SurfaceComposerClient::enableVSyncInjections(true);
+
+ initReplay();
+
+ ALOGV("Starting actual Replay!");
+ while (!mPendingIncrements.empty()) {
+ mCurrentIncrement = mTrace.increment(mIncrementIndex);
+
+ if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) {
+ mHasStopped = true;
+ sReplayingManually.store(true);
+ }
+
+ waitForConsoleCommmand();
+
+ if (mWaitForTimeStamps) {
+ waitUntilTimestamp(mCurrentIncrement.time_stamp());
+ }
+
+ auto event = mPendingIncrements.front();
+ mPendingIncrements.pop();
+
+ event->complete();
+
+ if (event->getIncrementType() == Increment::kVsyncEvent) {
+ mWaitingForNextVSync = false;
+ }
+
+ if (mIncrementIndex + mNumThreads < mTrace.increment_size()) {
+ status = dispatchEvent(mIncrementIndex + mNumThreads);
+
+ if (status != NO_ERROR) {
+ SurfaceComposerClient::enableVSyncInjections(false);
+ return status;
+ }
+ }
+
+ mIncrementIndex++;
+ mCurrentTime = mCurrentIncrement.time_stamp();
+ }
+
+ SurfaceComposerClient::enableVSyncInjections(false);
+
+ return status;
+}
+
+status_t Replayer::initReplay() {
+ for (int i = 0; i < mNumThreads && i < mTrace.increment_size(); i++) {
+ status_t status = dispatchEvent(i);
+
+ if (status != NO_ERROR) {
+ ALOGE("Unable to dispatch event (%d)", status);
+ return status;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void Replayer::stopAutoReplayHandler(int /*signal*/) {
+ if (sReplayingManually) {
+ SurfaceComposerClient::enableVSyncInjections(false);
+ exit(0);
+ }
+
+ sReplayingManually.store(true);
+}
+
+std::vector<std::string> split(const std::string& s, const char delim) {
+ std::vector<std::string> elems;
+ std::stringstream ss(s);
+ std::string item;
+ while (getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+}
+
+bool isNumber(const std::string& s) {
+ return !s.empty() &&
+ std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
+}
+
+void Replayer::waitForConsoleCommmand() {
+ if (!sReplayingManually || mWaitingForNextVSync) {
+ return;
+ }
+
+ while (true) {
+ std::string input = "";
+ std::cout << "> ";
+ getline(std::cin, input);
+
+ if (input.empty()) {
+ input = mLastInput;
+ } else {
+ mLastInput = input;
+ }
+
+ if (mLastInput.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> inputs = split(input, ' ');
+
+ if (inputs[0] == "n") { // next vsync
+ mWaitingForNextVSync = true;
+ break;
+
+ } else if (inputs[0] == "ni") { // next increment
+ break;
+
+ } else if (inputs[0] == "c") { // continue
+ if (inputs.size() > 1 && isNumber(inputs[1])) {
+ long milliseconds = stoi(inputs[1]);
+ std::thread([&] {
+ std::cout << "Started!" << std::endl;
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+ sReplayingManually.store(true);
+ std::cout << "Should have stopped!" << std::endl;
+ }).detach();
+ }
+ sReplayingManually.store(false);
+ mWaitingForNextVSync = false;
+ break;
+
+ } else if (inputs[0] == "s") { // stop at this timestamp
+ if (inputs.size() < 1) {
+ std::cout << "No time stamp given" << std::endl;
+ continue;
+ }
+ sReplayingManually.store(false);
+ mStopTimeStamp = stol(inputs[1]);
+ mHasStopped = false;
+ break;
+ } else if (inputs[0] == "l") { // list
+ std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n";
+ continue;
+ } else if (inputs[0] == "q") { // quit
+ SurfaceComposerClient::enableVSyncInjections(false);
+ exit(0);
+
+ } else if (inputs[0] == "h") { // help
+ // add help menu
+ std::cout << "Manual Replay options:\n";
+ std::cout << " n - Go to next VSync\n";
+ std::cout << " ni - Go to next increment\n";
+ std::cout << " c - Continue\n";
+ std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n";
+ std::cout << " s [timestamp] - Continue and stop at specified timestamp\n";
+ std::cout << " l - List out timestamp of current increment\n";
+ std::cout << " h - Display help menu\n";
+ std::cout << std::endl;
+ continue;
+ }
+
+ std::cout << "Invalid Command" << std::endl;
+ }
+}
+
+status_t Replayer::dispatchEvent(int index) {
+ auto increment = mTrace.increment(index);
+ std::shared_ptr<Event> event = std::make_shared<Event>(increment.increment_case());
+ mPendingIncrements.push(event);
+
+ status_t status = NO_ERROR;
+ switch (increment.increment_case()) {
+ case increment.kTransaction: {
+ std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach();
+ } break;
+ case increment.kSurfaceCreation: {
+ std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
+ .detach();
+ } break;
+ case increment.kSurfaceDeletion: {
+ std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
+ .detach();
+ } break;
+ case increment.kBufferUpdate: {
+ std::lock_guard<std::mutex> lock1(mLayerLock);
+ std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+
+ Dimensions dimensions(increment.buffer_update().w(), increment.buffer_update().h());
+ BufferEvent bufferEvent(event, dimensions);
+
+ auto layerId = increment.buffer_update().id();
+ if (mBufferQueueSchedulers.count(layerId) == 0) {
+ mBufferQueueSchedulers[layerId] = std::make_shared<BufferQueueScheduler>(
+ mLayers[layerId], mColors[layerId], layerId);
+ mBufferQueueSchedulers[layerId]->addEvent(bufferEvent);
+
+ std::thread(&BufferQueueScheduler::startScheduling,
+ mBufferQueueSchedulers[increment.buffer_update().id()].get())
+ .detach();
+ } else {
+ auto bqs = mBufferQueueSchedulers[increment.buffer_update().id()];
+ bqs->addEvent(bufferEvent);
+ }
+ } break;
+ case increment.kVsyncEvent: {
+ std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach();
+ } break;
+ case increment.kDisplayCreation: {
+ std::thread(&Replayer::createDisplay, this, increment.display_creation(), event)
+ .detach();
+ } break;
+ case increment.kDisplayDeletion: {
+ std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event)
+ .detach();
+ } break;
+ case increment.kPowerModeUpdate: {
+ std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event)
+ .detach();
+ } break;
+ default:
+ ALOGE("Unknown Increment Type: %d", increment.increment_case());
+ status = BAD_VALUE;
+ break;
+ }
+
+ return status;
+}
+
+status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) {
+ ALOGV("Started Transaction");
+
+ SurfaceComposerClient::openGlobalTransaction();
+
+ status_t status = NO_ERROR;
+
+ status = doSurfaceTransaction(t.surface_change());
+ doDisplayTransaction(t.display_change());
+
+ if (t.animation()) {
+ SurfaceComposerClient::setAnimationTransaction();
+ }
+
+ event->readyToExecute();
+
+ SurfaceComposerClient::closeGlobalTransaction(t.synchronous());
+
+ ALOGV("Ended Transaction");
+
+ return status;
+}
+
+status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) {
+ status_t status = NO_ERROR;
+
+ for (const SurfaceChange& change : surfaceChanges) {
+ std::unique_lock<std::mutex> lock(mLayerLock);
+ if (mLayers[change.id()] == nullptr) {
+ mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
+ }
+
+ switch (change.SurfaceChange_case()) {
+ case SurfaceChange::SurfaceChangeCase::kPosition:
+ status = setPosition(change.id(), change.position());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSize:
+ status = setSize(change.id(), change.size());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kAlpha:
+ status = setAlpha(change.id(), change.alpha());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayer:
+ status = setLayer(change.id(), change.layer());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kCrop:
+ status = setCrop(change.id(), change.crop());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kMatrix:
+ status = setMatrix(change.id(), change.matrix());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+ status = setFinalCrop(change.id(), change.final_crop());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+ status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+ status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayerStack:
+ status = setLayerStack(change.id(), change.layer_stack());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+ status = setHiddenFlag(change.id(), change.hidden_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+ status = setOpaqueFlag(change.id(), change.opaque_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+ status = setSecureFlag(change.id(), change.secure_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+ waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
+ status = setDeferredTransaction(change.id(), change.deferred_transaction());
+ break;
+ default:
+ status = NO_ERROR;
+ break;
+ }
+
+ if (status != NO_ERROR) {
+ ALOGE("SET TRANSACTION FAILED");
+ return status;
+ }
+ }
+ return status;
+}
+
+void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) {
+ for (const DisplayChange& change : displayChanges) {
+ ALOGV("Doing display transaction");
+ std::unique_lock<std::mutex> lock(mDisplayLock);
+ if (mDisplays[change.id()] == nullptr) {
+ mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); });
+ }
+
+ switch (change.DisplayChange_case()) {
+ case DisplayChange::DisplayChangeCase::kSurface:
+ setDisplaySurface(change.id(), change.surface());
+ break;
+ case DisplayChange::DisplayChangeCase::kLayerStack:
+ setDisplayLayerStack(change.id(), change.layer_stack());
+ break;
+ case DisplayChange::DisplayChangeCase::kSize:
+ setDisplaySize(change.id(), change.size());
+ break;
+ case DisplayChange::DisplayChangeCase::kProjection:
+ setDisplayProjection(change.id(), change.projection());
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+status_t Replayer::setPosition(layer_id id, const PositionChange& pc) {
+ ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y());
+ return mLayers[id]->setPosition(pc.x(), pc.y());
+}
+
+status_t Replayer::setSize(layer_id id, const SizeChange& sc) {
+ ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
+ return mLayers[id]->setSize(sc.w(), sc.h());
+}
+
+status_t Replayer::setLayer(layer_id id, const LayerChange& lc) {
+ ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer());
+ return mLayers[id]->setLayer(lc.layer());
+}
+
+status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) {
+ ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha());
+ return mLayers[id]->setAlpha(ac.alpha());
+}
+
+status_t Replayer::setCrop(layer_id id, const CropChange& cc) {
+ ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+ cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+ cc.rectangle().bottom());
+
+ Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+ cc.rectangle().bottom());
+ return mLayers[id]->setCrop(r);
+}
+
+status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) {
+ ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+ fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+ fcc.rectangle().bottom());
+ Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+ fcc.rectangle().bottom());
+ return mLayers[id]->setFinalCrop(r);
+}
+
+status_t Replayer::setMatrix(layer_id id, const MatrixChange& mc) {
+ ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
+ mc.dtdx(), mc.dsdy(), mc.dtdy());
+ return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
+}
+
+status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) {
+ ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
+ return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode());
+}
+
+status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) {
+ ALOGV("Setting Transparent Region Hint");
+ Region re = Region();
+
+ for (auto r : trhc.region()) {
+ Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
+ re.merge(rect);
+ }
+
+ return mLayers[id]->setTransparentRegionHint(re);
+}
+
+status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) {
+ ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
+ return mLayers[id]->setLayerStack(lsc.layer_stack());
+}
+
+status_t Replayer::setHiddenFlag(layer_id id, const HiddenFlagChange& hfc) {
+ ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag());
+ layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
+
+ return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden);
+}
+
+status_t Replayer::setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc) {
+ ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag());
+ layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
+
+ return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque);
+}
+
+status_t Replayer::setSecureFlag(layer_id id, const SecureFlagChange& sfc) {
+ ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag());
+ layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
+
+ return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure);
+}
+
+status_t Replayer::setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc) {
+ ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
+ "frame_number=%llu",
+ id, dtc.layer_id(), dtc.frame_number());
+ if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+ ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id());
+ return BAD_VALUE;
+ }
+
+ auto handle = mLayers[dtc.layer_id()]->getHandle();
+
+ return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number());
+}
+
+void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) {
+ sp<IGraphicBufferProducer> outProducer;
+ sp<IGraphicBufferConsumer> outConsumer;
+ BufferQueue::createBufferQueue(&outProducer, &outConsumer);
+
+ SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer);
+}
+
+void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) {
+ SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+}
+
+void Replayer::setDisplaySize(display_id id, const SizeChange& sc) {
+ SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h());
+}
+
+void Replayer::setDisplayProjection(display_id id, const ProjectionChange& pc) {
+ Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(),
+ pc.viewport().bottom());
+ Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
+
+ SurfaceComposerClient::setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame);
+}
+
+status_t Replayer::createSurfaceControl(
+ const SurfaceCreation& create, const std::shared_ptr<Event>& event) {
+ event->readyToExecute();
+
+ ALOGV("Creating Surface Control: ID: %d", create.id());
+ sp<SurfaceControl> surfaceControl = mComposerClient->createSurface(
+ String8(create.name().c_str()), create.w(), create.h(), PIXEL_FORMAT_RGBA_8888, 0);
+
+ if (surfaceControl == nullptr) {
+ ALOGE("CreateSurfaceControl: unable to create surface control");
+ return BAD_VALUE;
+ }
+
+ std::lock_guard<std::mutex> lock1(mLayerLock);
+ auto& layer = mLayers[create.id()];
+ layer = surfaceControl;
+
+ mColors[create.id()] = HSV(rand() % 360, 1, 1);
+
+ mLayerCond.notify_all();
+
+ std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+ if (mBufferQueueSchedulers.count(create.id()) != 0) {
+ mBufferQueueSchedulers[create.id()]->setSurfaceControl(
+ mLayers[create.id()], mColors[create.id()]);
+ }
+
+ return NO_ERROR;
+}
+
+status_t Replayer::deleteSurfaceControl(
+ const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
+ ALOGV("Deleting %d Surface Control", delete_.id());
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+
+ mLayersPendingRemoval.push_back(delete_.id());
+
+ const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
+ if (iterator != mBufferQueueSchedulers.end()) {
+ (*iterator).second->stopScheduling();
+ }
+
+ std::lock_guard<std::mutex> lock2(mLayerLock);
+ if (mLayers[delete_.id()] != nullptr) {
+ mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+ }
+
+ return NO_ERROR;
+}
+
+void Replayer::doDeleteSurfaceControls() {
+ std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+ std::lock_guard<std::mutex> lock2(mLayerLock);
+ if (!mLayersPendingRemoval.empty()) {
+ for (int id : mLayersPendingRemoval) {
+ mLayers.erase(id);
+ mColors.erase(id);
+ mBufferQueueSchedulers.erase(id);
+ }
+ mLayersPendingRemoval.clear();
+ }
+}
+
+status_t Replayer::injectVSyncEvent(
+ const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) {
+ ALOGV("Injecting VSync Event");
+
+ doDeleteSurfaceControls();
+
+ event->readyToExecute();
+
+ SurfaceComposerClient::injectVSync(vSyncEvent.when());
+
+ return NO_ERROR;
+}
+
+void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event) {
+ ALOGV("Creating display");
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock(mDisplayLock);
+ sp<IBinder> display = SurfaceComposerClient::createDisplay(
+ String8(create.name().c_str()), create.is_secure());
+ mDisplays[create.id()] = display;
+
+ mDisplayCond.notify_all();
+
+ ALOGV("Done creating display");
+}
+
+void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event) {
+ ALOGV("Delete display");
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock(mDisplayLock);
+ SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]);
+ mDisplays.erase(delete_.id());
+}
+
+void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr<Event>& event) {
+ ALOGV("Updating power mode");
+ event->readyToExecute();
+ SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode());
+}
+
+void Replayer::waitUntilTimestamp(int64_t timestamp) {
+ ALOGV("Waiting for %lld nanoseconds...", static_cast<int64_t>(timestamp - mCurrentTime));
+ std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
+}
+
+void Replayer::waitUntilDeferredTransactionLayerExists(
+ const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock) {
+ if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+ mLayerCond.wait(lock, [&] { return (mLayers[dtc.layer_id()] != nullptr); });
+ }
+}
+
+status_t Replayer::loadSurfaceComposerClient() {
+ mComposerClient = new SurfaceComposerClient;
+ return mComposerClient->initCheck();
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
new file mode 100644
index 0000000..f757fc3
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_H
+#define ANDROID_SURFACEREPLAYER_H
+
+#include "BufferQueueScheduler.h"
+#include "Color.h"
+#include "Event.h"
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+#include <utility>
+
+namespace android {
+
+const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
+const auto RAND_COLOR_SEED = 700;
+const auto DEFAULT_THREADS = 3;
+
+typedef int32_t layer_id;
+typedef int32_t display_id;
+
+typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
+typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
+
+class Replayer {
+ public:
+ Replayer(const std::string& filename, bool replayManually = false,
+ int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
+ Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
+ bool wait = true, nsecs_t stopHere = -1);
+
+ status_t replay();
+
+ private:
+ status_t initReplay();
+
+ void waitForConsoleCommmand();
+ static void stopAutoReplayHandler(int signal);
+
+ status_t dispatchEvent(int index);
+
+ status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
+ status_t createSurfaceControl(const SurfaceCreation& create,
+ const std::shared_ptr<Event>& event);
+ status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
+ const std::shared_ptr<Event>& event);
+ status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
+ void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
+ void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
+ void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
+
+ status_t doSurfaceTransaction(const SurfaceChanges& surfaceChange);
+ void doDisplayTransaction(const DisplayChanges& displayChange);
+
+ status_t setPosition(layer_id id, const PositionChange& pc);
+ status_t setSize(layer_id id, const SizeChange& sc);
+ status_t setAlpha(layer_id id, const AlphaChange& ac);
+ status_t setLayer(layer_id id, const LayerChange& lc);
+ status_t setCrop(layer_id id, const CropChange& cc);
+ status_t setFinalCrop(layer_id id, const FinalCropChange& fcc);
+ status_t setMatrix(layer_id id, const MatrixChange& mc);
+ status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc);
+ status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc);
+ status_t setLayerStack(layer_id id, const LayerStackChange& lsc);
+ status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc);
+ status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc);
+ status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc);
+ status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc);
+
+ void setDisplaySurface(display_id id, const DispSurfaceChange& dsc);
+ void setDisplayLayerStack(display_id id, const LayerStackChange& lsc);
+ void setDisplaySize(display_id id, const SizeChange& sc);
+ void setDisplayProjection(display_id id, const ProjectionChange& pc);
+
+ void doDeleteSurfaceControls();
+ void waitUntilTimestamp(int64_t timestamp);
+ void waitUntilDeferredTransactionLayerExists(
+ const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
+ status_t loadSurfaceComposerClient();
+
+ Trace mTrace;
+ bool mLoaded = false;
+ int32_t mIncrementIndex = 0;
+ int64_t mCurrentTime = 0;
+ int32_t mNumThreads = DEFAULT_THREADS;
+
+ Increment mCurrentIncrement;
+
+ std::string mLastInput;
+
+ static atomic_bool sReplayingManually;
+ bool mWaitingForNextVSync;
+ bool mWaitForTimeStamps;
+ nsecs_t mStopTimeStamp;
+ bool mHasStopped;
+
+ std::mutex mLayerLock;
+ std::condition_variable mLayerCond;
+ std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
+ std::unordered_map<layer_id, HSV> mColors;
+
+ std::mutex mPendingLayersLock;
+ std::vector<layer_id> mLayersPendingRemoval;
+
+ std::mutex mBufferQueueSchedulerLock;
+ std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
+
+ std::mutex mDisplayLock;
+ std::condition_variable mDisplayCond;
+ std::unordered_map<display_id, sp<IBinder>> mDisplays;
+
+ sp<SurfaceComposerClient> mComposerClient;
+ std::queue<std::shared_ptr<Event>> mPendingIncrements;
+};
+
+} // namespace android
+#endif
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
new file mode 100644
index 0000000..a892e46
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python
+from subprocess import call
+import os
+proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/"
+call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"])
+
+from trace_pb2 import *
+
+trace = Trace()
+
+def main():
+ global trace
+ while(1):
+ option = main_menu()
+
+ if option == 0:
+ break
+
+ increment = trace.increment.add()
+ increment.time_stamp = int(input("Time stamp of action: "))
+
+ if option == 1:
+ transaction(increment)
+ elif option == 2:
+ surface_create(increment)
+ elif option == 3:
+ surface_delete(increment)
+ elif option == 4:
+ display_create(increment)
+ elif option == 5:
+ display_delete(increment)
+ elif option == 6:
+ buffer_update(increment)
+ elif option == 7:
+ vsync_event(increment)
+ elif option == 8:
+ power_mode_update(increment)
+
+ seralizeTrace()
+
+def seralizeTrace():
+ with open("trace.dat", 'wb') as f:
+ f.write(trace.SerializeToString())
+
+
+def main_menu():
+ print ("")
+ print ("What would you like to do?")
+ print ("1. Add transaction")
+ print ("2. Add surface creation")
+ print ("3. Add surface deletion")
+ print ("4. Add display creation")
+ print ("5. Add display deletion")
+ print ("6. Add buffer update")
+ print ("7. Add VSync event")
+ print ("8. Add power mode update")
+ print ("0. Finish and serialize")
+ print ("")
+
+ return int(input("> "))
+
+def transaction_menu():
+ print ("")
+ print ("What kind of transaction?")
+ print ("1. Position Change")
+ print ("2. Size Change")
+ print ("3. Alpha Change")
+ print ("4. Layer Change")
+ print ("5. Crop Change")
+ print ("6. Final Crop Change")
+ print ("7. Matrix Change")
+ print ("8. Override Scaling Mode Change")
+ print ("9. Transparent Region Hint Change")
+ print ("10. Layer Stack Change")
+ print ("11. Hidden Flag Change")
+ print ("12. Opaque Flag Change")
+ print ("13. Secure Flag Change")
+ print ("14. Deferred Transaction Change")
+ print ("15. Display - Surface Change")
+ print ("16. Display - Layer Stack Change")
+ print ("17. Display - Size Change")
+ print ("18. Display - Projection Change")
+ print ("0. Finished adding Changes to this transaction")
+ print ("")
+
+ return int(input("> "))
+
+def transaction(increment):
+ global trace
+
+ increment.transaction.synchronous \
+ = bool(input("Is transaction synchronous (True/False): "))
+ increment.transaction.animation \
+ = bool(input("Is transaction animated (True/False): "))
+
+ while(1):
+ option = transaction_menu()
+
+ if option == 0:
+ break
+
+ change = None
+ if option <= 14:
+ change = increment.transaction.surface_change.add()
+ elif option >= 15 and option <= 18:
+ change = increment.transaction.display_change.add()
+
+ change.id = int(input("ID of layer/display to undergo a change: "))
+
+ if option == 1:
+ change.position.x, change.position.y = position()
+ elif option == 2:
+ change.size.w, change.size.h = size()
+ elif option == 3:
+ change.alpha.alpha = alpha()
+ elif option == 4:
+ change.layer.layer = layer()
+ elif option == 5:
+ change.crop.rectangle.left, change.crop.rectangle.top, \
+ change.crop.rectangle.right, change.crop.rectangle.bottom = crop()
+ elif option == 6:
+ change.final_crop.rectangle.left, \
+ change.final_crop.rectangle.top, \
+ change.final_crop.rectangle.right,\
+ change.final_crop.rectangle.bottom = final_crop()
+ elif option == 7:
+ change.matrix.dsdx,\
+ change.matrix.dtdx,\
+ change.matrix.dsdy,\
+ change.matrix.dtdy = layer()
+ elif option == 8:
+ change.override_scaling_mode.override_scaling_mode \
+ = override_scaling_mode()
+ elif option == 9:
+ for rect in transparent_region_hint():
+ new = increment.transparent_region_hint.region.add()
+ new.left = rect[0]
+ new.top = rect[1]
+ new.right = rect[2]
+ new.bottom = rect[3]
+ elif option == 10:
+ change.layer_stack.layer_stack = layer_stack()
+ elif option == 11:
+ change.hidden_flag.hidden_flag = hidden_flag()
+ elif option == 12:
+ change.opaque_flag.opaque_flag = opaque_flag()
+ elif option == 13:
+ change.secure_flag.secure_flag = secure_flag()
+ elif option == 14:
+ change.deferred_transaction.layer_id, \
+ change.deferred_transaction.frame_number = deferred_transaction()
+ elif option == 15:
+ change.surface.buffer_queue_id, \
+ change.surface.buffer_queue_name = surface()
+ elif option == 16:
+ change.layer_stack.layer_stack = layer_stack()
+ elif option == 17:
+ change.size.w, change.size.h = size()
+ elif option == 18:
+ projection(change)
+
+def surface_create(increment):
+ increment.surface_creation.id = int(input("Enter id: "))
+ n = str(raw_input("Enter name: "))
+ increment.surface_creation.name = n
+ increment.surface_creation.w = input("Enter w: ")
+ increment.surface_creation.h = input("Enter h: ")
+
+def surface_delete(increment):
+ increment.surface_deletion.id = int(input("Enter id: "))
+
+def display_create(increment):
+ increment.display_creation.id = int(input("Enter id: "))
+ increment.display_creation.name = str(raw_input("Enter name: "))
+ increment.display_creation.type = int(input("Enter type: "))
+ increment.display_creation.is_secure = bool(input("Enter if secure: "))
+
+def display_delete(increment):
+ increment.surface_deletion.id = int(input("Enter id: "))
+
+def buffer_update(increment):
+ increment.buffer_update.id = int(input("Enter id: "))
+ increment.buffer_update.w = int(input("Enter w: "))
+ increment.buffer_update.h = int(input("Enter h: "))
+ increment.buffer_update.frame_number = int(input("Enter frame_number: "))
+
+def vsync_event(increment):
+ increment.vsync_event.when = int(input("Enter when: "))
+
+def power_mode_update(increment):
+ increment.power_mode_update.id = int(input("Enter id: "))
+ increment.power_mode_update.mode = int(input("Enter mode: "))
+
+def position():
+ x = input("Enter x: ")
+ y = input("Enter y: ")
+
+ return float(x), float(y)
+
+def size():
+ w = input("Enter w: ")
+ h = input("Enter h: ")
+
+ return int(w), int(h)
+
+def alpha():
+ alpha = input("Enter alpha: ")
+
+ return float(alpha)
+
+def layer():
+ layer = input("Enter layer: ")
+
+ return int(layer)
+
+def crop():
+ return rectangle()
+
+def final_crop():
+ return rectangle()
+
+def matrix():
+ dsdx = input("Enter dsdx: ")
+ dtdx = input("Enter dtdx: ")
+ dsdy = input("Enter dsdy: ")
+ dtdy = input("Enter dtdy: ")
+
+ return float(dsdx)
+
+def override_scaling_mode():
+ mode = input("Enter override scaling mode: ")
+
+ return int(mode)
+
+def transparent_region_hint():
+ num = input("Enter number of rectangles in region: ")
+
+ return [rectangle() in range(x)]
+
+def layer_stack():
+ layer_stack = input("Enter layer stack: ")
+
+ return int(layer_stack)
+
+def hidden_flag():
+ flag = input("Enter hidden flag state (True/False): ")
+
+ return bool(flag)
+
+def opaque_flag():
+ flag = input("Enter opaque flag state (True/False): ")
+
+ return bool(flag)
+
+def secure_flag():
+ flag = input("Enter secure flag state (True/False): ")
+
+ return bool(flag)
+
+def deferred_transaction():
+ layer_id = input("Enter layer_id: ")
+ frame_number = input("Enter frame_number: ")
+
+ return int(layer_id), int(frame_number)
+
+def surface():
+ id = input("Enter id: ")
+ name = raw_input("Enter name: ")
+
+ return int(id), str(name)
+
+def projection(change):
+ change.projection.orientation = input("Enter orientation: ")
+ print("Enter rectangle for viewport")
+ change.projection.viewport.left, \
+ change.projection.viewport.top, \
+ change.projection.viewport.right,\
+ change.projection.viewport.bottom = rectangle()
+ print("Enter rectangle for frame")
+ change.projection.frame.left, \
+ change.projection.frame.top, \
+ change.projection.frame.right,\
+ change.projection.frame.bottom = rectangle()
+
+def rectangle():
+ left = input("Enter left: ")
+ top = input("Enter top: ")
+ right = input("Enter right: ")
+ bottom = input("Enter bottom: ")
+
+ return int(left), int(top), int(right), int(bottom)
+
+if __name__ == "__main__":
+ main()
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index f9464e8..467e0fd 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -42,6 +42,7 @@
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.input_methods" />
+ <feature name="android.software.picture_in_picture" />
<feature name="android.software.print" />
<!-- Feature to specify if the device supports adding device admins. -->
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 8128165..7f545e6 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -42,6 +42,7 @@
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.input_methods" />
+ <feature name="android.software.picture_in_picture" />
<feature name="android.software.print" />
<!-- Feature to specify if the device supports adding device admins. -->
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 8e10f67..b469e7e 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -300,6 +300,11 @@
* <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
+ /**
+ * UI mode: value that corresponds to
+ * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+ */
+ ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
/** UI night mode: not specified.*/
ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
diff --git a/include/android/native_window.h b/include/android/native_window.h
index b60b9f1..021dc42 100644
--- a/include/android/native_window.h
+++ b/include/android/native_window.h
@@ -38,12 +38,16 @@
* Pixel formats that a window can use.
*/
enum {
+ // NOTE: these values must match the values from graphics/common/x.x/types.hal
+
/** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
WINDOW_FORMAT_RGBA_8888 = 1,
/** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
WINDOW_FORMAT_RGBX_8888 = 2,
/** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
WINDOW_FORMAT_RGB_565 = 4,
+ /** Red: 16 bits, Green: 16 bits, Blue: 16 bits, Alpha: 16 bits. **/
+ WINDOW_FORMAT_RGBA_FP16 = 0x16,
};
struct ANativeWindow;
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
new file mode 100644
index 0000000..d561594
--- /dev/null
+++ b/include/audiomanager/AudioManager.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIOMANAGER_H
+#define ANDROID_AUDIOMANAGER_H
+
+namespace android {
+
+// must be kept in sync with definitions in AudioPlaybackConfiguration.java
+
+#define PLAYER_PIID_INVALID -1
+
+enum {
+ PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11,
+ PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12,
+};
+
+enum {
+ PLAYER_STATE_UNKNOWN = -1,
+ PLAYER_STATE_RELEASED = 0,
+ PLAYER_STATE_IDLE = 1,
+ PLAYER_STATE_STARTED = 2,
+ PLAYER_STATE_PAUSED = 3,
+ PLAYER_STATE_STOPPED = 4,
+};
+
+}; // namespace android
+
+#endif // ANDROID_AUDIOMANAGER_H
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
new file mode 100644
index 0000000..f5ea259
--- /dev/null
+++ b/include/audiomanager/IAudioManager.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IAUDIOMANAGER_H
+#define ANDROID_IAUDIOMANAGER_H
+
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <hardware/power.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioManager : public IInterface
+{
+public:
+ // These transaction IDs must be kept in sync with the method order from
+ // IAudioService.aidl.
+ enum {
+ // transaction IDs for the unsupported methods are commented out
+ /*
+ ADJUSTSUGGESTEDSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION,
+ ADJUSTSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 1,
+ SETSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 2,
+ ISSTREAMMUTE = IBinder::FIRST_CALL_TRANSACTION + 3,
+ FORCEREMOTESUBMIXFULLVOLUME = IBinder::FIRST_CALL_TRANSACTION + 4,
+ ISMASTERMUTE = IBinder::FIRST_CALL_TRANSACTION + 5,
+ SETMASTERMUTE = IBinder::FIRST_CALL_TRANSACTION + 6,
+ GETSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 7,
+ GETSTREAMMINVOLUME = IBinder::FIRST_CALL_TRANSACTION + 8,
+ GETSTREAMMAXVOLUME = IBinder::FIRST_CALL_TRANSACTION + 9,
+ GETLASTAUDIBLESTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 10,
+ SETMICROPHONEMUTE = IBinder::FIRST_CALL_TRANSACTION + 11,
+ SETRINGERMODEEXTERNAL = IBinder::FIRST_CALL_TRANSACTION + 12,
+ SETRINGERMODEINTERNAL = IBinder::FIRST_CALL_TRANSACTION + 13,
+ GETRINGERMODEEXTERNAL = IBinder::FIRST_CALL_TRANSACTION + 14,
+ GETRINGERMODEINTERNAL = IBinder::FIRST_CALL_TRANSACTION + 15,
+ ISVALIDRINGERMODE = IBinder::FIRST_CALL_TRANSACTION + 16,
+ SETVIBRATESETTING = IBinder::FIRST_CALL_TRANSACTION + 17,
+ GETVIBRATESETTING = IBinder::FIRST_CALL_TRANSACTION + 18,
+ SHOULDVIBRATE = IBinder::FIRST_CALL_TRANSACTION + 19,
+ SETMODE = IBinder::FIRST_CALL_TRANSACTION + 20,
+ GETMODE = IBinder::FIRST_CALL_TRANSACTION + 21,
+ PLAYSOUNDEFFECT = IBinder::FIRST_CALL_TRANSACTION + 22,
+ PLAYSOUNDEFFECTVOLUME = IBinder::FIRST_CALL_TRANSACTION + 23,
+ LOADSOUNDEFFECTS = IBinder::FIRST_CALL_TRANSACTION + 24,
+ UNLOADSOUNDEFFECTS = IBinder::FIRST_CALL_TRANSACTION + 25,
+ RELOADAUDIOSETTINGS = IBinder::FIRST_CALL_TRANSACTION + 26,
+ AVRCPSUPPORTSABSOLUTEVOLUME = IBinder::FIRST_CALL_TRANSACTION + 27,
+ SETSPEAKERPHONEON = IBinder::FIRST_CALL_TRANSACTION + 28,
+ ISSPEAKERPHONEON = IBinder::FIRST_CALL_TRANSACTION + 29,
+ SETBLUETOOTHSCOON = IBinder::FIRST_CALL_TRANSACTION + 30,
+ ISBLUETOOTHSCOON = IBinder::FIRST_CALL_TRANSACTION + 31,
+ SETBLUETOOTHA2DPON = IBinder::FIRST_CALL_TRANSACTION + 32,
+ ISBLUETOOTHA2DPON = IBinder::FIRST_CALL_TRANSACTION + 33,
+ REQUESTAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 34,
+ ABANDONAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 35,
+ UNREGISTERAUDIOFOCUSCLIENT = IBinder::FIRST_CALL_TRANSACTION + 36,
+ GETCURRENTAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 37,
+ STARTBLUETOOTHSCO = IBinder::FIRST_CALL_TRANSACTION + 38,
+ STARTBLUETOOTHSCOVIRTUALCALL = IBinder::FIRST_CALL_TRANSACTION + 39,
+ STOPBLUETOOTHSCO = IBinder::FIRST_CALL_TRANSACTION + 40,
+ FORCEVOLUMECONTROLSTREAM = IBinder::FIRST_CALL_TRANSACTION + 41,
+ SETRINGTONEPLAYER = IBinder::FIRST_CALL_TRANSACTION + 42,
+ GETRINGTONEPLAYER = IBinder::FIRST_CALL_TRANSACTION + 43,
+ GETUISOUNDSSTREAMTYPE = IBinder::FIRST_CALL_TRANSACTION + 44,
+ SETWIREDDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 45,
+ SETBLUETOOTHA2DPDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 46,
+ HANDLEBLUETOOTHA2DPDEVICECONFIGCHANGE = IBinder::FIRST_CALL_TRANSACTION + 47,
+ STARTWATCHINGROUTES = IBinder::FIRST_CALL_TRANSACTION + 48,
+ ISCAMERASOUNDFORCED = IBinder::FIRST_CALL_TRANSACTION + 49,
+ SETVOLUMECONTROLLER = IBinder::FIRST_CALL_TRANSACTION + 50,
+ NOTIFYVOLUMECONTROLLERVISIBLE = IBinder::FIRST_CALL_TRANSACTION + 51,
+ ISSTREAMAFFECTEDBYRINGERMODE = IBinder::FIRST_CALL_TRANSACTION + 52,
+ ISSTREAMAFFECTEDBYMUTE = IBinder::FIRST_CALL_TRANSACTION + 53,
+ DISABLESAFEMEDIAVOLUME = IBinder::FIRST_CALL_TRANSACTION + 54,
+ SETHDMISYSTEMAUDIOSUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 55,
+ ISHDMISYSTEMAUDIOSUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 56,
+ REGISTERAUDIOPOLICY = IBinder::FIRST_CALL_TRANSACTION + 57,
+ UNREGISTERAUDIOPOLICYASYNC = IBinder::FIRST_CALL_TRANSACTION + 58,
+ SETFOCUSPROPERTIESFORPOLICY = IBinder::FIRST_CALL_TRANSACTION + 59,
+ SETVOLUMEPOLICY = IBinder::FIRST_CALL_TRANSACTION + 60,
+ REGISTERRECORDINGCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 61,
+ UNREGISTERRECORDINGCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 62,
+ GETACTIVERECORDINGCONFIGURATIONS = IBinder::FIRST_CALL_TRANSACTION + 63,
+ REGISTERPLAYBACKCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 64,
+ UNREGISTERPLAYBACKCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 65,
+ GETACTIVEPLAYBACKCONFIGURATIONS = IBinder::FIRST_CALL_TRANSACTION + 66,
+ */
+
+ TRACK_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 67,
+ PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 68,
+ PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 69,
+ RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 70,
+
+ };
+
+ DECLARE_META_INTERFACE(AudioManager)
+
+ // The parcels created by these methods must be kept in sync with the
+ // corresponding methods from IAudioService.aidl and objects it imports.
+ virtual audio_unique_id_t trackPlayer(int playerType, int usage, int content) = 0;
+ /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, int usage, int content)= 0;
+ /*oneway*/ virtual status_t playerEvent(int piid, int event) = 0;
+ /*oneway*/ virtual status_t releasePlayer(int piid) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOMANAGER_H
diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h
index b399905..30d7317 100644
--- a/include/batteryservice/BatteryService.h
+++ b/include/batteryservice/BatteryService.h
@@ -24,25 +24,7 @@
namespace android {
-// must be kept in sync with definitions in BatteryManager.java
-enum {
- BATTERY_STATUS_UNKNOWN = 1, // equals BatteryManager.BATTERY_STATUS_UNKNOWN constant
- BATTERY_STATUS_CHARGING = 2, // equals BatteryManager.BATTERY_STATUS_CHARGING constant
- BATTERY_STATUS_DISCHARGING = 3, // equals BatteryManager.BATTERY_STATUS_DISCHARGING constant
- BATTERY_STATUS_NOT_CHARGING = 4, // equals BatteryManager.BATTERY_STATUS_NOT_CHARGING constant
- BATTERY_STATUS_FULL = 5, // equals BatteryManager.BATTERY_STATUS_FULL constant
-};
-
-// must be kept in sync with definitions in BatteryManager.java
-enum {
- BATTERY_HEALTH_UNKNOWN = 1, // equals BatteryManager.BATTERY_HEALTH_UNKNOWN constant
- BATTERY_HEALTH_GOOD = 2, // equals BatteryManager.BATTERY_HEALTH_GOOD constant
- BATTERY_HEALTH_OVERHEAT = 3, // equals BatteryManager.BATTERY_HEALTH_OVERHEAT constant
- BATTERY_HEALTH_DEAD = 4, // equals BatteryManager.BATTERY_HEALTH_DEAD constant
- BATTERY_HEALTH_OVER_VOLTAGE = 5, // equals BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE constant
- BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6, // equals BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE constant
- BATTERY_HEALTH_COLD = 7, // equals BatteryManager.BATTERY_HEALTH_COLD constant
-};
+#include "BatteryServiceConstants.h"
// must be kept in sync with definitions in BatteryProperty.java
enum {
diff --git a/include/batteryservice/BatteryServiceConstants.h b/include/batteryservice/BatteryServiceConstants.h
new file mode 100644
index 0000000..8a90a12
--- /dev/null
+++ b/include/batteryservice/BatteryServiceConstants.h
@@ -0,0 +1,32 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ BATTERY_STATUS_UNKNOWN = 1,
+ BATTERY_STATUS_CHARGING = 2,
+ BATTERY_STATUS_DISCHARGING = 3,
+ BATTERY_STATUS_NOT_CHARGING = 4,
+ BATTERY_STATUS_FULL = 5,
+};
+
+enum {
+ BATTERY_HEALTH_UNKNOWN = 1,
+ BATTERY_HEALTH_GOOD = 2,
+ BATTERY_HEALTH_OVERHEAT = 3,
+ BATTERY_HEALTH_DEAD = 4,
+ BATTERY_HEALTH_OVER_VOLTAGE = 5,
+ BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
+ BATTERY_HEALTH_COLD = 7,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/include/binder/AppOpsManager.h b/include/binder/AppOpsManager.h
index 042927c..4212776 100644
--- a/include/binder/AppOpsManager.h
+++ b/include/binder/AppOpsManager.h
@@ -91,7 +91,8 @@
OP_USE_SIP = 53,
OP_PROCESS_OUTGOING_CALLS = 54,
OP_USE_FINGERPRINT = 55,
- OP_BODY_SENSORS = 56
+ OP_BODY_SENSORS = 56,
+ OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
};
AppOpsManager();
diff --git a/include/binder/IActivityManager.h b/include/binder/IActivityManager.h
new file mode 100644
index 0000000..5ad2180
--- /dev/null
+++ b/include/binder/IActivityManager.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IACTIVITY_MANAGER_H
+#define ANDROID_IACTIVITY_MANAGER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class IActivityManager : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ActivityManager)
+
+ virtual int openContentUri(const String16& /* stringUri */) = 0;
+
+ enum {
+ OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ------------------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IACTIVITY_MANAGER_H
\ No newline at end of file
diff --git a/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/IInterface.h b/include/binder/IInterface.h
index be72d44..0f1fe5b 100644
--- a/include/binder/IInterface.h
+++ b/include/binder/IInterface.h
@@ -72,24 +72,24 @@
// ----------------------------------------------------------------------
#define DECLARE_META_INTERFACE(INTERFACE) \
- static const android::String16 descriptor; \
- static android::sp<I##INTERFACE> asInterface( \
- const android::sp<android::IBinder>& obj); \
- virtual const android::String16& getInterfaceDescriptor() const; \
+ static const ::android::String16 descriptor; \
+ static ::android::sp<I##INTERFACE> asInterface( \
+ const ::android::sp<::android::IBinder>& obj); \
+ virtual const ::android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
- const android::String16 I##INTERFACE::descriptor(NAME); \
- const android::String16& \
+ const ::android::String16 I##INTERFACE::descriptor(NAME); \
+ const ::android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
- android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
- const android::sp<android::IBinder>& obj) \
+ ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
+ const ::android::sp<::android::IBinder>& obj) \
{ \
- android::sp<I##INTERFACE> intr; \
+ ::android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
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 b0d53ef..69de136 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;
@@ -468,6 +479,8 @@
#pragma clang diagnostic ignored "-Wweak-vtables"
#endif
+ // FlattenableHelperInterface and FlattenableHelper avoid generating a vtable entry in objects
+ // following Flattenable template/protocol.
class FlattenableHelperInterface {
protected:
~FlattenableHelperInterface() { }
@@ -482,6 +495,9 @@
#pragma clang diagnostic pop
#endif
+ // Concrete implementation of FlattenableHelperInterface that delegates virtual calls to the
+ // specified class T implementing the Flattenable protocol. It "virtualizes" a compile-time
+ // protocol.
template<typename T>
class FlattenableHelper : public FlattenableHelperInterface {
friend class Parcel;
@@ -855,7 +871,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/gfx/.clang-format b/include/gfx/.clang-format
new file mode 100644
index 0000000..86763a0
--- /dev/null
+++ b/include/gfx/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+BinPackParameters: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 8
+IndentWidth: 4
diff --git a/include/gfx/FloatRect.h b/include/gfx/FloatRect.h
new file mode 100644
index 0000000..6be5e22
--- /dev/null
+++ b/include/gfx/FloatRect.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+namespace gfx {
+
+class FloatRect {
+ public:
+ FloatRect() = default;
+ constexpr FloatRect(float _left, float _top, float _right, float _bottom)
+ : left(_left), top(_top), right(_right), bottom(_bottom) {}
+
+ float getWidth() const { return right - left; }
+ float getHeight() const { return bottom - top; }
+
+ float left = 0.0f;
+ float top = 0.0f;
+ float right = 0.0f;
+ float bottom = 0.0f;
+};
+
+inline bool operator==(const FloatRect& a, const FloatRect& b) {
+ return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
+}
+
+} // namespace gfx
+} // namespace android
diff --git a/include/gfx/SafeLog.h b/include/gfx/SafeLog.h
new file mode 100644
index 0000000..e45e541
--- /dev/null
+++ b/include/gfx/SafeLog.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#include <fmt/format.h>
+#include <log/log.h>
+#pragma clang diagnostic pop
+
+#include <type_traits>
+
+namespace android {
+namespace gfx {
+
+/* SafeLog is a mix-in that can be used to easily add typesafe logging using fmtlib to any class.
+ * To use it, inherit from it using CRTP and implement the getLogTag method.
+ *
+ * For example:
+ *
+ * class Frobnicator : private SafeLog<Frobnicator> {
+ * friend class SafeLog<Frobnicator>; // Allows getLogTag to be private
+ *
+ * public:
+ * void frobnicate(int32_t i);
+ *
+ * private:
+ * // SafeLog does member access on the object calling alog*, so this tag can theoretically vary
+ * // by instance unless getLogTag is made static
+ * const char* getLogTag() { return "Frobnicator"; }
+ * };
+ *
+ * void Frobnicator::frobnicate(int32_t i) {
+ * // Logs something like "04-16 21:35:46.811 3513 3513 I Frobnicator: frobnicating 42"
+ * alogi("frobnicating {}", i);
+ * }
+ *
+ * See http://fmtlib.net for more information on the formatting API.
+ */
+
+template <typename T>
+class SafeLog {
+ protected:
+ template <typename... Args>
+#if LOG_NDEBUG
+ void alogv(Args&&... /*unused*/) const {
+ }
+#else
+ void alogv(Args&&... args) const {
+ alog<ANDROID_LOG_VERBOSE>(std::forward<Args>(args)...);
+ }
+#endif
+
+ template <typename... Args>
+ void alogd(Args&&... args) const {
+ alog<ANDROID_LOG_DEBUG>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void alogi(Args&&... args) const {
+ alog<ANDROID_LOG_INFO>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void alogw(Args&&... args) const {
+ alog<ANDROID_LOG_WARN>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void aloge(Args&&... args) const {
+ alog<ANDROID_LOG_ERROR>(std::forward<Args>(args)...);
+ }
+
+ private:
+ // Suppresses clang-tidy check cppcoreguidelines-pro-bounds-array-to-pointer-decay
+ template <size_t strlen, typename... Args>
+ void write(fmt::MemoryWriter* writer, const char (&str)[strlen], Args&&... args) const {
+ writer->write(static_cast<const char*>(str), std::forward<Args>(args)...);
+ }
+
+ template <int priority, typename... Args>
+ void alog(Args&&... args) const {
+ static_assert(std::is_base_of<SafeLog<T>, T>::value, "Can't convert to SafeLog pointer");
+ fmt::MemoryWriter writer;
+ write(&writer, std::forward<Args>(args)...);
+ auto derivedThis = static_cast<const T*>(this);
+ android_writeLog(priority, derivedThis->getLogTag(), writer.c_str());
+ }
+};
+
+} // namespace gfx
+} // namespace android
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index 5232d0f..ab676cc 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -20,6 +20,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <ui/FenceTime.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -59,6 +60,9 @@
// mFence is a fence that will signal when the buffer is idle.
sp<Fence> mFence;
+ // The std::shared_ptr<FenceTime> wrapper around mFence.
+ std::shared_ptr<FenceTime> mFenceTime{FenceTime::NO_FENCE};
+
// mCrop is the current crop rectangle for this buffer slot.
Rect mCrop;
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 266f0aa..a523cd8 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -66,8 +66,9 @@
virtual void onFrameReplaced(const BufferItem& item) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ virtual void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override;
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
@@ -79,7 +80,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/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index e2bafec..da574ec 100644
--- a/include/gui/BufferQueueConsumer.h
+++ b/include/gui/BufferQueueConsumer.h
@@ -22,6 +22,7 @@
#include <gui/BufferQueueDefs.h>
#include <gui/IGraphicBufferConsumer.h>
+#include <utils/String8.h>
namespace android {
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 79e7af2..5541468 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.
@@ -80,9 +80,9 @@
//
// In both cases, the producer will need to call requestBuffer to get a
// GraphicBuffer handle for the returned slot.
- virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
+ status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
- uint32_t usage);
+ uint32_t usage, FrameEventHistoryDelta* outTimestamps) override;
// See IGraphicBufferProducer::detachBuffer
virtual status_t detachBuffer(int slot);
@@ -177,8 +177,7 @@
sp<Fence>* outFence, float outTransformMatrix[16]) override;
// See IGraphicBufferProducer::getFrameTimestamps
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
// See IGraphicBufferProducer::getUniqueId
virtual status_t getUniqueId(uint64_t* outId) const override;
@@ -195,6 +194,9 @@
// BufferQueueCore::INVALID_BUFFER_SLOT otherwise
int getFreeSlotLocked() const;
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta);
+
// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
// block if there are no available slots and we are not in non-blocking
// mode (producer and consumer controlled by the application). If it blocks,
@@ -218,6 +220,10 @@
uint32_t mStickyTransform;
+ // This controls whether the GraphicBuffer pointer in the BufferItem is
+ // cleared after being queued
+ bool mConsumerIsSurfaceFlinger;
+
// This saves the fence from the last queueBuffer, such that the
// next queueBuffer call can throttle buffer production. The prior
// queueBuffer's fence is not nessessarily available elsewhere,
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 9f8b638..ce85fc3 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -180,7 +180,7 @@
// Derived classes should override this method to perform any cleanup that
// must take place when a buffer is released back to the BufferQueue. If
// it is overridden the derived class's implementation must call
- // ConsumerBase::releaseBufferLocked.e
+ // ConsumerBase::releaseBufferLocked.
virtual status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer,
EGLDisplay display, EGLSyncKHR eglFence);
@@ -244,6 +244,10 @@
// if none is supplied
sp<IGraphicBufferConsumer> mConsumer;
+ // The final release fence of the most recent buffer released by
+ // releaseBufferLocked.
+ sp<Fence> mPrevFinalReleaseFence;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of ConsumerBase objects. It must be locked whenever the
// member variables are accessed or when any of the *Locked methods are
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 4dc7467..46ca2c2 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -17,29 +17,300 @@
#ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
#define ANDROID_GUI_FRAMETIMESTAMPS_H
-#include <utils/Timers.h>
+#include <ui/FenceTime.h>
#include <utils/Flattenable.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <array>
+#include <bitset>
+#include <vector>
namespace android {
-struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
- FrameTimestamps() :
- frameNumber(0),
- postedTime(0),
- acquireTime(0),
- refreshStartTime(0),
- glCompositionDoneTime(0),
- displayRetireTime(0),
- releaseTime(0) {}
+struct FrameEvents;
+class FrameEventHistoryDelta;
+class String8;
- uint64_t frameNumber;
- nsecs_t postedTime;
- nsecs_t acquireTime;
- nsecs_t refreshStartTime;
- nsecs_t glCompositionDoneTime;
- nsecs_t displayRetireTime;
- nsecs_t releaseTime;
+
+// Identifiers for all the events that may be recorded or reported.
+enum class FrameEvent {
+ POSTED,
+ REQUESTED_PRESENT,
+ LATCH,
+ ACQUIRE,
+ FIRST_REFRESH_START,
+ LAST_REFRESH_START,
+ GL_COMPOSITION_DONE,
+ DISPLAY_PRESENT,
+ DISPLAY_RETIRE,
+ DEQUEUE_READY,
+ RELEASE,
+ EVENT_COUNT, // Not an actual event.
};
+
+// A collection of timestamps corresponding to a single frame.
+struct FrameEvents {
+ bool hasPostedInfo() const;
+ bool hasRequestedPresentInfo() const;
+ bool hasLatchInfo() const;
+ bool hasFirstRefreshStartInfo() const;
+ bool hasLastRefreshStartInfo() const;
+ bool hasAcquireInfo() const;
+ bool hasGpuCompositionDoneInfo() const;
+ bool hasDisplayPresentInfo() const;
+ bool hasDisplayRetireInfo() const;
+ bool hasReleaseInfo() const;
+ bool hasDequeueReadyInfo() const;
+
+ void checkFencesForCompletion();
+ void dump(String8& outString) const;
+
+ static constexpr size_t EVENT_COUNT =
+ static_cast<size_t>(FrameEvent::EVENT_COUNT);
+ static_assert(EVENT_COUNT <= 32, "Event count sanity check failed.");
+
+ bool valid{false};
+ uint64_t frameNumber{0};
+
+ // Whether or not certain points in the frame's life cycle have been
+ // encountered help us determine if timestamps aren't available because
+ // a) we'll just never get them or b) they're not ready yet.
+ bool addPostCompositeCalled{false};
+ bool addRetireCalled{false};
+ bool addReleaseCalled{false};
+
+ nsecs_t postedTime{-1};
+ nsecs_t requestedPresentTime{-1};
+ nsecs_t latchTime{-1};
+ nsecs_t firstRefreshStartTime{-1};
+ nsecs_t lastRefreshStartTime{-1};
+ nsecs_t dequeueReadyTime{-1};
+
+ std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> displayPresentFence{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> displayRetireFence{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE};
+};
+
+
+// A short history of frames that are synchronized between the consumer and
+// producer via deltas.
+class FrameEventHistory {
+public:
+ virtual ~FrameEventHistory();
+
+ FrameEvents* getFrame(uint64_t frameNumber);
+ FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint);
+ void checkFencesForCompletion();
+ void dump(String8& outString) const;
+
+ static constexpr size_t MAX_FRAME_HISTORY = 8;
+
+protected:
+ std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames;
+};
+
+
+// The producer's interface to FrameEventHistory
+class ProducerFrameEventHistory : public FrameEventHistory {
+public:
+ ~ProducerFrameEventHistory() override;
+
+ // virtual for testing.
+ virtual void updateAcquireFence(
+ uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire);
+ void applyDelta(const FrameEventHistoryDelta& delta);
+
+ void updateSignalTimes();
+
+protected:
+ void applyFenceDelta(FenceTimeline* timeline,
+ std::shared_ptr<FenceTime>* dst,
+ const FenceTime::Snapshot& src) const;
+
+ // virtual for testing.
+ virtual std::shared_ptr<FenceTime> createFenceTime(
+ const sp<Fence>& fence) const;
+
+ size_t mAcquireOffset{0};
+
+ // The consumer updates it's timelines in Layer and SurfaceFlinger since
+ // they can coordinate shared timelines better. The producer doesn't have
+ // shared timelines though, so just let it own and update all of them.
+ FenceTimeline mAcquireTimeline;
+ FenceTimeline mGpuCompositionDoneTimeline;
+ FenceTimeline mPresentTimeline;
+ FenceTimeline mRetireTimeline;
+ FenceTimeline mReleaseTimeline;
+};
+
+
+// Used by the consumer to create a new frame event record that is
+// partially complete.
+struct NewFrameEventsEntry {
+ uint64_t frameNumber{0};
+ nsecs_t postedTime{0};
+ nsecs_t requestedPresentTime{0};
+ std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
+};
+
+
+// Used by the consumer to keep track of which fields it already sent to
+// the producer.
+class FrameEventDirtyFields {
+public:
+ inline void reset() { mBitset.reset(); }
+ inline bool anyDirty() const { return mBitset.any(); }
+
+ template <FrameEvent event>
+ inline void setDirty() {
+ constexpr size_t eventIndex = static_cast<size_t>(event);
+ static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+ mBitset.set(eventIndex);
+ }
+
+ template <FrameEvent event>
+ inline bool isDirty() const {
+ constexpr size_t eventIndex = static_cast<size_t>(event);
+ static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+ return mBitset[eventIndex];
+ }
+
+private:
+ std::bitset<FrameEvents::EVENT_COUNT> mBitset;
+};
+
+
+// The consumer's interface to FrameEventHistory
+class ConsumerFrameEventHistory : public FrameEventHistory {
+public:
+ ~ConsumerFrameEventHistory() override;
+
+ void addQueue(const NewFrameEventsEntry& newEntry);
+ void addLatch(uint64_t frameNumber, nsecs_t latchTime);
+ void addPreComposition(uint64_t frameNumber, nsecs_t refreshStartTime);
+ void addPostComposition(uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& gpuCompositionDone,
+ const std::shared_ptr<FenceTime>& displayPresent);
+ void addRetire(uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& displayRetire);
+ void addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime,
+ std::shared_ptr<FenceTime>&& release);
+
+ void getAndResetDelta(FrameEventHistoryDelta* delta);
+
+private:
+ void getFrameDelta(FrameEventHistoryDelta* delta,
+ const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame);
+
+ std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty;
+ size_t mQueueOffset{0};
+ size_t mCompositionOffset{0};
+ size_t mRetireOffset{0};
+ size_t mReleaseOffset{0};
+
+ bool mProducerWantsEvents{false};
+};
+
+
+// A single frame update from the consumer to producer that can be sent
+// through Binder.
+// Although this may be sent multiple times for the same frame as new
+// timestamps are set, Fences only need to be sent once.
+class FrameEventsDelta : public Flattenable<FrameEventsDelta> {
+friend class ProducerFrameEventHistory;
+public:
+ FrameEventsDelta() = default;
+ FrameEventsDelta(size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields);
+
+ // Movable.
+ FrameEventsDelta(FrameEventsDelta&& src) = default;
+ FrameEventsDelta& operator=(FrameEventsDelta&& src) = default;
+ // Not copyable.
+ FrameEventsDelta(const FrameEventsDelta& src) = delete;
+ FrameEventsDelta& operator=(const FrameEventsDelta& src) = delete;
+
+ // Flattenable implementation
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+private:
+ static size_t minFlattenedSize();
+
+ size_t mIndex{0};
+ uint64_t mFrameNumber{0};
+
+ bool mAddPostCompositeCalled{0};
+ bool mAddRetireCalled{0};
+ bool mAddReleaseCalled{0};
+
+ nsecs_t mPostedTime{0};
+ nsecs_t mRequestedPresentTime{0};
+ nsecs_t mLatchTime{0};
+ nsecs_t mFirstRefreshStartTime{0};
+ nsecs_t mLastRefreshStartTime{0};
+ nsecs_t mDequeueReadyTime{0};
+
+ FenceTime::Snapshot mGpuCompositionDoneFence;
+ FenceTime::Snapshot mDisplayPresentFence;
+ FenceTime::Snapshot mDisplayRetireFence;
+ FenceTime::Snapshot mReleaseFence;
+
+ // This is a static method with an auto return value so we can call
+ // it without needing const and non-const versions.
+ template <typename ThisT>
+ static inline auto allFences(ThisT fed) ->
+ std::array<decltype(&fed->mReleaseFence), 4> {
+ return {{
+ &fed->mGpuCompositionDoneFence, &fed->mDisplayPresentFence,
+ &fed->mDisplayRetireFence, &fed->mReleaseFence
+ }};
+ }
+};
+
+
+// A collection of updates from consumer to producer that can be sent
+// through Binder.
+class FrameEventHistoryDelta
+ : public Flattenable<FrameEventHistoryDelta> {
+
+friend class ConsumerFrameEventHistory;
+friend class ProducerFrameEventHistory;
+
+public:
+ FrameEventHistoryDelta() = default;
+
+ // Movable.
+ FrameEventHistoryDelta(FrameEventHistoryDelta&& src) = default;
+ FrameEventHistoryDelta& operator=(FrameEventHistoryDelta&& src);
+ // Not copyable.
+ FrameEventHistoryDelta(const FrameEventHistoryDelta& src) = delete;
+ FrameEventHistoryDelta& operator=(
+ const FrameEventHistoryDelta& src) = delete;
+
+ // Flattenable implementation.
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+private:
+ static size_t minFlattenedSize();
+
+ std::vector<FrameEventsDelta> mDeltas;
+};
+
+
} // namespace android
#endif
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 6267625..493ecde 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -187,6 +187,10 @@
// ready to be read from.
sp<Fence> getCurrentFence() const;
+ // getCurrentFence returns the FenceTime indicating when the current
+ // buffer is ready to be read from.
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
// doGLFenceWait inserts a wait command into the OpenGL ES command stream
// to ensure that it is safe for future OpenGL ES commands to access the
// current texture buffer.
@@ -250,7 +254,7 @@
// mEglSlots array in addition to the ConsumerBase.
virtual status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer,
- EGLDisplay display, EGLSyncKHR eglFence);
+ EGLDisplay display, EGLSyncKHR eglFence) override;
status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) {
@@ -398,6 +402,9 @@
// mCurrentFence is the fence received from BufferQueue in updateTexImage.
sp<Fence> mCurrentFence;
+ // The FenceTime wrapper around mCurrentFence.
+ std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
// mCurrentTransformMatrix is the transform matrix for the current texture.
// It gets computed by computeTransformMatrix each time updateTexImage is
// called.
diff --git a/include/gui/GraphicBufferAlloc.h b/include/gui/GraphicBufferAlloc.h
index 62e3877..b19a1ac 100644
--- a/include/gui/GraphicBufferAlloc.h
+++ b/include/gui/GraphicBufferAlloc.h
@@ -34,8 +34,9 @@
GraphicBufferAlloc();
virtual ~GraphicBufferAlloc();
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) override;
+ uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint32_t usage, std::string requestorName,
+ status_t* error) override;
};
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 460a03d..93dd4ac 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.
@@ -81,10 +82,11 @@
// different stream.
virtual void onSidebandStreamChanged() = 0; /* Asynchronous */
- // See IGraphicBufferProducer::getFrameTimestamps
- // This queries the consumer for the timestamps
- virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
- FrameTimestamps* /*outTimestamps*/) const { return false; }
+ // Notifies the consumer of any new producer-side timestamps and
+ // returns the combined frame history that hasn't already been retrieved.
+ virtual void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* /*newTimestamps*/,
+ FrameEventHistoryDelta* /*outDelta*/) {}
};
diff --git a/include/gui/IGraphicBufferAlloc.h b/include/gui/IGraphicBufferAlloc.h
index acc2f30..2a7690a 100644
--- a/include/gui/IGraphicBufferAlloc.h
+++ b/include/gui/IGraphicBufferAlloc.h
@@ -38,12 +38,14 @@
/* Create a new GraphicBuffer for the client to use.
*/
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage, std::string requestorName,
- status_t* error) = 0;
+ PixelFormat format, uint32_t layerCount, uint32_t usage,
+ std::string requestorName, status_t* error) = 0;
sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage, status_t* error) {
- return createGraphicBuffer(w, h, format, usage, "<Unknown>", error);
+ PixelFormat format, uint32_t layerCount, uint32_t usage,
+ status_t* error) {
+ return createGraphicBuffer(w, h, format, layerCount, usage, "<Unknown>",
+ error);
}
};
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index c2dba50..304a0c0 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -190,7 +190,8 @@
// All other negative values are an unknown error returned downstream
// from the graphics allocator (typically errno).
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t usage) = 0;
+ uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) = 0;
// detachBuffer attempts to remove all ownership of the buffer in the given
// slot from the buffer queue. If this call succeeds, the slot will be
@@ -295,6 +296,7 @@
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
friend class Flattenable<QueueBufferInput>;
explicit inline QueueBufferInput(const Parcel& parcel);
+
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
// dataSpace - description of the contents, interpretation depends on format
@@ -305,19 +307,23 @@
// set this to Fence::NO_FENCE if the buffer is ready immediately
// sticky - the sticky transform set in Surface (only used by the LEGACY
// camera mode).
+ // getFrameTimestamps - whether or not the latest frame timestamps
+ // should be retrieved from the consumer.
inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
android_dataspace _dataSpace, const Rect& _crop,
int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
- uint32_t _sticky = 0)
+ uint32_t _sticky = 0, bool _getFrameTimestamps = false)
: timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
transform(_transform), stickyTransform(_sticky), fence(_fence),
- surfaceDamage() { }
+ surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+
inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
android_dataspace* outDataSpace,
Rect* outCrop, int* outScalingMode,
uint32_t* outTransform, sp<Fence>* outFence,
- uint32_t* outStickyTransform = NULL) const {
+ uint32_t* outStickyTransform = nullptr,
+ bool* outGetFrameTimestamps = nullptr) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outDataSpace = dataSpace;
@@ -328,9 +334,13 @@
if (outStickyTransform != NULL) {
*outStickyTransform = stickyTransform;
}
+ if (outGetFrameTimestamps) {
+ *outGetFrameTimestamps = getFrameTimestamps;
+ }
}
// Flattenable protocol
+ static constexpr size_t minFlattenedSize();
size_t getFlattenedSize() const;
size_t getFdCount() const;
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
@@ -340,51 +350,41 @@
void setSurfaceDamage(const Region& damage) { surfaceDamage = damage; }
private:
- int64_t timestamp;
- int isAutoTimestamp;
- android_dataspace dataSpace;
+ int64_t timestamp{0};
+ int isAutoTimestamp{0};
+ android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN};
Rect crop;
- int scalingMode;
- uint32_t transform;
- uint32_t stickyTransform;
+ int scalingMode{0};
+ uint32_t transform{0};
+ uint32_t stickyTransform{0};
sp<Fence> fence;
Region surfaceDamage;
+ bool getFrameTimestamps{false};
};
- // QueueBufferOutput must be a POD structure
- struct QueueBufferOutput {
- inline QueueBufferOutput() { }
- // outWidth - filled with default width applied to the buffer
- // outHeight - filled with default height applied to the buffer
- // outTransformHint - filled with default transform applied to the buffer
- // outNumPendingBuffers - num buffers queued that haven't yet been acquired
- // (counting the currently queued buffer)
- inline void deflate(uint32_t* outWidth,
- uint32_t* outHeight,
- uint32_t* outTransformHint,
- uint32_t* outNumPendingBuffers,
- uint64_t* outNextFrameNumber) const {
- *outWidth = width;
- *outHeight = height;
- *outTransformHint = transformHint;
- *outNumPendingBuffers = numPendingBuffers;
- *outNextFrameNumber = nextFrameNumber;
- }
- inline void inflate(uint32_t inWidth, uint32_t inHeight,
- uint32_t inTransformHint, uint32_t inNumPendingBuffers,
- uint64_t inNextFrameNumber) {
- width = inWidth;
- height = inHeight;
- transformHint = inTransformHint;
- numPendingBuffers = inNumPendingBuffers;
- nextFrameNumber = inNextFrameNumber;
- }
- private:
- uint32_t width;
- uint32_t height;
- uint32_t transformHint;
- uint32_t numPendingBuffers;
+ struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
+ QueueBufferOutput() = default;
+
+ // Moveable.
+ QueueBufferOutput(QueueBufferOutput&& src) = default;
+ QueueBufferOutput& operator=(QueueBufferOutput&& src) = default;
+ // Not copyable.
+ QueueBufferOutput(const QueueBufferOutput& src) = delete;
+ QueueBufferOutput& operator=(const QueueBufferOutput& src) = delete;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ uint32_t width{0};
+ uint32_t height{0};
+ uint32_t transformHint{0};
+ uint32_t numPendingBuffers{0};
uint64_t nextFrameNumber{0};
+ FrameEventHistoryDelta frameTimestamps;
};
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
@@ -581,13 +581,8 @@
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
- // Attempts to retrieve timestamp information for the given frame number.
- // If information for the given frame number is not found, returns false.
- // Returns true otherwise.
- //
- // If a fence has not yet signaled the timestamp returned will be 0;
- virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
- FrameTimestamps* /*outTimestamps*/) const { return false; }
+ // Gets the frame events that haven't already been retrieved.
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {}
// Returns a unique id for this BufferQueue
virtual status_t getUniqueId(uint64_t* outId) const = 0;
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 555a0cc..824e5c4 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -32,6 +32,8 @@
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposerClient.h>
+#include <vector>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -43,6 +45,7 @@
class IDisplayEventConnection;
class IMemoryHeap;
class Rect;
+enum class FrameEvent;
/*
* This class defines the Binder IPC interface for accessing various
@@ -112,6 +115,11 @@
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& surface) const = 0;
+ /* Returns the frame timestamps supported by SurfaceFlinger.
+ */
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const = 0;
+
/* set display power mode. depending on the mode, it can either trigger
* screen on, off or low power mode and wait for it to complete.
* requires ACCESS_SURFACE_FLINGER permission.
@@ -171,6 +179,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;
};
// ----------------------------------------------------------------------------
@@ -189,6 +201,7 @@
GET_BUILT_IN_DISPLAY,
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE,
+ GET_SUPPORTED_FRAME_TIMESTAMPS,
GET_DISPLAY_CONFIGS,
GET_ACTIVE_CONFIG,
SET_ACTIVE_CONFIG,
@@ -202,6 +215,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..0b6a8f7 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -33,6 +33,8 @@
namespace android {
+class ISurfaceComposer;
+
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
@@ -66,7 +68,8 @@
* the controlledByApp flag indicates that this Surface (producer) is
* controlled by the application. This flag is used at connect time.
*/
- explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false);
+ explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
+ bool controlledByApp = false);
/* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
* Surface was created with. Usually it's an error to use the
@@ -134,17 +137,28 @@
status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]);
+ /* Enables or disables frame timestamp tracking. It is disabled by default
+ * to avoid overhead during queue and dequeue for applications that don't
+ * need the feature. If disabled, calls to getFrameTimestamps will fail.
+ */
+ void enableFrameTimestamps(bool enable);
+
// See IGraphicBufferProducer::getFrameTimestamps
- bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
- nsecs_t* outReleaseTime);
+ status_t getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+ nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime);
status_t getUniqueId(uint64_t* outId) const;
protected:
virtual ~Surface();
+ // Virtual for testing.
+ virtual sp<ISurfaceComposer> composerService() const;
+
private:
// can't be copied
Surface& operator = (const Surface& rhs);
@@ -191,6 +205,7 @@
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSharedBufferMode(va_list args);
int dispatchSetAutoRefresh(va_list args);
+ int dispatchEnableFrameTimestamps(va_list args);
int dispatchGetFrameTimestamps(va_list args);
protected:
@@ -204,7 +219,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 +238,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;
@@ -237,7 +252,8 @@
enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
-private:
+ void querySupportedTimestampsLocked() const;
+
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
@@ -379,7 +395,17 @@
Condition mQueueBufferCondition;
- uint64_t mNextFrameNumber;
+ uint64_t mNextFrameNumber = 1;
+ uint64_t mLastFrameNumber = 0;
+
+ // Mutable because ANativeWindow::query needs this class const.
+ mutable bool mQueriedSupportedTimestamps;
+ mutable bool mFrameTimestampsSupportsPresent;
+ mutable bool mFrameTimestampsSupportsRetire;
+
+ // A cached copy of the FrameEventHistory maintained by the consumer.
+ bool mEnableFrameTimestamps = false;
+ std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
};
namespace view {
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index f2932f2..b86c72c 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();
@@ -210,7 +214,13 @@
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ,
bool useIdentityTransform);
-
+ static status_t captureToBuffer(
+ const sp<IBinder>& display,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool useIdentityTransform,
+ uint32_t rotation,
+ sp<GraphicBuffer>* outbuffer);
private:
mutable sp<CpuConsumer> mCpuConsumer;
mutable sp<IGraphicBufferProducer> mProducer;
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index 7ae07ad..4556c8f 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";
@@ -533,6 +534,8 @@
case OMX_IndexParamAudioAndroidAc3: return "ParamAudioAndroidAc3";
case OMX_IndexParamAudioAndroidOpus: return "ParamAudioAndroidOpus";
case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation";
+ case OMX_IndexParamAudioAndroidEac3: return "ParamAudioAndroidEac3";
+ case OMX_IndexParamAudioProfileQuerySupported: return "ParamAudioProfileQuerySupported";
// case OMX_IndexParamNalStreamFormatSupported: return "ParamNalStreamFormatSupported";
// case OMX_IndexParamNalStreamFormat: return "ParamNalStreamFormat";
// case OMX_IndexParamNalStreamFormatSelect: return "ParamNalStreamFormatSelect";
@@ -545,6 +548,10 @@
case OMX_IndexConfigAndroidIntraRefresh: return "ConfigAndroidIntraRefresh";
case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering";
case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering";
+ case OMX_IndexParamMaxFrameDurationForBitrateControl:
+ return "ParamMaxFrameDurationForBitrateControl";
+ case OMX_IndexParamVideoVp9: return "ParamVideoVp9";
+ case OMX_IndexParamVideoAndroidVp9Encoder: return "ParamVideoAndroidVp9Encoder";
case OMX_IndexConfigAutoFramerateConversion: return "ConfigAutoFramerateConversion";
case OMX_IndexConfigPriority: return "ConfigPriority";
case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate";
diff --git a/include/media/openmax/OMX_Audio.h b/include/media/openmax/OMX_Audio.h
index d8bee76..9c0296b 100644
--- a/include/media/openmax/OMX_Audio.h
+++ b/include/media/openmax/OMX_Audio.h
@@ -259,6 +259,7 @@
OMX_AUDIO_AACObjectHE, /**< AAC High Efficiency (object type SBR, HE-AAC profile) */
OMX_AUDIO_AACObjectScalable, /**< AAC Scalable object */
OMX_AUDIO_AACObjectERLC = 17, /**< ER AAC Low Complexity object (Error Resilient AAC-LC) */
+ OMX_AUDIO_AACObjectER_Scalable = 20, /**< ER AAC scalable object */
OMX_AUDIO_AACObjectLD = 23, /**< AAC Low Delay object (Error Resilient) */
OMX_AUDIO_AACObjectHE_PS = 29, /**< AAC High Efficiency with Parametric Stereo coding (HE-AAC v2, object type PS) */
OMX_AUDIO_AACObjectELD = 39, /** AAC Enhanced Low Delay. NOTE: Pending Khronos standardization **/
diff --git a/include/media/openmax/OMX_Index.h b/include/media/openmax/OMX_Index.h
index 1a2a548..5be1355 100644
--- a/include/media/openmax/OMX_Index.h
+++ b/include/media/openmax/OMX_Index.h
@@ -98,10 +98,13 @@
OMX_IndexParamMetadataKeyFilter, /**< reference: OMX_PARAM_METADATAFILTERTYPE */
OMX_IndexConfigPriorityMgmt, /**< reference: OMX_PRIORITYMGMTTYPE */
OMX_IndexParamStandardComponentRole, /**< reference: OMX_PARAM_COMPONENTROLETYPE */
+ OMX_IndexComponentEndUnused,
OMX_IndexPortStartUnused = 0x02000000,
OMX_IndexParamPortDefinition, /**< reference: OMX_PARAM_PORTDEFINITIONTYPE */
OMX_IndexParamCompBufferSupplier, /**< reference: OMX_PARAM_BUFFERSUPPLIERTYPE */
+ OMX_IndexPortEndUnused,
+
OMX_IndexReservedStartUnused = 0x03000000,
/* Audio parameters and configurations */
@@ -134,6 +137,7 @@
OMX_IndexParamAudioSmv, /**< reference: OMX_AUDIO_PARAM_SMVTYPE */
OMX_IndexParamAudioVorbis, /**< reference: OMX_AUDIO_PARAM_VORBISTYPE */
OMX_IndexParamAudioFlac, /**< reference: OMX_AUDIO_PARAM_FLACTYPE */
+ OMX_IndexAudioEndUnused,
OMX_IndexConfigAudioMidiImmediateEvent, /**< reference: OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE */
OMX_IndexConfigAudioMidiControl, /**< reference: OMX_AUDIO_CONFIG_MIDICONTROLTYPE */
@@ -194,6 +198,7 @@
OMX_IndexParamVideoSliceFMO, /**< reference: OMX_VIDEO_PARAM_AVCSLICEFMO */
OMX_IndexConfigVideoAVCIntraPeriod, /**< reference: OMX_VIDEO_CONFIG_AVCINTRAPERIOD */
OMX_IndexConfigVideoNalSize, /**< reference: OMX_VIDEO_CONFIG_NALSIZE */
+ OMX_IndexVideoEndUnused,
/* Image & Video common Configurations */
OMX_IndexCommonStartUnused = 0x07000000,
@@ -231,6 +236,7 @@
OMX_IndexConfigCommonFocusRegion, /**< reference: OMX_CONFIG_FOCUSREGIONTYPE */
OMX_IndexConfigCommonFocusStatus, /**< reference: OMX_PARAM_FOCUSSTATUSTYPE */
OMX_IndexConfigCommonTransitionEffect, /**< reference: OMX_CONFIG_TRANSITIONEFFECTTYPE */
+ OMX_IndexCommonEndUnused,
/* Reserved Configuration range */
OMX_IndexOtherStartUnused = 0x08000000,
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index b688d1d..d0ae867 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -62,6 +62,7 @@
OMX_IndexParamAudioAndroidAacPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE */
OMX_IndexParamAudioAndroidEac3, /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
OMX_IndexParamAudioProfileQuerySupported, /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
+ OMX_IndexExtAudioEndUnused,
/* Image parameters and configurations */
OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000,
@@ -80,6 +81,10 @@
OMX_IndexConfigAndroidIntraRefresh, /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */
OMX_IndexParamAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */
OMX_IndexConfigAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */
+ OMX_IndexParamMaxFrameDurationForBitrateControl,/**< reference: OMX_PARAM_U32TYPE */
+ OMX_IndexParamVideoVp9, /**< reference: OMX_VIDEO_PARAM_VP9TYPE */
+ OMX_IndexParamVideoAndroidVp9Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP9ENCODERTYPE */
+ OMX_IndexExtVideoEndUnused,
/* Image & Video common configurations */
OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000,
@@ -90,6 +95,7 @@
OMX_IndexConfigPriority, /**< reference: OMX_PARAM_U32TYPE */
OMX_IndexConfigOperatingRate, /**< reference: OMX_PARAM_U32TYPE in Q16 format for video and in Hz for audio */
OMX_IndexParamConsumerUsageBits, /**< reference: OMX_PARAM_U32TYPE */
+ OMX_IndexExtOtherEndUnused,
/* Time configurations */
OMX_IndexExtTimeStartUnused = OMX_IndexKhronosExtensions + 0x00900000,
diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
index 2c02431..128dd2d 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -75,39 +75,6 @@
OMX_VIDEO_VP8LevelMax = 0x7FFFFFFF
} OMX_VIDEO_VP8LEVELTYPE;
-/** VP9 profiles */
-typedef enum OMX_VIDEO_VP9PROFILETYPE {
- OMX_VIDEO_VP9Profile0 = 0x1,
- OMX_VIDEO_VP9Profile1 = 0x2,
- OMX_VIDEO_VP9Profile2 = 0x4,
- OMX_VIDEO_VP9Profile3 = 0x8,
- // HDR profiles also support passing HDR metadata
- OMX_VIDEO_VP9Profile2HDR = 0x1000,
- OMX_VIDEO_VP9Profile3HDR = 0x2000,
- OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
- OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
-} OMX_VIDEO_VP9PROFILETYPE;
-
-/** VP9 levels */
-typedef enum OMX_VIDEO_VP9LEVELTYPE {
- OMX_VIDEO_VP9Level1 = 0x1,
- OMX_VIDEO_VP9Level11 = 0x2,
- OMX_VIDEO_VP9Level2 = 0x4,
- OMX_VIDEO_VP9Level21 = 0x8,
- OMX_VIDEO_VP9Level3 = 0x10,
- OMX_VIDEO_VP9Level31 = 0x20,
- OMX_VIDEO_VP9Level4 = 0x40,
- OMX_VIDEO_VP9Level41 = 0x80,
- OMX_VIDEO_VP9Level5 = 0x100,
- OMX_VIDEO_VP9Level51 = 0x200,
- OMX_VIDEO_VP9Level52 = 0x400,
- OMX_VIDEO_VP9Level6 = 0x800,
- OMX_VIDEO_VP9Level61 = 0x1000,
- OMX_VIDEO_VP9Level62 = 0x2000,
- OMX_VIDEO_VP9LevelUnknown = 0x6EFFFFFF,
- OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF
-} OMX_VIDEO_VP9LEVELTYPE;
-
/** VP8 Param */
typedef struct OMX_VIDEO_PARAM_VP8TYPE {
OMX_U32 nSize;
@@ -152,7 +119,7 @@
} OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE;
/**
- * Android specific VP8 encoder params
+ * Android specific VP8/VP9 encoder params
*
* STRUCT MEMBERS:
* nSize : Size of the structure in bytes
@@ -182,6 +149,59 @@
OMX_U32 nMaxQuantizer;
} OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE;
+/** VP9 profiles */
+typedef enum OMX_VIDEO_VP9PROFILETYPE {
+ OMX_VIDEO_VP9Profile0 = 0x1,
+ OMX_VIDEO_VP9Profile1 = 0x2,
+ OMX_VIDEO_VP9Profile2 = 0x4,
+ OMX_VIDEO_VP9Profile3 = 0x8,
+ // HDR profiles also support passing HDR metadata
+ OMX_VIDEO_VP9Profile2HDR = 0x1000,
+ OMX_VIDEO_VP9Profile3HDR = 0x2000,
+ OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
+} OMX_VIDEO_VP9PROFILETYPE;
+
+/** VP9 levels */
+typedef enum OMX_VIDEO_VP9LEVELTYPE {
+ OMX_VIDEO_VP9Level1 = 0x0,
+ OMX_VIDEO_VP9Level11 = 0x1,
+ OMX_VIDEO_VP9Level2 = 0x2,
+ OMX_VIDEO_VP9Level21 = 0x4,
+ OMX_VIDEO_VP9Level3 = 0x8,
+ OMX_VIDEO_VP9Level31 = 0x10,
+ OMX_VIDEO_VP9Level4 = 0x20,
+ OMX_VIDEO_VP9Level41 = 0x40,
+ OMX_VIDEO_VP9Level5 = 0x80,
+ OMX_VIDEO_VP9Level51 = 0x100,
+ OMX_VIDEO_VP9Level52 = 0x200,
+ OMX_VIDEO_VP9Level6 = 0x400,
+ OMX_VIDEO_VP9Level61 = 0x800,
+ OMX_VIDEO_VP9Level62 = 0x1000,
+ OMX_VIDEO_VP9LevelUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF
+} OMX_VIDEO_VP9LEVELTYPE;
+
+/**
+* VP9 Parameters.
+* Encoder specific parameters (decoders should ignore these fields):
+* - bErrorResilientMode
+* - nTileRows
+* - nTileColumns
+* - bEnableFrameParallelDecoding
+*/
+typedef struct OMX_VIDEO_PARAM_VP9TYPE {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_VIDEO_VP9PROFILETYPE eProfile;
+ OMX_VIDEO_VP9LEVELTYPE eLevel;
+ OMX_BOOL bErrorResilientMode;
+ OMX_U32 nTileRows;
+ OMX_U32 nTileColumns;
+ OMX_BOOL bEnableFrameParallelDecoding;
+} OMX_VIDEO_PARAM_VP9TYPE;
+
/** HEVC Profile enum type */
typedef enum OMX_VIDEO_HEVCPROFILETYPE {
OMX_VIDEO_HEVCProfileUnknown = 0x0,
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4b3fcc6..292dd3b 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -74,10 +74,10 @@
status_t read(const Parcel& input);
struct matrix22_t {
- float dsdx;
- float dtdx;
- float dsdy;
- float dtdy;
+ float dsdx{0};
+ float dtdx{0};
+ float dsdy{0};
+ float dtdy{0};
};
sp<IBinder> surface;
uint32_t what;
diff --git a/include/ui/ColorSpace.h b/include/ui/ColorSpace.h
new file mode 100644
index 0000000..c6a20c9
--- /dev/null
+++ b/include/ui/ColorSpace.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_COLOR_SPACE
+#define ANDROID_UI_COLOR_SPACE
+
+#include <array>
+#include <cmath>
+#include <functional>
+#include <string>
+
+#include <ui/mat3.h>
+#include <ui/scalar.h>
+#include <ui/vec2.h>
+#include <ui/vec3.h>
+
+namespace android {
+
+class ColorSpace {
+public:
+ typedef std::function<float(float)> transfer_function;
+ typedef std::function<float(float)> clamping_function;
+
+ /**
+ * Creates a named color space with the specified RGB->XYZ
+ * conversion matrix. The white point and primaries will be
+ * computed from the supplied matrix.
+ *
+ * The default transfer functions are a linear response x->x
+ * and the default clamping function is a simple saturate
+ * (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ transfer_function OETF = linearReponse,
+ transfer_function EOTF = linearReponse,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ /**
+ * Creates a named color space with the specified primaries
+ * and white point. The RGB<>XYZ conversion matrices are
+ * computed from the primaries and white point.
+ *
+ * The default transfer functions are a linear response x->x
+ * and the default clamping function is a simple saturate
+ * (clamp(x, 0, 1)).
+ */
+ ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ transfer_function OETF = linearReponse,
+ transfer_function EOTF = linearReponse,
+ clamping_function clamper = saturate<float>
+ ) noexcept;
+
+ ColorSpace() noexcept = delete;
+
+ /**
+ * Encodes the supplied RGB value using this color space's
+ * opto-electronic transfer function.
+ */
+ constexpr float3 fromLinear(const float3& v) const noexcept {
+ return apply(v, mOETF);
+ }
+
+ /**
+ * Decodes the supplied RGB value using this color space's
+ * electro-optical transfer function.
+ */
+ constexpr float3 toLinear(const float3& v) const noexcept {
+ return apply(v, mEOTF);
+ }
+
+ /**
+ * Converts the supplied XYZ value to RGB. The returned value
+ * is encoded with this color space's opto-electronic transfer
+ * function and clamped by this color space's clamping function.
+ */
+ constexpr float3 xyzToRGB(const float3& xyz) const noexcept {
+ return apply(fromLinear(mXYZtoRGB * xyz), mClamper);
+ }
+
+ /**
+ * Converts the supplied RGB value to XYZ. The input RGB value
+ * is decoded using this color space's electro-optical function
+ * before being converted to XYZ.
+ */
+ constexpr float3 rgbToXYZ(const float3& rgb) const noexcept {
+ return mRGBtoXYZ * toLinear(rgb);
+ }
+
+ constexpr const std::string& getName() const noexcept {
+ return mName;
+ }
+
+ constexpr const mat3& getRGBtoXYZ() const noexcept {
+ return mRGBtoXYZ;
+ }
+
+ constexpr const mat3& getXYZtoRGB() const noexcept {
+ return mXYZtoRGB;
+ }
+
+ constexpr const transfer_function& getOETF() const noexcept {
+ return mOETF;
+ }
+
+ constexpr const transfer_function& getEOTF() const noexcept {
+ return mEOTF;
+ }
+
+ constexpr const clamping_function& getClamper() const noexcept {
+ return mClamper;
+ }
+
+ constexpr const std::array<float2, 3>& getPrimaries() const noexcept {
+ return mPrimaries;
+ }
+
+ constexpr const float2& getWhitePoint() const noexcept {
+ return mWhitePoint;
+ }
+
+ /**
+ * Converts the supplied XYZ value to xyY.
+ */
+ static constexpr float2 xyY(const float3& XYZ) {
+ return XYZ.xy / dot(XYZ, float3{1});
+ }
+
+ /**
+ * Converts the supplied xyY value to XYZ.
+ */
+ static constexpr float3 XYZ(const float3& xyY) {
+ return float3{(xyY.x * xyY.z) / xyY.y, xyY.z, ((1 - xyY.x - xyY.y) * xyY.z) / xyY.y};
+ }
+
+ static const ColorSpace sRGB();
+ static const ColorSpace linearSRGB();
+ static const ColorSpace extendedSRGB();
+ static const ColorSpace linearExtendedSRGB();
+ static const ColorSpace NTSC();
+ static const ColorSpace BT709();
+ static const ColorSpace BT2020();
+ static const ColorSpace AdobeRGB();
+ static const ColorSpace ProPhotoRGB();
+ static const ColorSpace DisplayP3();
+ static const ColorSpace DCIP3();
+ static const ColorSpace ACES();
+ static const ColorSpace ACEScg();
+
+private:
+ static constexpr mat3 computeXYZMatrix(
+ const std::array<float2, 3>& primaries, const float2& whitePoint);
+
+ static constexpr float linearReponse(float v) {
+ return v;
+ }
+
+ const std::string mName;
+
+ const mat3 mRGBtoXYZ;
+ const mat3 mXYZtoRGB;
+
+ const transfer_function mOETF;
+ const transfer_function mEOTF;
+ const clamping_function mClamper;
+
+ std::array<float2, 3> mPrimaries;
+ float2 mWhitePoint;
+};
+
+}; // namespace android
+
+#endif // ANDROID_UI_COLOR_SPACE
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
index 799944f..842806e 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -26,16 +26,16 @@
namespace android {
struct DisplayInfo {
- uint32_t w;
- uint32_t h;
- float xdpi;
- float ydpi;
- float fps;
- float density;
- uint8_t orientation;
- bool secure;
- nsecs_t appVsyncOffset;
- nsecs_t presentationDeadline;
+ uint32_t w{0};
+ uint32_t h{0};
+ float xdpi{0};
+ float ydpi{0};
+ float fps{0};
+ float density{0};
+ uint8_t orientation{0};
+ bool secure{false};
+ nsecs_t appVsyncOffset{0};
+ nsecs_t presentationDeadline{0};
};
/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/include/ui/DisplayStatInfo.h b/include/ui/DisplayStatInfo.h
index 0549a83..09543ec 100644
--- a/include/ui/DisplayStatInfo.h
+++ b/include/ui/DisplayStatInfo.h
@@ -22,8 +22,8 @@
namespace android {
struct DisplayStatInfo {
- nsecs_t vsyncTime;
- nsecs_t vsyncPeriod;
+ nsecs_t vsyncTime{0};
+ nsecs_t vsyncPeriod{0};
};
}; // namespace android
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index 1df15f8..58df24c 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -18,21 +18,17 @@
#define ANDROID_FENCE_H
#include <stdint.h>
-#include <sys/types.h>
-#include <ui/ANativeObjectBase.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
#include <utils/Flattenable.h>
-#include <utils/String8.h>
+#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <experimental/optional>
-struct ANativeWindowBuffer;
-
namespace android {
+class String8;
+
// ===========================================================================
// Fence
// ===========================================================================
@@ -42,6 +38,11 @@
{
public:
static const sp<Fence> NO_FENCE;
+ static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX;
+ static constexpr nsecs_t SIGNAL_TIME_INVALID = -1;
+ static inline bool isValidTimestamp(nsecs_t time) {
+ return time >= 0 && time < INT64_MAX;
+ }
// TIMEOUT_NEVER may be passed to the wait method to indicate that it
// should wait indefinitely for the fence to signal.
@@ -57,6 +58,12 @@
// closed.
explicit Fence(int fenceFd);
+ // Not copyable or movable.
+ Fence(const Fence& rhs) = delete;
+ Fence& operator=(const Fence& rhs) = delete;
+ Fence(Fence&& rhs) = delete;
+ Fence& operator=(Fence&& rhs) = delete;
+
// Check whether the Fence has an open fence file descriptor. Most Fence
// methods treat an invalid file descriptor just like a valid fence that
// is already signalled, so using this is usually not necessary.
@@ -94,8 +101,8 @@
// getSignalTime returns the system monotonic clock time at which the
// fence transitioned to the signaled state. If the fence is not signaled
- // then INT64_MAX is returned. If the fence is invalid or if an error
- // occurs then -1 is returned.
+ // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
+ // error occurs then SIGNAL_TIME_INVALID is returned.
nsecs_t getSignalTime() const;
#if __cplusplus > 201103L
@@ -130,11 +137,6 @@
friend class LightRefBase<Fence>;
~Fence();
- // Disallow copying
- Fence(const Fence& rhs);
- Fence& operator = (const Fence& rhs);
- const Fence& operator = (const Fence& rhs) const;
-
int mFenceFd;
};
diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h
new file mode 100644
index 0000000..871fcf2
--- /dev/null
+++ b/include/ui/FenceTime.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FENCE_TIME_H
+#define ANDROID_FENCE_TIME_H
+
+#include <ui/Fence.h>
+#include <utils/Flattenable.h>
+#include <utils/Timers.h>
+
+#include <atomic>
+#include <mutex>
+#include <queue>
+#include <unordered_map>
+
+namespace android {
+
+class FenceToFenceTimeMap;
+
+// A wrapper around fence that only implements isValid and getSignalTime.
+// It automatically closes the fence in a thread-safe manner once the signal
+// time is known.
+class FenceTime {
+friend class FenceToFenceTimeMap;
+public:
+ // An atomic snapshot of the FenceTime that is flattenable.
+ //
+ // This class is needed because the FenceTime class may not stay
+ // consistent for all steps of the flattening process.
+ //
+ // Not thread safe.
+ struct Snapshot : public Flattenable<Snapshot> {
+ enum class State {
+ EMPTY,
+ FENCE,
+ SIGNAL_TIME,
+ };
+
+ Snapshot() = default; // Creates an empty snapshot.
+ explicit Snapshot(const sp<Fence>& fence);
+ explicit Snapshot(nsecs_t signalTime);
+
+ // Movable.
+ Snapshot(Snapshot&& src) = default;
+ Snapshot& operator=(Snapshot&& src) = default;
+ // Not copyable.
+ Snapshot(const Snapshot& src) = delete;
+ Snapshot& operator=(const Snapshot&& src) = delete;
+
+ // Flattenable implementation.
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+ State state{State::EMPTY};
+ sp<Fence> fence{Fence::NO_FENCE};
+ nsecs_t signalTime{Fence::SIGNAL_TIME_INVALID};
+ };
+
+ static const std::shared_ptr<FenceTime> NO_FENCE;
+
+ explicit FenceTime(const sp<Fence>& fence);
+ explicit FenceTime(sp<Fence>&& fence);
+
+ // Passing in Fence::SIGNAL_TIME_PENDING is not allowed.
+ // Doing so will convert the signalTime to Fence::SIGNAL_TIME_INVALID.
+ explicit FenceTime(nsecs_t signalTime);
+
+ // Do not allow default construction. Share NO_FENCE or explicitly construct
+ // with Fence::SIGNAL_TIME_INVALID instead.
+ FenceTime() = delete;
+
+ // Do not allow copy, assign, or move. Use a shared_ptr to share the
+ // signalTime result. Or use getSnapshot() if a thread-safe copy is really
+ // needed.
+ FenceTime(const FenceTime&) = delete;
+ FenceTime(FenceTime&&) = delete;
+ FenceTime& operator=(const FenceTime&) = delete;
+ FenceTime& operator=(FenceTime&&) = delete;
+
+ // This method should only be called when replacing the fence with
+ // a signalTime. Since this is an indirect way of setting the signal time
+ // of a fence, the snapshot should come from a trusted source.
+ void applyTrustedSnapshot(const Snapshot& src);
+
+ bool isValid() const;
+
+ // Attempts to get the timestamp from the Fence if the timestamp isn't
+ // already cached. Otherwise, it returns the cached value.
+ nsecs_t getSignalTime();
+
+ // Gets the cached timestamp without attempting to query the Fence.
+ nsecs_t getCachedSignalTime() const;
+
+ // Returns a snapshot of the FenceTime in its current state.
+ Snapshot getSnapshot() const;
+
+ void signalForTest(nsecs_t signalTime);
+
+ // Override new and delete since this needs 8-byte alignment, which
+ // is not guaranteed on x86.
+ static void* operator new(size_t nbytes) noexcept;
+ static void operator delete(void *p);
+
+private:
+ // For tests only. If forceValidForTest is true, then getSignalTime will
+ // never return SIGNAL_TIME_INVALID and isValid will always return true.
+ FenceTime(const sp<Fence>& fence, bool forceValidForTest);
+
+ enum class State {
+ VALID,
+ INVALID,
+ FORCED_VALID_FOR_TEST,
+ };
+
+ const State mState{State::INVALID};
+
+ // mMutex guards mFence and mSignalTime.
+ // mSignalTime is also atomic since it is sometimes read outside the lock
+ // for quick checks.
+ mutable std::mutex mMutex;
+ sp<Fence> mFence{Fence::NO_FENCE};
+ std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
+};
+
+// A queue of FenceTimes that are expected to signal in FIFO order.
+// Only maintains a queue of weak pointers so it doesn't keep references
+// to Fences on its own.
+//
+// Can be used to get the signal time of a fence and close its file descriptor
+// without making a syscall for every fence later in the timeline.
+// Additionally, since the FenceTime caches the timestamp internally,
+// other timelines that reference the same FenceTime can avoid the syscall.
+//
+// FenceTimeline only keeps track of a limited number of entries to avoid
+// growing unbounded. Users of FenceTime must make sure they can work even
+// if FenceTimeline did nothing. i.e. they should eventually call
+// Fence::getSignalTime(), not only Fence::getCachedSignalTime().
+//
+// push() and updateSignalTimes() are safe to call simultaneously from
+// different threads.
+class FenceTimeline {
+public:
+ static constexpr size_t MAX_ENTRIES = 64;
+
+ void push(const std::shared_ptr<FenceTime>& fence);
+ void updateSignalTimes();
+
+private:
+ mutable std::mutex mMutex;
+ std::queue<std::weak_ptr<FenceTime>> mQueue;
+};
+
+// Used by test code to create or get FenceTimes for a given Fence.
+//
+// By design, Fences cannot be signaled from user space. However, this class
+// allows test code to set the apparent signalTime of a Fence and
+// have it be visible to all FenceTimes. Release code should not use
+// FenceToFenceTimeMap.
+//
+// FenceToFenceTimeMap keeps a weak reference to the FenceTime and automatically
+// garbage collects entries every time a new FenceTime is created to avoid
+// leaks. This prevents us from having to make the Fence destructor
+// automatically notify that the underlying fence has been destroyed, which
+// would affect release code paths. Garbage collecting so often is inefficient,
+// but acceptable for testing.
+//
+// Since FenceTimes maintain a strong reference to underlying Fences, there
+// should not be any aliasing issues where a new Fence happens to have the same
+// address as a previous Fence; the previous entry will be garbage collected
+// before the new one is added.
+class FenceToFenceTimeMap {
+public:
+ // Create a new FenceTime with that wraps the provided Fence.
+ std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence);
+
+ // Signals all FenceTimes created through this class that are wrappers
+ // around |fence|.
+ void signalAllForTest(const sp<Fence>& fence, nsecs_t signalTime);
+
+private:
+ // Cleans up the entries that no longer have a strong reference.
+ void garbageCollectLocked();
+
+ mutable std::mutex mMutex;
+ std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap;
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_FENCE_TIME_H
diff --git a/include/ui/Gralloc1.h b/include/ui/Gralloc1.h
index cf8c173..64dacd7 100644
--- a/include/ui/Gralloc1.h
+++ b/include/ui/Gralloc1.h
@@ -49,6 +49,7 @@
mWidth(0),
mHeight(0),
mFormat(static_cast<android_pixel_format_t>(0)),
+ mLayerCount(0),
mProducerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
mConsumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
@@ -58,6 +59,7 @@
gralloc1_error_t setDimensions(uint32_t width, uint32_t height);
gralloc1_error_t setFormat(android_pixel_format_t format);
+ gralloc1_error_t setLayerCount(uint32_t layerCount);
gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage);
gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage);
@@ -68,6 +70,7 @@
uint32_t mWidth;
uint32_t mHeight;
android_pixel_format_t mFormat;
+ uint32_t mLayerCount;
gralloc1_producer_usage_t mProducerUsage;
gralloc1_consumer_usage_t mConsumerUsage;
@@ -178,6 +181,8 @@
GRALLOC1_FUNCTION_SET_DIMENSIONS> setDimensions;
FunctionLoader<GRALLOC1_PFN_SET_FORMAT,
GRALLOC1_FUNCTION_SET_FORMAT> setFormat;
+ FunctionLoader<GRALLOC1_PFN_SET_LAYER_COUNT,
+ GRALLOC1_FUNCTION_SET_LAYER_COUNT> setLayerCount;
FunctionLoader<GRALLOC1_PFN_SET_PRODUCER_USAGE,
GRALLOC1_FUNCTION_SET_PRODUCER_USAGE> setProducerUsage;
FunctionLoader<GRALLOC1_PFN_GET_BACKING_STORE,
@@ -188,6 +193,8 @@
GRALLOC1_FUNCTION_GET_DIMENSIONS> getDimensions;
FunctionLoader<GRALLOC1_PFN_GET_FORMAT,
GRALLOC1_FUNCTION_GET_FORMAT> getFormat;
+ FunctionLoader<GRALLOC1_PFN_GET_LAYER_COUNT,
+ GRALLOC1_FUNCTION_GET_LAYER_COUNT> getLayerCount;
FunctionLoader<GRALLOC1_PFN_GET_PRODUCER_USAGE,
GRALLOC1_FUNCTION_GET_PRODUCER_USAGE> getProducerUsage;
FunctionLoader<GRALLOC1_PFN_GET_STRIDE,
diff --git a/include/ui/Gralloc1On0Adapter.h b/include/ui/Gralloc1On0Adapter.h
index d523c4f..2508ce9 100644
--- a/include/ui/Gralloc1On0Adapter.h
+++ b/include/ui/Gralloc1On0Adapter.h
@@ -131,6 +131,7 @@
width(0),
height(0),
format(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+ layerCount(1),
producerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
consumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
@@ -145,6 +146,11 @@
return GRALLOC1_ERROR_NONE;
}
+ gralloc1_error_t setLayerCount(uint32_t lc) {
+ layerCount = lc;
+ return GRALLOC1_ERROR_NONE;
+ }
+
gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage) {
producerUsage = usage;
return GRALLOC1_ERROR_NONE;
@@ -161,6 +167,7 @@
uint32_t width;
uint32_t height;
int32_t format;
+ uint32_t layerCount;
gralloc1_producer_usage_t producerUsage;
gralloc1_consumer_usage_t consumerUsage;
};
@@ -197,6 +204,12 @@
&Descriptor::setFormat, format);
}
+ static int32_t setLayerCountHook(gralloc1_device_t* device,
+ gralloc1_buffer_descriptor_t descriptorId, uint32_t layerCount) {
+ return callDescriptorFunction(device, descriptorId,
+ &Descriptor::setLayerCount, layerCount);
+ }
+
static int32_t setProducerUsageHook(gralloc1_device_t* device,
gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
auto usage = static_cast<gralloc1_producer_usage_t>(intUsage);
@@ -246,6 +259,11 @@
return GRALLOC1_ERROR_NONE;
}
+ gralloc1_error_t getLayerCount(uint32_t* outLayerCount) const {
+ *outLayerCount = mDescriptor.layerCount;
+ return GRALLOC1_ERROR_NONE;
+ }
+
gralloc1_error_t getNumFlexPlanes(uint32_t* outNumPlanes) const {
// TODO: This is conservative, and we could do better by examining
// the format, but it won't hurt anything for now
diff --git a/include/ui/GrallocAllocator.h b/include/ui/GrallocAllocator.h
new file mode 100644
index 0000000..dd0f9e0
--- /dev/null
+++ b/include/ui/GrallocAllocator.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GRALLOC_ALLOCATOR_H
+#define ANDROID_UI_GRALLOC_ALLOCATOR_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::Error;
+using hardware::graphics::allocator::V2_0::ProducerUsage;
+using hardware::graphics::allocator::V2_0::ConsumerUsage;
+using hardware::graphics::allocator::V2_0::BufferDescriptor;
+using hardware::graphics::allocator::V2_0::Buffer;
+using hardware::graphics::allocator::V2_0::IAllocator;
+using hardware::graphics::allocator::V2_0::IAllocatorClient;
+using hardware::graphics::common::V1_0::PixelFormat;
+
+// Allocator is a wrapper to IAllocator, a proxy to server-side allocator.
+class Allocator {
+public:
+ Allocator();
+
+ // this will be removed and Allocator will be always valid
+ bool valid() const { return (mAllocator != nullptr); }
+
+ std::string dumpDebugInfo() const;
+
+ Error createBufferDescriptor(
+ const IAllocatorClient::BufferDescriptorInfo& descriptorInfo,
+ BufferDescriptor* outDescriptor) const;
+ void destroyBufferDescriptor(BufferDescriptor descriptor) const;
+
+ Error allocate(BufferDescriptor descriptor, Buffer* outBuffer) const;
+ void free(Buffer buffer) const;
+
+ Error exportHandle(BufferDescriptor descriptor, Buffer buffer,
+ native_handle_t** outBufferHandle) const;
+
+private:
+ sp<IAllocator> mAllocator;
+ sp<IAllocatorClient> mClient;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_ALLOCATOR_H
diff --git a/include/ui/GrallocMapper.h b/include/ui/GrallocMapper.h
new file mode 100644
index 0000000..f533dfb
--- /dev/null
+++ b/include/ui/GrallocMapper.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GRALLOC_MAPPER_H
+#define ANDROID_UI_GRALLOC_MAPPER_H
+
+#include <memory>
+
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <system/window.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::Error;
+using hardware::graphics::allocator::V2_0::ProducerUsage;
+using hardware::graphics::allocator::V2_0::ConsumerUsage;
+using hardware::graphics::common::V1_0::PixelFormat;
+using hardware::graphics::mapper::V2_0::FlexLayout;
+using hardware::graphics::mapper::V2_0::BackingStore;
+using hardware::graphics::mapper::V2_0::IMapper;
+
+// Mapper is a wrapper to IMapper, a client-side graphics buffer mapper.
+class Mapper {
+public:
+ Mapper();
+
+ // this will be removed and Mapper will be always valid
+ bool valid() const { return (mMapper != nullptr); }
+
+ Error retain(buffer_handle_t handle) const;
+ void release(buffer_handle_t handle) const;
+
+ Error getStride(buffer_handle_t handle, uint32_t* outStride) const;
+
+ Error lock(buffer_handle_t handle,
+ uint64_t producerUsageMask,
+ uint64_t consumerUsageMask,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, void** outData) const;
+ Error lock(buffer_handle_t handle,
+ uint64_t producerUsageMask,
+ uint64_t consumerUsageMask,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, FlexLayout* outLayout) const;
+ int unlock(buffer_handle_t handle) const;
+
+private:
+ sp<IMapper> mMapper;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_MAPPER_H
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 3e127a1..1bbcee2 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -76,10 +76,15 @@
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inUsage, std::string requestorName = "<Unknown>");
+ // creates w * h buffer with a layer count
+ GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+ uint32_t inLayerCount, uint32_t inUsage,
+ std::string requestorName = "<Unknown>");
+
// create a buffer from an existing handle
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
- uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle,
- bool keepOwnership);
+ uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride,
+ native_handle_t* inHandle, bool keepOwnership);
// create a buffer from an existing ANativeWindowBuffer
GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership);
@@ -92,6 +97,7 @@
uint32_t getStride() const { return static_cast<uint32_t>(stride); }
uint32_t getUsage() const { return static_cast<uint32_t>(usage); }
PixelFormat getPixelFormat() const { return format; }
+ uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); }
Rect getBounds() const { return Rect(width, height); }
uint64_t getId() const { return mId; }
@@ -101,10 +107,10 @@
}
status_t reallocate(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage);
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage);
bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage);
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage);
status_t lock(uint32_t inUsage, void** vaddr);
status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
@@ -160,7 +166,7 @@
const GraphicBuffer& operator = (const GraphicBuffer& rhs) const;
status_t initSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
- uint32_t inUsage, std::string requestorName);
+ uint32_t inLayerCount, uint32_t inUsage, std::string requestorName);
void free_handle();
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 28d0238..16967d4 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -32,7 +32,12 @@
namespace android {
+namespace Gralloc2 {
+class Allocator;
+}
+
class Gralloc1Loader;
+class GraphicBufferMapper;
class String8;
class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
@@ -60,8 +65,9 @@
static inline GraphicBufferAllocator& get() { return getInstance(); }
status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
- uint32_t usage, buffer_handle_t* handle, uint32_t* stride,
- uint64_t graphicBufferId, std::string requestorName);
+ uint32_t layerCount, uint32_t usage, buffer_handle_t* handle,
+ uint32_t* stride, uint64_t graphicBufferId,
+ std::string requestorName);
status_t free(buffer_handle_t handle);
@@ -74,6 +80,7 @@
uint32_t height;
uint32_t stride;
PixelFormat format;
+ uint32_t layerCount;
uint32_t usage;
size_t size;
std::string requestorName;
@@ -86,6 +93,9 @@
GraphicBufferAllocator();
~GraphicBufferAllocator();
+ const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
+ GraphicBufferMapper& mMapper;
+
std::unique_ptr<Gralloc1::Loader> mLoader;
std::unique_ptr<Gralloc1::Device> mDevice;
};
diff --git a/include/ui/GraphicBufferMapper.h b/include/ui/GraphicBufferMapper.h
index a25809c..b6de1b2 100644
--- a/include/ui/GraphicBufferMapper.h
+++ b/include/ui/GraphicBufferMapper.h
@@ -28,6 +28,10 @@
// ---------------------------------------------------------------------------
+namespace Gralloc2 {
+class Mapper;
+}
+
class Rect;
class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -57,11 +61,18 @@
status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
+ const Gralloc2::Mapper& getGrallocMapper() const
+ {
+ return *mMapper;
+ }
+
private:
friend class Singleton<GraphicBufferMapper>;
GraphicBufferMapper();
+ const std::unique_ptr<const Gralloc2::Mapper> mMapper;
+
std::unique_ptr<Gralloc1::Loader> mLoader;
std::unique_ptr<Gralloc1::Device> mDevice;
};
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index f26fecb..ab7a9a3 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -60,6 +60,7 @@
PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA
PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB
PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
+ PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA
};
typedef int32_t PixelFormat;
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index e9859fe..ce33d4e 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_UI_RECT
#define ANDROID_UI_RECT
+#include <gfx/FloatRect.h>
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
@@ -179,11 +180,15 @@
// this calculates (Region(*this) - exclude).bounds() efficiently
Rect reduce(const Rect& exclude) const;
-
// for backward compatibility
inline int32_t width() const { return getWidth(); }
inline int32_t height() const { return getHeight(); }
inline void set(const Rect& rhs) { operator = (rhs); }
+
+ gfx::FloatRect toFloatRect() const {
+ return {static_cast<float>(left), static_cast<float>(top),
+ static_cast<float>(right), static_cast<float>(bottom)};
+ }
};
ANDROID_BASIC_TYPES_TRAITS(Rect)
diff --git a/include/ui/TMatHelpers.h b/include/ui/TMatHelpers.h
index a6aadca..8edf5f8 100644
--- a/include/ui/TMatHelpers.h
+++ b/include/ui/TMatHelpers.h
@@ -14,25 +14,41 @@
* limitations under the License.
*/
-#ifndef TMAT_IMPLEMENTATION
-#error "Don't include TMatHelpers.h directly. use ui/mat*.h instead"
-#else
-#undef TMAT_IMPLEMENTATION
-#endif
+#ifndef UI_TMATHELPERS_H_
+#define UI_TMATHELPERS_H_
-
-#ifndef UI_TMAT_HELPERS_H
-#define UI_TMAT_HELPERS_H
-
+#include <math.h>
#include <stdint.h>
#include <sys/types.h>
-#include <math.h>
-#include <utils/Debug.h>
-#include <utils/String8.h>
+
+#include <cmath>
+#include <exception>
+#include <iomanip>
+#include <stdexcept>
+
+#include <ui/quat.h>
+#include <ui/TVecHelpers.h>
+
+#include <utils/String8.h>
+
+#ifdef __cplusplus
+# define LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
#define PURE __attribute__((pure))
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
namespace android {
+namespace details {
// -------------------------------------------------------------------------------------
/*
@@ -48,121 +64,285 @@
namespace matrix {
-inline int PURE transpose(int v) { return v; }
-inline float PURE transpose(float v) { return v; }
-inline double PURE transpose(double v) { return v; }
+inline constexpr int transpose(int v) { return v; }
+inline constexpr float transpose(float v) { return v; }
+inline constexpr double transpose(double v) { return v; }
-inline int PURE trace(int v) { return v; }
-inline float PURE trace(float v) { return v; }
-inline double PURE trace(double v) { return v; }
+inline constexpr int trace(int v) { return v; }
+inline constexpr float trace(float v) { return v; }
+inline constexpr double trace(double v) { return v; }
+/*
+ * Matrix inversion
+ */
template<typename MATRIX>
-MATRIX PURE inverse(const MATRIX& src) {
-
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::COL_SIZE == MATRIX::ROW_SIZE );
-
- typename MATRIX::value_type t;
- const size_t N = MATRIX::col_size();
- size_t swap;
+MATRIX PURE gaussJordanInverse(const MATRIX& src) {
+ typedef typename MATRIX::value_type T;
+ static constexpr unsigned int N = MATRIX::NUM_ROWS;
MATRIX tmp(src);
- MATRIX inverse(1);
+ MATRIX inverted(1);
- for (size_t i=0 ; i<N ; i++) {
- // look for largest element in column
- swap = i;
- for (size_t j=i+1 ; j<N ; j++) {
- if (fabs(tmp[j][i]) > fabs(tmp[i][i])) {
+ for (size_t i = 0; i < N; ++i) {
+ // look for largest element in i'th column
+ size_t swap = i;
+ T t = std::abs(tmp[i][i]);
+ for (size_t j = i + 1; j < N; ++j) {
+ const T t2 = std::abs(tmp[j][i]);
+ if (t2 > t) {
swap = j;
+ t = t2;
}
}
if (swap != i) {
- /* swap rows. */
- for (size_t k=0 ; k<N ; k++) {
- t = tmp[i][k];
- tmp[i][k] = tmp[swap][k];
- tmp[swap][k] = t;
-
- t = inverse[i][k];
- inverse[i][k] = inverse[swap][k];
- inverse[swap][k] = t;
- }
+ // swap columns.
+ std::swap(tmp[i], tmp[swap]);
+ std::swap(inverted[i], inverted[swap]);
}
- t = 1 / tmp[i][i];
- for (size_t k=0 ; k<N ; k++) {
- tmp[i][k] *= t;
- inverse[i][k] *= t;
+ const T denom(tmp[i][i]);
+ for (size_t k = 0; k < N; ++k) {
+ tmp[i][k] /= denom;
+ inverted[i][k] /= denom;
}
- for (size_t j=0 ; j<N ; j++) {
+
+ // Factor out the lower triangle
+ for (size_t j = 0; j < N; ++j) {
if (j != i) {
- t = tmp[j][i];
- for (size_t k=0 ; k<N ; k++) {
- tmp[j][k] -= tmp[i][k] * t;
- inverse[j][k] -= inverse[i][k] * t;
+ const T d = tmp[j][i];
+ for (size_t k = 0; k < N; ++k) {
+ tmp[j][k] -= tmp[i][k] * d;
+ inverted[j][k] -= inverted[i][k] * d;
}
}
}
}
- return inverse;
+
+ return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// 2x2 matrix inverse is easy.
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) {
+ typedef typename MATRIX::value_type T;
+
+ // Assuming the input matrix is:
+ // | a b |
+ // | c d |
+ //
+ // The analytic inverse is
+ // | d -b |
+ // | -c a | / (a d - b c)
+ //
+ // Importantly, our matrices are column-major!
+
+ MATRIX inverted(MATRIX::NO_INIT);
+
+ const T a = x[0][0];
+ const T c = x[0][1];
+ const T b = x[1][0];
+ const T d = x[1][1];
+
+ const T det((a * d) - (b * c));
+ inverted[0][0] = d / det;
+ inverted[0][1] = -c / det;
+ inverted[1][0] = -b / det;
+ inverted[1][1] = a / det;
+ return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// From the Wikipedia article on matrix inversion's section on fast 3x3
+// matrix inversion:
+// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) {
+ typedef typename MATRIX::value_type T;
+
+ // Assuming the input matrix is:
+ // | a b c |
+ // | d e f |
+ // | g h i |
+ //
+ // The analytic inverse is
+ // | A B C |^T
+ // | D E F |
+ // | G H I | / determinant
+ //
+ // Which is
+ // | A D G |
+ // | B E H |
+ // | C F I | / determinant
+ //
+ // Where:
+ // A = (ei - fh), B = (fg - di), C = (dh - eg)
+ // D = (ch - bi), E = (ai - cg), F = (bg - ah)
+ // G = (bf - ce), H = (cd - af), I = (ae - bd)
+ //
+ // and the determinant is a*A + b*B + c*C (The rule of Sarrus)
+ //
+ // Importantly, our matrices are column-major!
+
+ MATRIX inverted(MATRIX::NO_INIT);
+
+ const T a = x[0][0];
+ const T b = x[1][0];
+ const T c = x[2][0];
+ const T d = x[0][1];
+ const T e = x[1][1];
+ const T f = x[2][1];
+ const T g = x[0][2];
+ const T h = x[1][2];
+ const T i = x[2][2];
+
+ // Do the full analytic inverse
+ const T A = e * i - f * h;
+ const T B = f * g - d * i;
+ const T C = d * h - e * g;
+ inverted[0][0] = A; // A
+ inverted[0][1] = B; // B
+ inverted[0][2] = C; // C
+ inverted[1][0] = c * h - b * i; // D
+ inverted[1][1] = a * i - c * g; // E
+ inverted[1][2] = b * g - a * h; // F
+ inverted[2][0] = b * f - c * e; // G
+ inverted[2][1] = c * d - a * f; // H
+ inverted[2][2] = a * e - b * d; // I
+
+ const T det(a * A + b * B + c * C);
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ inverted[col][row] /= det;
+ }
+ }
+
+ return inverted;
+}
+
+/**
+ * Inversion function which switches on the matrix size.
+ * @warning This function assumes the matrix is invertible. The result is
+ * undefined if it is not. It is the responsibility of the caller to
+ * make sure the matrix is not singular.
+ */
+template <typename MATRIX>
+inline constexpr MATRIX PURE inverse(const MATRIX& matrix) {
+ static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted");
+ return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) :
+ ((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) :
+ gaussJordanInverse<MATRIX>(matrix));
}
template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
-MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
+CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
// pre-requisite:
// lhs : D columns, R rows
// rhs : C columns, D rows
// res : C columns, R rows
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_A::ROW_SIZE == MATRIX_B::COL_SIZE );
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::ROW_SIZE == MATRIX_B::ROW_SIZE );
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::COL_SIZE == MATRIX_A::COL_SIZE );
+ static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS,
+ "matrices can't be multiplied. invalid dimensions.");
+ static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS,
+ "invalid dimension of matrix multiply result.");
+ static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS,
+ "invalid dimension of matrix multiply result.");
MATRIX_R res(MATRIX_R::NO_INIT);
- for (size_t r=0 ; r<MATRIX_R::row_size() ; r++) {
- res[r] = lhs * rhs[r];
+ for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) {
+ res[col] = lhs * rhs[col];
}
return res;
}
// transpose. this handles matrices of matrices
template <typename MATRIX>
-MATRIX PURE transpose(const MATRIX& m) {
+CONSTEXPR MATRIX PURE transpose(const MATRIX& m) {
// for now we only handle square matrix transpose
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
MATRIX result(MATRIX::NO_INIT);
- for (size_t r=0 ; r<MATRIX::row_size() ; r++)
- for (size_t c=0 ; c<MATRIX::col_size() ; c++)
- result[c][r] = transpose(m[r][c]);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) {
+ result[col][row] = transpose(m[row][col]);
+ }
+ }
return result;
}
// trace. this handles matrices of matrices
template <typename MATRIX>
-typename MATRIX::value_type PURE trace(const MATRIX& m) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
+CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) {
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices");
typename MATRIX::value_type result(0);
- for (size_t r=0 ; r<MATRIX::row_size() ; r++)
- result += trace(m[r][r]);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ result += trace(m[col][col]);
+ }
return result;
}
-// trace. this handles matrices of matrices
+// diag. this handles matrices of matrices
template <typename MATRIX>
-typename MATRIX::col_type PURE diag(const MATRIX& m) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
+CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) {
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices");
typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
- for (size_t r=0 ; r<MATRIX::row_size() ; r++)
- result[r] = m[r][r];
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ result[col] = m[col][col];
+ }
return result;
}
+//------------------------------------------------------------------------------
+// This is taken from the Imath MatrixAlgo code, and is identical to Eigen.
+template <typename MATRIX>
+TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) {
+ typedef typename MATRIX::value_type T;
+
+ TQuaternion<T> quat(TQuaternion<T>::NO_INIT);
+
+ // Compute the trace to see if it is positive or not.
+ const T trace = mat[0][0] + mat[1][1] + mat[2][2];
+
+ // check the sign of the trace
+ if (LIKELY(trace > 0)) {
+ // trace is positive
+ T s = std::sqrt(trace + 1);
+ quat.w = T(0.5) * s;
+ s = T(0.5) / s;
+ quat.x = (mat[1][2] - mat[2][1]) * s;
+ quat.y = (mat[2][0] - mat[0][2]) * s;
+ quat.z = (mat[0][1] - mat[1][0]) * s;
+ } else {
+ // trace is negative
+
+ // Find the index of the greatest diagonal
+ size_t i = 0;
+ if (mat[1][1] > mat[0][0]) { i = 1; }
+ if (mat[2][2] > mat[i][i]) { i = 2; }
+
+ // Get the next indices: (n+1)%3
+ static constexpr size_t next_ijk[3] = { 1, 2, 0 };
+ size_t j = next_ijk[i];
+ size_t k = next_ijk[j];
+ T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1);
+ quat[i] = T(0.5) * s;
+ if (s != 0) {
+ s = T(0.5) / s;
+ }
+ quat.w = (mat[j][k] - mat[k][j]) * s;
+ quat[j] = (mat[i][j] + mat[j][i]) * s;
+ quat[k] = (mat[i][k] + mat[k][i]) * s;
+ }
+ return quat;
+}
+
template <typename MATRIX>
String8 asString(const MATRIX& m) {
String8 s;
- for (size_t c=0 ; c<MATRIX::col_size() ; c++) {
+ for (size_t c = 0; c < MATRIX::col_size(); c++) {
s.append("| ");
- for (size_t r=0 ; r<MATRIX::row_size() ; r++) {
+ for (size_t r = 0; r < MATRIX::row_size(); r++) {
s.appendFormat("%7.2f ", m[r][c]);
}
s.append("|\n");
@@ -170,7 +350,7 @@
return s;
}
-}; // namespace matrix
+} // namespace matrix
// -------------------------------------------------------------------------------------
@@ -189,29 +369,36 @@
// multiply by a scalar
BASE<T>& operator *= (T v) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
- for (size_t r=0 ; r<lhs.row_size() ; r++) {
- lhs[r] *= v;
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ lhs[col] *= v;
}
return lhs;
}
+ // matrix *= matrix
+ template<typename U>
+ const BASE<T>& operator *= (const BASE<U>& rhs) {
+ BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+ lhs = matrix::multiply<BASE<T> >(lhs, rhs);
+ return lhs;
+ }
+
// divide by a scalar
BASE<T>& operator /= (T v) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
- for (size_t r=0 ; r<lhs.row_size() ; r++) {
- lhs[r] /= v;
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ lhs[col] /= v;
}
return lhs;
}
// matrix * matrix, result is a matrix of the same type than the lhs matrix
template<typename U>
- friend BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
+ friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
return matrix::multiply<BASE<T> >(lhs, rhs);
}
};
-
/*
* TMatSquareFunctions implements functions on a matrix of type BASE<T>.
*
@@ -229,6 +416,7 @@
template<template<typename U> class BASE, typename T>
class TMatSquareFunctions {
public:
+
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
@@ -236,22 +424,217 @@
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
- friend BASE<T> PURE inverse(const BASE<T>& m) { return matrix::inverse(m); }
- friend BASE<T> PURE transpose(const BASE<T>& m) { return matrix::transpose(m); }
- friend T PURE trace(const BASE<T>& m) { return matrix::trace(m); }
+ friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) {
+ return matrix::inverse(matrix);
+ }
+ friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) {
+ return matrix::transpose(m);
+ }
+ friend inline constexpr T PURE trace(const BASE<T>& m) {
+ return matrix::trace(m);
+ }
};
+template<template<typename U> class BASE, typename T>
+class TMatHelpers {
+public:
+ constexpr inline size_t getColumnSize() const { return BASE<T>::COL_SIZE; }
+ constexpr inline size_t getRowSize() const { return BASE<T>::ROW_SIZE; }
+ constexpr inline size_t getColumnCount() const { return BASE<T>::NUM_COLS; }
+ constexpr inline size_t getRowCount() const { return BASE<T>::NUM_ROWS; }
+ constexpr inline size_t size() const { return BASE<T>::ROW_SIZE; } // for TVec*<>
+
+ // array access
+ constexpr T const* asArray() const {
+ return &static_cast<BASE<T> const &>(*this)[0][0];
+ }
+
+ // element access
+ inline constexpr T const& operator()(size_t row, size_t col) const {
+ return static_cast<BASE<T> const &>(*this)[col][row];
+ }
+
+ inline T& operator()(size_t row, size_t col) {
+ return static_cast<BASE<T>&>(*this)[col][row];
+ }
+
+ template <typename VEC>
+ static CONSTEXPR BASE<T> translate(const VEC& t) {
+ BASE<T> r;
+ r[BASE<T>::NUM_COLS-1] = t;
+ return r;
+ }
+
+ template <typename VEC>
+ static constexpr BASE<T> scale(const VEC& s) {
+ return BASE<T>(s);
+ }
+
+ friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) {
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ m[col] = abs(m[col]);
+ }
+ return m;
+ }
+};
+
+// functions for 3x3 and 4x4 matrices
+template<template<typename U> class BASE, typename T>
+class TMatTransform {
+public:
+ inline constexpr TMatTransform() {
+ static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only");
+ }
+
+ template <typename A, typename VEC>
+ static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) {
+ BASE<T> r;
+ T c = std::cos(radian);
+ T s = std::sin(radian);
+ if (about.x == 1 && about.y == 0 && about.z == 0) {
+ r[1][1] = c; r[2][2] = c;
+ r[1][2] = s; r[2][1] = -s;
+ } else if (about.x == 0 && about.y == 1 && about.z == 0) {
+ r[0][0] = c; r[2][2] = c;
+ r[2][0] = s; r[0][2] = -s;
+ } else if (about.x == 0 && about.y == 0 && about.z == 1) {
+ r[0][0] = c; r[1][1] = c;
+ r[0][1] = s; r[1][0] = -s;
+ } else {
+ VEC nabout = normalize(about);
+ typename VEC::value_type x = nabout.x;
+ typename VEC::value_type y = nabout.y;
+ typename VEC::value_type z = nabout.z;
+ T nc = 1 - c;
+ T xy = x * y;
+ T yz = y * z;
+ T zx = z * x;
+ T xs = x * s;
+ T ys = y * s;
+ T zs = z * s;
+ r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys;
+ r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs;
+ r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c;
+
+ // Clamp results to -1, 1.
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+ }
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Create a matrix from euler angles using YPR around YXZ respectively
+ * @param yaw about Y axis
+ * @param pitch about X axis
+ * @param roll about Z axis
+ */
+ template <
+ typename Y, typename P, typename R,
+ typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+ >
+ static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
+ return eulerZYX(roll, pitch, yaw);
+ }
+
+ /**
+ * Create a matrix from euler angles using YPR around ZYX respectively
+ * @param roll about X axis
+ * @param pitch about Y axis
+ * @param yaw about Z axis
+ *
+ * The euler angles are applied in ZYX order. i.e: a vector is first rotated
+ * about X (roll) then Y (pitch) and then Z (yaw).
+ */
+ template <
+ typename Y, typename P, typename R,
+ typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+ >
+ static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
+ BASE<T> r;
+ T cy = std::cos(yaw);
+ T sy = std::sin(yaw);
+ T cp = std::cos(pitch);
+ T sp = std::sin(pitch);
+ T cr = std::cos(roll);
+ T sr = std::sin(roll);
+ T cc = cr * cy;
+ T cs = cr * sy;
+ T sc = sr * cy;
+ T ss = sr * sy;
+ r[0][0] = cp * cy;
+ r[0][1] = cp * sy;
+ r[0][2] = -sp;
+ r[1][0] = sp * sc - cs;
+ r[1][1] = sp * ss + cc;
+ r[1][2] = cp * sr;
+ r[2][0] = sp * cc + ss;
+ r[2][1] = sp * cs - sc;
+ r[2][2] = cp * cr;
+
+ // Clamp results to -1, 1.
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+ }
+ }
+ return r;
+ }
+
+ TQuaternion<T> toQuaternion() const {
+ return matrix::extractQuat(static_cast<const BASE<T>&>(*this));
+ }
+};
+
+
template <template<typename T> class BASE, typename T>
class TMatDebug {
public:
+ friend std::ostream& operator<<(std::ostream& stream, const BASE<T>& m) {
+ for (size_t row = 0; row < BASE<T>::NUM_ROWS; ++row) {
+ if (row != 0) {
+ stream << std::endl;
+ }
+ if (row == 0) {
+ stream << "/ ";
+ } else if (row == BASE<T>::NUM_ROWS-1) {
+ stream << "\\ ";
+ } else {
+ stream << "| ";
+ }
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ stream << std::setw(10) << std::to_string(m[col][row]);
+ }
+ if (row == 0) {
+ stream << " \\";
+ } else if (row == BASE<T>::NUM_ROWS-1) {
+ stream << " /";
+ } else {
+ stream << " |";
+ }
+ }
+ return stream;
+ }
+
String8 asString() const {
- return matrix::asString( static_cast< const BASE<T>& >(*this) );
+ return matrix::asString(static_cast<const BASE<T>&>(*this));
}
};
// -------------------------------------------------------------------------------------
-}; // namespace android
+} // namespace details
+} // namespace android
+#undef LIKELY
+#undef UNLIKELY
#undef PURE
+#undef CONSTEXPR
-#endif /* UI_TMAT_HELPERS_H */
+#endif // UI_TMATHELPERS_H_
diff --git a/include/ui/TQuatHelpers.h b/include/ui/TQuatHelpers.h
new file mode 100644
index 0000000..2f0f70f
--- /dev/null
+++ b/include/ui/TQuatHelpers.h
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef UI_TQUATHELPERS_H_
+#define UI_TQUATHELPERS_H_
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <iostream>
+
+#include <ui/vec3.h>
+
+#define PURE __attribute__((pure))
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/quat.h
+ */
+
+
+/*
+ * TQuatProductOperators implements basic arithmetic and basic compound assignment
+ * operators on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class QUATERNION, typename T>
+class TQuatProductOperators {
+public:
+ /* compound assignment from a another quaternion of the same size but different
+ * element type.
+ */
+ template <typename OTHER>
+ QUATERNION<T>& operator *= (const QUATERNION<OTHER>& r) {
+ QUATERNION<T>& q = static_cast<QUATERNION<T>&>(*this);
+ q = q * r;
+ return q;
+ }
+
+ /* compound assignment products by a scalar
+ */
+ QUATERNION<T>& operator *= (T v) {
+ QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+ for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+ lhs[i] *= v;
+ }
+ return lhs;
+ }
+ QUATERNION<T>& operator /= (T v) {
+ QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+ for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+ lhs[i] /= v;
+ }
+ return lhs;
+ }
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ /* The operators below handle operation between quaternion of the same size
+ * but of a different element type.
+ */
+ template<typename RT>
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(const QUATERNION<T>& q, const QUATERNION<RT>& r) {
+ // could be written as:
+ // return QUATERNION<T>(
+ // q.w*r.w - dot(q.xyz, r.xyz),
+ // q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz));
+
+ return QUATERNION<T>(
+ q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+ q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+ q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+ q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+ }
+
+ template<typename RT>
+ friend inline
+ constexpr TVec3<T> PURE operator *(const QUATERNION<T>& q, const TVec3<RT>& v) {
+ // note: if q is known to be a unit quaternion, then this simplifies to:
+ // TVec3<T> t = 2 * cross(q.xyz, v)
+ // return v + (q.w * t) + cross(q.xyz, t)
+ return imaginary(q * QUATERNION<T>(v, 0) * inverse(q));
+ }
+
+
+ /* For quaternions, we use explicit "by a scalar" products because it's much faster
+ * than going (implicitly) through the quaternion multiplication.
+ * For reference: we could use the code below instead, but it would be a lot slower.
+ * friend inline
+ * constexpr BASE<T> PURE operator *(const BASE<T>& q, const BASE<T>& r) {
+ * return BASE<T>(
+ * q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+ * q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+ * q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+ * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+ *
+ */
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(QUATERNION<T> q, T scalar) {
+ // don't pass q by reference because we need a copy anyways
+ return q *= scalar;
+ }
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(T scalar, QUATERNION<T> q) {
+ // don't pass q by reference because we need a copy anyways
+ return q *= scalar;
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE operator /(QUATERNION<T> q, T scalar) {
+ // don't pass q by reference because we need a copy anyways
+ return q /= scalar;
+ }
+};
+
+
+/*
+ * TQuatFunctions implements functions on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatFunctions {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ template<typename RT>
+ friend inline
+ constexpr T PURE dot(const QUATERNION<T>& p, const QUATERNION<RT>& q) {
+ return p.x * q.x +
+ p.y * q.y +
+ p.z * q.z +
+ p.w * q.w;
+ }
+
+ friend inline
+ constexpr T PURE norm(const QUATERNION<T>& q) {
+ return std::sqrt( dot(q, q) );
+ }
+
+ friend inline
+ constexpr T PURE length(const QUATERNION<T>& q) {
+ return norm(q);
+ }
+
+ friend inline
+ constexpr T PURE length2(const QUATERNION<T>& q) {
+ return dot(q, q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE normalize(const QUATERNION<T>& q) {
+ return length(q) ? q / length(q) : QUATERNION<T>(1);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE conj(const QUATERNION<T>& q) {
+ return QUATERNION<T>(q.w, -q.x, -q.y, -q.z);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE inverse(const QUATERNION<T>& q) {
+ return conj(q) * (1 / dot(q, q));
+ }
+
+ friend inline
+ constexpr T PURE real(const QUATERNION<T>& q) {
+ return q.w;
+ }
+
+ friend inline
+ constexpr TVec3<T> PURE imaginary(const QUATERNION<T>& q) {
+ return q.xyz;
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE unreal(const QUATERNION<T>& q) {
+ return QUATERNION<T>(q.xyz, 0);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE cross(const QUATERNION<T>& p, const QUATERNION<T>& q) {
+ return unreal(p*q);
+ }
+
+ friend inline
+ QUATERNION<T> PURE exp(const QUATERNION<T>& q) {
+ const T nq(norm(q.xyz));
+ return std::exp(q.w)*QUATERNION<T>((sin(nq)/nq)*q.xyz, cos(nq));
+ }
+
+ friend inline
+ QUATERNION<T> PURE log(const QUATERNION<T>& q) {
+ const T nq(norm(q));
+ return QUATERNION<T>((std::acos(q.w/nq)/norm(q.xyz))*q.xyz, log(nq));
+ }
+
+ friend inline
+ QUATERNION<T> PURE pow(const QUATERNION<T>& q, T a) {
+ // could also be computed as: exp(a*log(q));
+ const T nq(norm(q));
+ const T theta(a*std::acos(q.w / nq));
+ return std::pow(nq, a) * QUATERNION<T>(normalize(q.xyz) * std::sin(theta), std::cos(theta));
+ }
+
+ friend inline
+ QUATERNION<T> PURE slerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ // could also be computed as: pow(q * inverse(p), t) * p;
+ const T d = dot(p, q);
+ const T npq = sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q||
+ const T a = std::acos(std::abs(d) / npq);
+ const T a0 = a * (1 - t);
+ const T a1 = a * t;
+ const T isina = 1 / sin(a);
+ const T s0 = std::sin(a0) * isina;
+ const T s1 = std::sin(a1) * isina;
+ // ensure we're taking the "short" side
+ return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE lerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ return ((1 - t) * p) + (t * q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE nlerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ return normalize(lerp(p, q, t));
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE positive(const QUATERNION<T>& q) {
+ return q.w < 0 ? -q : q;
+ }
+};
+
+/*
+ * TQuatDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatDebug {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend std::ostream& operator<< (std::ostream& stream, const QUATERNION<T>& q) {
+ return stream << "< " << q.w << " + " << q.x << "i + " << q.y << "j + " << q.z << "k >";
+ }
+};
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace android
+
+
+#endif // UI_TQUATHELPERS_H_
diff --git a/include/ui/TVecHelpers.h b/include/ui/TVecHelpers.h
index bb7dbfc..de1d9ff 100644
--- a/include/ui/TVecHelpers.h
+++ b/include/ui/TVecHelpers.h
@@ -14,22 +14,28 @@
* limitations under the License.
*/
-#ifndef TVEC_IMPLEMENTATION
-#error "Don't include TVecHelpers.h directly. use ui/vec*.h instead"
-#else
-#undef TVEC_IMPLEMENTATION
-#endif
+#ifndef UI_TVECHELPERS_H_
+#define UI_TVECHELPERS_H_
-#ifndef UI_TVEC_HELPERS_H
-#define UI_TVEC_HELPERS_H
-
+#include <math.h>
#include <stdint.h>
#include <sys/types.h>
+#include <cmath>
+#include <limits>
+#include <iostream>
+
#define PURE __attribute__((pure))
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
namespace android {
+namespace details {
// -------------------------------------------------------------------------------------
/*
@@ -39,24 +45,6 @@
*/
/*
- * This class casts itself into anything and assign itself from anything!
- * Use with caution!
- */
-template <typename TYPE>
-struct Impersonator {
- Impersonator& operator = (const TYPE& rhs) {
- reinterpret_cast<TYPE&>(*this) = rhs;
- return *this;
- }
- operator TYPE& () {
- return reinterpret_cast<TYPE&>(*this);
- }
- operator TYPE const& () const {
- return reinterpret_cast<TYPE const&>(*this);
- }
-};
-
-/*
* TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
* operators on a vector of type BASE<T>.
*
@@ -65,27 +53,27 @@
* get all the functionality here.
*/
-template <template<typename T> class BASE, typename T>
+template <template<typename T> class VECTOR, typename T>
class TVecAddOperators {
public:
/* compound assignment from a another vector of the same size but different
* element type.
*/
- template <typename OTHER>
- BASE<T>& operator += (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] += v[i];
+ template<typename OTHER>
+ VECTOR<T>& operator +=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] += v[i];
}
- return rhs;
+ return lhs;
}
- template <typename OTHER>
- BASE<T>& operator -= (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] -= v[i];
+ template<typename OTHER>
+ VECTOR<T>& operator -=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] -= v[i];
}
- return rhs;
+ return lhs;
}
/* compound assignment from a another vector of the same type.
@@ -93,19 +81,19 @@
* like "vector *= scalar" by letting the compiler implicitly convert a scalar
* to a vector (assuming the BASE<T> allows it).
*/
- BASE<T>& operator += (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] += v[i];
+ VECTOR<T>& operator +=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] += v[i];
}
- return rhs;
+ return lhs;
}
- BASE<T>& operator -= (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] -= v[i];
+ VECTOR<T>& operator -=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] -= v[i];
}
- return rhs;
+ return lhs;
}
/*
@@ -116,57 +104,57 @@
* (the first one, BASE<T> being known).
*/
- /* The operators below handle operation between vectors of the same side
+ /* The operators below handle operation between vectors of the same size
* but of a different element type.
*/
template<typename RT>
- friend inline
- BASE<T> PURE operator +(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) += rv;
+ friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv += rv;
}
template<typename RT>
- friend inline
- BASE<T> PURE operator -(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) -= rv;
+ friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv -= rv;
}
/* The operators below (which are not templates once this class is instanced,
* i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
- * These handle operations like "vector * scalar" and "scalar * vector" by
+ * These handle operations like "vector + scalar" and "scalar + vector" by
* letting the compiler implicitly convert a scalar to a vector (assuming
* the BASE<T> allows it).
*/
- friend inline
- BASE<T> PURE operator +(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) += rv;
+ friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv += rv;
}
- friend inline
- BASE<T> PURE operator -(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) -= rv;
+ friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv -= rv;
}
};
-template <template<typename T> class BASE, typename T>
+template<template<typename T> class VECTOR, typename T>
class TVecProductOperators {
public:
/* compound assignment from a another vector of the same size but different
* element type.
*/
- template <typename OTHER>
- BASE<T>& operator *= (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] *= v[i];
+ template<typename OTHER>
+ VECTOR<T>& operator *=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] *= v[i];
}
- return rhs;
+ return lhs;
}
- template <typename OTHER>
- BASE<T>& operator /= (const BASE<OTHER>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] /= v[i];
+ template<typename OTHER>
+ VECTOR<T>& operator /=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] /= v[i];
}
- return rhs;
+ return lhs;
}
/* compound assignment from a another vector of the same type.
@@ -174,19 +162,19 @@
* like "vector *= scalar" by letting the compiler implicitly convert a scalar
* to a vector (assuming the BASE<T> allows it).
*/
- BASE<T>& operator *= (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] *= v[i];
+ VECTOR<T>& operator *=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] *= v[i];
}
- return rhs;
+ return lhs;
}
- BASE<T>& operator /= (const BASE<T>& v) {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
- rhs[i] /= v[i];
+ VECTOR<T>& operator /=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] /= v[i];
}
- return rhs;
+ return lhs;
}
/*
@@ -197,18 +185,18 @@
* (the first one, BASE<T> being known).
*/
- /* The operators below handle operation between vectors of the same side
+ /* The operators below handle operation between vectors of the same size
* but of a different element type.
*/
template<typename RT>
- friend inline
- BASE<T> PURE operator *(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) *= rv;
+ friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv *= rv;
}
template<typename RT>
- friend inline
- BASE<T> PURE operator /(const BASE<T>& lv, const BASE<RT>& rv) {
- return BASE<T>(lv) /= rv;
+ friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv /= rv;
}
/* The operators below (which are not templates once this class is instanced,
@@ -217,13 +205,13 @@
* letting the compiler implicitly convert a scalar to a vector (assuming
* the BASE<T> allows it).
*/
- friend inline
- BASE<T> PURE operator *(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) *= rv;
+ friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv *= rv;
}
- friend inline
- BASE<T> PURE operator /(const BASE<T>& lv, const BASE<T>& rv) {
- return BASE<T>(lv) /= rv;
+ friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv /= rv;
}
};
@@ -236,34 +224,35 @@
*
* These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
*/
-template <template<typename T> class BASE, typename T>
+template<template<typename T> class VECTOR, typename T>
class TVecUnaryOperators {
public:
- BASE<T>& operator ++ () {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
+ VECTOR<T>& operator ++() {
+ VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < rhs.size(); i++) {
++rhs[i];
}
return rhs;
}
- BASE<T>& operator -- () {
- BASE<T>& rhs = static_cast<BASE<T>&>(*this);
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
+
+ VECTOR<T>& operator --() {
+ VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < rhs.size(); i++) {
--rhs[i];
}
return rhs;
}
- BASE<T> operator - () const {
- BASE<T> r(BASE<T>::NO_INIT);
- BASE<T> const& rv(static_cast<BASE<T> const&>(*this));
- for (size_t i=0 ; i<BASE<T>::size() ; i++) {
+
+ CONSTEXPR VECTOR<T> operator -() const {
+ VECTOR<T> r(VECTOR<T>::NO_INIT);
+ VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+ for (size_t i = 0; i < r.size(); i++) {
r[i] = -rv[i];
}
return r;
}
};
-
/*
* TVecComparisonOperators implements relational/comparison operators
* on a vector of type BASE<T>.
@@ -272,7 +261,7 @@
* By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
* get all the functionality here.
*/
-template <template<typename T> class BASE, typename T>
+template<template<typename T> class VECTOR, typename T>
class TVecComparisonOperators {
public:
/*
@@ -284,8 +273,8 @@
*/
template<typename RT>
friend inline
- bool PURE operator ==(const BASE<T>& lv, const BASE<RT>& rv) {
- for (size_t i = 0; i < BASE<T>::size(); i++)
+ bool PURE operator ==(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++)
if (lv[i] != rv[i])
return false;
return true;
@@ -293,42 +282,47 @@
template<typename RT>
friend inline
- bool PURE operator !=(const BASE<T>& lv, const BASE<RT>& rv) {
+ bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
return !operator ==(lv, rv);
}
template<typename RT>
friend inline
- bool PURE operator >(const BASE<T>& lv, const BASE<RT>& rv) {
- for (size_t i = 0; i < BASE<T>::size(); i++)
- if (lv[i] <= rv[i])
- return false;
- return true;
+ bool PURE operator >(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++) {
+ if (lv[i] == rv[i]) {
+ continue;
+ }
+ return lv[i] > rv[i];
+ }
+ return false;
}
template<typename RT>
friend inline
- bool PURE operator <=(const BASE<T>& lv, const BASE<RT>& rv) {
+ constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
return !(lv > rv);
}
template<typename RT>
friend inline
- bool PURE operator <(const BASE<T>& lv, const BASE<RT>& rv) {
- for (size_t i = 0; i < BASE<T>::size(); i++)
- if (lv[i] >= rv[i])
- return false;
- return true;
+ bool PURE operator <(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++) {
+ if (lv[i] == rv[i]) {
+ continue;
+ }
+ return lv[i] < rv[i];
+ }
+ return false;
}
template<typename RT>
friend inline
- bool PURE operator >=(const BASE<T>& lv, const BASE<RT>& rv) {
+ constexpr bool PURE operator >=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
return !(lv < rv);
}
};
-
/*
* TVecFunctions implements functions on a vector of type BASE<T>.
*
@@ -336,7 +330,7 @@
* By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
* get all the functionality here.
*/
-template <template<typename T> class BASE, typename T>
+template<template<typename T> class VECTOR, typename T>
class TVecFunctions {
public:
/*
@@ -347,35 +341,188 @@
* (the first one, BASE<T> being known).
*/
template<typename RT>
- friend inline
- T PURE dot(const BASE<T>& lv, const BASE<RT>& rv) {
+ friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
T r(0);
- for (size_t i = 0; i < BASE<T>::size(); i++)
- r += lv[i]*rv[i];
+ for (size_t i = 0; i < lv.size(); i++) {
+ //r = std::fma(lv[i], rv[i], r);
+ r += lv[i] * rv[i];
+ }
return r;
}
- friend inline
- T PURE length(const BASE<T>& lv) {
- return sqrt( dot(lv, lv) );
+ friend inline constexpr T PURE norm(const VECTOR<T>& lv) {
+ return std::sqrt(dot(lv, lv));
+ }
+
+ friend inline constexpr T PURE length(const VECTOR<T>& lv) {
+ return norm(lv);
+ }
+
+ friend inline constexpr T PURE norm2(const VECTOR<T>& lv) {
+ return dot(lv, lv);
+ }
+
+ friend inline constexpr T PURE length2(const VECTOR<T>& lv) {
+ return norm2(lv);
}
template<typename RT>
- friend inline
- T PURE distance(const BASE<T>& lv, const BASE<RT>& rv) {
+ friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
return length(rv - lv);
}
- friend inline
- BASE<T> PURE normalize(const BASE<T>& lv) {
- return lv * (1 / length(lv));
+ template<typename RT>
+ friend inline constexpr T PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return length2(rv - lv);
+ }
+
+ friend inline constexpr VECTOR<T> PURE normalize(const VECTOR<T>& lv) {
+ return lv * (T(1) / length(lv));
+ }
+
+ friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) {
+ return T(1) / v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::abs(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::floor(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::ceil(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::round(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = T(1) / std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::pow(v[i], p);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
+ return clamp(lv, T(0), T(1));
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
+ for (size_t i=0 ; i< v.size() ; i++) {
+ v[i] = std::min(max, std::max(min, v[i]));
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
+ for (size_t i=0 ; i<lv.size() ; i++) {
+ //a[i] = std::fma(lv[i], rv[i], a[i]);
+ a[i] += (lv[i] * rv[i]);
+ }
+ return a;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::min(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = std::max(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) {
+ T r(std::numeric_limits<T>::lowest());
+ for (size_t i=0 ; i<v.size() ; i++) {
+ r = std::max(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) {
+ T r(std::numeric_limits<T>::max());
+ for (size_t i=0 ; i<v.size() ; i++) {
+ r = std::min(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) {
+ for (size_t i=0 ; i<v.size() ; i++) {
+ v[i] = f(v[i]);
+ }
+ return v;
}
};
+/*
+ * TVecDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecDebug {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend std::ostream& operator<<(std::ostream& stream, const VECTOR<T>& v) {
+ stream << "< ";
+ for (size_t i = 0; i < v.size() - 1; i++) {
+ stream << T(v[i]) << ", ";
+ }
+ stream << T(v[v.size() - 1]) << " >";
+ return stream;
+ }
+};
+
+#undef CONSTEXPR
#undef PURE
// -------------------------------------------------------------------------------------
-}; // namespace android
+} // namespace details
+} // namespace android
-#endif /* UI_TVEC_HELPERS_H */
+#endif // UI_TVECHELPERS_H_
diff --git a/include/ui/half.h b/include/ui/half.h
new file mode 100644
index 0000000..7a271dc
--- /dev/null
+++ b/include/ui/half.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UI_HALF_H
+#define UI_HALF_H
+
+#include <stdint.h>
+#include <iosfwd>
+#include <limits>
+#include <type_traits>
+
+#ifdef __cplusplus
+# define LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+
+/*
+ * half-float
+ *
+ * 1 5 10
+ * +-+------+------------+
+ * |s|eee.ee|mm.mmmm.mmmm|
+ * +-+------+------------+
+ *
+ * minimum (denormal) value: 2^-24 = 5.96e-8
+ * minimum (normal) value: 2^-14 = 6.10e-5
+ * maximum value: 2-2^-10 = 65504
+ *
+ * Integers between 0 and 2048 can be represented exactly
+ */
+class half {
+ struct fp16 {
+ uint16_t bits = 0;
+ fp16() noexcept = default;
+ explicit constexpr fp16(uint16_t b) noexcept : bits(b) { }
+ void setS(unsigned int s) noexcept { bits = uint16_t((bits & 0x7FFF) | (s<<15)); }
+ void setE(unsigned int s) noexcept { bits = uint16_t((bits & 0xE3FF) | (s<<10)); }
+ void setM(unsigned int s) noexcept { bits = uint16_t((bits & 0xFC00) | (s<< 0)); }
+ constexpr unsigned int getS() const noexcept { return bits >> 15u; }
+ constexpr unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; }
+ constexpr unsigned int getM() const noexcept { return bits & 0x3FFu; }
+ };
+ struct fp32 {
+ union {
+ uint32_t bits = 0;
+ float fp;
+ };
+ fp32() noexcept = default;
+ explicit constexpr fp32(float f) : fp(f) { }
+ void setS(unsigned int s) noexcept { bits = uint32_t((bits & 0x7FFFFFFF) | (s<<31)); }
+ void setE(unsigned int s) noexcept { bits = uint32_t((bits & 0x807FFFFF) | (s<<23)); }
+ void setM(unsigned int s) noexcept { bits = uint32_t((bits & 0xFF800000) | (s<< 0)); }
+ constexpr unsigned int getS() const noexcept { return bits >> 31u; }
+ constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; }
+ constexpr unsigned int getM() const noexcept { return bits & 0x7FFFFFu; }
+ };
+
+public:
+ CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
+ CONSTEXPR operator float() const noexcept { return htof(mBits); }
+
+ uint16_t getBits() const noexcept { return mBits.bits; }
+ unsigned int getExponent() const noexcept { return mBits.getE(); }
+ unsigned int getMantissa() const noexcept { return mBits.getM(); }
+
+private:
+ friend class std::numeric_limits<half>;
+ friend CONSTEXPR half operator"" _hf(long double v);
+
+ enum Binary { binary };
+ explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { }
+ static CONSTEXPR fp16 ftoh(float v) noexcept;
+ static CONSTEXPR float htof(fp16 v) noexcept;
+ fp16 mBits;
+};
+
+inline CONSTEXPR half::fp16 half::ftoh(float v) noexcept {
+ fp16 out;
+ fp32 in(v);
+ if (UNLIKELY(in.getE() == 0xFF)) { // inf or nan
+ out.setE(0x1F);
+ out.setM(in.getM() ? 0x200 : 0);
+ } else {
+ int e = static_cast<int>(in.getE()) - 127 + 15;
+ if (e >= 0x1F) {
+ // overflow
+ out.setE(0x31); // +/- inf
+ } else if (e <= 0) {
+ // underflow
+ // flush to +/- 0
+ } else {
+ unsigned int m = in.getM();
+ out.setE(uint16_t(e));
+ out.setM(m >> 13);
+ if (m & 0x1000) {
+ // rounding
+ out.bits++;
+ }
+ }
+ }
+ out.setS(in.getS());
+ return out;
+}
+
+inline CONSTEXPR float half::htof(half::fp16 in) noexcept {
+ fp32 out;
+ if (UNLIKELY(in.getE() == 0x1F)) { // inf or nan
+ out.setE(0xFF);
+ out.setM(in.getM() ? 0x400000 : 0);
+ } else {
+ if (in.getE() == 0) {
+ if (in.getM()) {
+ // TODO: denormal half float, treat as zero for now
+ // (it's stupid because they can be represented as regular float)
+ }
+ } else {
+ int e = static_cast<int>(in.getE()) - 15 + 127;
+ unsigned int m = in.getM();
+ out.setE(uint32_t(e));
+ out.setM(m << 13);
+ }
+ }
+ out.setS(in.getS());
+ return out.fp;
+}
+
+inline CONSTEXPR android::half operator"" _hf(long double v) {
+ return android::half(android::half::binary, android::half::ftoh(static_cast<float>(v)).bits);
+}
+
+} // namespace android
+
+namespace std {
+
+template<> struct is_floating_point<android::half> : public std::true_type {};
+
+template<>
+class numeric_limits<android::half> {
+public:
+ typedef android::half type;
+
+ static constexpr const bool is_specialized = true;
+ static constexpr const bool is_signed = true;
+ static constexpr const bool is_integer = false;
+ static constexpr const bool is_exact = false;
+ static constexpr const bool has_infinity = true;
+ static constexpr const bool has_quiet_NaN = true;
+ static constexpr const bool has_signaling_NaN = false;
+ static constexpr const float_denorm_style has_denorm = denorm_absent;
+ static constexpr const bool has_denorm_loss = true;
+ static constexpr const bool is_iec559 = false;
+ static constexpr const bool is_bounded = true;
+ static constexpr const bool is_modulo = false;
+ static constexpr const bool traps = false;
+ static constexpr const bool tinyness_before = false;
+ static constexpr const float_round_style round_style = round_indeterminate;
+
+ static constexpr const int digits = 11;
+ static constexpr const int digits10 = 3;
+ static constexpr const int max_digits10 = 5;
+ static constexpr const int radix = 2;
+ static constexpr const int min_exponent = -13;
+ static constexpr const int min_exponent10 = -4;
+ static constexpr const int max_exponent = 16;
+ static constexpr const int max_exponent10 = 4;
+
+ inline static constexpr type round_error() noexcept { return android::half(android::half::binary, 0x3800); }
+ inline static constexpr type min() noexcept { return android::half(android::half::binary, 0x0400); }
+ inline static constexpr type max() noexcept { return android::half(android::half::binary, 0x7bff); }
+ inline static constexpr type lowest() noexcept { return android::half(android::half::binary, 0xfbff); }
+ inline static constexpr type epsilon() noexcept { return android::half(android::half::binary, 0x1400); }
+ inline static constexpr type infinity() noexcept { return android::half(android::half::binary, 0x7c00); }
+ inline static constexpr type quiet_NaN() noexcept { return android::half(android::half::binary, 0x7fff); }
+ inline static constexpr type denorm_min() noexcept { return android::half(android::half::binary, 0x0001); }
+ inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
+};
+
+} // namespace std
+
+#undef LIKELY
+#undef UNLIKELY
+#undef CONSTEXPR
+
+#endif // UI_HALF_H
diff --git a/include/ui/mat2.h b/include/ui/mat2.h
new file mode 100644
index 0000000..37c7221
--- /dev/null
+++ b/include/ui/mat2.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UI_MAT2_H_
+#define UI_MAT2_H_
+
+#include <ui/TMatHelpers.h>
+#include <ui/vec2.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+/**
+ * A 2x2 column-major matrix class.
+ *
+ * Conceptually a 2x2 matrix is a an array of 2 column vec2:
+ *
+ * mat2 m =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0] & m[1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0][0] & m[1][0] \\
+ * m[0][1] & m[1][1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m(0,0) & m(0,1) \\
+ * m(1,0) & m(1,1) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2.
+ *
+ */
+template <typename T>
+class TMat22 : public TVecUnaryOperators<TMat22, T>,
+ public TVecComparisonOperators<TMat22, T>,
+ public TVecAddOperators<TMat22, T>,
+ public TMatProductOperators<TMat22, T>,
+ public TMatSquareFunctions<TMat22, T>,
+ public TMatHelpers<TMat22, T>,
+ public TMatDebug<TMat22, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec2<T> col_type;
+ typedef TVec2<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat22(const TMat22&) = default;
+ ~TMat22() = default;
+ TMat22& operator = (const TMat22&) = default;
+
+ /**
+ * constructors
+ */
+
+ /**
+ * leaves object uninitialized. use with caution.
+ */
+ explicit constexpr TMat22(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+
+ /**
+ * initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * 1 & 0 \\
+ * 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat22();
+
+ /**
+ * initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v & 0 \\
+ * 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat22(U v);
+
+ /**
+ * sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v[0] & 0 \\
+ * 0 & v[1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(const TVec2<U>& v);
+
+ /**
+ * construct from another matrix of the same size
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(const TMat22<U>& rhs);
+
+ /**
+ * construct from 2 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v0 & v1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B>
+ CONSTEXPR TMat22(const TVec2<A>& v0, const TVec2<B>& v1);
+
+ /** construct from 4 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0][0] & m[1][0] \\
+ * m[0][1] & m[1][1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B,
+ typename C, typename D>
+ CONSTEXPR TMat22(A m00, B m01, C m10, D m11);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(U const* rawArray);
+
+ /**
+ * Rotate by radians in the 2D plane
+ */
+ static CONSTEXPR TMat22<T> rotate(T radian) {
+ TMat22<T> r(TMat22<T>::NO_INIT);
+ T c = std::cos(radian);
+ T s = std::sin(radian);
+ r[0][0] = c; r[1][1] = c;
+ r[0][1] = s; r[1][0] = -s;
+ return r;
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat22<T>::TMat22() {
+ m_value[0] = col_type(1, 0);
+ m_value[1] = col_type(0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U v) {
+ m_value[0] = col_type(v, 0);
+ m_value[1] = col_type(0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<U>& v) {
+ m_value[0] = col_type(v.x, 0);
+ m_value[1] = col_type(0, v.y);
+}
+
+// construct from 4 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+ typename A, typename B,
+ typename C, typename D>
+CONSTEXPR TMat22<T>::TMat22( A m00, B m01, C m10, D m11) {
+ m_value[0] = col_type(m00, m01);
+ m_value[1] = col_type(m10, m11);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(const TMat22<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 2 column vectors.
+template <typename T>
+template <typename A, typename B>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<A>& v0, const TVec2<B>& v1) {
+ m_value[0] = v0;
+ m_value[1] = v1;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::col_type PURE operator *(const TMat22<T>& lhs, const TVec2<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat22<U>::col_type result;
+ for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::row_type PURE operator *(const TVec2<U>& lhs, const TMat22<T>& rhs) {
+ typename TMat22<U>::row_type result(TMat22<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(TMat22<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(U lhs, const TMat22<T>& rhs) {
+ return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat22<T>::col_type PURE diag(const TMat22<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat22<double> mat2d;
+typedef details::TMat22<float> mat2;
+typedef details::TMat22<float> mat2f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
+
+#endif // UI_MAT2_H_
diff --git a/include/ui/mat3.h b/include/ui/mat3.h
new file mode 100644
index 0000000..4f5dba9
--- /dev/null
+++ b/include/ui/mat3.h
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UI_MAT3_H_
+#define UI_MAT3_H_
+
+#include <ui/quat.h>
+#include <ui/TMatHelpers.h>
+#include <ui/vec3.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 3x3 column-major matrix class.
+ *
+ * Conceptually a 3x3 matrix is a an array of 3 column vec3:
+ *
+ * mat3 m =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0] & m[1] & m[2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0][0] & m[1][0] & m[2][0] \\
+ * m[0][1] & m[1][1] & m[2][1] \\
+ * m[0][2] & m[1][2] & m[2][2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m(0,0) & m(0,1) & m(0,2) \\
+ * m(1,0) & m(1,1) & m(1,2) \\
+ * m(2,0) & m(2,1) & m(2,2) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3.
+ *
+ */
+template <typename T>
+class TMat33 : public TVecUnaryOperators<TMat33, T>,
+ public TVecComparisonOperators<TMat33, T>,
+ public TVecAddOperators<TMat33, T>,
+ public TMatProductOperators<TMat33, T>,
+ public TMatSquareFunctions<TMat33, T>,
+ public TMatTransform<TMat33, T>,
+ public TMatHelpers<TMat33, T>,
+ public TMatDebug<TMat33, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec3<T> col_type;
+ typedef TVec3<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat33(const TMat33&) = default;
+ ~TMat33() = default;
+ TMat33& operator = (const TMat33&) = default;
+
+ /**
+ * constructors
+ */
+
+ /**
+ * leaves object uninitialized. use with caution.
+ */
+ explicit constexpr TMat33(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+
+ /**
+ * initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * 1 & 0 & 0 \\
+ * 0 & 1 & 0 \\
+ * 0 & 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat33();
+
+ /**
+ * initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v & 0 & 0 \\
+ * 0 & v & 0 \\
+ * 0 & 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat33(U v);
+
+ /**
+ * sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v[0] & 0 & 0 \\
+ * 0 & v[1] & 0 \\
+ * 0 & 0 & v[2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TVec3<U>& v);
+
+ /**
+ * construct from another matrix of the same size
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TMat33<U>& rhs);
+
+ /**
+ * construct from 3 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v0 & v1 & v2 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B, typename C>
+ CONSTEXPR TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2);
+
+ /** construct from 9 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0][0] & m[1][0] & m[2][0] \\
+ * m[0][1] & m[1][1] & m[2][1] \\
+ * m[0][2] & m[1][2] & m[2][2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B, typename C,
+ typename D, typename E, typename F,
+ typename G, typename H, typename I>
+ CONSTEXPR TMat33(
+ A m00, B m01, C m02,
+ D m10, E m11, F m12,
+ G m20, H m21, I m22);
+
+ /**
+ * construct from a quaternion
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TQuaternion<U>& q);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(U const* rawArray);
+
+ /**
+ * orthogonalize only works on matrices of size 3x3
+ */
+ friend inline
+ CONSTEXPR TMat33 orthogonalize(const TMat33& m) {
+ TMat33 ret(TMat33::NO_INIT);
+ ret[0] = normalize(m[0]);
+ ret[2] = normalize(cross(ret[0], m[1]));
+ ret[1] = normalize(cross(ret[2], ret[0]));
+ return ret;
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat33<T>::TMat33() {
+ m_value[0] = col_type(1, 0, 0);
+ m_value[1] = col_type(0, 1, 0);
+ m_value[2] = col_type(0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U v) {
+ m_value[0] = col_type(v, 0, 0);
+ m_value[1] = col_type(0, v, 0);
+ m_value[2] = col_type(0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<U>& v) {
+ m_value[0] = col_type(v.x, 0, 0);
+ m_value[1] = col_type(0, v.y, 0);
+ m_value[2] = col_type(0, 0, v.z);
+}
+
+// construct from 9 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+ typename A, typename B, typename C,
+ typename D, typename E, typename F,
+ typename G, typename H, typename I>
+CONSTEXPR TMat33<T>::TMat33(
+ A m00, B m01, C m02,
+ D m10, E m11, F m12,
+ G m20, H m21, I m22) {
+ m_value[0] = col_type(m00, m01, m02);
+ m_value[1] = col_type(m10, m11, m12);
+ m_value[2] = col_type(m20, m21, m22);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TMat33<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 3 column vectors.
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) {
+ m_value[0] = v0;
+ m_value[1] = v1;
+ m_value[2] = v2;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TQuaternion<U>& q) {
+ const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ const U s = n > 0 ? 2/n : 0;
+ const U x = s*q.x;
+ const U y = s*q.y;
+ const U z = s*q.z;
+ const U xx = x*q.x;
+ const U xy = x*q.y;
+ const U xz = x*q.z;
+ const U xw = x*q.w;
+ const U yy = y*q.y;
+ const U yz = y*q.z;
+ const U yw = y*q.w;
+ const U zz = z*q.z;
+ const U zw = z*q.w;
+ m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw); // NOLINT
+ m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw); // NOLINT
+ m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy); // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::col_type PURE operator *(const TMat33<T>& lhs, const TVec3<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat33<U>::col_type result;
+ for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::row_type PURE operator *(const TVec3<U>& lhs, const TMat33<T>& rhs) {
+ typename TMat33<U>::row_type result(TMat33<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(TMat33<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(U lhs, const TMat33<T>& rhs) {
+ return rhs * lhs;
+}
+
+//------------------------------------------------------------------------------
+template <typename T>
+CONSTEXPR TMat33<T> orthogonalize(const TMat33<T>& m) {
+ TMat33<T> ret(TMat33<T>::NO_INIT);
+ ret[0] = normalize(m[0]);
+ ret[2] = normalize(cross(ret[0], m[1]));
+ ret[1] = normalize(cross(ret[2], ret[0]));
+ return ret;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat33<T>::col_type PURE diag(const TMat33<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat33<double> mat3d;
+typedef details::TMat33<float> mat3;
+typedef details::TMat33<float> mat3f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
+
+#endif // UI_MAT3_H_
diff --git a/include/ui/mat4.h b/include/ui/mat4.h
index 4fd1eff..f63d40a 100644
--- a/include/ui/mat4.h
+++ b/include/ui/mat4.h
@@ -14,173 +14,331 @@
* limitations under the License.
*/
-#ifndef UI_MAT4_H
-#define UI_MAT4_H
+#ifndef UI_MAT4_H_
+#define UI_MAT4_H_
+
+#include <ui/mat3.h>
+#include <ui/quat.h>
+#include <ui/TMatHelpers.h>
+#include <ui/vec3.h>
+#include <ui/vec4.h>
#include <stdint.h>
#include <sys/types.h>
-
-#include <ui/vec4.h>
-#include <utils/String8.h>
-
-#define TMAT_IMPLEMENTATION
-#include <ui/TMatHelpers.h>
+#include <limits>
#define PURE __attribute__((pure))
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
namespace android {
// -------------------------------------------------------------------------------------
+namespace details {
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 4x4 column-major matrix class.
+ *
+ * Conceptually a 4x4 matrix is a an array of 4 column double4:
+ *
+ * mat4 m =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0] & m[1] & m[2] & m[3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m(0,0) & m(0,1) & m(0,2) & m(0,3) \\
+ * m(1,0) & m(1,1) & m(1,2) & m(1,3) \\
+ * m(2,0) & m(2,1) & m(2,2) & m(2,3) \\
+ * m(3,0) & m(3,1) & m(3,2) & m(3,3) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4.
+ *
+ */
template <typename T>
-class tmat44 : public TVecUnaryOperators<tmat44, T>,
- public TVecComparisonOperators<tmat44, T>,
- public TVecAddOperators<tmat44, T>,
- public TMatProductOperators<tmat44, T>,
- public TMatSquareFunctions<tmat44, T>,
- public TMatDebug<tmat44, T>
-{
+class TMat44 : public TVecUnaryOperators<TMat44, T>,
+ public TVecComparisonOperators<TMat44, T>,
+ public TVecAddOperators<TMat44, T>,
+ public TMatProductOperators<TMat44, T>,
+ public TMatSquareFunctions<TMat44, T>,
+ public TMatTransform<TMat44, T>,
+ public TMatHelpers<TMat44, T>,
+ public TMatDebug<TMat44, T> {
public:
enum no_init { NO_INIT };
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
typedef size_t size_type;
- typedef tvec4<T> col_type;
- typedef tvec4<T> row_type;
+ typedef TVec4<T> col_type;
+ typedef TVec4<T> row_type;
- // size of a column (i.e.: number of rows)
- enum { COL_SIZE = col_type::SIZE };
- static inline size_t col_size() { return COL_SIZE; }
-
- // size of a row (i.e.: number of columns)
- enum { ROW_SIZE = row_type::SIZE };
- static inline size_t row_size() { return ROW_SIZE; }
- static inline size_t size() { return row_size(); } // for TVec*<>
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
private:
-
/*
* <-- N columns -->
*
- * a00 a10 a20 ... aN0 ^
- * a01 a11 a21 ... aN1 |
- * a02 a12 a22 ... aN2 M rows
- * ... |
- * a0M a1M a2M ... aNM v
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
*
* COL_SIZE = M
* ROW_SIZE = N
- * m[0] = [a00 a01 a02 ... a01M]
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
*/
- col_type mValue[ROW_SIZE];
+ col_type m_value[NUM_COLS];
public:
// array access
- inline col_type const& operator [] (size_t i) const { return mValue[i]; }
- inline col_type& operator [] (size_t i) { return mValue[i]; }
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
- T const* asArray() const { return &mValue[0][0]; }
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
// -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
+ // we want the compiler generated versions for these...
+ TMat44(const TMat44&) = default;
+ ~TMat44() = default;
+ TMat44& operator = (const TMat44&) = default;
/*
* constructors
*/
// leaves object uninitialized. use with caution.
- explicit tmat44(no_init) { }
+ explicit constexpr TMat44(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
- // initialize to identity
- tmat44();
+ /** initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * 1 & 0 & 0 & 0 \\
+ * 0 & 1 & 0 & 0 \\
+ * 0 & 0 & 1 & 0 \\
+ * 0 & 0 & 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat44();
- // initialize to Identity*scalar.
+ /** initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v & 0 & 0 & 0 \\
+ * 0 & v & 0 & 0 \\
+ * 0 & 0 & v & 0 \\
+ * 0 & 0 & 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
template<typename U>
- explicit tmat44(U v);
+ explicit CONSTEXPR TMat44(U v);
- // sets the diagonal to the passed vector
+ /** sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v[0] & 0 & 0 & 0 \\
+ * 0 & v[1] & 0 & 0 \\
+ * 0 & 0 & v[2] & 0 \\
+ * 0 & 0 & 0 & v[3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
template <typename U>
- explicit tmat44(const tvec4<U>& rhs);
+ explicit CONSTEXPR TMat44(const TVec4<U>& v);
// construct from another matrix of the same size
template <typename U>
- explicit tmat44(const tmat44<U>& rhs);
+ explicit CONSTEXPR TMat44(const TMat44<U>& rhs);
- // construct from 4 column vectors
+ /** construct from 4 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v0 & v1 & v2 & v3 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
template <typename A, typename B, typename C, typename D>
- tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3);
+ CONSTEXPR TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2, const TVec4<D>& v3);
- // construct from 16 scalars
+ /** construct from 16 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
template <
typename A, typename B, typename C, typename D,
typename E, typename F, typename G, typename H,
typename I, typename J, typename K, typename L,
typename M, typename N, typename O, typename P>
- tmat44( A m00, B m01, C m02, D m03,
+ CONSTEXPR TMat44(
+ A m00, B m01, C m02, D m03,
E m10, F m11, G m12, H m13,
I m20, J m21, K m22, L m23,
M m30, N m31, O m32, P m33);
- // construct from a C array
+ /**
+ * construct from a quaternion
+ */
template <typename U>
- explicit tmat44(U const* rawArray);
+ explicit CONSTEXPR TMat44(const TQuaternion<U>& q);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(U const* rawArray);
+
+ /**
+ * construct from a 3x3 matrix
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TMat33<U>& matrix);
+
+ /**
+ * construct from a 3x3 matrix and 3d translation
+ */
+ template <typename U, typename V>
+ CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec3<V>& translation);
+
+ /**
+ * construct from a 3x3 matrix and 4d last column.
+ */
+ template <typename U, typename V>
+ CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec4<V>& column3);
/*
* helpers
*/
- static tmat44 ortho(T left, T right, T bottom, T top, T near, T far);
+ static CONSTEXPR TMat44 ortho(T left, T right, T bottom, T top, T near, T far);
- static tmat44 frustum(T left, T right, T bottom, T top, T near, T far);
+ static CONSTEXPR TMat44 frustum(T left, T right, T bottom, T top, T near, T far);
+
+ enum class Fov {
+ HORIZONTAL,
+ VERTICAL
+ };
+ static CONSTEXPR TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL);
template <typename A, typename B, typename C>
- static tmat44 lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up);
+ static CONSTEXPR TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up);
template <typename A>
- static tmat44 translate(const tvec4<A>& t);
+ static CONSTEXPR TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) {
+ TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
+ return r.xyz / r.w;
+ }
template <typename A>
- static tmat44 scale(const tvec4<A>& s);
+ static CONSTEXPR TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) {
+ vertice = projectionMatrix * vertice;
+ return { vertice.xyz / vertice.w, 1 };
+ }
- template <typename A, typename B>
- static tmat44 rotate(A radian, const tvec3<B>& about);
+ /**
+ * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix
+ */
+ inline constexpr TMat33<T> upperLeft() const {
+ return TMat33<T>(m_value[0].xyz, m_value[1].xyz, m_value[2].xyz);
+ }
};
// ----------------------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------------------
-/*
- * Since the matrix code could become pretty big quickly, we don't inline most
- * operations.
- */
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
template <typename T>
-tmat44<T>::tmat44() {
- mValue[0] = col_type(1,0,0,0);
- mValue[1] = col_type(0,1,0,0);
- mValue[2] = col_type(0,0,1,0);
- mValue[3] = col_type(0,0,0,1);
+CONSTEXPR TMat44<T>::TMat44() {
+ m_value[0] = col_type(1, 0, 0, 0);
+ m_value[1] = col_type(0, 1, 0, 0);
+ m_value[2] = col_type(0, 0, 1, 0);
+ m_value[3] = col_type(0, 0, 0, 1);
}
template <typename T>
template <typename U>
-tmat44<T>::tmat44(U v) {
- mValue[0] = col_type(v,0,0,0);
- mValue[1] = col_type(0,v,0,0);
- mValue[2] = col_type(0,0,v,0);
- mValue[3] = col_type(0,0,0,v);
+CONSTEXPR TMat44<T>::TMat44(U v) {
+ m_value[0] = col_type(v, 0, 0, 0);
+ m_value[1] = col_type(0, v, 0, 0);
+ m_value[2] = col_type(0, 0, v, 0);
+ m_value[3] = col_type(0, 0, 0, v);
}
template<typename T>
template<typename U>
-tmat44<T>::tmat44(const tvec4<U>& v) {
- mValue[0] = col_type(v.x,0,0,0);
- mValue[1] = col_type(0,v.y,0,0);
- mValue[2] = col_type(0,0,v.z,0);
- mValue[3] = col_type(0,0,0,v.w);
+CONSTEXPR TMat44<T>::TMat44(const TVec4<U>& v) {
+ m_value[0] = col_type(v.x, 0, 0, 0);
+ m_value[1] = col_type(0, v.y, 0, 0);
+ m_value[2] = col_type(0, 0, v.z, 0);
+ m_value[3] = col_type(0, 0, 0, v.w);
}
// construct from 16 scalars
@@ -190,38 +348,96 @@
typename E, typename F, typename G, typename H,
typename I, typename J, typename K, typename L,
typename M, typename N, typename O, typename P>
-tmat44<T>::tmat44( A m00, B m01, C m02, D m03,
- E m10, F m11, G m12, H m13,
- I m20, J m21, K m22, L m23,
- M m30, N m31, O m32, P m33) {
- mValue[0] = col_type(m00, m01, m02, m03);
- mValue[1] = col_type(m10, m11, m12, m13);
- mValue[2] = col_type(m20, m21, m22, m23);
- mValue[3] = col_type(m30, m31, m32, m33);
+CONSTEXPR TMat44<T>::TMat44(
+ A m00, B m01, C m02, D m03,
+ E m10, F m11, G m12, H m13,
+ I m20, J m21, K m22, L m23,
+ M m30, N m31, O m32, P m33) {
+ m_value[0] = col_type(m00, m01, m02, m03);
+ m_value[1] = col_type(m10, m11, m12, m13);
+ m_value[2] = col_type(m20, m21, m22, m23);
+ m_value[3] = col_type(m30, m31, m32, m33);
}
template <typename T>
template <typename U>
-tmat44<T>::tmat44(const tmat44<U>& rhs) {
- for (size_t r=0 ; r<row_size() ; r++)
- mValue[r] = rhs[r];
+CONSTEXPR TMat44<T>::TMat44(const TMat44<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
}
+// Construct from 4 column vectors.
template <typename T>
template <typename A, typename B, typename C, typename D>
-tmat44<T>::tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3) {
- mValue[0] = v0;
- mValue[1] = v1;
- mValue[2] = v2;
- mValue[3] = v3;
+CONSTEXPR TMat44<T>::TMat44(
+ const TVec4<A>& v0, const TVec4<B>& v1,
+ const TVec4<C>& v2, const TVec4<D>& v3) {
+ m_value[0] = col_type(v0);
+ m_value[1] = col_type(v1);
+ m_value[2] = col_type(v2);
+ m_value[3] = col_type(v3);
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
}
template <typename T>
template <typename U>
-tmat44<T>::tmat44(U const* rawArray) {
- for (size_t r=0 ; r<row_size() ; r++)
- for (size_t c=0 ; c<col_size() ; c++)
- mValue[r][c] = *rawArray++;
+CONSTEXPR TMat44<T>::TMat44(const TQuaternion<U>& q) {
+ const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ const U s = n > 0 ? 2/n : 0;
+ const U x = s*q.x;
+ const U y = s*q.y;
+ const U z = s*q.z;
+ const U xx = x*q.x;
+ const U xy = x*q.y;
+ const U xz = x*q.z;
+ const U xw = x*q.w;
+ const U yy = y*q.y;
+ const U yz = y*q.z;
+ const U yw = y*q.w;
+ const U zz = z*q.z;
+ const U zw = z*q.w;
+ m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw, 0);
+ m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw, 0); // NOLINT
+ m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy, 0); // NOLINT
+ m_value[3] = col_type( 0, 0, 0, 1); // NOLINT
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+ m_value[3] = col_type( 0, 0, 0, 1); // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec3<V>& v) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+ m_value[3] = col_type( v[0], v[1], v[2], 1); // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec4<V>& v) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); // NOLINT
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); // NOLINT
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); // NOLINT
+ m_value[3] = col_type( v[0], v[1], v[2], v[3]); // NOLINT
}
// ----------------------------------------------------------------------------------------
@@ -229,8 +445,8 @@
// ----------------------------------------------------------------------------------------
template <typename T>
-tmat44<T> tmat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
- tmat44<T> m;
+CONSTEXPR TMat44<T> TMat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
+ TMat44<T> m;
m[0][0] = 2 / (right - left);
m[1][1] = 2 / (top - bottom);
m[2][2] = -2 / (far - near);
@@ -241,88 +457,55 @@
}
template <typename T>
-tmat44<T> tmat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
- tmat44<T> m;
- T A = (right + left) / (right - left);
- T B = (top + bottom) / (top - bottom);
- T C = (far + near) / (far - near);
- T D = (2 * far * near) / (far - near);
- m[0][0] = (2 * near) / (right - left);
- m[1][1] = (2 * near) / (top - bottom);
- m[2][0] = A;
- m[2][1] = B;
- m[2][2] = C;
- m[2][3] =-1;
- m[3][2] = D;
- m[3][3] = 0;
+CONSTEXPR TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
+ TMat44<T> m;
+ m[0][0] = (2 * near) / (right - left);
+ m[1][1] = (2 * near) / (top - bottom);
+ m[2][0] = (right + left) / (right - left);
+ m[2][1] = (top + bottom) / (top - bottom);
+ m[2][2] = -(far + near) / (far - near);
+ m[2][3] = -1;
+ m[3][2] = -(2 * far * near) / (far - near);
+ m[3][3] = 0;
return m;
}
template <typename T>
-template <typename A, typename B, typename C>
-tmat44<T> tmat44<T>::lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up) {
- tvec3<T> L(normalize(center - eye));
- tvec3<T> S(normalize( cross(L, up) ));
- tvec3<T> U(cross(S, L));
- return tmat44<T>(
- tvec4<T>( S, 0),
- tvec4<T>( U, 0),
- tvec4<T>(-L, 0),
- tvec4<T>(-eye, 1));
-}
+CONSTEXPR TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) {
+ T h;
+ T w;
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::translate(const tvec4<A>& t) {
- tmat44<T> r;
- r[3] = t;
- return r;
-}
-
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::scale(const tvec4<A>& s) {
- tmat44<T> r;
- r[0][0] = s[0];
- r[1][1] = s[1];
- r[2][2] = s[2];
- r[3][3] = s[3];
- return r;
-}
-
-template <typename T>
-template <typename A, typename B>
-tmat44<T> tmat44<T>::rotate(A radian, const tvec3<B>& about) {
- tmat44<T> rotation;
- T* r = const_cast<T*>(rotation.asArray());
- T c = cos(radian);
- T s = sin(radian);
- if (about.x==1 && about.y==0 && about.z==0) {
- r[5] = c; r[10]= c;
- r[6] = s; r[9] = -s;
- } else if (about.x==0 && about.y==1 && about.z==0) {
- r[0] = c; r[10]= c;
- r[8] = s; r[2] = -s;
- } else if (about.x==0 && about.y==0 && about.z==1) {
- r[0] = c; r[5] = c;
- r[1] = s; r[4] = -s;
+ if (direction == TMat44::Fov::VERTICAL) {
+ h = std::tan(fov * M_PI / 360.0f) * near;
+ w = h * aspect;
} else {
- tvec3<B> nabout = normalize(about);
- B x = nabout.x;
- B y = nabout.y;
- B z = nabout.z;
- T nc = 1 - c;
- T xy = x * y;
- T yz = y * z;
- T zx = z * x;
- T xs = x * s;
- T ys = y * s;
- T zs = z * s;
- r[ 0] = x*x*nc + c; r[ 4] = xy*nc - zs; r[ 8] = zx*nc + ys;
- r[ 1] = xy*nc + zs; r[ 5] = y*y*nc + c; r[ 9] = yz*nc - xs;
- r[ 2] = zx*nc - ys; r[ 6] = yz*nc + xs; r[10] = z*z*nc + c;
+ w = std::tan(fov * M_PI / 360.0f) * near;
+ h = w / aspect;
}
- return rotation;
+ return frustum(-w, w, -h, h, near, far);
+}
+
+/*
+ * Returns a matrix representing the pose of a virtual camera looking towards -Z in its
+ * local Y-up coordinate system. "eye" is where the camera is located, "center" is the points its
+ * looking at and "up" defines where the Y axis of the camera's local coordinate system is.
+ */
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) {
+ TVec3<T> z_axis(normalize(center - eye));
+ TVec3<T> norm_up(normalize(up));
+ if (std::abs(dot(z_axis, norm_up)) > 0.999) {
+ // Fix up vector if we're degenerate (looking straight up, basically)
+ norm_up = { norm_up.z, norm_up.x, norm_up.y };
+ }
+ TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
+ TVec3<T> y_axis(cross(x_axis, z_axis));
+ return TMat44<T>(
+ TVec4<T>(x_axis, 0),
+ TVec4<T>(y_axis, 0),
+ TVec4<T>(-z_axis, 0),
+ TVec4<T>(eye, 1));
}
// ----------------------------------------------------------------------------------------
@@ -337,40 +520,46 @@
* it determines the output type (only relevant when T != U).
*/
-// matrix * vector, result is a vector of the same type than the input vector
+// matrix * column-vector, result is a vector of the same type than the input vector
template <typename T, typename U>
-typename tmat44<U>::col_type PURE operator *(const tmat44<T>& lv, const tvec4<U>& rv) {
- typename tmat44<U>::col_type result;
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result += rv[r]*lv[r];
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec4<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat44<T>::col_type result;
+ for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
return result;
}
-// vector * matrix, result is a vector of the same type than the input vector
+// mat44 * vec3, result is vec3( mat44 * {vec3, 1} )
template <typename T, typename U>
-typename tmat44<U>::row_type PURE operator *(const tvec4<U>& rv, const tmat44<T>& lv) {
- typename tmat44<U>::row_type result(tmat44<U>::row_type::NO_INIT);
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result[r] = dot(rv, lv[r]);
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec3<U>& rhs) {
+ return lhs * TVec4<U>{ rhs, 1 };
+}
+
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<U>::row_type PURE operator *(const TVec4<U>& lhs, const TMat44<T>& rhs) {
+ typename TMat44<U>::row_type result(TMat44<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
return result;
}
// matrix * scalar, result is a matrix of the same type than the input matrix
template <typename T, typename U>
-tmat44<T> PURE operator *(const tmat44<T>& lv, U rv) {
- tmat44<T> result(tmat44<T>::NO_INIT);
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result[r] = lv[r]*rv;
- return result;
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(TMat44<T> lhs, U rhs) {
+ return lhs *= rhs;
}
// scalar * matrix, result is a matrix of the same type than the input matrix
template <typename T, typename U>
-tmat44<T> PURE operator *(U rv, const tmat44<T>& lv) {
- tmat44<T> result(tmat44<T>::NO_INIT);
- for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
- result[r] = lv[r]*rv;
- return result;
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(U lhs, const TMat44<T>& rhs) {
+ return rhs * lhs;
}
// ----------------------------------------------------------------------------------------
@@ -379,17 +568,22 @@
* BASE<T>::col_type is not accessible from there (???)
*/
template<typename T>
-typename tmat44<T>::col_type PURE diag(const tmat44<T>& m) {
+typename TMat44<T>::col_type PURE diag(const TMat44<T>& m) {
return matrix::diag(m);
}
-// ----------------------------------------------------------------------------------------
-
-typedef tmat44<float> mat4;
+} // namespace details
// ----------------------------------------------------------------------------------------
-}; // namespace android
+
+typedef details::TMat44<double> mat4d;
+typedef details::TMat44<float> mat4;
+typedef details::TMat44<float> mat4f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
#undef PURE
+#undef CONSTEXPR
-#endif /* UI_MAT4_H */
+#endif // UI_MAT4_H_
diff --git a/include/ui/quat.h b/include/ui/quat.h
new file mode 100644
index 0000000..5b8cd8b
--- /dev/null
+++ b/include/ui/quat.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UI_QUAT_H_
+#define UI_QUAT_H_
+
+#include <ui/half.h>
+#include <ui/TQuatHelpers.h>
+#include <ui/vec3.h>
+#include <ui/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifndef PURE
+#define PURE __attribute__((pure))
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TQuaternion : public TVecAddOperators<TQuaternion, T>,
+ public TVecUnaryOperators<TQuaternion, T>,
+ public TVecComparisonOperators<TQuaternion, T>,
+ public TQuatProductOperators<TQuaternion, T>,
+ public TQuatFunctions<TQuaternion, T>,
+ public TQuatDebug<TQuaternion, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ /*
+ * quaternion internals stored as:
+ *
+ * q = w + xi + yj + zk
+ *
+ * q[0] = x;
+ * q[1] = y;
+ * q[2] = z;
+ * q[3] = w;
+ *
+ */
+ union {
+ struct { T x, y, z, w; };
+ TVec4<T> xyzw;
+ TVec3<T> xyz;
+ TVec2<T> xy;
+ };
+
+ enum { SIZE = 4 };
+ inline constexpr static size_type size() { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TQuaternion(const TQuaternion&) = default;
+ ~TQuaternion() = default;
+ TQuaternion& operator = (const TQuaternion&) = default;
+
+ // constructors
+
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TQuaternion(no_init) : xyzw(TVec4<T>::NO_INIT) {}
+
+ // default constructor. sets all values to zero.
+ constexpr TQuaternion() : x(0), y(0), z(0), w(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A>
+ constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {
+ static_assert(std::is_arithmetic<A>::value, "requires arithmetic type");
+ }
+
+ // initialize from 4 values to w + xi + yj + zk
+ template<typename A, typename B, typename C, typename D>
+ constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { }
+
+ // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w
+ template<typename A, typename B>
+ constexpr TQuaternion(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+ // initialize from a double4
+ template<typename A>
+ constexpr explicit TQuaternion(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+ // initialize from a quaternion of a different type
+ template<typename A>
+ constexpr explicit TQuaternion(const TQuaternion<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+ // conjugate operator
+ constexpr TQuaternion operator~() const {
+ return conj(*this);
+ }
+
+ // constructs a quaternion from an axis and angle
+ template <typename A, typename B>
+ constexpr static TQuaternion PURE fromAxisAngle(const TVec3<A>& axis, B angle) {
+ return TQuaternion(std::sin(angle*0.5) * normalize(axis), std::cos(angle*0.5));
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TQuaternion<double> quatd;
+typedef details::TQuaternion<float> quat;
+typedef details::TQuaternion<float> quatf;
+typedef details::TQuaternion<half> quath;
+
+constexpr inline quat operator"" _i(long double v) {
+ return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(long double v) {
+ return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(long double v) {
+ return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quat operator"" _i(unsigned long long v) { // NOLINT
+ return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(unsigned long long v) { // NOLINT
+ return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(unsigned long long v) { // NOLINT
+ return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quatd operator"" _id(long double v) {
+ return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(long double v) {
+ return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(long double v) {
+ return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+constexpr inline quatd operator"" _id(unsigned long long v) { // NOLINT
+ return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(unsigned long long v) { // NOLINT
+ return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(unsigned long long v) { // NOLINT
+ return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
+
+#undef PURE
+
+#endif // UI_QUAT_H_
diff --git a/include/ui/scalar.h b/include/ui/scalar.h
new file mode 100644
index 0000000..5f8329e
--- /dev/null
+++ b/include/ui/scalar.h
@@ -0,0 +1,47 @@
+/*
+ * 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 UI_SCALAR_H
+#define UI_SCALAR_H
+
+#include <algorithm>
+#include <cmath>
+
+namespace android {
+
+template<typename T>
+static constexpr T saturate(T v) noexcept {
+ return T(std::min(T(1), std::max(T(0), v)));
+}
+
+template<typename T>
+static constexpr T clamp(T v, T min, T max) noexcept {
+ return T(std::min(max, std::max(min, v)));
+}
+
+template<typename T>
+static constexpr T mix(T x, T y, T a) noexcept {
+ return x * (T(1) - a) + y * a;
+}
+
+template<typename T>
+static constexpr T lerp(T x, T y, T a) noexcept {
+ return mix(x, y, a);
+}
+
+} // namespace std
+
+#endif // UI_SCALAR_H
diff --git a/include/ui/vec2.h b/include/ui/vec2.h
index c31d0e4..fdd2e20 100644
--- a/include/ui/vec2.h
+++ b/include/ui/vec2.h
@@ -14,25 +14,32 @@
* limitations under the License.
*/
-#ifndef UI_VEC2_H
-#define UI_VEC2_H
+#ifndef UI_VEC2_H_
+#define UI_VEC2_H_
+#include <ui/TVecHelpers.h>
+#include <ui/half.h>
+#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
+#include <type_traits>
-#define TVEC_IMPLEMENTATION
-#include <ui/TVecHelpers.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
namespace android {
// -------------------------------------------------------------------------------------
+namespace details {
+
template <typename T>
-class tvec2 : public TVecProductOperators<tvec2, T>,
- public TVecAddOperators<tvec2, T>,
- public TVecUnaryOperators<tvec2, T>,
- public TVecComparisonOperators<tvec2, T>,
- public TVecFunctions<tvec2, T>
-{
+class TVec2 : public TVecProductOperators<TVec2, T>,
+ public TVecAddOperators<TVec2, T>,
+ public TVecUnaryOperators<TVec2, T>,
+ public TVecComparisonOperators<TVec2, T>,
+ public TVecFunctions<TVec2, T>,
+ public TVecDebug<TVec2, T> {
public:
enum no_init { NO_INIT };
typedef T value_type;
@@ -46,46 +53,75 @@
struct { T r, g; };
};
- enum { SIZE = 2 };
- inline static size_type size() { return SIZE; }
+ static constexpr size_t SIZE = 2;
+ inline constexpr size_type size() const { return SIZE; }
// array access
- inline T const& operator [] (size_t i) const { return (&x)[i]; }
- inline T& operator [] (size_t i) { return (&x)[i]; }
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
// -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
+ // we want the compiler generated versions for these...
+ TVec2(const TVec2&) = default;
+ ~TVec2() = default;
+ TVec2& operator = (const TVec2&) = default;
// constructors
// leaves object uninitialized. use with caution.
- explicit tvec2(no_init) { }
+ explicit
+ constexpr TVec2(no_init) { }
// default constructor
- tvec2() : x(0), y(0) { }
+ constexpr TVec2() : x(0), y(0) { }
// handles implicit conversion to a tvec4. must not be explicit.
- template<typename A>
- tvec2(A v) : x(v), y(v) { }
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec2(A v) : x(v), y(v) { }
template<typename A, typename B>
- tvec2(A x, B y) : x(x), y(y) { }
+ constexpr TVec2(A x, B y) : x(x), y(y) { }
template<typename A>
- explicit tvec2(const tvec2<A>& v) : x(v.x), y(v.y) { }
+ explicit
+ constexpr TVec2(const TVec2<A>& v) : x(v.x), y(v.y) { }
- template<typename A>
- tvec2(const Impersonator< tvec2<A> >& v)
- : x(((const tvec2<A>&)v).x),
- y(((const tvec2<A>&)v).y) { }
+ // cross product works only on vectors of size 2 or 3
+ template<typename RT>
+ friend inline
+ constexpr value_type cross(const TVec2& u, const TVec2<RT>& v) {
+ return value_type(u.x*v.y - u.y*v.x);
+ }
};
-// ----------------------------------------------------------------------------------------
-
-typedef tvec2<float> vec2;
+} // namespace details
// ----------------------------------------------------------------------------------------
-}; // namespace android
-#endif /* UI_VEC4_H */
+typedef details::TVec2<double> double2;
+typedef details::TVec2<float> float2;
+typedef details::TVec2<float> vec2;
+typedef details::TVec2<half> half2;
+typedef details::TVec2<int32_t> int2;
+typedef details::TVec2<uint32_t> uint2;
+typedef details::TVec2<int16_t> short2;
+typedef details::TVec2<uint16_t> ushort2;
+typedef details::TVec2<int8_t> byte2;
+typedef details::TVec2<uint8_t> ubyte2;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
+
+#endif // UI_VEC2_H_
diff --git a/include/ui/vec3.h b/include/ui/vec3.h
index dde59a9..f76b2ec 100644
--- a/include/ui/vec3.h
+++ b/include/ui/vec3.h
@@ -14,24 +14,30 @@
* limitations under the License.
*/
-#ifndef UI_VEC3_H
-#define UI_VEC3_H
+#ifndef UI_VEC3_H_
+#define UI_VEC3_H_
+#include <ui/vec2.h>
+#include <ui/half.h>
#include <stdint.h>
#include <sys/types.h>
-#include <ui/vec2.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
namespace android {
// -------------------------------------------------------------------------------------
+namespace details {
+
template <typename T>
-class tvec3 : public TVecProductOperators<tvec3, T>,
- public TVecAddOperators<tvec3, T>,
- public TVecUnaryOperators<tvec3, T>,
- public TVecComparisonOperators<tvec3, T>,
- public TVecFunctions<tvec3, T>
-{
+class TVec3 : public TVecProductOperators<TVec3, T>,
+ public TVecAddOperators<TVec3, T>,
+ public TVecUnaryOperators<TVec3, T>,
+ public TVecComparisonOperators<TVec3, T>,
+ public TVecFunctions<TVec3, T>,
+ public TVecDebug<TVec3, T> {
public:
enum no_init { NO_INIT };
typedef T value_type;
@@ -43,71 +49,85 @@
struct { T x, y, z; };
struct { T s, t, p; };
struct { T r, g, b; };
- Impersonator< tvec2<T> > xy;
- Impersonator< tvec2<T> > st;
- Impersonator< tvec2<T> > rg;
+ TVec2<T> xy;
+ TVec2<T> st;
+ TVec2<T> rg;
};
- enum { SIZE = 3 };
- inline static size_type size() { return SIZE; }
+ static constexpr size_t SIZE = 3;
+ inline constexpr size_type size() const { return SIZE; }
// array access
- inline T const& operator [] (size_t i) const { return (&x)[i]; }
- inline T& operator [] (size_t i) { return (&x)[i]; }
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
// -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
+ // we want the compiler generated versions for these...
+ TVec3(const TVec3&) = default;
+ ~TVec3() = default;
+ TVec3& operator = (const TVec3&) = default;
// constructors
// leaves object uninitialized. use with caution.
- explicit tvec3(no_init) { }
+ explicit
+ constexpr TVec3(no_init) { }
// default constructor
- tvec3() : x(0), y(0), z(0) { }
+ constexpr TVec3() : x(0), y(0), z(0) { }
// handles implicit conversion to a tvec4. must not be explicit.
- template<typename A>
- tvec3(A v) : x(v), y(v), z(v) { }
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec3(A v) : x(v), y(v), z(v) { }
template<typename A, typename B, typename C>
- tvec3(A x, B y, C z) : x(x), y(y), z(z) { }
+ constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { }
template<typename A, typename B>
- tvec3(const tvec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
+ constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
template<typename A>
- explicit tvec3(const tvec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
-
- template<typename A>
- tvec3(const Impersonator< tvec3<A> >& v)
- : x(((const tvec3<A>&)v).x),
- y(((const tvec3<A>&)v).y),
- z(((const tvec3<A>&)v).z) { }
-
- template<typename A, typename B>
- tvec3(const Impersonator< tvec2<A> >& v, B z)
- : x(((const tvec2<A>&)v).x),
- y(((const tvec2<A>&)v).y),
- z(z) { }
+ explicit
+ constexpr TVec3(const TVec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
// cross product works only on vectors of size 3
template <typename RT>
friend inline
- tvec3 __attribute__((pure)) cross(const tvec3& u, const tvec3<RT>& v) {
- return tvec3(
+ constexpr TVec3 cross(const TVec3& u, const TVec3<RT>& v) {
+ return TVec3(
u.y*v.z - u.z*v.y,
u.z*v.x - u.x*v.z,
u.x*v.y - u.y*v.x);
}
};
+} // namespace details
// ----------------------------------------------------------------------------------------
-typedef tvec3<float> vec3;
+typedef details::TVec3<double> double3;
+typedef details::TVec3<float> float3;
+typedef details::TVec3<float> vec3;
+typedef details::TVec3<half> half3;
+typedef details::TVec3<int32_t> int3;
+typedef details::TVec3<uint32_t> uint3;
+typedef details::TVec3<int16_t> short3;
+typedef details::TVec3<uint16_t> ushort3;
+typedef details::TVec3<int8_t> byte3;
+typedef details::TVec3<uint8_t> ubyte3;
// ----------------------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
-#endif /* UI_VEC4_H */
+#pragma clang diagnostic pop
+
+#endif // UI_VEC3_H_
diff --git a/include/ui/vec4.h b/include/ui/vec4.h
index e03d331..e13ad96 100644
--- a/include/ui/vec4.h
+++ b/include/ui/vec4.h
@@ -14,24 +14,30 @@
* limitations under the License.
*/
-#ifndef UI_VEC4_H
-#define UI_VEC4_H
+#ifndef UI_VEC4_H_
+#define UI_VEC4_H_
+#include <ui/vec3.h>
+#include <ui/half.h>
#include <stdint.h>
#include <sys/types.h>
-#include <ui/vec3.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
namespace android {
// -------------------------------------------------------------------------------------
+namespace details {
+
template <typename T>
-class tvec4 : public TVecProductOperators<tvec4, T>,
- public TVecAddOperators<tvec4, T>,
- public TVecUnaryOperators<tvec4, T>,
- public TVecComparisonOperators<tvec4, T>,
- public TVecFunctions<tvec4, T>
-{
+class TVec4 : public TVecProductOperators<TVec4, T>,
+ public TVecAddOperators<TVec4, T>,
+ public TVecUnaryOperators<TVec4, T>,
+ public TVecComparisonOperators<TVec4, T>,
+ public TVecFunctions<TVec4, T>,
+ public TVecDebug<TVec4, T> {
public:
enum no_init { NO_INIT };
typedef T value_type;
@@ -43,76 +49,82 @@
struct { T x, y, z, w; };
struct { T s, t, p, q; };
struct { T r, g, b, a; };
- Impersonator< tvec2<T> > xy;
- Impersonator< tvec2<T> > st;
- Impersonator< tvec2<T> > rg;
- Impersonator< tvec3<T> > xyz;
- Impersonator< tvec3<T> > stp;
- Impersonator< tvec3<T> > rgb;
+ TVec2<T> xy;
+ TVec2<T> st;
+ TVec2<T> rg;
+ TVec3<T> xyz;
+ TVec3<T> stp;
+ TVec3<T> rgb;
};
- enum { SIZE = 4 };
- inline static size_type size() { return SIZE; }
+ static constexpr size_t SIZE = 4;
+ inline constexpr size_type size() const { return SIZE; }
// array access
- inline T const& operator [] (size_t i) const { return (&x)[i]; }
- inline T& operator [] (size_t i) { return (&x)[i]; }
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
// -----------------------------------------------------------------------
- // we don't provide copy-ctor and operator= on purpose
- // because we want the compiler generated versions
+ // we want the compiler generated versions for these...
+ TVec4(const TVec4&) = default;
+ ~TVec4() = default;
+ TVec4& operator = (const TVec4&) = default;
// constructors
// leaves object uninitialized. use with caution.
- explicit tvec4(no_init) { }
+ explicit
+ constexpr TVec4(no_init) { }
// default constructor
- tvec4() : x(0), y(0), z(0), w(0) { }
+ constexpr TVec4() : x(0), y(0), z(0), w(0) { }
// handles implicit conversion to a tvec4. must not be explicit.
- template<typename A>
- tvec4(A v) : x(v), y(v), z(v), w(v) { }
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec4(A v) : x(v), y(v), z(v), w(v) { }
template<typename A, typename B, typename C, typename D>
- tvec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
+ constexpr TVec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
template<typename A, typename B, typename C>
- tvec4(const tvec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
+ constexpr TVec4(const TVec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
template<typename A, typename B>
- tvec4(const tvec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+ constexpr TVec4(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
template<typename A>
- explicit tvec4(const tvec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
-
- template<typename A>
- tvec4(const Impersonator< tvec4<A> >& v)
- : x(((const tvec4<A>&)v).x),
- y(((const tvec4<A>&)v).y),
- z(((const tvec4<A>&)v).z),
- w(((const tvec4<A>&)v).w) { }
-
- template<typename A, typename B>
- tvec4(const Impersonator< tvec3<A> >& v, B w)
- : x(((const tvec3<A>&)v).x),
- y(((const tvec3<A>&)v).y),
- z(((const tvec3<A>&)v).z),
- w(w) { }
-
- template<typename A, typename B, typename C>
- tvec4(const Impersonator< tvec2<A> >& v, B z, C w)
- : x(((const tvec2<A>&)v).x),
- y(((const tvec2<A>&)v).y),
- z(z),
- w(w) { }
+ explicit
+ constexpr TVec4(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
};
-// ----------------------------------------------------------------------------------------
-
-typedef tvec4<float> vec4;
+} // namespace details
// ----------------------------------------------------------------------------------------
-}; // namespace android
-#endif /* UI_VEC4_H */
+typedef details::TVec4<double> double4;
+typedef details::TVec4<float> float4;
+typedef details::TVec4<float> vec4;
+typedef details::TVec4<half> half4;
+typedef details::TVec4<int32_t> int4;
+typedef details::TVec4<uint32_t> uint4;
+typedef details::TVec4<int16_t> short4;
+typedef details::TVec4<uint16_t> ushort4;
+typedef details::TVec4<int8_t> byte4;
+typedef details::TVec4<uint8_t> ubyte4;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
+
+#endif // UI_VEC4_H_
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 4780757..62b75ba 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -21,6 +21,7 @@
"BpBinder.cpp",
"BufferedTextOutput.cpp",
"Debug.cpp",
+ "IActivityManager.cpp",
"IAppOpsCallback.cpp",
"IAppOpsService.cpp",
"IBatteryStats.cpp",
@@ -32,6 +33,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/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
new file mode 100644
index 0000000..87b295a
--- /dev/null
+++ b/libs/binder/IActivityManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IActivityManager.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class BpActivityManager : public BpInterface<IActivityManager>
+{
+public:
+ explicit BpActivityManager(const sp<IBinder>& impl)
+ : BpInterface<IActivityManager>(impl)
+ {
+ }
+
+ virtual int openContentUri(const String16& stringUri)
+ {
+ Parcel data, reply;
+ data.writeString16(stringUri);
+ status_t ret = remote()->transact(OPEN_CONTENT_URI_TRANSACTION, data, & reply);
+ int fd = -1;
+ if (ret == NO_ERROR) {
+ int32_t exceptionCode = reply.readExceptionCode();
+ if (!exceptionCode) {
+ // Success is indicated here by a nonzero int followed by the fd;
+ // failure by a zero int with no data following.
+ if (reply.readInt32() != 0) {
+ fd = fcntl(reply.readParcelFileDescriptor(), F_DUPFD_CLOEXEC, 0);
+ }
+ } else {
+ // An exception was thrown back; fall through to return failure
+ ALOGD("openContentUri(%s) caught exception %d\n",
+ String8(stringUri).string(), exceptionCode);
+ }
+ }
+ return fd;
+ }
+};
+
+// ------------------------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
+
+}; // namespace android
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 4800f5b..77e3d23 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -25,7 +25,7 @@
class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
public:
- BpMediaResourceMonitor(const sp<IBinder>& impl)
+ explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
: BpInterface<IMediaResourceMonitor>(impl) {}
virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 6b5b1af..f4e0a60 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -287,7 +287,7 @@
mBase = heap->mBase;
mSize = heap->mSize;
mOffset = heap->mOffset;
- int fd = dup(heap->mHeapId.load(memory_order_relaxed));
+ int fd = fcntl(heap->mHeapId.load(memory_order_relaxed), F_DUPFD_CLOEXEC, 0);
ALOGE_IF(fd==-1, "cannot dup fd=%d",
heap->mHeapId.load(memory_order_relaxed));
mHeapId.store(fd, memory_order_release);
@@ -322,7 +322,7 @@
Mutex::Autolock _l(mLock);
if (mHeapId.load(memory_order_relaxed) == -1) {
- int fd = dup( parcel_fd );
+ int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0);
ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
parcel_fd, size, err, strerror(errno));
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..c793df3
--- /dev/null
+++ b/libs/binder/IShellCallback.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ShellCallback"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IShellCallback.h>
+
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpShellCallback : public BpInterface<IShellCallback>
+{
+public:
+ explicit BpShellCallback(const sp<IBinder>& impl)
+ : BpInterface<IShellCallback>(impl)
+ {
+ }
+
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IShellCallback::getInterfaceDescriptor());
+ data.writeString16(path);
+ data.writeString16(seLinuxContext);
+ remote()->transact(OP_OPEN_OUTPUT_FILE, data, &reply, 0);
+ reply.readExceptionCode();
+ int fd = reply.readParcelFileDescriptor();
+ return fd >= 0 ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd;
+
+ }
+};
+
+IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback");
+
+// ----------------------------------------------------------------------
+
+status_t BnShellCallback::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case OP_OPEN_OUTPUT_FILE: {
+ CHECK_INTERFACE(IShellCallback, data, reply);
+ String16 path(data.readString16());
+ String16 seLinuxContext(data.readString16());
+ int fd = openOutputFile(path, seLinuxContext);
+ if (reply != NULL) {
+ reply->writeNoException();
+ if (fd >= 0) {
+ reply->writeInt32(1);
+ reply->writeParcelFileDescriptor(fd, true);
+ } else {
+ reply->writeInt32(0);
+ }
+ } else if (fd >= 0) {
+ close(fd);
+ }
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index aed0134..03f00be 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -26,9 +26,9 @@
#include <unistd.h>
#include <binder/MemoryHeapBase.h>
-#include <log/log.h>
#include <cutils/ashmem.h>
#include <cutils/atomic.h>
+#include <log/log.h>
namespace android {
@@ -82,7 +82,7 @@
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(dup(fd), size, offset);
+ mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
}
status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index d753eb5..a6ccb53 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -539,7 +539,7 @@
// If this is a file descriptor, we need to dup it so the
// new Parcel now owns its own fd, and can declare that we
// officially know we have fds.
- flat->handle = dup(flat->handle);
+ flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
flat->cookie = 1;
mHasFds = mFdsKnown = true;
if (!mAllowFds) {
@@ -1142,7 +1142,7 @@
status_t Parcel::writeDupFileDescriptor(int fd)
{
- int dupFd = dup(fd);
+ int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (dupFd < 0) {
return -errno;
}
@@ -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;
}
@@ -1966,7 +1972,7 @@
}
for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
- h->data[i] = dup(readFileDescriptor());
+ h->data[i] = fcntl(readFileDescriptor(), F_DUPFD_CLOEXEC, 0);
if (h->data[i] < 0) {
for (int j = 0; j < i; j++) {
close(h->data[j]);
@@ -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();
@@ -2004,7 +2020,7 @@
return BAD_TYPE;
}
- val->reset(dup(got));
+ val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
if (val->get() < 0) {
return BAD_VALUE;
@@ -2078,11 +2094,15 @@
status_t err = NO_ERROR;
for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
- fds[i] = dup(this->readFileDescriptor());
- if (fds[i] < 0) {
+ int fd = this->readFileDescriptor();
+ if (fd < 0 || ((fds[i] = fcntl(fd, F_DUPFD_CLOEXEC, 0)) < 0)) {
err = BAD_VALUE;
- ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
- i, fds[i], fd_count, strerror(errno));
+ ALOGE("fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
+ i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno));
+ // Close all the file descriptors that were dup-ed.
+ for (size_t j=0; j<i ;j++) {
+ close(fds[j]);
+ }
}
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 54e12b6..757291c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -45,6 +45,7 @@
BINDER_LIB_TEST_ADD_SERVER,
BINDER_LIB_TEST_CALL_BACK,
BINDER_LIB_TEST_NOP_CALL_BACK,
+ BINDER_LIB_TEST_GET_SELF_TRANSACTION,
BINDER_LIB_TEST_GET_ID_TRANSACTION,
BINDER_LIB_TEST_INDIRECT_TRANSACTION,
BINDER_LIB_TEST_SET_ERROR_TRANSACTION,
@@ -56,6 +57,7 @@
BINDER_LIB_TEST_EXIT_TRANSACTION,
BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
+ BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
};
pid_t start_server_process(int arg2)
@@ -389,7 +391,7 @@
ret = reply.readInt32(&count);
ASSERT_EQ(NO_ERROR, ret);
- EXPECT_EQ(ARRAY_SIZE(serverId), count);
+ EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
BinderLibTestBundle replyi(&reply);
@@ -439,7 +441,7 @@
ret = reply.readInt32(&count);
ASSERT_EQ(NO_ERROR, ret);
- EXPECT_EQ(ARRAY_SIZE(serverId), count);
+ EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
int32_t counti;
@@ -631,7 +633,7 @@
}
ret = read(pipefd[0], buf, sizeof(buf));
- EXPECT_EQ(sizeof(buf), ret);
+ EXPECT_EQ(sizeof(buf), (size_t)ret);
EXPECT_EQ(write_value, buf[0]);
waitForReadData(pipefd[0], 5000); /* wait for other proccess to close pipe */
@@ -670,6 +672,62 @@
EXPECT_GE(ret, 0);
}
+TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
+ status_t ret;
+ Parcel data, reply;
+
+ ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ const flat_binder_object *fb = reply.readObject(false);
+ ASSERT_TRUE(fb != NULL);
+ EXPECT_EQ(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:
@@ -771,6 +829,9 @@
binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_SELF_TRANSACTION:
+ reply->writeStrongBinder(this);
+ return NO_ERROR;
case BINDER_LIB_TEST_GET_ID_TRANSACTION:
reply->writeInt32(m_id);
return NO_ERROR;
@@ -884,6 +945,16 @@
while (wait(NULL) != -1 || errno != ECHILD)
;
exit(EXIT_SUCCESS);
+ case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: {
+ bool strongRef = data.readBool();
+ sp<IBinder> binder = new BBinder();
+ if (strongRef) {
+ reply->writeStrongBinder(binder);
+ } else {
+ reply->writeWeakBinder(binder);
+ }
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 71b96d4..6e8f7df 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -170,6 +170,8 @@
int num,
int worker_count,
int iterations,
+ int payload_size,
+ bool cs_pair,
Pipe p)
{
// Create BinderWorkerService and for go.
@@ -182,22 +184,32 @@
p.signal();
p.wait();
+ // If client/server pairs, then half the workers are
+ // servers and half are clients
+ int server_count = cs_pair ? worker_count / 2 : worker_count;
+
// Get references to other binder services.
cout << "Created BinderWorker" << num << endl;
(void)worker_count;
vector<sp<IBinder> > workers;
- for (int i = 0; i < worker_count; i++) {
+ for (int i = 0; i < server_count; i++) {
if (num == i)
continue;
workers.push_back(serviceMgr->getService(generateServiceName(i)));
}
- // Run the benchmark.
+ // Run the benchmark if client
ProcResults results;
chrono::time_point<chrono::high_resolution_clock> start, end;
- for (int i = 0; i < iterations; i++) {
- int target = rand() % workers.size();
+ for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) {
Parcel data, reply;
+ int target = cs_pair ? num % server_count : rand() % workers.size();
+ int sz = payload_size;
+
+ while (sz > sizeof(uint32_t)) {
+ data.writeInt32(0);
+ sz -= sizeof(uint32_t);
+ }
start = chrono::high_resolution_clock::now();
status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
end = chrono::high_resolution_clock::now();
@@ -210,6 +222,7 @@
exit(EXIT_FAILURE);
}
}
+
// Signal completion to master and wait.
p.signal();
p.wait();
@@ -221,7 +234,7 @@
exit(EXIT_SUCCESS);
}
-Pipe make_worker(int num, int iterations, int worker_count)
+Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bool cs_pair)
{
auto pipe_pair = Pipe::createPipePair();
pid_t pid = fork();
@@ -230,7 +243,7 @@
return move(get<0>(pipe_pair));
} else {
/* child */
- worker_fx(num, worker_count, iterations, move(get<1>(pipe_pair)));
+ worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair)));
/* never get here */
return move(get<0>(pipe_pair));
}
@@ -255,6 +268,8 @@
{
int workers = 2;
int iterations = 10000;
+ int payload_size = 0;
+ bool cs_pair = false;
(void)argc;
(void)argv;
vector<Pipe> pipes;
@@ -271,11 +286,21 @@
i++;
continue;
}
+ if (string(argv[i]) == "-s") {
+ payload_size = atoi(argv[i+1]);
+ i++;
+ }
+ if (string(argv[i]) == "-p") {
+ // client/server pairs instead of spreading
+ // requests to all workers. If true, half
+ // the workers become clients and half servers
+ cs_pair = true;
+ }
}
// Create all the workers and wait for them to spawn.
for (int i = 0; i < workers; i++) {
- pipes.push_back(make_worker(i, iterations, workers));
+ pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
}
wait_all(pipes);
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index bb6164a..6d4e9ae 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -42,6 +42,9 @@
"-Wno-nested-anon-types",
"-Wno-gnu-anonymous-struct",
+ // We are aware of the risks inherent in comparing floats for equality
+ "-Wno-float-equal",
+
"-DDEBUG_ONLY_CODE=0",
],
@@ -71,6 +74,7 @@
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DisplayEventReceiver.cpp",
+ "FrameTimestamps.cpp",
"GLConsumer.cpp",
"GraphicBufferAlloc.cpp",
"GraphicsEnv.cpp",
@@ -96,17 +100,17 @@
],
shared_libs: [
+ "libsync",
"libbinder",
"libcutils",
"libEGL",
"libGLESv2",
- "libsync",
"libui",
"libutils",
"liblog",
],
- export_shared_lib_headers: ["libbinder"],
+ export_shared_lib_headers: ["libbinder", "libui"],
}
subdirs = ["tests"]
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 1357a4a..69b5962 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;
}
@@ -88,11 +91,11 @@
size_t size = sizeof(uint32_t); // Flags
if (mGraphicBuffer != 0) {
size += mGraphicBuffer->getFlattenedSize();
- FlattenableUtils::align<4>(size);
+ size = FlattenableUtils::align<4>(size);
}
if (mFence != 0) {
size += mFence->getFlattenedSize();
- FlattenableUtils::align<4>(size);
+ size = FlattenableUtils::align<4>(size);
}
size += mSurfaceDamage.getFlattenedSize();
size = FlattenableUtils::align<8>(size);
@@ -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;
}
@@ -198,6 +204,8 @@
status_t err = mFence->unflatten(buffer, size, fds, count);
if (err) return err;
size -= FlattenableUtils::align<4>(buffer);
+
+ mFenceTime = std::make_shared<FenceTime>(mFence);
}
status_t err = mSurfaceDamage.unflatten(buffer, size);
@@ -227,6 +235,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..f76a282 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,18 +61,19 @@
}
}
-bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
- uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ if (listener != nullptr) {
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
- const sp<IGraphicBufferAlloc>& allocator) {
+ 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/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index ee4c58c..d66aa1a 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -205,6 +205,7 @@
// was cached when it was last queued.
outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
outBuffer->mFence = Fence::NO_FENCE;
+ outBuffer->mFenceTime = FenceTime::NO_FENCE;
outBuffer->mCrop = mCore->mSharedBufferCache.crop;
outBuffer->mTransform = mCore->mSharedBufferCache.transform &
~static_cast<uint32_t>(
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 13b900c..39b9a0b 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -42,12 +42,17 @@
namespace android {
-BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
+static constexpr uint32_t BQ_LAYER_COUNT = 1;
+
+BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
+ bool consumerIsSurfaceFlinger) :
mCore(core),
mSlots(core->mSlots),
mConsumerName(),
mStickyTransform(0),
+ mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),
mLastQueueBufferFence(Fence::NO_FENCE),
+ mLastQueuedTransform(0),
mCallbackMutex(),
mNextCallbackTicket(0),
mCurrentCallbackTicket(0),
@@ -343,7 +348,8 @@
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
sp<android::Fence> *outFence, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t usage) {
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
ATRACE_CALL();
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -411,7 +417,8 @@
// buffer. If this buffer would require reallocation to meet the
// requested attributes, we free it and attempt to get another one.
if (!mCore->mAllowAllocation) {
- if (buffer->needsReallocation(width, height, format, usage)) {
+ if (buffer->needsReallocation(width, height, format,
+ BQ_LAYER_COUNT, usage)) {
if (mCore->mSharedBufferSlot == found) {
BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
"buffer");
@@ -427,7 +434,8 @@
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if (mCore->mSharedBufferSlot == found &&
- buffer->needsReallocation(width, height, format, usage)) {
+ buffer->needsReallocation(width, height, format,
+ BQ_LAYER_COUNT, usage)) {
BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
"buffer");
@@ -446,7 +454,8 @@
mSlots[found].mBufferState.dequeue();
if ((buffer == NULL) ||
- buffer->needsReallocation(width, height, format, usage))
+ buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT,
+ usage))
{
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL;
@@ -497,7 +506,7 @@
status_t error;
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
- width, height, format, usage,
+ width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()}, &error));
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -552,6 +561,8 @@
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+ addAndGetFrameTimestamps(nullptr, outTimestamps);
+
return returnFlags;
}
@@ -732,23 +743,27 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- int64_t timestamp;
+ int64_t requestedPresentTimestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
Rect crop(Rect::EMPTY_RECT);
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
- sp<Fence> fence;
- input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
- &transform, &fence, &stickyTransform);
+ sp<Fence> acquireFence;
+ bool getFrameTimestamps = false;
+ input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
+ &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
+ &getFrameTimestamps);
Region surfaceDamage = input.getSurfaceDamage();
- if (fence == NULL) {
+ if (acquireFence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
return BAD_VALUE;
}
+ auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
+
switch (scalingMode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
@@ -763,6 +778,7 @@
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
+ uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -801,8 +817,9 @@
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
" crop=[%d,%d,%d,%d] transform=%#x scale=%s",
- slot, mCore->mFrameCounter + 1, timestamp, dataSpace,
- crop.left, crop.top, crop.right, crop.bottom, transform,
+ slot, mCore->mFrameCounter + 1, requestedPresentTimestamp,
+ dataSpace, crop.left, crop.top, crop.right, crop.bottom,
+ transform,
BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
@@ -820,11 +837,14 @@
dataSpace = mCore->mDefaultBufferDataSpace;
}
- mSlots[slot].mFence = fence;
+ mSlots[slot].mFence = acquireFence;
mSlots[slot].mBufferState.queue();
+ // Increment the frame counter and store a local version of it
+ // for use outside the lock on mCore->mMutex.
++mCore->mFrameCounter;
- mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+ currentFrameNumber = mCore->mFrameCounter;
+ mSlots[slot].mFrameNumber = currentFrameNumber;
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
@@ -834,12 +854,13 @@
item.mTransformToDisplayInverse =
(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
item.mScalingMode = static_cast<uint32_t>(scalingMode);
- item.mTimestamp = timestamp;
+ item.mTimestamp = requestedPresentTimestamp;
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
- item.mFrameNumber = mCore->mFrameCounter;
+ item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
- item.mFence = fence;
+ item.mFence = acquireFence;
+ item.mFenceTime = acquireFenceTime;
item.mIsDroppable = mCore->mAsyncMode ||
mCore->mDequeueBufferCannotBlock ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
@@ -900,10 +921,11 @@
mCore->mDequeueCondition.broadcast();
mCore->mLastQueuedSlot = slot;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
+ output->nextFrameNumber = mCore->mFrameCounter + 1;
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
@@ -915,9 +937,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
@@ -945,10 +972,21 @@
// small trade-off in favor of latency rather than throughput.
mLastQueueBufferFence->waitForever("Throttling EGL Production");
}
- mLastQueueBufferFence = fence;
+ mLastQueueBufferFence = std::move(acquireFence);
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
+ // Update and get FrameEventHistory.
+ nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ NewFrameEventsEntry newFrameEventsEntry = {
+ currentFrameNumber,
+ postedTime,
+ requestedPresentTimestamp,
+ std::move(acquireFenceTime)
+ };
+ addAndGetFrameTimestamps(&newFrameEventsEntry,
+ getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
return NO_ERROR;
}
@@ -1031,6 +1069,10 @@
case NATIVE_WINDOW_FORMAT:
value = static_cast<int32_t>(mCore->mDefaultBufferFormat);
break;
+ case NATIVE_WINDOW_LAYER_COUNT:
+ // All BufferQueue buffers have a single layer.
+ value = BQ_LAYER_COUNT;
+ break;
case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
value = mCore->getMinUndequeuedBufferCountLocked();
break;
@@ -1109,10 +1151,13 @@
case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
mCore->mConnectedApi = api;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ output->numPendingBuffers =
+ static_cast<uint32_t>(mCore->mQueue.size());
+ output->nextFrameNumber = mCore->mFrameCounter + 1;
if (listener != NULL) {
// Set up a death notification so that we can disconnect
@@ -1279,8 +1324,9 @@
for (size_t i = 0; i < newBufferCount; ++i) {
status_t result = NO_ERROR;
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
- allocWidth, allocHeight, allocFormat, allocUsage,
- {mConsumerName.string(), mConsumerName.size()}, &result));
+ allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+ allocUsage, {mConsumerName.string(), mConsumerName.size()},
+ &result));
if (result != NO_ERROR) {
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
" %u, usage %u)", width, height, format, usage);
@@ -1431,20 +1477,27 @@
return NO_ERROR;
}
-bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- ATRACE_CALL();
- BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
- sp<IConsumerListener> listener;
+void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ addAndGetFrameTimestamps(nullptr, outDelta);
+}
+void BufferQueueProducer::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
+ if (newTimestamps == nullptr && outDelta == nullptr) {
+ return;
+ }
+
+ ATRACE_CALL();
+ BQ_LOGV("addAndGetFrameTimestamps");
+ sp<IConsumerListener> listener;
{
Mutex::Autolock lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 3cf3078..be2b1af 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -56,7 +56,8 @@
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
- mConsumer(bufferQueue) {
+ mConsumer(bufferQueue),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
@@ -366,6 +367,7 @@
freeBufferLocked(slot);
}
+ mPrevFinalReleaseFence = mSlots[slot].mFence;
mSlots[slot].mFence = Fence::NO_FENCE;
return err;
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 8393160..a9bafef 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -64,6 +64,7 @@
switch (static_cast<int>(format)) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_BGRA_8888:
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..73537bf
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,690 @@
+/*
+* Copyright 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <gui/FrameTimestamps.h>
+
+#include <cutils/compiler.h> // For CC_[UN]LIKELY
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+#include <numeric>
+
+namespace android {
+
+
+// ============================================================================
+// FrameEvents
+// ============================================================================
+
+bool FrameEvents::hasPostedInfo() const {
+ return Fence::isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+ return Fence::isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+ return Fence::isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+ return Fence::isValidTimestamp(firstRefreshStartTime);
+}
+
+bool FrameEvents::hasLastRefreshStartInfo() const {
+ // The last refresh start time may continue to update until a new frame
+ // is latched. We know we have the final value once the release or retire
+ // info is set. See ConsumerFrameEventHistory::addRetire/Release.
+ return addRetireCalled || addReleaseCalled;
+}
+
+bool FrameEvents::hasDequeueReadyInfo() const {
+ return Fence::isValidTimestamp(dequeueReadyTime);
+}
+
+bool FrameEvents::hasAcquireInfo() const {
+ return acquireFence->isValid();
+}
+
+bool FrameEvents::hasGpuCompositionDoneInfo() const {
+ // We may not get a gpuCompositionDone in addPostComposite if
+ // client/gles compositing isn't needed.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayPresentInfo() const {
+ // We may not get a displayPresent in addPostComposite for HWC1.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayRetireInfo() const {
+ // We may not get a displayRetire in addRetire for HWC2.
+ return addRetireCalled;
+}
+
+bool FrameEvents::hasReleaseInfo() const {
+ return addReleaseCalled;
+}
+
+void FrameEvents::checkFencesForCompletion() {
+ acquireFence->getSignalTime();
+ gpuCompositionDoneFence->getSignalTime();
+ displayPresentFence->getSignalTime();
+ displayRetireFence->getSignalTime();
+ releaseFence->getSignalTime();
+}
+
+static void dumpFenceTime(String8& outString, const char* name,
+ bool pending, const FenceTime& fenceTime) {
+ outString.appendFormat("--- %s", name);
+ nsecs_t signalTime = fenceTime.getCachedSignalTime();
+ if (Fence::isValidTimestamp(signalTime)) {
+ outString.appendFormat("%" PRId64 "\n", signalTime);
+ } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) {
+ outString.appendFormat("Pending\n");
+ } else if (&fenceTime == FenceTime::NO_FENCE.get()){
+ outString.appendFormat("N/A\n");
+ } else {
+ outString.appendFormat("Error\n");
+ }
+}
+
+void FrameEvents::dump(String8& outString) const
+{
+ if (!valid) {
+ return;
+ }
+
+ outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
+ outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime);
+ outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+
+ outString.appendFormat("--- Latched \t");
+ if (Fence::isValidTimestamp(latchTime)) {
+ outString.appendFormat("%" PRId64 "\n", latchTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (First)\t");
+ if (Fence::isValidTimestamp(firstRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (Last)\t");
+ if (Fence::isValidTimestamp(lastRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ dumpFenceTime(outString, "Acquire \t",
+ true, *acquireFence);
+ dumpFenceTime(outString, "GPU Composite Done\t",
+ !addPostCompositeCalled, *gpuCompositionDoneFence);
+ dumpFenceTime(outString, "Display Present \t",
+ !addPostCompositeCalled, *displayPresentFence);
+ dumpFenceTime(outString, "Display Retire \t",
+ !addRetireCalled, *displayRetireFence);
+
+ outString.appendFormat("--- DequeueReady \t");
+ if (Fence::isValidTimestamp(dequeueReadyTime)) {
+ outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ dumpFenceTime(outString, "Release \t",
+ true, *releaseFence);
+}
+
+
+// ============================================================================
+// FrameEventHistory
+// ============================================================================
+
+namespace {
+
+struct FrameNumberEqual {
+ FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
+ bool operator()(const FrameEvents& frame) {
+ return frame.valid && mFrameNumber == frame.frameNumber;
+ }
+ const uint64_t mFrameNumber;
+};
+
+} // namespace
+
+FrameEventHistory::~FrameEventHistory() = default;
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
+ auto frame = std::find_if(
+ mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
+ return frame == mFrames.end() ? nullptr : &(*frame);
+}
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
+ *iHint = std::min(*iHint, mFrames.size());
+ auto hint = mFrames.begin() + *iHint;
+ auto frame = std::find_if(
+ hint, mFrames.end(), FrameNumberEqual(frameNumber));
+ if (frame == mFrames.end()) {
+ frame = std::find_if(
+ mFrames.begin(), hint, FrameNumberEqual(frameNumber));
+ if (frame == hint) {
+ return nullptr;
+ }
+ }
+ *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ return &(*frame);
+}
+
+void FrameEventHistory::checkFencesForCompletion() {
+ for (auto& frame : mFrames) {
+ frame.checkFencesForCompletion();
+ }
+}
+
+// Uses !|valid| as the MSB.
+static bool FrameNumberLessThan(
+ const FrameEvents& lhs, const FrameEvents& rhs) {
+ if (lhs.valid == rhs.valid) {
+ return lhs.frameNumber < rhs.frameNumber;
+ }
+ return lhs.valid;
+}
+
+void FrameEventHistory::dump(String8& outString) const {
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ if (!earliestFrame->valid) {
+ outString.appendFormat("-- N/A\n");
+ return;
+ }
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ frame->dump(outString);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ frame->dump(outString);
+ }
+}
+
+
+// ============================================================================
+// ProducerFrameEventHistory
+// ============================================================================
+
+ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
+
+void ProducerFrameEventHistory::updateAcquireFence(
+ uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
+ FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+ if (frame == nullptr) {
+ ALOGE("ProducerFrameEventHistory::updateAcquireFence: "
+ "Did not find frame.");
+ return;
+ }
+
+ if (acquire->isValid()) {
+ mAcquireTimeline.push(acquire);
+ frame->acquireFence = std::move(acquire);
+ } else {
+ // If there isn't an acquire fence, assume that buffer was
+ // ready for the consumer when posted.
+ frame->acquireFence = std::make_shared<FenceTime>(frame->postedTime);
+ }
+}
+
+void ProducerFrameEventHistory::applyDelta(
+ const FrameEventHistoryDelta& delta) {
+ for (auto& d : delta.mDeltas) {
+ // Avoid out-of-bounds access.
+ if (d.mIndex >= mFrames.size()) {
+ ALOGE("ProducerFrameEventHistory::applyDelta: Bad index.");
+ return;
+ }
+
+ FrameEvents& frame = mFrames[d.mIndex];
+
+ frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+ frame.addRetireCalled = d.mAddRetireCalled != 0;
+ frame.addReleaseCalled = d.mAddReleaseCalled != 0;
+
+ frame.postedTime = d.mPostedTime;
+ frame.requestedPresentTime = d.mRequestedPresentTime;
+ frame.latchTime = d.mLatchTime;
+ frame.firstRefreshStartTime = d.mFirstRefreshStartTime;
+ frame.lastRefreshStartTime = d.mLastRefreshStartTime;
+ frame.dequeueReadyTime = d.mDequeueReadyTime;
+
+ if (frame.frameNumber != d.mFrameNumber) {
+ // We got a new frame. Initialize some of the fields.
+ frame.frameNumber = d.mFrameNumber;
+ frame.acquireFence = FenceTime::NO_FENCE;
+ frame.gpuCompositionDoneFence = FenceTime::NO_FENCE;
+ frame.displayPresentFence = FenceTime::NO_FENCE;
+ frame.displayRetireFence = FenceTime::NO_FENCE;
+ frame.releaseFence = FenceTime::NO_FENCE;
+ // The consumer only sends valid frames.
+ frame.valid = true;
+ }
+
+ applyFenceDelta(&mGpuCompositionDoneTimeline,
+ &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
+ applyFenceDelta(&mPresentTimeline,
+ &frame.displayPresentFence, d.mDisplayPresentFence);
+ applyFenceDelta(&mRetireTimeline,
+ &frame.displayRetireFence, d.mDisplayRetireFence);
+ applyFenceDelta(&mReleaseTimeline,
+ &frame.releaseFence, d.mReleaseFence);
+ }
+}
+
+void ProducerFrameEventHistory::updateSignalTimes() {
+ mAcquireTimeline.updateSignalTimes();
+ mGpuCompositionDoneTimeline.updateSignalTimes();
+ mPresentTimeline.updateSignalTimes();
+ mRetireTimeline.updateSignalTimes();
+ mReleaseTimeline.updateSignalTimes();
+}
+
+void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline,
+ std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const {
+ if (CC_UNLIKELY(dst == nullptr)) {
+ ALOGE("applyFenceDelta: dst is null.");
+ return;
+ }
+
+ switch (src.state) {
+ case FenceTime::Snapshot::State::EMPTY:
+ return;
+ case FenceTime::Snapshot::State::FENCE:
+ if (CC_UNLIKELY((*dst)->isValid())) {
+ ALOGE("applyFenceDelta: Unexpected fence.");
+ }
+ *dst = createFenceTime(src.fence);
+ timeline->push(*dst);
+ return;
+ case FenceTime::Snapshot::State::SIGNAL_TIME:
+ if ((*dst)->isValid()) {
+ (*dst)->applyTrustedSnapshot(src);
+ } else {
+ *dst = std::make_shared<FenceTime>(src.signalTime);
+ }
+ return;
+ }
+}
+
+std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime(
+ const sp<Fence>& fence) const {
+ return std::make_shared<FenceTime>(fence);
+}
+
+
+// ============================================================================
+// ConsumerFrameEventHistory
+// ============================================================================
+
+ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
+
+void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
+ // Overwrite all fields of the frame with default values unless set here.
+ FrameEvents newTimestamps;
+ newTimestamps.frameNumber = newEntry.frameNumber;
+ newTimestamps.postedTime = newEntry.postedTime;
+ newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
+ newTimestamps.acquireFence = newEntry.acquireFence;
+ newTimestamps.valid = true;
+ mFrames[mQueueOffset] = newTimestamps;
+
+ // Note: We avoid sending the acquire fence back to the caller since
+ // they have the original one already, so there is no need to set the
+ // acquire dirty bit.
+ mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
+
+ mQueueOffset = (mQueueOffset + 1) % mFrames.size();
+}
+
+void ConsumerFrameEventHistory::addLatch(
+ uint64_t frameNumber, nsecs_t latchTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame.");
+ return;
+ }
+ frame->latchTime = latchTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
+}
+
+void ConsumerFrameEventHistory::addPreComposition(
+ uint64_t frameNumber, nsecs_t refreshStartTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents,
+ "addPreComposition: Did not find frame.");
+ return;
+ }
+ frame->lastRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+ if (!Fence::isValidTimestamp(frame->firstRefreshStartTime)) {
+ frame->firstRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+ }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& gpuCompositionDone,
+ const std::shared_ptr<FenceTime>& displayPresent) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents,
+ "addPostComposition: Did not find frame.");
+ return;
+ }
+ // Only get GPU and present info for the first composite.
+ if (!frame->addPostCompositeCalled) {
+ frame->addPostCompositeCalled = true;
+ frame->gpuCompositionDoneFence = gpuCompositionDone;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GL_COMPOSITION_DONE>();
+ if (!frame->displayPresentFence->isValid()) {
+ frame->displayPresentFence = displayPresent;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+ }
+ }
+}
+
+void ConsumerFrameEventHistory::addRetire(
+ uint64_t frameNumber, const std::shared_ptr<FenceTime>& displayRetire) {
+ FrameEvents* frame = getFrame(frameNumber, &mRetireOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents, "addRetire: Did not find frame.");
+ return;
+ }
+ frame->addRetireCalled = true;
+ frame->displayRetireFence = displayRetire;
+ mFramesDirty[mRetireOffset].setDirty<FrameEvent::DISPLAY_RETIRE>();
+}
+
+void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber,
+ nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& release) {
+ FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addRelease: Did not find frame.");
+ return;
+ }
+ frame->addReleaseCalled = true;
+ frame->dequeueReadyTime = dequeueReadyTime;
+ frame->releaseFence = std::move(release);
+ mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
+}
+
+void ConsumerFrameEventHistory::getFrameDelta(
+ FrameEventHistoryDelta* delta,
+ const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) {
+ mProducerWantsEvents = true;
+ size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ if (mFramesDirty[i].anyDirty()) {
+ delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]);
+ mFramesDirty[i].reset();
+ }
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+ FrameEventHistoryDelta* delta) {
+ // Write these in order of frame number so that it is easy to
+ // add them to a FenceTimeline in the proper order producer side.
+ delta->mDeltas.reserve(mFramesDirty.size());
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ getFrameDelta(delta, frame);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ getFrameDelta(delta, frame);
+ }
+}
+
+
+// ============================================================================
+// FrameEventsDelta
+// ============================================================================
+
+FrameEventsDelta::FrameEventsDelta(
+ size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields)
+ : mIndex(index),
+ mFrameNumber(frameTimestamps.frameNumber),
+ mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
+ mAddRetireCalled(frameTimestamps.addRetireCalled),
+ mAddReleaseCalled(frameTimestamps.addReleaseCalled),
+ mPostedTime(frameTimestamps.postedTime),
+ mRequestedPresentTime(frameTimestamps.requestedPresentTime),
+ mLatchTime(frameTimestamps.latchTime),
+ mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
+ mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime),
+ mDequeueReadyTime(frameTimestamps.dequeueReadyTime) {
+ if (dirtyFields.isDirty<FrameEvent::GL_COMPOSITION_DONE>()) {
+ mGpuCompositionDoneFence =
+ frameTimestamps.gpuCompositionDoneFence->getSnapshot();
+ }
+ if (dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>()) {
+ mDisplayPresentFence =
+ frameTimestamps.displayPresentFence->getSnapshot();
+ }
+ if (dirtyFields.isDirty<FrameEvent::DISPLAY_RETIRE>()) {
+ mDisplayRetireFence = frameTimestamps.displayRetireFence->getSnapshot();
+ }
+ if (dirtyFields.isDirty<FrameEvent::RELEASE>()) {
+ mReleaseFence = frameTimestamps.releaseFence->getSnapshot();
+ }
+}
+
+size_t FrameEventsDelta::minFlattenedSize() {
+ constexpr size_t min =
+ sizeof(FrameEventsDelta::mFrameNumber) +
+ sizeof(uint8_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddRetireCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(FrameEventsDelta::mPostedTime) +
+ sizeof(FrameEventsDelta::mRequestedPresentTime) +
+ sizeof(FrameEventsDelta::mLatchTime) +
+ sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
+ sizeof(FrameEventsDelta::mLastRefreshStartTime) +
+ sizeof(FrameEventsDelta::mDequeueReadyTime);
+ return min;
+}
+
+// Flattenable implementation
+size_t FrameEventsDelta::getFlattenedSize() const {
+ auto fences = allFences(this);
+ return minFlattenedSize() +
+ std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const FenceTime::Snapshot* fence) {
+ return a + fence->getFlattenedSize();
+ });
+}
+
+size_t FrameEventsDelta::getFdCount() const {
+ auto fences = allFences(this);
+ return std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const FenceTime::Snapshot* fence) {
+ return a + fence->getFdCount();
+ });
+}
+
+status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (size < getFlattenedSize() || count < getFdCount()) {
+ return NO_MEMORY;
+ }
+
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
+ mIndex > std::numeric_limits<uint8_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+
+ // These are static_cast to uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint8_t>(mIndex));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddRetireCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, mPostedTime);
+ FlattenableUtils::write(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::write(buffer, size, mLatchTime);
+ FlattenableUtils::write(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::write(buffer, size, mLastRefreshStartTime);
+ FlattenableUtils::write(buffer, size, mDequeueReadyTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ status_t status = fence->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+
+ // These were written as uint8_t for alignment.
+ uint8_t temp = 0;
+ FlattenableUtils::read(buffer, size, temp);
+ mIndex = temp;
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ FlattenableUtils::read(buffer, size, temp);
+ mAddPostCompositeCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ mAddRetireCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ mAddReleaseCalled = static_cast<bool>(temp);
+
+ FlattenableUtils::read(buffer, size, mPostedTime);
+ FlattenableUtils::read(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::read(buffer, size, mLatchTime);
+ FlattenableUtils::read(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::read(buffer, size, mLastRefreshStartTime);
+ FlattenableUtils::read(buffer, size, mDequeueReadyTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ status_t status = fence->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+// ============================================================================
+// FrameEventHistoryDelta
+// ============================================================================
+
+FrameEventHistoryDelta& FrameEventHistoryDelta::operator=(
+ FrameEventHistoryDelta&& src) {
+ if (CC_UNLIKELY(!mDeltas.empty())) {
+ ALOGE("FrameEventHistoryDelta: Clobbering history.");
+ }
+ mDeltas = std::move(src.mDeltas);
+ ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty.");
+ return *this;
+}
+
+size_t FrameEventHistoryDelta::minFlattenedSize() {
+ return sizeof(uint32_t);
+}
+
+size_t FrameEventHistoryDelta::getFlattenedSize() const {
+ return minFlattenedSize() +
+ std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFlattenedSize();
+ });
+}
+
+size_t FrameEventHistoryDelta::getFdCount() const {
+ return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFdCount();
+ });
+}
+
+status_t FrameEventHistoryDelta::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint32_t>(mDeltas.size()));
+ for (auto& d : mDeltas) {
+ status_t status = d.flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventHistoryDelta::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ uint32_t deltaCount = 0;
+ FlattenableUtils::read(buffer, size, deltaCount);
+ if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ mDeltas.resize(deltaCount);
+ for (auto& d : mDeltas) {
+ status_t status = d.unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+} // namespace android
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 10e999c..fea9df7 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -322,6 +322,7 @@
mCurrentTransform = 0;
mCurrentTimestamp = 0;
mCurrentFence = Fence::NO_FENCE;
+ mCurrentFenceTime = FenceTime::NO_FENCE;
if (mAttached) {
// This binds a dummy buffer (mReleasedTexImage).
@@ -488,6 +489,7 @@
mCurrentScalingMode = item.mScalingMode;
mCurrentTimestamp = item.mTimestamp;
mCurrentFence = item.mFence;
+ mCurrentFenceTime = item.mFenceTime;
mCurrentFrameNumber = item.mFrameNumber;
computeCurrentTransformMatrixLocked();
@@ -856,6 +858,7 @@
switch (buf->getPixelFormat()) {
case PIXEL_FORMAT_RGBA_8888:
case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
case PIXEL_FORMAT_RGB_888:
case PIXEL_FORMAT_RGB_565:
case PIXEL_FORMAT_BGRA_8888:
@@ -981,6 +984,11 @@
return mCurrentFence;
}
+std::shared_ptr<FenceTime> GLConsumer::getCurrentFenceTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFenceTime;
+}
+
status_t GLConsumer::doGLFenceWait() const {
Mutex::Autolock lock(mMutex);
return doGLFenceWaitLocked();
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
index a8f40e0..30f5e53 100644
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ b/libs/gui/GraphicBufferAlloc.cpp
@@ -32,19 +32,21 @@
}
sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) {
+ uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint32_t usage, std::string requestorName, status_t* error) {
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
- width, height, format, usage, std::move(requestorName)));
+ width, height, format, layerCount, usage,
+ std::move(requestorName)));
status_t err = graphicBuffer->initCheck();
*error = err;
if (err != 0 || graphicBuffer->handle == 0) {
if (err == NO_MEMORY) {
GraphicBuffer::dumpAllocationsToSystemLog();
}
- ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
+ ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%u, h=%u, lc=%u) "
"failed (%s), handle=%p",
- width, height, strerror(-err), graphicBuffer->handle);
+ width, height, layerCount, strerror(-err),
+ graphicBuffer->handle);
return 0;
}
return graphicBuffer;
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index ff7b83a..3d893b1 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -61,42 +61,6 @@
data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
-
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IConsumerListener::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
- }
- result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
- }
- bool found = false;
- result = reply.readBool(&found);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
- }
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
- }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -125,30 +89,6 @@
CHECK_INTERFACE(IConsumerListener, data, reply);
onSidebandStreamChanged();
return NO_ERROR; }
- case GET_FRAME_TIMESTAMPS: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
- return result;
- }
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, ×tamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
- return result;
- }
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
- return NO_ERROR;
- }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
index 2fb380c..a3d3b74 100644
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ b/libs/gui/IGraphicBufferAlloc.cpp
@@ -45,13 +45,14 @@
virtual ~BpGraphicBufferAlloc();
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) {
+ uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint32_t usage, std::string requestorName, status_t* error) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
data.writeUint32(width);
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
+ data.writeUint32(layerCount);
data.writeUint32(usage);
if (requestorName.empty()) {
requestorName += "[PID ";
@@ -106,12 +107,13 @@
uint32_t width = data.readUint32();
uint32_t height = data.readUint32();
PixelFormat format = static_cast<PixelFormat>(data.readInt32());
+ uint32_t layerCount = data.readUint32();
uint32_t usage = data.readUint32();
status_t error = NO_ERROR;
std::string requestorName;
data.readUtf8FromUtf16(&requestorName);
sp<GraphicBuffer> result = createGraphicBuffer(width, height,
- format, usage, requestorName, &error);
+ format, layerCount, usage, requestorName, &error);
reply->writeInt32(error);
if (result != 0) {
reply->write(*result);
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 2401464..ef770e8 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -19,6 +19,7 @@
#include <utils/Errors.h>
#include <utils/NativeHandle.h>
+#include <utils/String8.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 846c205..18a80e8 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -20,6 +20,7 @@
#include <utils/Errors.h>
#include <utils/NativeHandle.h>
#include <utils/RefBase.h>
+#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
@@ -118,24 +119,35 @@
}
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage) {
+ uint32_t height, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
Parcel data, reply;
+ bool getFrameTimestamps = (outTimestamps != nullptr);
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeUint32(width);
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
data.writeUint32(usage);
+ data.writeBool(getFrameTimestamps);
+
status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
+
*buf = reply.readInt32();
- bool nonNull = reply.readInt32();
- if (nonNull) {
- *fence = new Fence();
- result = reply.read(**fence);
+ *fence = new Fence();
+ result = reply.read(**fence);
+ if (result != NO_ERROR) {
+ fence->clear();
+ return result;
+ }
+ if (getFrameTimestamps) {
+ result = reply.read(*outTimestamps);
if (result != NO_ERROR) {
- fence->clear();
+ ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d",
+ result);
return result;
}
}
@@ -211,14 +223,21 @@
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
data.write(input);
+
status_t result = remote()->transact(QUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+
+ result = reply.read(*output);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
result = reply.readInt32();
return result;
}
@@ -265,7 +284,7 @@
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+ reply.read(*output);
result = reply.readInt32();
return result;
}
@@ -422,40 +441,24 @@
return result;
}
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(
IGraphicBufferProducer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to write token: %d", result);
+ return;
}
result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to transact: %d", result);
+ return;
}
- bool found = false;
- result = reply.readBool(&found);
+ result = reply.read(*outDelta);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to read timestamps: %d",
+ result);
}
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
}
virtual status_t getUniqueId(uint64_t* outId) const {
@@ -522,14 +525,18 @@
uint32_t height = data.readUint32();
PixelFormat format = static_cast<PixelFormat>(data.readInt32());
uint32_t usage = data.readUint32();
+ bool getTimestamps = data.readBool();
+
int buf = 0;
- sp<Fence> fence;
+ sp<Fence> fence = Fence::NO_FENCE;
+ FrameEventHistoryDelta frameTimestamps;
int result = dequeueBuffer(&buf, &fence, width, height, format,
- usage);
+ usage, getTimestamps ? &frameTimestamps : nullptr);
+
reply->writeInt32(buf);
- reply->writeInt32(fence != NULL);
- if (fence != NULL) {
- reply->write(*fence);
+ reply->write(*fence);
+ if (getTimestamps) {
+ reply->write(frameTimestamps);
}
reply->writeInt32(result);
return NO_ERROR;
@@ -573,14 +580,14 @@
}
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+
int buf = data.readInt32();
QueueBufferInput input(data);
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t result = queueBuffer(buf, input, output);
+ QueueBufferOutput output;
+ status_t result = queueBuffer(buf, input, &output);
+ reply->write(output);
reply->writeInt32(result);
+
return NO_ERROR;
}
case CANCEL_BUFFER: {
@@ -611,11 +618,9 @@
}
int api = data.readInt32();
bool producerControlledByApp = data.readInt32();
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t res = connect(listener, api, producerControlledByApp, output);
+ QueueBufferOutput output;
+ status_t res = connect(listener, api, producerControlledByApp, &output);
+ reply->write(output);
reply->writeInt32(res);
return NO_ERROR;
}
@@ -718,26 +723,14 @@
}
case GET_FRAME_TIMESTAMPS: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
+ FrameEventHistoryDelta frameTimestamps;
+ getFrameTimestamps(&frameTimestamps);
+ status_t result = reply->write(frameTimestamps);
if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
+ ALOGE("BnGBP::GET_FRAME_TIMESTAMPS failed to write buffer: %d",
+ result);
return result;
}
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, ×tamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
- return result;
- }
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
return NO_ERROR;
}
case GET_UNIQUE_ID: {
@@ -764,16 +757,21 @@
parcel.read(*this);
}
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps);
+}
+
size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform)
- + fence->getFlattenedSize()
- + surfaceDamage.getFlattenedSize();
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize();
}
size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -786,6 +784,7 @@
if (size < getFlattenedSize()) {
return NO_MEMORY;
}
+
FlattenableUtils::write(buffer, size, timestamp);
FlattenableUtils::write(buffer, size, isAutoTimestamp);
FlattenableUtils::write(buffer, size, dataSpace);
@@ -793,6 +792,8 @@
FlattenableUtils::write(buffer, size, scalingMode);
FlattenableUtils::write(buffer, size, transform);
FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
status_t result = fence->flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
return result;
@@ -803,16 +804,7 @@
status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count)
{
- size_t minNeeded =
- sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform);
-
- if (size < minNeeded) {
+ if (size < minFlattenedSize()) {
return NO_MEMORY;
}
@@ -823,6 +815,7 @@
FlattenableUtils::read(buffer, size, scalingMode);
FlattenableUtils::read(buffer, size, transform);
FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
fence = new Fence();
status_t result = fence->unflatten(buffer, size, fds, count);
@@ -832,4 +825,53 @@
return surfaceDamage.unflatten(buffer, size);
}
+// ----------------------------------------------------------------------------
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) +
+ sizeof(height) +
+ sizeof(transformHint) +
+ sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber);
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+
+ return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+
+ return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
}; // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a8e6a5..fa2f59a 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -158,6 +158,50 @@
return result != 0;
}
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ if (!outSupported) {
+ return UNEXPECTED_NULL;
+ }
+ outSupported->clear();
+
+ Parcel data, reply;
+
+ status_t err = data.writeInterfaceToken(
+ ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = remote()->transact(
+ BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS,
+ data, &reply);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ int32_t result = 0;
+ err = reply.readInt32(&result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ err = reply.readInt32Vector(&supported);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ outSupported->reserve(supported.size());
+ for (int32_t s : supported) {
+ outSupported->push_back(static_cast<FrameEvent>(s));
+ }
+ return NO_ERROR;
+ }
+
virtual sp<IDisplayEventConnection> createDisplayEventConnection()
{
Parcel data, reply;
@@ -383,6 +427,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
@@ -478,6 +564,25 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_SUPPORTED_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ std::vector<FrameEvent> supportedTimestamps;
+ status_t result = getSupportedFrameTimestamps(&supportedTimestamps);
+ status_t err = reply->writeInt32(result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ supported.reserve(supportedTimestamps.size());
+ for (FrameEvent s : supportedTimestamps) {
+ supported.push_back(static_cast<int32_t>(s));
+ }
+ return reply->writeInt32Vector(supported);
+ }
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IDisplayEventConnection> connection(createDisplayEventConnection());
@@ -635,6 +740,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/Sensor.cpp b/libs/gui/Sensor.cpp
index 7510429..a69de5e 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -243,6 +243,14 @@
mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
break;
+
+ // TODO: Placeholder for LLOB sensor type
+
+
+ case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
+ mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
+ mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+ break;
default:
// Only pipe the stringType, requiredPermission and flags for custom sensors.
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d1a9cbb..c859828 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -49,7 +49,11 @@
mAutoRefresh(false),
mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
mSharedBufferHasBeenQueued(false),
- mNextFrameNumber(1)
+ mQueriedSupportedTimestamps(false),
+ mFrameTimestampsSupportsPresent(false),
+ mFrameTimestampsSupportsRetire(false),
+ mEnableFrameTimestamps(false),
+ mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>())
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -93,6 +97,10 @@
}
}
+sp<ISurfaceComposer> Surface::composerService() const {
+ return ComposerService::getComposerService();
+}
+
sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
return mGraphicBufferProducer;
}
@@ -135,37 +143,123 @@
outTransformMatrix);
}
-bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
- nsecs_t* outReleaseTime) {
+void Surface::enableFrameTimestamps(bool enable) {
+ Mutex::Autolock lock(mMutex);
+ mEnableFrameTimestamps = enable;
+}
+
+static bool checkConsumerForUpdates(
+ const FrameEvents* e, const uint64_t lastFrameNumber,
+ const nsecs_t* outLatchTime,
+ const nsecs_t* outFirstRefreshStartTime,
+ const nsecs_t* outLastRefreshStartTime,
+ const nsecs_t* outGlCompositionDoneTime,
+ const nsecs_t* outDisplayPresentTime,
+ const nsecs_t* outDisplayRetireTime,
+ const nsecs_t* outDequeueReadyTime,
+ const nsecs_t* outReleaseTime) {
+ bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo();
+ bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) &&
+ !e->hasFirstRefreshStartInfo();
+ bool checkForGlCompositionDone = (outGlCompositionDoneTime != nullptr) &&
+ !e->hasGpuCompositionDoneInfo();
+ bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) &&
+ !e->hasDisplayPresentInfo();
+
+ // LastRefreshStart, DisplayRetire, DequeueReady, and Release are never
+ // available for the last frame.
+ bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) &&
+ !e->hasLastRefreshStartInfo() &&
+ (e->frameNumber != lastFrameNumber);
+ bool checkForDisplayRetire = (outDisplayRetireTime != nullptr) &&
+ !e->hasDisplayRetireInfo() && (e->frameNumber != lastFrameNumber);
+ bool checkForDequeueReady = (outDequeueReadyTime != nullptr) &&
+ !e->hasDequeueReadyInfo() && (e->frameNumber != lastFrameNumber);
+ bool checkForRelease = (outReleaseTime != nullptr) &&
+ !e->hasReleaseInfo() && (e->frameNumber != lastFrameNumber);
+
+ // RequestedPresent and Acquire info are always available producer-side.
+ return checkForLatch || checkForFirstRefreshStart ||
+ checkForLastRefreshStart || checkForGlCompositionDone ||
+ checkForDisplayPresent || checkForDisplayRetire ||
+ checkForDequeueReady || checkForRelease;
+}
+
+static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) {
+ if (dst != nullptr) {
+ *dst = Fence::isValidTimestamp(src) ? src : 0;
+ }
+}
+
+static void getFrameTimestampFence(nsecs_t *dst, const std::shared_ptr<FenceTime>& src) {
+ if (dst != nullptr) {
+ nsecs_t signalTime = src->getSignalTime();
+ *dst = Fence::isValidTimestamp(signalTime) ? signalTime : 0;
+ }
+}
+
+status_t Surface::getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+ nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime) {
ATRACE_CALL();
- FrameTimestamps timestamps;
- bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
- ×tamps);
- if (found) {
- if (outPostedTime) {
- *outPostedTime = timestamps.postedTime;
- }
- if (outAcquireTime) {
- *outAcquireTime = timestamps.acquireTime;
- }
- if (outRefreshStartTime) {
- *outRefreshStartTime = timestamps.refreshStartTime;
- }
- if (outGlCompositionDoneTime) {
- *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
- }
- if (outDisplayRetireTime) {
- *outDisplayRetireTime = timestamps.displayRetireTime;
- }
- if (outReleaseTime) {
- *outReleaseTime = timestamps.releaseTime;
- }
- return true;
+ Mutex::Autolock lock(mMutex);
+
+ if (!mEnableFrameTimestamps) {
+ return INVALID_OPERATION;
}
- return false;
+
+ // Verify the requested timestamps are supported.
+ querySupportedTimestampsLocked();
+ if (outDisplayPresentTime != nullptr && !mFrameTimestampsSupportsPresent) {
+ return BAD_VALUE;
+ }
+ if (outDisplayRetireTime != nullptr && !mFrameTimestampsSupportsRetire) {
+ return BAD_VALUE;
+ }
+
+ FrameEvents* events = mFrameEventHistory->getFrame(frameNumber);
+ if (events == nullptr) {
+ // If the entry isn't available in the producer, it's definitely not
+ // available in the consumer.
+ return NAME_NOT_FOUND;
+ }
+
+ // Update our cache of events if the requested events are not available.
+ if (checkConsumerForUpdates(events, mLastFrameNumber,
+ outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGlCompositionDoneTime, outDisplayPresentTime,
+ outDisplayRetireTime, outDequeueReadyTime, outReleaseTime)) {
+ FrameEventHistoryDelta delta;
+ mGraphicBufferProducer->getFrameTimestamps(&delta);
+ mFrameEventHistory->applyDelta(delta);
+ events = mFrameEventHistory->getFrame(frameNumber);
+ }
+
+ if (events == nullptr) {
+ // The entry was available before the update, but was overwritten
+ // after the update. Make sure not to send the wrong frame's data.
+ return NAME_NOT_FOUND;
+ }
+
+ getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime);
+ getFrameTimestamp(outLatchTime, events->latchTime);
+ getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime);
+ getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime);
+ getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime);
+
+ getFrameTimestampFence(outAcquireTime, events->acquireFence);
+ getFrameTimestampFence(
+ outGlCompositionDoneTime, events->gpuCompositionDoneFence);
+ getFrameTimestampFence(
+ outDisplayPresentTime, events->displayPresentFence);
+ getFrameTimestampFence(outDisplayRetireTime, events->displayRetireFence);
+ getFrameTimestampFence(outReleaseTime, events->releaseFence);
+
+ return NO_ERROR;
}
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,6 +365,7 @@
uint32_t reqHeight;
PixelFormat reqFormat;
uint32_t reqUsage;
+ bool enableFrameTimestamps;
{
Mutex::Autolock lock(mMutex);
@@ -281,6 +376,8 @@
reqFormat = mReqFormat;
reqUsage = mReqUsage;
+ enableFrameTimestamps = mEnableFrameTimestamps;
+
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -295,8 +392,11 @@
int buf = -1;
sp<Fence> fence;
nsecs_t now = systemTime();
+
+ FrameEventHistoryDelta frameTimestamps;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
- reqWidth, reqHeight, reqFormat, reqUsage);
+ reqWidth, reqHeight, reqFormat, reqUsage,
+ enableFrameTimestamps ? &frameTimestamps : nullptr);
mLastDequeueDuration = systemTime() - now;
if (result < 0) {
@@ -317,6 +417,10 @@
freeAllBuffers();
}
+ if (enableFrameTimestamps) {
+ mFrameEventHistory->applyDelta(frameTimestamps);
+ }
+
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
@@ -435,7 +539,7 @@
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
- fence, mStickyTransform);
+ fence, mStickyTransform, mEnableFrameTimestamps);
if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
input.setSurfaceDamage(Region::INVALID_REGION);
@@ -507,17 +611,31 @@
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ if (mEnableFrameTimestamps) {
+ mFrameEventHistory->applyDelta(output.frameTimestamps);
+ // Update timestamps with the local acquire fence.
+ // The consumer doesn't send it back to prevent us from having two
+ // file descriptors of the same fence.
+ mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
+ std::make_shared<FenceTime>(std::move(fence)));
+
+ // Cache timestamps of signaled fences so we can close their file
+ // descriptors.
+ mFrameEventHistory->updateSignalTimes();
+ }
+
+ mLastFrameNumber = mNextFrameNumber;
+
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
if (!mConnectedToCpu) {
// Clear surface damage back to full-buffer
@@ -533,6 +651,31 @@
return err;
}
+void Surface::querySupportedTimestampsLocked() const {
+ // mMutex must be locked when calling this method.
+
+ if (mQueriedSupportedTimestamps) {
+ return;
+ }
+ mQueriedSupportedTimestamps = true;
+
+ std::vector<FrameEvent> supportedFrameTimestamps;
+ status_t err = composerService()->getSupportedFrameTimestamps(
+ &supportedFrameTimestamps);
+
+ if (err != NO_ERROR) {
+ return;
+ }
+
+ for (auto sft : supportedFrameTimestamps) {
+ if (sft == FrameEvent::DISPLAY_PRESENT) {
+ mFrameTimestampsSupportsPresent = true;
+ } else if (sft == FrameEvent::DISPLAY_RETIRE) {
+ mFrameTimestampsSupportsRetire = true;
+ }
+ }
+}
+
int Surface::query(int what, int* value) const {
ATRACE_CALL();
ALOGV("Surface::query");
@@ -546,9 +689,8 @@
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
- sp<ISurfaceComposer> composer(
- ComposerService::getComposerService());
- if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) {
+ if (composerService()->authenticateSurfaceTexture(
+ mGraphicBufferProducer)) {
*value = 1;
} else {
*value = 0;
@@ -595,6 +737,16 @@
static_cast<int>(durationUs);
return NO_ERROR;
}
+ case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT: {
+ querySupportedTimestampsLocked();
+ *value = mFrameTimestampsSupportsPresent ? 1 : 0;
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE: {
+ querySupportedTimestampsLocked();
+ *value = mFrameTimestampsSupportsRetire ? 1 : 0;
+ return NO_ERROR;
+ }
}
}
return mGraphicBufferProducer->query(what, value);
@@ -670,6 +822,9 @@
case NATIVE_WINDOW_SET_AUTO_REFRESH:
res = dispatchSetAutoRefresh(args);
break;
+ case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
+ res = dispatchEnableFrameTimestamps(args);
+ break;
case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
res = dispatchGetFrameTimestamps(args);
break;
@@ -793,18 +948,29 @@
return setAutoRefresh(autoRefresh);
}
+int Surface::dispatchEnableFrameTimestamps(va_list args) {
+ bool enable = va_arg(args, int);
+ enableFrameTimestamps(enable);
+ return NO_ERROR;
+}
+
int Surface::dispatchGetFrameTimestamps(va_list args) {
uint32_t framesAgo = va_arg(args, uint32_t);
- nsecs_t* outPostedTime = va_arg(args, int64_t*);
+ nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*);
nsecs_t* outAcquireTime = va_arg(args, int64_t*);
- nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outLatchTime = va_arg(args, int64_t*);
+ nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*);
nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
+ nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
+ nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*);
nsecs_t* outReleaseTime = va_arg(args, int64_t*);
- bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
- outPostedTime, outAcquireTime, outRefreshStartTime,
- outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
- return ret ? NO_ERROR : BAD_VALUE;
+ return getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
+ outRequestedPresentTime, outAcquireTime, outLatchTime,
+ outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGlCompositionDoneTime, outDisplayPresentTime,
+ outDisplayRetireTime, outDequeueReadyTime, outReleaseTime);
}
int Surface::connect(int api) {
@@ -819,17 +985,16 @@
IGraphicBufferProducer::QueueBufferOutput output;
int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
@@ -1396,14 +1561,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..58b2a87 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -33,6 +33,7 @@
#include <ui/DisplayInfo.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
@@ -129,6 +130,8 @@
void openGlobalTransactionImpl();
void closeGlobalTransactionImpl(bool synchronous);
void setAnimationTransactionImpl();
+ status_t enableVSyncInjectionsImpl(bool enable);
+ status_t injectVSyncImpl(nsecs_t when);
layer_state_t* getLayerStateLocked(
const sp<SurfaceComposerClient>& client, const sp<IBinder>& id);
@@ -190,6 +193,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 +264,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 +673,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) {
@@ -831,6 +860,33 @@
reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
}
+status_t ScreenshotClient::captureToBuffer(const sp<IBinder>& display,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ, bool useIdentityTransform,
+ uint32_t rotation,
+ sp<GraphicBuffer>* outBuffer) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+
+ sp<IGraphicBufferConsumer> gbpConsumer;
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &gbpConsumer);
+ sp<BufferItemConsumer> consumer(new BufferItemConsumer(gbpConsumer,
+ GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER,
+ 1, true));
+
+ status_t ret = s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight,
+ minLayerZ, maxLayerZ, useIdentityTransform,
+ static_cast<ISurfaceComposer::Rotation>(rotation));
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ BufferItem b;
+ consumer->acquireBuffer(&b, 0, true);
+ *outBuffer = b.mGraphicBuffer;
+ return ret;
+}
+
ScreenshotClient::ScreenshotClient()
: mHaveBuffer(false) {
memset(&mBuffer, 0, sizeof(mBuffer));
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3c7958f..092d597 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -35,7 +35,6 @@
"libbinder",
"libcutils",
"libgui",
- "libsync",
"libui",
"libutils",
],
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 65df7dc..98c0449 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -139,7 +139,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -183,7 +183,7 @@
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -191,7 +191,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
@@ -234,7 +234,7 @@
for (int i = 0; i < 3; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -270,7 +270,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -280,7 +280,7 @@
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -330,7 +330,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -379,7 +379,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0, false,
HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
@@ -415,7 +415,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataOut;
@@ -438,7 +438,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -487,13 +487,13 @@
// This should return an error since it would require an allocation
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0,
- 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// This should succeed, now that we've lifted the prohibition
ASSERT_EQ(OK, mProducer->allowAllocation(true));
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// Release the previous buffer back to the BufferQueue
mProducer->cancelBuffer(slot, fence);
@@ -501,7 +501,7 @@
// This should fail since we're requesting a different size
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence,
- WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(BufferQueueTest, TestGenerationNumbers) {
@@ -518,7 +518,7 @@
int slot;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -561,7 +561,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -575,7 +575,8 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -612,7 +613,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -639,7 +640,8 @@
// always return the same one.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -678,7 +680,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Enable shared buffer mode
@@ -695,7 +697,8 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -730,7 +733,8 @@
for (int i = 0; i < 5; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0);
+ auto result = mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr);
if (i < 2) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
result);
@@ -757,7 +761,8 @@
for (int i = 0; i < 2; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0ull, true,
HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -768,7 +773,8 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
auto startTime = systemTime();
- ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_GE(systemTime() - startTime, TIMEOUT);
// We're technically attaching the same buffer multiple times (since we
@@ -789,7 +795,7 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -812,7 +818,7 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> firstBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
@@ -824,7 +830,7 @@
// Dequeue a second buffer
slot = BufferQueue::INVALID_BUFFER_SLOT;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> secondBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
@@ -876,7 +882,7 @@
mProducer->setMaxDequeuedBufferCount(3);
for (size_t i = 0; i < 3; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -889,7 +895,8 @@
// The first segment is a two-buffer segment, so we only put one buffer into
// the queue at a time
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -904,16 +911,17 @@
// two-buffer segment, but then at the end, we put two buffers in the queue
// at the same time before draining it.
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -928,10 +936,11 @@
// The third segment is a triple-buffer segment, so the queue is switching
// between one buffer and two buffers deep.
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1012,7 +1021,7 @@
mProducer->setMaxDequeuedBufferCount(4);
for (size_t i = 0; i < 4; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1023,14 +1032,14 @@
// Get buffers in all states: dequeued, filled, acquired, free
// Fill 3 buffers
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Dequeue 1 buffer
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
// Acquire and free 1 buffer
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9f33047..0329a6d 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -196,7 +196,7 @@
};
status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
- return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage);
+ return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr);
}
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -210,7 +210,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, *slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
@@ -349,7 +349,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, dequeuedSlot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -366,20 +366,12 @@
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
{
- uint32_t width;
- uint32_t height;
- uint32_t transformHint;
- uint32_t numPendingBuffers;
- uint64_t nextFrameNumber;
-
- output.deflate(&width, &height, &transformHint, &numPendingBuffers,
- &nextFrameNumber);
-
- EXPECT_EQ(DEFAULT_WIDTH, width);
- EXPECT_EQ(DEFAULT_HEIGHT, height);
- EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
- EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
- EXPECT_EQ(2u, nextFrameNumber);
+ EXPECT_EQ(DEFAULT_WIDTH, output.width);
+ EXPECT_EQ(DEFAULT_HEIGHT, output.height);
+ EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint);
+ // Since queueBuffer was called exactly once
+ EXPECT_EQ(1u, output.numPendingBuffers);
+ EXPECT_EQ(2u, output.nextFrameNumber);
}
// Buffer was not in the dequeued state
@@ -416,7 +408,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// Slot was enqueued without requesting a buffer
{
@@ -485,7 +477,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// No return code, but at least test that it doesn't blow up...
// TODO: add a return code
@@ -534,7 +526,7 @@
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "iteration: " << i << ", slot: " << dequeuedSlot;
}
@@ -571,7 +563,7 @@
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "slot: " << dequeuedSlot;
}
@@ -606,7 +598,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot : " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot : " << dequeuedSlot;
ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
}
@@ -622,7 +615,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot: " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot: " << dequeuedSlot;
}
// Abandon buffer queue
@@ -639,7 +633,7 @@
sp<Fence> fence;
ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr));
}
TEST_F(IGraphicBufferProducerTest,
@@ -659,7 +653,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+ nullptr)));
EXPECT_LE(0, slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot);
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 498492e..80e30da 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -81,7 +81,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -115,7 +115,7 @@
// received the buffer back from the output BufferQueue
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
@@ -153,7 +153,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -190,7 +190,7 @@
// received the buffer back from the output BufferQueues
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OutputAbandonment) {
@@ -217,7 +217,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
// Abandon the output
@@ -230,7 +230,7 @@
// Input should be abandoned
ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
} // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c9..e40b4eb 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -19,18 +19,28 @@
#include <gtest/gtest.h>
#include <binder/IMemory.h>
+#include <binder/ProcessState.h>
+#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/BufferItemConsumer.h>
+#include <private/gui/ComposerService.h>
#include <ui/Rect.h>
#include <utils/String8.h>
-#include <private/gui/ComposerService.h>
-#include <binder/ProcessState.h>
+#include <limits>
+#include <thread>
namespace android {
+using namespace std::chrono_literals;
+
+class FakeSurfaceComposer;
+class FakeProducerFrameEventHistory;
+
+static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
+
class SurfaceTest : public ::testing::Test {
protected:
@@ -77,6 +87,8 @@
TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) {
mSurfaceControl.clear();
+ // Wait for the async clean-up to complete.
+ std::this_thread::sleep_for(50ms);
sp<ANativeWindow> anw(mSurface);
int result = -123;
@@ -96,7 +108,8 @@
BufferQueue::createBufferQueue(&producer, &consumer);
sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ sp<IBinder> display(sf->getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(),
64, 64, 0, 0x7fffffff, false));
@@ -140,6 +153,14 @@
EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
}
+TEST_F(SurfaceTest, LayerCountIsOne) {
+ sp<ANativeWindow> anw(mSurface);
+ int result = -123;
+ int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result);
+ EXPECT_EQ(NO_ERROR, err);
+ EXPECT_EQ(1, result);
+}
+
TEST_F(SurfaceTest, QueryConsumerUsage) {
const int TEST_USAGE_FLAGS =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
@@ -258,4 +279,947 @@
ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
}
+
+class FakeConsumer : public BnConsumerListener {
+public:
+ void onFrameAvailable(const BufferItem& /*item*/) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+
+ void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override {
+ if (newTimestamps) {
+ if (mGetFrameTimestampsEnabled) {
+ EXPECT_GT(mNewFrameEntryOverride.frameNumber, 0u) <<
+ "Test should set mNewFrameEntryOverride before queuing "
+ "a frame.";
+ EXPECT_EQ(newTimestamps->frameNumber,
+ mNewFrameEntryOverride.frameNumber) <<
+ "Test attempting to add NewFrameEntryOverride with "
+ "incorrect frame number.";
+ mFrameEventHistory.addQueue(mNewFrameEntryOverride);
+ mNewFrameEntryOverride.frameNumber = 0;
+ }
+ mAddFrameTimestampsCount++;
+ mLastAddedFrameNumber = newTimestamps->frameNumber;
+ }
+ if (outDelta) {
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ mGetFrameTimestampsCount++;
+ }
+ mAddAndGetFrameTimestampsCallCount++;
+ }
+
+ bool mGetFrameTimestampsEnabled = false;
+
+ ConsumerFrameEventHistory mFrameEventHistory;
+ int mAddAndGetFrameTimestampsCallCount = 0;
+ int mAddFrameTimestampsCount = 0;
+ int mGetFrameTimestampsCount = 0;
+ uint64_t mLastAddedFrameNumber = NO_FRAME_INDEX;
+
+ NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr };
+};
+
+
+class FakeSurfaceComposer : public ISurfaceComposer{
+public:
+ ~FakeSurfaceComposer() override {}
+
+ void setSupportedTimestamps(bool supportsPresent, bool supportsRetire) {
+ mSupportsPresent = supportsPresent;
+ mSupportsRetire = supportsRetire;
+ }
+
+ sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
+ sp<IGraphicBufferAlloc> createGraphicBufferAlloc() override {
+ return nullptr;
+ }
+ sp<IDisplayEventConnection> createDisplayEventConnection() override {
+ return nullptr;
+ }
+ sp<IBinder> createDisplay(const String8& /*displayName*/,
+ bool /*secure*/) override { return nullptr; }
+ void destroyDisplay(const sp<IBinder>& /*display */) override {}
+ sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
+ void setTransactionState(const Vector<ComposerState>& /*state*/,
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
+ override {}
+ void bootFinished() override {}
+ bool authenticateSurfaceTexture(
+ const sp<IGraphicBufferProducer>& /*surface*/) const override {
+ return false;
+ }
+
+ status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported)
+ const override {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE
+ };
+ if (mSupportsPresent) {
+ outSupported->push_back(
+ FrameEvent::DISPLAY_PRESENT);
+ }
+ if (mSupportsRetire) {
+ outSupported->push_back(
+ FrameEvent::DISPLAY_RETIRE);
+ }
+ return NO_ERROR;
+ }
+
+ void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
+ status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
+ Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+ status_t getDisplayStats(const sp<IBinder>& /*display*/,
+ DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
+ int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
+ status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
+ override {
+ return NO_ERROR;
+ }
+ status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
+ Vector<android_color_mode_t>* /*outColorModes*/) override {
+ return NO_ERROR;
+ }
+ android_color_mode_t getActiveColorMode(const sp<IBinder>& /*display*/)
+ override {
+ return HAL_COLOR_MODE_NATIVE;
+ }
+ status_t setActiveColorMode(const sp<IBinder>& /*display*/,
+ android_color_mode_t /*colorMode*/) override { return NO_ERROR; }
+ status_t captureScreen(const sp<IBinder>& /*display*/,
+ const sp<IGraphicBufferProducer>& /*producer*/,
+ Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
+ uint32_t /*minLayerZ*/, uint32_t /*maxLayerZ*/,
+ bool /*useIdentityTransform*/,
+ Rotation /*rotation*/) override { return NO_ERROR; }
+ status_t clearAnimationFrameStats() override { return NO_ERROR; }
+ status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
+ return NO_ERROR;
+ }
+ status_t getHdrCapabilities(const sp<IBinder>& /*display*/,
+ HdrCapabilities* /*outCapabilities*/) const override {
+ return NO_ERROR;
+ }
+ status_t enableVSyncInjections(bool /*enable*/) override {
+ return NO_ERROR;
+ }
+ status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
+
+protected:
+ IBinder* onAsBinder() override { return nullptr; }
+
+private:
+ bool mSupportsPresent{true};
+ bool mSupportsRetire{true};
+};
+
+class FakeProducerFrameEventHistory : public ProducerFrameEventHistory {
+public:
+ FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap)
+ : mFenceMap(fenceMap) {}
+
+ ~FakeProducerFrameEventHistory() {}
+
+ void updateAcquireFence(uint64_t frameNumber,
+ std::shared_ptr<FenceTime>&& acquire) override {
+ // Verify the acquire fence being added isn't the one from the consumer.
+ EXPECT_NE(mConsumerAcquireFence, acquire);
+ // Override the fence, so we can verify this was called by the
+ // producer after the frame is queued.
+ ProducerFrameEventHistory::updateAcquireFence(frameNumber,
+ std::shared_ptr<FenceTime>(mAcquireFenceOverride));
+ }
+
+ void setAcquireFenceOverride(
+ const std::shared_ptr<FenceTime>& acquireFenceOverride,
+ const std::shared_ptr<FenceTime>& consumerAcquireFence) {
+ mAcquireFenceOverride = acquireFenceOverride;
+ mConsumerAcquireFence = consumerAcquireFence;
+ }
+
+protected:
+ std::shared_ptr<FenceTime> createFenceTime(const sp<Fence>& fence)
+ const override {
+ return mFenceMap->createFenceTimeForTest(fence);
+ }
+
+ FenceToFenceTimeMap* mFenceMap{nullptr};
+
+ std::shared_ptr<FenceTime> mAcquireFenceOverride{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> mConsumerAcquireFence{FenceTime::NO_FENCE};
+};
+
+
+class TestSurface : public Surface {
+public:
+ TestSurface(const sp<IGraphicBufferProducer>& bufferProducer,
+ FenceToFenceTimeMap* fenceMap)
+ : Surface(bufferProducer),
+ mFakeSurfaceComposer(new FakeSurfaceComposer) {
+ mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap);
+ mFrameEventHistory.reset(mFakeFrameEventHistory);
+ }
+
+ ~TestSurface() override {}
+
+ sp<ISurfaceComposer> composerService() const override {
+ return mFakeSurfaceComposer;
+ }
+
+public:
+ sp<FakeSurfaceComposer> mFakeSurfaceComposer;
+
+ // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory,
+ // but this raw pointer gives access to test functionality.
+ FakeProducerFrameEventHistory* mFakeFrameEventHistory;
+};
+
+
+class GetFrameTimestampsTest : public SurfaceTest {
+protected:
+ struct FenceAndFenceTime {
+ explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap)
+ : mFence(new Fence),
+ mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
+ sp<Fence> mFence { nullptr };
+ std::shared_ptr<FenceTime> mFenceTime { nullptr };
+ };
+
+ struct RefreshEvents {
+ RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart)
+ : mFenceMap(fenceMap),
+ kStartTime(refreshStart + 1),
+ kGpuCompositionDoneTime(refreshStart + 2),
+ kPresentTime(refreshStart + 3) {}
+
+ void signalPostCompositeFences() {
+ mFenceMap.signalAllForTest(
+ mGpuCompositionDone.mFence, kGpuCompositionDoneTime);
+ mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mGpuCompositionDone { mFenceMap };
+ FenceAndFenceTime mPresent { mFenceMap };
+
+ const nsecs_t kStartTime;
+ const nsecs_t kGpuCompositionDoneTime;
+ const nsecs_t kPresentTime;
+ };
+
+ struct FrameEvents {
+ FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime)
+ : mFenceMap(fenceMap),
+ kPostedTime(frameStartTime + 100),
+ kRequestedPresentTime(frameStartTime + 200),
+ kProducerAcquireTime(frameStartTime + 300),
+ kConsumerAcquireTime(frameStartTime + 301),
+ kLatchTime(frameStartTime + 500),
+ kDequeueReadyTime(frameStartTime + 600),
+ kRetireTime(frameStartTime + 700),
+ kReleaseTime(frameStartTime + 800),
+ mRefreshes {
+ { mFenceMap, frameStartTime + 410 },
+ { mFenceMap, frameStartTime + 420 },
+ { mFenceMap, frameStartTime + 430 } } {}
+
+ void signalQueueFences() {
+ mFenceMap.signalAllForTest(
+ mAcquireConsumer.mFence, kConsumerAcquireTime);
+ mFenceMap.signalAllForTest(
+ mAcquireProducer.mFence, kProducerAcquireTime);
+ }
+
+ void signalRefreshFences() {
+ for (auto& re : mRefreshes) {
+ re.signalPostCompositeFences();
+ }
+ }
+
+ void signalReleaseFences() {
+ mFenceMap.signalAllForTest(mRetire.mFence, kRetireTime);
+ mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mAcquireConsumer { mFenceMap };
+ FenceAndFenceTime mAcquireProducer { mFenceMap };
+ FenceAndFenceTime mRetire { mFenceMap };
+ FenceAndFenceTime mRelease { mFenceMap };
+
+ const nsecs_t kPostedTime;
+ const nsecs_t kRequestedPresentTime;
+ const nsecs_t kProducerAcquireTime;
+ const nsecs_t kConsumerAcquireTime;
+ const nsecs_t kLatchTime;
+ const nsecs_t kDequeueReadyTime;
+ const nsecs_t kRetireTime;
+ const nsecs_t kReleaseTime;
+
+ RefreshEvents mRefreshes[3];
+ };
+
+ GetFrameTimestampsTest() : SurfaceTest() {}
+
+ virtual void SetUp() {
+ SurfaceTest::SetUp();
+
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mFakeConsumer = new FakeConsumer;
+ mCfeh = &mFakeConsumer->mFrameEventHistory;
+ mConsumer->consumerConnect(mFakeConsumer, false);
+ mConsumer->setConsumerName(String8("TestConsumer"));
+ mSurface = new TestSurface(mProducer, &mFenceMap);
+ mWindow = mSurface;
+
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(),
+ NATIVE_WINDOW_API_CPU));
+ native_window_set_buffer_count(mWindow.get(), 4);
+ }
+
+ void enableFrameTimestamps() {
+ mFakeConsumer->mGetFrameTimestampsEnabled = true;
+ native_window_enable_frame_timestamps(mWindow.get(), 1);
+ mFrameTimestampsEnabled = true;
+ }
+
+ int getAllFrameTimestamps(uint32_t framesAgo) {
+ return native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, &outLatchTime,
+ &outFirstRefreshStartTime, &outLastRefreshStartTime,
+ &outGpuCompositionDoneTime, &outDisplayPresentTime,
+ &outDisplayRetireTime, &outDequeueReadyTime, &outReleaseTime);
+ }
+
+ void resetTimestamps() {
+ outRequestedPresentTime = -1;
+ outAcquireTime = -1;
+ outLatchTime = -1;
+ outFirstRefreshStartTime = -1;
+ outLastRefreshStartTime = -1;
+ outGpuCompositionDoneTime = -1;
+ outDisplayPresentTime = -1;
+ outDisplayRetireTime = -1;
+ outDequeueReadyTime = -1;
+ outReleaseTime = -1;
+ }
+
+ void dequeueAndQueue(uint64_t frameIndex) {
+ int fence = -1;
+ ANativeWindowBuffer* buffer = nullptr;
+ ASSERT_EQ(NO_ERROR,
+ mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+
+ int oldAddFrameTimestampsCount =
+ mFakeConsumer->mAddFrameTimestampsCount;
+
+ FrameEvents* frame = &mFrames[frameIndex];
+ uint64_t frameNumber = frameIndex + 1;
+
+ NewFrameEventsEntry fe;
+ fe.frameNumber = frameNumber;
+ fe.postedTime = frame->kPostedTime;
+ fe.requestedPresentTime = frame->kRequestedPresentTime;
+ fe.acquireFence = frame->mAcquireConsumer.mFenceTime;
+ mFakeConsumer->mNewFrameEntryOverride = fe;
+
+ mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+ frame->mAcquireProducer.mFenceTime,
+ frame->mAcquireConsumer.mFenceTime);
+
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+
+ EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber);
+
+ EXPECT_EQ(
+ oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0),
+ mFakeConsumer->mAddFrameTimestampsCount);
+ }
+
+ void addFrameEvents(
+ bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) {
+ FrameEvents* oldFrame =
+ (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame];
+ FrameEvents* newFrame = &mFrames[iNewFrame];
+
+ uint64_t nOldFrame = iOldFrame + 1;
+ uint64_t nNewFrame = iNewFrame + 1;
+
+ // Latch, Composite, Retire, and Release the frames in a plausible
+ // order. Note: The timestamps won't necessarily match the order, but
+ // that's okay for the purposes of this test.
+ std::shared_ptr<FenceTime> gpuDoneFenceTime = FenceTime::NO_FENCE;
+
+ // Composite the previous frame one more time, which helps verify
+ // LastRefresh is updated properly.
+ if (oldFrame != nullptr) {
+ mCfeh->addPreComposition(nOldFrame,
+ oldFrame->mRefreshes[2].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime,
+ oldFrame->mRefreshes[2].mPresent.mFenceTime);
+ }
+
+ // Latch the new frame.
+ mCfeh->addLatch(nNewFrame, newFrame->kLatchTime);
+
+ mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ // HWC2 releases the previous buffer after a new latch just before
+ // calling postComposition.
+ if (oldFrame != nullptr) {
+ mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime,
+ std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime));
+ }
+ mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+ newFrame->mRefreshes[0].mPresent.mFenceTime);
+
+ // Retire the previous buffer just after compositing the new buffer.
+ if (oldFrame != nullptr) {
+ mCfeh->addRetire(nOldFrame, oldFrame->mRetire.mFenceTime);
+ }
+
+ mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[1].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+ newFrame->mRefreshes[1].mPresent.mFenceTime);
+ }
+
+ void QueryPresentRetireSupported(
+ bool displayPresentSupported, bool displayRetireSupported);
+ void PresentOrRetireUnsupportedNoSyncTest(
+ bool displayPresentSupported, bool displayRetireSupported);
+
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<FakeConsumer> mFakeConsumer;
+ ConsumerFrameEventHistory* mCfeh;
+ sp<TestSurface> mSurface;
+ sp<ANativeWindow> mWindow;
+
+ FenceToFenceTimeMap mFenceMap;
+
+ bool mFrameTimestampsEnabled = false;
+
+ int64_t outRequestedPresentTime = -1;
+ int64_t outAcquireTime = -1;
+ int64_t outLatchTime = -1;
+ int64_t outFirstRefreshStartTime = -1;
+ int64_t outLastRefreshStartTime = -1;
+ int64_t outGpuCompositionDoneTime = -1;
+ int64_t outDisplayPresentTime = -1;
+ int64_t outDisplayRetireTime = -1;
+ int64_t outDequeueReadyTime = -1;
+ int64_t outReleaseTime = -1;
+
+ FrameEvents mFrames[2] { { mFenceMap, 1000 }, { mFenceMap, 2000 } };
+};
+
+
+// This test verifies that the frame timestamps are not retrieved when not
+// explicitly enabled via native_window_enable_frame_timestamps.
+// We want to check this to make sure there's no overhead for users
+// that don't need the timestamp information.
+TEST_F(GetFrameTimestampsTest, DefaultDisabled) {
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify the producer doesn't get frame timestamps piggybacked on dequeue.
+ ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify the producer doesn't get frame timestamps piggybacked on queue.
+ // It is okay that frame timestamps are added in the consumer since it is
+ // still needed for SurfaceFlinger dumps.
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+ EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify attempts to get frame timestamps fail.
+ const uint32_t framesAgo = 0;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(INVALID_OPERATION, result);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+// This test verifies that the frame timestamps are retrieved if explicitly
+// enabled via native_window_enable_frame_timestamps.
+TEST_F(GetFrameTimestampsTest, EnabledSimple) {
+ enableFrameTimestamps();
+
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify getFrameTimestamps is piggybacked on dequeue.
+ ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount);
+
+ NewFrameEventsEntry f1;
+ f1.frameNumber = 1;
+ f1.postedTime = mFrames[0].kPostedTime;
+ f1.requestedPresentTime = mFrames[0].kRequestedPresentTime;
+ f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime;
+ mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+ mFrames[0].mAcquireProducer.mFenceTime,
+ mFrames[0].mAcquireConsumer.mFenceTime);
+ mFakeConsumer->mNewFrameEntryOverride = f1;
+ mFrames[0].signalQueueFences();
+
+ // Verify getFrameTimestamps is piggybacked on queue.
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+ EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber);
+ EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify queries for timestamps that the producer doesn't know about
+ // triggers a call to see if the consumer has any new timestamps.
+ const uint32_t framesAgo = 0;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+void GetFrameTimestampsTest::QueryPresentRetireSupported(
+ bool displayPresentSupported, bool displayRetireSupported) {
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(
+ displayPresentSupported, displayRetireSupported);
+
+ // Verify supported bits are forwarded.
+ int supportsPresent = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+ EXPECT_EQ(displayPresentSupported, supportsPresent);
+
+ int supportsRetire = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &supportsRetire);
+ EXPECT_EQ(displayRetireSupported, supportsRetire);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentSupported) {
+ QueryPresentRetireSupported(true, false);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryRetireSupported) {
+ QueryPresentRetireSupported(false, true);
+}
+
+// This test verifies that:
+// 1) The timestamps recorded in the consumer's FrameTimestampsHistory are
+// properly retrieved by the producer for the correct frames.
+// 2) When framesAgo is 0, it is querying for the most recently queued frame.
+TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
+ enableFrameTimestamps();
+
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ mFrames[0].signalRefreshFences();
+ addFrameEvents(true, 0, 1);
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify timestamps are correct for frame 1.
+ uint32_t framesAgo = 1;
+ resetTimestamps();
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+
+ // Verify timestamps are correct for frame 2.
+ framesAgo = 0;
+ resetTimestamps();
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(0, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+}
+
+// This test verifies the acquire fence recorded by the consumer is not sent
+// back to the producer and the producer saves its own fence.
+TEST_F(GetFrameTimestampsTest, QueueTimestampsNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ const uint32_t framesAgo = 0;
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+
+ // Verify queue-related timestamps for f1 are available immediately in the
+ // producer without asking the consumer again, even before signaling the
+ // acquire fence.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(0, outAcquireTime);
+
+ // Signal acquire fences. Verify a sync call still isn't necessary.
+ mFrames[0].signalQueueFences();
+
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+
+ // Verify queue-related timestamps for f2 are available immediately in the
+ // producer without asking the consumer again, even before signaling the
+ // acquire fence.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(0, outAcquireTime);
+
+ // Signal acquire fences. Verify a sync call still isn't necessary.
+ mFrames[1].signalQueueFences();
+
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+}
+
+TEST_F(GetFrameTimestampsTest, ZeroRequestedTimestampsNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ mFrames[0].signalRefreshFences();
+ addFrameEvents(true, 0, 1);
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify a request for no timestamps doesn't result in a sync call.
+ const uint32_t framesAgo = 0;
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+// This test verifies that fences can signal and update timestamps producer
+// side without an additional sync call to the consumer.
+TEST_F(GetFrameTimestampsTest, FencesInProducerNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ addFrameEvents(true, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ uint32_t framesAgo = 1;
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ // Verify available timestamps are correct for frame 1 again, before any
+ // fence has been signaled.
+ // This time a sync call should not be necessary.
+ framesAgo = 1;
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ // Signal the fences for frame 1.
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+
+ // Verify all timestamps are available without a sync call.
+ framesAgo = 1;
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the frame wasn't GPU composited but has a refresh
+// event a sync call isn't made to get the GPU composite done time since it will
+// never exist.
+TEST_F(GetFrameTimestampsTest, NoGpuNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ const uint32_t framesAgo = 1;
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(false, NO_FRAME_INDEX, 0);
+ addFrameEvents(false, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ // Signal the fences for frame 1.
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+
+ // Verify all timestamps, except GPU composition, are available without a
+ // sync call.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the certain timestamps can't possibly exist for
+// the most recent frame, then a sync call is not done.
+TEST_F(GetFrameTimestampsTest, NoRetireOrReleaseNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(false, NO_FRAME_INDEX, 0);
+ addFrameEvents(false, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ uint32_t framesAgo = 1;
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(0, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify querying for all timestmaps of f2 does not do a sync call.
+ // Even though the lastRefresh, retire, dequeueReady, and release times aren't
+ // available, a sync call should not occur because it's not possible for f2
+ // to encounter the final value for those events until another frame is
+ // queued.
+ framesAgo = 0;
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(framesAgo);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(0, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(0, outDisplayRetireTime);
+ EXPECT_EQ(0, outDequeueReadyTime);
+ EXPECT_EQ(0, outReleaseTime);
+}
+
+// This test verifies there are no sync calls for present or retire times
+// when they aren't supported and that an error is returned.
+void GetFrameTimestampsTest::PresentOrRetireUnsupportedNoSyncTest(
+ bool displayPresentSupported, bool displayRetireSupported) {
+
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportedTimestamps(
+ displayPresentSupported, displayRetireSupported);
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+
+ // Verify a query for the Present and Retire times do not trigger
+ // a sync call if they are not supported.
+ const uint32_t framesAgo = 0;
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ displayPresentSupported ? nullptr : &outDisplayPresentTime,
+ displayRetireSupported ? nullptr : &outDisplayRetireTime,
+ nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(BAD_VALUE, result);
+ EXPECT_EQ(-1, outDisplayRetireTime);
+ EXPECT_EQ(-1, outDisplayPresentTime);
+}
+
+TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) {
+ PresentOrRetireUnsupportedNoSyncTest(false, true);
+}
+
+TEST_F(GetFrameTimestampsTest, RetireUnsupportedNoSync) {
+ PresentOrRetireUnsupportedNoSyncTest(true, false);
+}
+
}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d5ff753..4cd0d3a 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -28,6 +28,9 @@
// We only care about compiling as C++14
"-Wno-c++98-compat-pedantic",
+ // We are aware of the risks inherent in comparing floats for equality
+ "-Wno-float-equal",
+
// We use four-character constants for the GraphicBuffer header, and don't care
// that they're non-portable as long as they're consistent within one execution
"-Wno-four-char-constants",
@@ -41,10 +44,14 @@
},
srcs: [
+ "ColorSpace.cpp",
"Fence.cpp",
+ "FenceTime.cpp",
"FrameStats.cpp",
"Gralloc1.cpp",
"Gralloc1On0Adapter.cpp",
+ "GrallocAllocator.cpp",
+ "GrallocMapper.cpp",
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
@@ -56,9 +63,13 @@
],
shared_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.mapper@2.0",
"libbinder",
"libcutils",
"libhardware",
+ "libhidlbase",
+ "libhidltransport",
"libsync",
"libutils",
"liblog",
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
new file mode 100644
index 0000000..6296abe
--- /dev/null
+++ b/libs/ui/ColorSpace.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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 <ui/ColorSpace.h>
+
+using namespace std::placeholders;
+
+namespace android {
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ transfer_function OETF,
+ transfer_function EOTF,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(rgbToXYZ)
+ , mXYZtoRGB(inverse(rgbToXYZ))
+ , mOETF(std::move(OETF))
+ , mEOTF(std::move(EOTF))
+ , mClamper(std::move(clamper)) {
+
+ float3 r(rgbToXYZ * float3{1, 0, 0});
+ float3 g(rgbToXYZ * float3{0, 1, 0});
+ float3 b(rgbToXYZ * float3{0, 0, 1});
+
+ mPrimaries[0] = r.xy / dot(r, float3{1});
+ mPrimaries[1] = g.xy / dot(g, float3{1});
+ mPrimaries[2] = b.xy / dot(b, float3{1});
+
+ float3 w(rgbToXYZ * float3{1});
+ mWhitePoint = w.xy / dot(w, float3{1});
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ transfer_function OETF,
+ transfer_function EOTF,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+ , mXYZtoRGB(inverse(mRGBtoXYZ))
+ , mOETF(std::move(OETF))
+ , mEOTF(std::move(EOTF))
+ , mClamper(std::move(clamper))
+ , mPrimaries(primaries)
+ , mWhitePoint(whitePoint) {
+}
+
+constexpr mat3 ColorSpace::computeXYZMatrix(
+ const std::array<float2, 3>& primaries, const float2& whitePoint) {
+ const float2& R = primaries[0];
+ const float2& G = primaries[1];
+ const float2& B = primaries[2];
+ const float2& W = whitePoint;
+
+ float oneRxRy = (1 - R.x) / R.y;
+ float oneGxGy = (1 - G.x) / G.y;
+ float oneBxBy = (1 - B.x) / B.y;
+ float oneWxWy = (1 - W.x) / W.y;
+
+ float RxRy = R.x / R.y;
+ float GxGy = G.x / G.y;
+ float BxBy = B.x / B.y;
+ float WxWy = W.x / W.y;
+
+ float BY =
+ ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
+ ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
+ float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
+ float RY = 1 - GY - BY;
+
+ float RYRy = RY / R.y;
+ float GYGy = GY / G.y;
+ float BYBy = BY / B.y;
+
+ return {
+ float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)},
+ float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)},
+ float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)}
+ };
+}
+
+static constexpr float rcpResponse(float x, float g,float a, float b, float c, float d) {
+ return x >= d * c ? (std::pow(x, 1.0f / g) - b) / a : x / c;
+}
+
+static constexpr float response(float x, float g, float a, float b, float c, float d) {
+ return x >= d ? std::pow(a * x + b, g) : c * x;
+}
+
+static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
+ return std::copysign(rcpResponse(std::abs(x), g, a, b, c, d), x);
+}
+
+static float absResponse(float x, float g, float a, float b, float c, float d) {
+ return std::copysign(response(std::abs(x), g, a, b, c, d), x);
+}
+
+const ColorSpace ColorSpace::sRGB() {
+ return {
+ "sRGB IEC61966-2.1",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(response, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f)
+ };
+}
+
+const ColorSpace ColorSpace::linearSRGB() {
+ return {
+ "sRGB IEC61966-2.1 (Linear)",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f}
+ };
+}
+
+const ColorSpace ColorSpace::extendedSRGB() {
+ return {
+ "scRGB-nl IEC 61966-2-2:2003",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(clamp<float>, _1, -0.799f, 2.399f)
+ };
+}
+
+const ColorSpace ColorSpace::linearExtendedSRGB() {
+ return {
+ "scRGB IEC 61966-2-2:2003",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ linearReponse,
+ linearReponse,
+ std::bind(clamp<float>, _1, -0.5f, 7.499f)
+ };
+}
+
+const ColorSpace ColorSpace::NTSC() {
+ return {
+ "NTSC (1953)",
+ {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
+ {0.310f, 0.316f},
+ std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
+ std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+ };
+}
+
+const ColorSpace ColorSpace::BT709() {
+ return {
+ "Rec. ITU-R BT.709-5",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
+ std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+ };
+}
+
+const ColorSpace ColorSpace::BT2020() {
+ return {
+ "Rec. ITU-R BT.2020-1",
+ {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
+ {0.3127f, 0.3290f},
+ std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
+ std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+ };
+}
+
+const ColorSpace ColorSpace::AdobeRGB() {
+ return {
+ "Adobe RGB (1998)",
+ {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
+ {0.3127f, 0.3290f},
+ std::bind(powf, _1, 1.0f / 2.2f),
+ std::bind(powf, _1, 2.2f)
+ };
+}
+
+const ColorSpace ColorSpace::ProPhotoRGB() {
+ return {
+ "ROMM RGB ISO 22028-2:2013",
+ {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
+ {0.3457f, 0.3585f},
+ std::bind(rcpResponse, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f),
+ std::bind(response, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f)
+ };
+}
+
+const ColorSpace ColorSpace::DisplayP3() {
+ return {
+ "Display P3",
+ {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(response, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f)
+ };
+}
+
+const ColorSpace ColorSpace::DCIP3() {
+ return {
+ "SMPTE RP 431-2-2007 DCI (P3)",
+ {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+ {0.314f, 0.351f},
+ std::bind(powf, _1, 1.0f / 2.6f),
+ std::bind(powf, _1, 2.6f)
+ };
+}
+
+const ColorSpace ColorSpace::ACES() {
+ return {
+ "SMPTE ST 2065-1:2012 ACES",
+ {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
+ {0.32168f, 0.33767f},
+ linearReponse,
+ linearReponse,
+ std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+ };
+}
+
+const ColorSpace ColorSpace::ACEScg() {
+ return {
+ "Academy S-2014-004 ACEScg",
+ {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
+ {0.32168f, 0.33767f},
+ linearReponse,
+ linearReponse,
+ std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+ };
+}
+
+}; // namespace android
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 7cf8233..02d4137 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <ui/Fence.h>
+
#define LOG_TAG "Fence"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -25,9 +27,10 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
-#include <ui/Fence.h>
+#include <sys/types.h>
#include <unistd.h>
#include <utils/Log.h>
+#include <utils/String8.h>
#include <utils/Trace.h>
namespace android {
@@ -109,17 +112,17 @@
nsecs_t Fence::getSignalTime() const {
if (mFenceFd == -1) {
- return -1;
+ return SIGNAL_TIME_INVALID;
}
struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
if (finfo == NULL) {
ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
- return -1;
+ return SIGNAL_TIME_INVALID;
}
if (finfo->status != 1) {
sync_fence_info_free(finfo);
- return INT64_MAX;
+ return SIGNAL_TIME_PENDING;
}
struct sync_pt_info* pinfo = NULL;
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
new file mode 100644
index 0000000..8106b16
--- /dev/null
+++ b/libs/ui/FenceTime.cpp
@@ -0,0 +1,365 @@
+/*
+* 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 <ui/FenceTime.h>
+
+#include <cutils/compiler.h> // For CC_[UN]LIKELY
+#include <utils/Log.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <memory>
+
+namespace android {
+
+// ============================================================================
+// FenceTime
+// ============================================================================
+
+const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);
+
+void* FenceTime::operator new(size_t byteCount) noexcept {
+ void *p = nullptr;
+ if (posix_memalign(&p, alignof(FenceTime), byteCount)) {
+ return nullptr;
+ }
+ return p;
+}
+
+void FenceTime::operator delete(void *p) {
+ free(p);
+}
+
+FenceTime::FenceTime(const sp<Fence>& fence)
+ : mState(((fence.get() != nullptr) && fence->isValid()) ?
+ State::VALID : State::INVALID),
+ mFence(fence),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(sp<Fence>&& fence)
+ : mState(((fence.get() != nullptr) && fence->isValid()) ?
+ State::VALID : State::INVALID),
+ mFence(std::move(fence)),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(nsecs_t signalTime)
+ : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID),
+ mFence(nullptr),
+ mSignalTime(signalTime == Fence::SIGNAL_TIME_PENDING ?
+ Fence::SIGNAL_TIME_INVALID : signalTime) {
+}
+
+void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
+ if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
+ // Applying Snapshot::State::FENCE, could change the valid state of the
+ // FenceTime, which is not allowed. Callers should create a new
+ // FenceTime from the snapshot instead.
+ ALOGE("FenceTime::applyTrustedSnapshot: Unexpected fence.");
+ return;
+ }
+
+ if (src.state == Snapshot::State::EMPTY) {
+ return;
+ }
+
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ // We should always get the same signalTime here that we did in
+ // getSignalTime(). This check races with getSignalTime(), but it is
+ // only a sanity check so that's okay.
+ if (CC_UNLIKELY(signalTime != src.signalTime)) {
+ ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. "
+ "(%" PRId64 " (old) != %" PRId64 " (new))",
+ signalTime, src.signalTime);
+ }
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(src.signalTime, std::memory_order_relaxed);
+}
+
+bool FenceTime::isValid() const {
+ // We store the valid state in the constructors and return it here.
+ // This lets release code remember the valid state even after the
+ // underlying fence is destroyed.
+ return mState != State::INVALID;
+}
+
+nsecs_t FenceTime::getSignalTime() {
+ // See if we already have a cached value we can return.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return signalTime;
+ }
+
+ // Hold a reference to the fence on the stack in case the class'
+ // reference is removed by another thread. This prevents the
+ // fence from being destroyed until the end of this method, where
+ // we conveniently do not have the lock held.
+ sp<Fence> fence;
+ {
+ // With the lock acquired this time, see if we have the cached
+ // value or if we need to poll the fence.
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mFence.get()) {
+ // Another thread set the signal time just before we added the
+ // reference to mFence.
+ return mSignalTime.load(std::memory_order_relaxed);
+ }
+ fence = mFence;
+ }
+
+ // Make the system call without the lock held.
+ signalTime = fence->getSignalTime();
+
+ // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests
+ // use invalid underlying Fences without real file descriptors.
+ if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) {
+ if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+ signalTime = Fence::SIGNAL_TIME_PENDING;
+ }
+ }
+
+ // Make the signal time visible to everyone if it is no longer pending
+ // and remove the class' reference to the fence.
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(signalTime, std::memory_order_relaxed);
+ }
+
+ return signalTime;
+}
+
+nsecs_t FenceTime::getCachedSignalTime() const {
+ // memory_order_acquire since we don't have a lock fallback path
+ // that will do an acquire.
+ return mSignalTime.load(std::memory_order_acquire);
+}
+
+FenceTime::Snapshot FenceTime::getSnapshot() const {
+ // Quick check without the lock.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return Snapshot(signalTime);
+ }
+
+ // Do the full check with the lock.
+ std::lock_guard<std::mutex> lock(mMutex);
+ signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return Snapshot(signalTime);
+ }
+ return Snapshot(mFence);
+}
+
+// For tests only. If forceValidForTest is true, then getSignalTime will
+// never return SIGNAL_TIME_INVALID and isValid will always return true.
+FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest)
+ : mState(forceValidForTest ?
+ State::FORCED_VALID_FOR_TEST : State::INVALID),
+ mFence(fence),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+void FenceTime::signalForTest(nsecs_t signalTime) {
+ // To be realistic, this should really set a hidden value that
+ // gets picked up in the next call to getSignalTime, but this should
+ // be good enough.
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(signalTime, std::memory_order_relaxed);
+}
+
+// ============================================================================
+// FenceTime::Snapshot
+// ============================================================================
+FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence)
+ : state(State::FENCE), fence(srcFence) {
+}
+
+FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime)
+ : state(State::SIGNAL_TIME), signalTime(srcSignalTime) {
+}
+
+size_t FenceTime::Snapshot::getFlattenedSize() const {
+ constexpr size_t min = sizeof(state);
+ switch (state) {
+ case State::EMPTY:
+ return min;
+ case State::FENCE:
+ return min + fence->getFlattenedSize();
+ case State::SIGNAL_TIME:
+ return min + sizeof(signalTime);
+ }
+ return 0;
+}
+
+size_t FenceTime::Snapshot::getFdCount() const {
+ return state == State::FENCE ? fence->getFdCount() : 0u;
+}
+
+status_t FenceTime::Snapshot::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, state);
+ switch (state) {
+ case State::EMPTY:
+ return NO_ERROR;
+ case State::FENCE:
+ return fence->flatten(buffer, size, fds, count);
+ case State::SIGNAL_TIME:
+ FlattenableUtils::write(buffer, size, signalTime);
+ return NO_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+status_t FenceTime::Snapshot::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < sizeof(state)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, state);
+ switch (state) {
+ case State::EMPTY:
+ return NO_ERROR;
+ case State::FENCE:
+ fence = new Fence;
+ return fence->unflatten(buffer, size, fds, count);
+ case State::SIGNAL_TIME:
+ if (size < sizeof(signalTime)) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, signalTime);
+ return NO_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+// ============================================================================
+// FenceTimeline
+// ============================================================================
+void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ while (mQueue.size() >= MAX_ENTRIES) {
+ // This is a sanity check to make sure the queue doesn't grow unbounded.
+ // MAX_ENTRIES should be big enough not to trigger this path.
+ // In case this path is taken though, users of FenceTime must make sure
+ // not to rely solely on FenceTimeline to get the final timestamp and
+ // should eventually call Fence::getSignalTime on their own.
+ std::shared_ptr<FenceTime> front = mQueue.front().lock();
+ if (front) {
+ // Make a last ditch effort to get the signalTime here since
+ // we are removing it from the timeline.
+ front->getSignalTime();
+ }
+ mQueue.pop();
+ }
+ mQueue.push(fence);
+}
+
+void FenceTimeline::updateSignalTimes() {
+ while (!mQueue.empty()) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ std::shared_ptr<FenceTime> fence = mQueue.front().lock();
+ if (!fence) {
+ // The shared_ptr no longer exists and no one cares about the
+ // timestamp anymore.
+ mQueue.pop();
+ continue;
+ } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ // The fence has signaled and we've removed the sp<Fence> ref.
+ mQueue.pop();
+ continue;
+ } else {
+ // The fence didn't signal yet. Break since the later ones
+ // shouldn't have signaled either.
+ break;
+ }
+ }
+}
+
+// ============================================================================
+// FenceToFenceTimeMap
+// ============================================================================
+std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest(
+ const sp<Fence>& fence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ // Always garbage collecting isn't efficient, but this is only for testing.
+ garbageCollectLocked();
+ std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true));
+ mMap[fence.get()].push_back(fenceTime);
+ return fenceTime;
+}
+
+void FenceToFenceTimeMap::signalAllForTest(
+ const sp<Fence>& fence, nsecs_t signalTime) {
+ bool signaled = false;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto it = mMap.find(fence.get());
+ if (it != mMap.end()) {
+ for (auto& weakFenceTime : it->second) {
+ std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock();
+ if (!fenceTime) {
+ continue;
+ }
+ ALOGE_IF(!fenceTime->isValid(),
+ "FenceToFenceTimeMap::signalAllForTest: "
+ "Signaling invalid fence.");
+ fenceTime->signalForTest(signalTime);
+ signaled = true;
+ }
+ }
+
+ if (!signaled) {
+ ALOGE("FenceToFenceTimeMap::signalAllForTest: Nothing to signal.");
+ }
+}
+
+void FenceToFenceTimeMap::garbageCollectLocked() {
+ for (auto& it : mMap) {
+ // Erase all expired weak pointers from the vector.
+ auto& vect = it.second;
+ vect.erase(
+ std::remove_if(vect.begin(), vect.end(),
+ [](const std::weak_ptr<FenceTime>& ft) {
+ return ft.expired();
+ }),
+ vect.end());
+
+ // Also erase the map entry if the vector is now empty.
+ if (vect.empty()) {
+ mMap.erase(it.first);
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/ui/Gralloc1.cpp b/libs/ui/Gralloc1.cpp
index 4c73ce4..367d1ce 100644
--- a/libs/ui/Gralloc1.cpp
+++ b/libs/ui/Gralloc1.cpp
@@ -77,6 +77,17 @@
mShimDevice.mDevice, mDeviceId, format, &mFormat);
}
+gralloc1_error_t Descriptor::setLayerCount(uint32_t layerCount)
+{
+ if (mShimDevice.hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+ return setHelper<uint32_t>(mShimDevice.mFunctions.setLayerCount.pfn,
+ mShimDevice.mDevice, mDeviceId, layerCount, &mLayerCount);
+ } else {
+ // Layered buffers are not supported on this device.
+ return GRALLOC1_ERROR_UNSUPPORTED;
+ }
+}
+
gralloc1_error_t Descriptor::setProducerUsage(gralloc1_producer_usage_t usage)
{
return setHelper<uint64_t>(mShimDevice.mFunctions.setProducerUsage.pfn,
@@ -366,6 +377,15 @@
mFunctions.allocate.load(mDevice, false);
}
+ if (hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+ if (!mFunctions.setLayerCount.load(mDevice, true)) {
+ return false;
+ }
+ if (!mFunctions.getLayerCount.load(mDevice, true)) {
+ return false;
+ }
+ }
+
return true;
}
diff --git a/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp
index ec7df31..a0bbe63 100644
--- a/libs/ui/Gralloc1On0Adapter.cpp
+++ b/libs/ui/Gralloc1On0Adapter.cpp
@@ -97,6 +97,8 @@
return asFP<GRALLOC1_PFN_SET_DIMENSIONS>(setDimensionsHook);
case GRALLOC1_FUNCTION_SET_FORMAT:
return asFP<GRALLOC1_PFN_SET_FORMAT>(setFormatHook);
+ case GRALLOC1_FUNCTION_SET_LAYER_COUNT:
+ return asFP<GRALLOC1_PFN_SET_LAYER_COUNT>(setLayerCountHook);
case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE:
return asFP<GRALLOC1_PFN_SET_PRODUCER_USAGE>(setProducerUsageHook);
case GRALLOC1_FUNCTION_GET_BACKING_STORE:
@@ -113,6 +115,10 @@
return asFP<GRALLOC1_PFN_GET_FORMAT>(
bufferHook<decltype(&Buffer::getFormat),
&Buffer::getFormat, int32_t*>);
+ case GRALLOC1_FUNCTION_GET_LAYER_COUNT:
+ return asFP<GRALLOC1_PFN_GET_LAYER_COUNT>(
+ bufferHook<decltype(&Buffer::getLayerCount),
+ &Buffer::getLayerCount, uint32_t*>);
case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE:
return asFP<GRALLOC1_PFN_GET_PRODUCER_USAGE>(getProducerUsageHook);
case GRALLOC1_FUNCTION_GET_STRIDE:
diff --git a/libs/ui/GrallocAllocator.cpp b/libs/ui/GrallocAllocator.cpp
new file mode 100644
index 0000000..ca67990
--- /dev/null
+++ b/libs/ui/GrallocAllocator.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GrallocAllocator"
+
+#include <log/log.h>
+#include <ui/GrallocAllocator.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+Allocator::Allocator()
+{
+ mAllocator = IAllocator::getService("gralloc");
+ if (mAllocator != nullptr) {
+ mAllocator->createClient(
+ [&](const auto& tmpError, const auto& tmpClient) {
+ if (tmpError == Error::NONE) {
+ mClient = tmpClient;
+ }
+ });
+ if (mClient == nullptr) {
+ mAllocator.clear();
+ }
+ }
+}
+
+std::string Allocator::dumpDebugInfo() const
+{
+ std::string info;
+
+ mAllocator->dumpDebugInfo([&](const auto& tmpInfo) {
+ info = tmpInfo.c_str();
+ });
+
+ return info;
+}
+
+Error Allocator::createBufferDescriptor(
+ const IAllocatorClient::BufferDescriptorInfo& descriptorInfo,
+ BufferDescriptor* outDescriptor) const
+{
+ Error error = kDefaultError;
+ mClient->createDescriptor(descriptorInfo,
+ [&](const auto& tmpError, const auto& tmpDescriptor) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outDescriptor = tmpDescriptor;
+ });
+
+ return error;
+}
+
+void Allocator::destroyBufferDescriptor(BufferDescriptor descriptor) const
+{
+ mClient->destroyDescriptor(descriptor);
+}
+
+Error Allocator::allocate(BufferDescriptor descriptor,
+ Buffer* outBuffer) const
+{
+ hardware::hidl_vec<BufferDescriptor> descriptors;
+ descriptors.setToExternal(&descriptor, 1);
+
+ Error error = kDefaultError;
+ auto status = mClient->allocate(descriptors,
+ [&](const auto& tmpError, const auto& tmpBuffers) {
+ error = tmpError;
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ *outBuffer = tmpBuffers[0];
+ });
+
+ return error;
+}
+
+void Allocator::free(Buffer buffer) const
+{
+ mClient->free(buffer);
+}
+
+Error Allocator::exportHandle(BufferDescriptor descriptor, Buffer buffer,
+ native_handle_t** outBufferHandle) const
+{
+ Error error = kDefaultError;
+ auto status = mClient->exportHandle(descriptor, buffer,
+ [&](const auto& tmpError, const auto& tmpBufferHandle) {
+ error = tmpError;
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ *outBufferHandle = native_handle_clone(tmpBufferHandle);
+ if (!*outBufferHandle) {
+ error = Error::NO_RESOURCES;
+ }
+ });
+
+ return error;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GrallocMapper.cpp b/libs/ui/GrallocMapper.cpp
new file mode 100644
index 0000000..7ee01ad
--- /dev/null
+++ b/libs/ui/GrallocMapper.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GrallocMapper"
+
+#include <array>
+#include <string>
+
+#include <log/log.h>
+#include <ui/GrallocMapper.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+static constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+Mapper::Mapper()
+{
+ mMapper = IMapper::getService("gralloc-mapper");
+ if (mMapper != nullptr && mMapper->isRemote()) {
+ LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+ }
+}
+
+Error Mapper::retain(buffer_handle_t handle) const
+{
+ auto ret = mMapper->retain(handle);
+ return (ret.isOk()) ? static_cast<Error>(ret) : kDefaultError;
+}
+
+void Mapper::release(buffer_handle_t handle) const
+{
+ auto ret = mMapper->release(handle);
+
+ auto error = (ret.isOk()) ? static_cast<Error>(ret) : kDefaultError;
+ ALOGE_IF(error != Error::NONE,
+ "release(%p) failed with %d", handle, error);
+}
+
+Error Mapper::getStride(buffer_handle_t handle, uint32_t* outStride) const
+{
+ Error error = kDefaultError;
+ mMapper->getStride(handle,
+ [&](const auto& tmpError, const auto& tmpStride)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outStride = tmpStride;
+ });
+
+ return error;
+}
+
+Error Mapper::lock(buffer_handle_t handle,
+ uint64_t producerUsageMask,
+ uint64_t consumerUsageMask,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, void** outData) const
+{
+ hardware::hidl_handle acquireFenceHandle;
+
+ NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+ if (acquireFence >= 0) {
+ auto h = native_handle_init(acquireFenceStorage, 1, 0);
+ h->data[0] = acquireFence;
+ acquireFenceHandle = h;
+ }
+
+ Error error = kDefaultError;
+ mMapper->lock(handle, producerUsageMask, consumerUsageMask,
+ accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpData)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outData = tmpData;
+ });
+
+ if (error == Error::NONE && acquireFence >= 0) {
+ close(acquireFence);
+ }
+
+ return error;
+}
+
+Error Mapper::lock(buffer_handle_t handle,
+ uint64_t producerUsageMask,
+ uint64_t consumerUsageMask,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, FlexLayout* outLayout) const
+{
+ hardware::hidl_handle acquireFenceHandle;
+
+ NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+ if (acquireFence >= 0) {
+ auto h = native_handle_init(acquireFenceStorage, 1, 0);
+ h->data[0] = acquireFence;
+ acquireFenceHandle = h;
+ }
+
+ Error error = kDefaultError;
+ mMapper->lockFlex(handle, producerUsageMask, consumerUsageMask,
+ accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpLayout)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outLayout = tmpLayout;
+ });
+
+ if (error == Error::NONE && acquireFence >= 0) {
+ close(acquireFence);
+ }
+
+ return error;
+}
+
+int Mapper::unlock(buffer_handle_t handle) const
+{
+ int releaseFence = -1;
+
+ Error error = kDefaultError;
+ mMapper->unlock(handle,
+ [&](const auto& tmpError, const auto& tmpReleaseFence)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ auto fenceHandle = tmpReleaseFence.getNativeHandle();
+ if (fenceHandle && fenceHandle->numFds == 1) {
+ int fd = dup(fenceHandle->data[0]);
+ if (fd >= 0) {
+ releaseFence = fd;
+ } else {
+ error = Error::NO_RESOURCES;
+ }
+ } else {
+ releaseFence = -1;
+ }
+ });
+
+ if (error != Error::NONE) {
+ ALOGE("unlock(%p) failed with %d", handle, error);
+ releaseFence = -1;
+ }
+
+ return releaseFence;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 97b948d..07164a4 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -23,6 +23,7 @@
#include <utils/Errors.h>
#include <utils/Log.h>
+#include <ui/GrallocMapper.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
@@ -49,6 +50,7 @@
height =
stride =
format =
+ layerCount =
usage = 0;
handle = NULL;
}
@@ -62,15 +64,33 @@
height =
stride =
format =
+ layerCount =
usage = 0;
handle = NULL;
- mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage,
+ mInitCheck = initSize(inWidth, inHeight, inFormat, 1, inUsage,
std::move(requestorName));
}
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage, uint32_t inStride,
- native_handle_t* inHandle, bool keepOwnership)
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+ std::string requestorName)
+ : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
+ mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+{
+ width =
+ height =
+ stride =
+ format =
+ layerCount =
+ usage = 0;
+ handle = NULL;
+ mInitCheck = initSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+ std::move(requestorName));
+}
+
+GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+ uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
: BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
mBufferMapper(GraphicBufferMapper::get()),
mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
@@ -79,6 +99,7 @@
height = static_cast<int>(inHeight);
stride = static_cast<int>(inStride);
format = inFormat;
+ layerCount = inLayerCount;
usage = static_cast<int>(inUsage);
handle = inHandle;
}
@@ -93,6 +114,7 @@
height = buffer->height;
stride = buffer->stride;
format = buffer->format;
+ layerCount = buffer->layerCount;
usage = buffer->usage;
handle = buffer->handle;
}
@@ -108,8 +130,10 @@
{
if (mOwner == ownHandle) {
mBufferMapper.unregisterBuffer(handle);
- native_handle_close(handle);
- native_handle_delete(const_cast<native_handle*>(handle));
+ if (!mBufferMapper.getGrallocMapper().valid()) {
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle*>(handle));
+ }
} else if (mOwner == ownData) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
@@ -135,7 +159,7 @@
}
status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage)
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage)
{
if (mOwner != ownData)
return INVALID_OPERATION;
@@ -144,6 +168,7 @@
static_cast<int>(inWidth) == width &&
static_cast<int>(inHeight) == height &&
inFormat == format &&
+ inLayerCount == layerCount &&
static_cast<int>(inUsage) == usage)
return NO_ERROR;
@@ -152,30 +177,34 @@
allocator.free(handle);
handle = 0;
}
- return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]");
+ return initSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+ "[Reallocation]");
}
bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage)
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage)
{
if (static_cast<int>(inWidth) != width) return true;
if (static_cast<int>(inHeight) != height) return true;
if (inFormat != format) return true;
+ if (inLayerCount != layerCount) return true;
if ((static_cast<uint32_t>(usage) & inUsage) != inUsage) return true;
return false;
}
status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage, std::string requestorName)
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+ std::string requestorName)
{
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
uint32_t outStride = 0;
- status_t err = allocator.allocate(inWidth, inHeight, inFormat, inUsage,
- &handle, &outStride, mId, std::move(requestorName));
+ status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
+ inUsage, &handle, &outStride, mId, std::move(requestorName));
if (err == NO_ERROR) {
width = static_cast<int>(inWidth);
height = static_cast<int>(inHeight);
format = inFormat;
+ layerCount = inLayerCount;
usage = static_cast<int>(inUsage);
stride = static_cast<int>(outStride);
}
@@ -281,7 +310,7 @@
}
size_t GraphicBuffer::getFlattenedSize() const {
- return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
+ return static_cast<size_t>(12 + (handle ? handle->numInts : 0)) * sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
@@ -301,19 +330,20 @@
buf[2] = height;
buf[3] = stride;
buf[4] = format;
- buf[5] = usage;
- buf[6] = static_cast<int32_t>(mId >> 32);
- buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
- buf[8] = static_cast<int32_t>(mGenerationNumber);
- buf[9] = 0;
+ buf[5] = static_cast<int32_t>(layerCount);
+ buf[6] = usage;
+ buf[7] = static_cast<int32_t>(mId >> 32);
+ buf[8] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
+ buf[9] = static_cast<int32_t>(mGenerationNumber);
buf[10] = 0;
+ buf[11] = 0;
if (handle) {
- buf[9] = handle->numFds;
- buf[10] = handle->numInts;
+ buf[10] = handle->numFds;
+ buf[11] = handle->numInts;
memcpy(fds, handle->data,
static_cast<size_t>(handle->numFds) * sizeof(int));
- memcpy(&buf[11], handle->data + handle->numFds,
+ memcpy(&buf[12], handle->data + handle->numFds,
static_cast<size_t>(handle->numInts) * sizeof(int));
}
@@ -329,28 +359,28 @@
status_t GraphicBuffer::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
- if (size < 11 * sizeof(int)) return NO_MEMORY;
+ if (size < 12 * sizeof(int)) return NO_MEMORY;
int const* buf = static_cast<int const*>(buffer);
if (buf[0] != 'GBFR') return BAD_TYPE;
- const size_t numFds = static_cast<size_t>(buf[9]);
- const size_t numInts = static_cast<size_t>(buf[10]);
+ const size_t numFds = static_cast<size_t>(buf[10]);
+ const size_t numInts = static_cast<size_t>(buf[11]);
// Limit the maxNumber to be relatively small. The number of fds or ints
// should not come close to this number, and the number itself was simply
// chosen to be high enough to not cause issues and low enough to prevent
// overflow problems.
const size_t maxNumber = 4096;
- if (numFds >= maxNumber || numInts >= (maxNumber - 11)) {
- width = height = stride = format = usage = 0;
+ if (numFds >= maxNumber || numInts >= (maxNumber - 12)) {
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
ALOGE("unflatten: numFds or numInts is too large: %zd, %zd",
numFds, numInts);
return BAD_VALUE;
}
- const size_t sizeNeeded = (11 + numInts) * sizeof(int);
+ const size_t sizeNeeded = (12 + numInts) * sizeof(int);
if (size < sizeNeeded) return NO_MEMORY;
size_t fdCountNeeded = numFds;
@@ -366,34 +396,35 @@
height = buf[2];
stride = buf[3];
format = buf[4];
- usage = buf[5];
+ layerCount = static_cast<uintptr_t>(buf[5]);
+ usage = buf[6];
native_handle* h = native_handle_create(
static_cast<int>(numFds), static_cast<int>(numInts));
if (!h) {
- width = height = stride = format = usage = 0;
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
ALOGE("unflatten: native_handle_create failed");
return NO_MEMORY;
}
memcpy(h->data, fds, numFds * sizeof(int));
- memcpy(h->data + numFds, &buf[11], numInts * sizeof(int));
+ memcpy(h->data + numFds, &buf[12], numInts * sizeof(int));
handle = h;
} else {
- width = height = stride = format = usage = 0;
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
}
- mId = static_cast<uint64_t>(buf[6]) << 32;
- mId |= static_cast<uint32_t>(buf[7]);
+ mId = static_cast<uint64_t>(buf[7]) << 32;
+ mId |= static_cast<uint32_t>(buf[8]);
- mGenerationNumber = static_cast<uint32_t>(buf[8]);
+ mGenerationNumber = static_cast<uint32_t>(buf[9]);
mOwner = ownHandle;
if (handle != 0) {
status_t err = mBufferMapper.registerBuffer(this);
if (err != NO_ERROR) {
- width = height = stride = format = usage = 0;
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
ALOGE("unflatten: registerBuffer failed: %s (%d)",
strerror(-err), err);
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index f885309..5ae4faa 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -25,6 +25,9 @@
#include <ui/GraphicBufferAllocator.h>
#include <ui/Gralloc1On0Adapter.h>
+#include <ui/GrallocAllocator.h>
+#include <ui/GrallocMapper.h>
+#include <ui/GraphicBufferMapper.h>
namespace android {
// ---------------------------------------------------------------------------
@@ -36,8 +39,14 @@
GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
GraphicBufferAllocator::GraphicBufferAllocator()
- : mLoader(std::make_unique<Gralloc1::Loader>()),
- mDevice(mLoader->getDevice()) {}
+ : mAllocator(std::make_unique<Gralloc2::Allocator>()),
+ mMapper(GraphicBufferMapper::getInstance())
+{
+ if (!mAllocator->valid()) {
+ mLoader = std::make_unique<Gralloc1::Loader>();
+ mDevice = mLoader->getDevice();
+ }
+}
GraphicBufferAllocator::~GraphicBufferAllocator() {}
@@ -54,22 +63,29 @@
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
if (rec.size) {
- snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+ snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%08x | %s\n",
list.keyAt(i), rec.size/1024.0f,
- rec.width, rec.stride, rec.height, rec.format, rec.usage,
- rec.requestorName.c_str());
+ rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+ rec.usage, rec.requestorName.c_str());
} else {
- snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+ snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%08x | %s\n",
list.keyAt(i),
- rec.width, rec.stride, rec.height, rec.format, rec.usage,
- rec.requestorName.c_str());
+ rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+ rec.usage, rec.requestorName.c_str());
}
result.append(buffer);
total += rec.size;
}
snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0f);
result.append(buffer);
- std::string deviceDump = mDevice->dump();
+
+ std::string deviceDump;
+ if (mAllocator->valid()) {
+ deviceDump = mAllocator->dumpDebugInfo();
+ } else {
+ deviceDump = mDevice->dump();
+ }
+
result.append(deviceDump.c_str(), deviceDump.size());
}
@@ -80,9 +96,108 @@
ALOGD("%s", s.string());
}
+namespace {
+
+class HalBuffer {
+public:
+ HalBuffer(const Gralloc2::Allocator* allocator,
+ uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint32_t usage)
+ : mAllocator(allocator), mBufferValid(false)
+ {
+ Gralloc2::IAllocatorClient::BufferDescriptorInfo info = {};
+ info.width = width;
+ info.height = height;
+ info.format = static_cast<Gralloc2::PixelFormat>(format);
+ info.layerCount = layerCount;
+ info.producerUsageMask = usage;
+ info.consumerUsageMask = usage;
+
+ Gralloc2::BufferDescriptor descriptor;
+ auto error = mAllocator->createBufferDescriptor(info, &descriptor);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGE("Failed to create desc (%u x %u) layerCount %u format %d usage %u: %d",
+ width, height, layerCount, format, usage, error);
+ return;
+ }
+
+ error = mAllocator->allocate(descriptor, &mBuffer);
+ if (error == Gralloc2::Error::NOT_SHARED) {
+ error = Gralloc2::Error::NONE;
+ }
+
+ if (error != Gralloc2::Error::NONE) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d usage %u: %d",
+ width, height, layerCount, format, usage, error);
+ mAllocator->destroyBufferDescriptor(descriptor);
+ return;
+ }
+
+ error = mAllocator->exportHandle(descriptor, mBuffer, &mHandle);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGE("Failed to export handle");
+ mAllocator->free(mBuffer);
+ mAllocator->destroyBufferDescriptor(descriptor);
+ return;
+ }
+
+ mAllocator->destroyBufferDescriptor(descriptor);
+
+ mBufferValid = true;
+ }
+
+ ~HalBuffer()
+ {
+ if (mBufferValid) {
+ if (mHandle) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+
+ mAllocator->free(mBuffer);
+ }
+ }
+
+ bool exportHandle(GraphicBufferMapper& mapper,
+ buffer_handle_t* handle, uint32_t* stride)
+ {
+ if (!mBufferValid) {
+ return false;
+ }
+
+ if (mapper.registerBuffer(mHandle)) {
+ return false;
+ }
+
+ *handle = mHandle;
+
+ auto error = mapper.getGrallocMapper().getStride(mHandle, stride);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGW("Failed to get stride from buffer: %d", error);
+ *stride = 0;
+ }
+
+ mHandle = nullptr;
+ mAllocator->free(mBuffer);
+ mBufferValid = false;
+
+ return true;
+ }
+
+private:
+ const Gralloc2::Allocator* mAllocator;
+
+ bool mBufferValid;
+ Gralloc2::Buffer mBuffer;
+ native_handle_t* mHandle;
+};
+
+} // namespace
+
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
- PixelFormat format, uint32_t usage, buffer_handle_t* handle,
- uint32_t* stride, uint64_t graphicBufferId, std::string requestorName)
+ PixelFormat format, uint32_t layerCount, uint32_t usage,
+ buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
+ std::string requestorName)
{
ATRACE_CALL();
@@ -91,43 +206,70 @@
if (!width || !height)
width = height = 1;
+ // Ensure that layerCount is valid.
+ if (layerCount < 1)
+ layerCount = 1;
+
// Filter out any usage bits that should not be passed to the gralloc module
usage &= GRALLOC_USAGE_ALLOC_MASK;
- auto descriptor = mDevice->createDescriptor();
- auto error = descriptor->setDimensions(width, height);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set dimensions to (%u, %u): %d", width, height, error);
- return BAD_VALUE;
- }
- error = descriptor->setFormat(static_cast<android_pixel_format_t>(format));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set format to %d: %d", format, error);
- return BAD_VALUE;
- }
- error = descriptor->setProducerUsage(
- static_cast<gralloc1_producer_usage_t>(usage));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set producer usage to %u: %d", usage, error);
- return BAD_VALUE;
- }
- error = descriptor->setConsumerUsage(
- static_cast<gralloc1_consumer_usage_t>(usage));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set consumer usage to %u: %d", usage, error);
- return BAD_VALUE;
- }
+ gralloc1_error_t error;
+ if (mAllocator->valid()) {
+ HalBuffer buffer(mAllocator.get(), width, height, format, layerCount,
+ usage);
+ if (!buffer.exportHandle(mMapper, handle, stride)) {
+ return NO_MEMORY;
+ }
+ error = GRALLOC1_ERROR_NONE;
+ } else {
+ auto descriptor = mDevice->createDescriptor();
+ error = descriptor->setDimensions(width, height);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set dimensions to (%u, %u): %d",
+ width, height, error);
+ return BAD_VALUE;
+ }
+ error = descriptor->setFormat(
+ static_cast<android_pixel_format_t>(format));
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set format to %d: %d", format, error);
+ return BAD_VALUE;
+ }
+ if (mDevice->hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+ error = descriptor->setLayerCount(layerCount);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set layer count to %u: %d", layerCount, error);
+ return BAD_VALUE;
+ }
+ } else if (layerCount > 1) {
+ ALOGE("Failed to set layer count to %u: capability unsupported",
+ layerCount);
+ return BAD_VALUE;
+ }
+ error = descriptor->setProducerUsage(
+ static_cast<gralloc1_producer_usage_t>(usage));
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set producer usage to %u: %d", usage, error);
+ return BAD_VALUE;
+ }
+ error = descriptor->setConsumerUsage(
+ static_cast<gralloc1_consumer_usage_t>(usage));
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to set consumer usage to %u: %d", usage, error);
+ return BAD_VALUE;
+ }
- error = mDevice->allocate(descriptor, graphicBufferId, handle);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
- width, height, format, usage, error);
- return NO_MEMORY;
- }
+ error = mDevice->allocate(descriptor, graphicBufferId, handle);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d usage %u: %d",
+ width, height, layerCount, format, usage, error);
+ return NO_MEMORY;
+ }
- error = mDevice->getStride(*handle, stride);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGW("Failed to get stride from buffer: %d", error);
+ error = mDevice->getStride(*handle, stride);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGW("Failed to get stride from buffer: %d", error);
+ }
}
if (error == NO_ERROR) {
@@ -139,6 +281,7 @@
rec.height = height;
rec.stride = *stride;
rec.format = format;
+ rec.layerCount = layerCount;
rec.usage = usage;
rec.size = static_cast<size_t>(height * (*stride) * bpp);
rec.requestorName = std::move(requestorName);
@@ -152,7 +295,14 @@
{
ATRACE_CALL();
- auto error = mDevice->release(handle);
+ gralloc1_error_t error;
+ if (mAllocator->valid()) {
+ error = static_cast<gralloc1_error_t>(
+ mMapper.unregisterBuffer(handle));
+ } else {
+ error = mDevice->release(handle);
+ }
+
if (error != GRALLOC1_ERROR_NONE) {
ALOGE("Failed to free buffer: %d", error);
}
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 481d43c..1ff934b 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -33,6 +33,7 @@
#include <utils/Trace.h>
#include <ui/Gralloc1On0Adapter.h>
+#include <ui/GrallocMapper.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/Rect.h>
@@ -44,8 +45,13 @@
ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
GraphicBufferMapper::GraphicBufferMapper()
- : mLoader(std::make_unique<Gralloc1::Loader>()),
- mDevice(mLoader->getDevice()) {}
+ : mMapper(std::make_unique<const Gralloc2::Mapper>())
+{
+ if (!mMapper->valid()) {
+ mLoader = std::make_unique<Gralloc1::Loader>();
+ mDevice = mLoader->getDevice();
+ }
+}
@@ -53,7 +59,13 @@
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->retain(handle);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ error = static_cast<gralloc1_error_t>(mMapper->retain(handle));
+ } else {
+ error = mDevice->retain(handle);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
handle, error);
@@ -64,7 +76,14 @@
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->retain(buffer);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ error = static_cast<gralloc1_error_t>(
+ mMapper->retain(buffer->getNativeBuffer()->handle));
+ } else {
+ error = mDevice->retain(buffer);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
buffer->getNativeBuffer()->handle, error);
@@ -75,7 +94,14 @@
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->release(handle);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ mMapper->release(handle);
+ error = GRALLOC1_ERROR_NONE;
+ } else {
+ error = mDevice->release(handle);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d",
handle, error);
@@ -120,11 +146,20 @@
ATRACE_CALL();
gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
- sp<Fence> fence = new Fence(fenceFd);
- gralloc1_error_t error = mDevice->lock(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, vaddr, fence);
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ const Gralloc2::IMapper::Rect& accessRect =
+ *reinterpret_cast<Gralloc2::IMapper::Rect*>(&accessRegion);
+ error = static_cast<gralloc1_error_t>(mMapper->lock(
+ handle, usage, usage, accessRect, fenceFd, vaddr));
+ } else {
+ sp<Fence> fence = new Fence(fenceFd);
+ error = mDevice->lock(handle,
+ static_cast<gralloc1_producer_usage_t>(usage),
+ static_cast<gralloc1_consumer_usage_t>(usage),
+ &accessRegion, vaddr, fence);
+ }
+
ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle,
error);
@@ -160,38 +195,63 @@
ATRACE_CALL();
gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
- sp<Fence> fence = new Fence(fenceFd);
- if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
- gralloc1_error_t error = mDevice->lockYCbCr(handle,
+ std::vector<android_flex_plane_t> planes;
+ android_flex_layout_t flexLayout{};
+ gralloc1_error_t error;
+
+ if (mMapper->valid()) {
+ const Gralloc2::IMapper::Rect& accessRect =
+ *reinterpret_cast<Gralloc2::IMapper::Rect*>(&accessRegion);
+ Gralloc2::FlexLayout layout{};
+ error = static_cast<gralloc1_error_t>(mMapper->lock(
+ handle, usage, usage, accessRect, fenceFd, &layout));
+
+ if (error == GRALLOC1_ERROR_NONE) {
+ planes.resize(layout.planes.size());
+ memcpy(planes.data(), layout.planes.data(),
+ sizeof(planes[0]) * planes.size());
+
+ flexLayout.format = static_cast<android_flex_format_t>(
+ layout.format);
+ flexLayout.num_planes = static_cast<uint32_t>(planes.size());
+ flexLayout.planes = planes.data();
+ }
+ } else {
+ sp<Fence> fence = new Fence(fenceFd);
+
+ if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
+ error = mDevice->lockYCbCr(handle,
+ static_cast<gralloc1_producer_usage_t>(usage),
+ static_cast<gralloc1_consumer_usage_t>(usage),
+ &accessRegion, ycbcr, fence);
+ ALOGW_IF(error != GRALLOC1_ERROR_NONE,
+ "lockYCbCr(%p, ...) failed: %d", handle, error);
+ return error;
+ }
+
+ uint32_t numPlanes = 0;
+ error = mDevice->getNumFlexPlanes(handle, &numPlanes);
+
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGV("Failed to retrieve number of flex planes: %d", error);
+ return error;
+ }
+ if (numPlanes < 3) {
+ ALOGV("Not enough planes for YCbCr (%u found)", numPlanes);
+ return GRALLOC1_ERROR_UNSUPPORTED;
+ }
+
+ planes.resize(numPlanes);
+ flexLayout.num_planes = numPlanes;
+ flexLayout.planes = planes.data();
+
+ error = mDevice->lockFlex(handle,
static_cast<gralloc1_producer_usage_t>(usage),
static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, ycbcr, fence);
- ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lockYCbCr(%p, ...) failed: %d",
- handle, error);
- return error;
+ &accessRegion, &flexLayout, fence);
}
- uint32_t numPlanes = 0;
- gralloc1_error_t error = mDevice->getNumFlexPlanes(handle, &numPlanes);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGV("Failed to retrieve number of flex planes: %d", error);
- return error;
- }
- if (numPlanes < 3) {
- ALOGV("Not enough planes for YCbCr (%u found)", numPlanes);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
-
- std::vector<android_flex_plane_t> planes(numPlanes);
- android_flex_layout_t flexLayout{};
- flexLayout.num_planes = numPlanes;
- flexLayout.planes = planes.data();
-
- error = mDevice->lockFlex(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, &flexLayout, fence);
if (error != GRALLOC1_ERROR_NONE) {
ALOGW("lockFlex(%p, ...) failed: %d", handle, error);
return error;
@@ -276,14 +336,20 @@
{
ATRACE_CALL();
- sp<Fence> fence = Fence::NO_FENCE;
- gralloc1_error_t error = mDevice->unlock(handle, &fence);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("unlock(%p) failed: %d", handle, error);
- return error;
- }
+ gralloc1_error_t error;
+ if (mMapper->valid()) {
+ *fenceFd = mMapper->unlock(handle);
+ error = GRALLOC1_ERROR_NONE;
+ } else {
+ sp<Fence> fence = Fence::NO_FENCE;
+ error = mDevice->unlock(handle, &fence);
+ if (error != GRALLOC1_ERROR_NONE) {
+ ALOGE("unlock(%p) failed: %d", handle, error);
+ return error;
+ }
- *fenceFd = fence->dup();
+ *fenceFd = fence->dup();
+ }
return error;
}
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index cab1dde..734472d 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -22,6 +22,8 @@
uint32_t bytesPerPixel(PixelFormat format) {
switch (format) {
+ case PIXEL_FORMAT_RGBA_FP16:
+ return 8;
case PIXEL_FORMAT_RGBA_8888:
case PIXEL_FORMAT_RGBX_8888:
case PIXEL_FORMAT_BGRA_8888:
@@ -38,6 +40,8 @@
uint32_t bitsPerPixel(PixelFormat format) {
switch (format) {
+ case PIXEL_FORMAT_RGBA_FP16:
+ return 64;
case PIXEL_FORMAT_RGBA_8888:
case PIXEL_FORMAT_RGBX_8888:
case PIXEL_FORMAT_BGRA_8888:
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 8cdab8c..c4f34d5 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,3 +29,19 @@
name: "mat_test",
srcs: ["mat_test.cpp"],
}
+
+cc_test {
+ name: "half_test",
+ srcs: ["half_test.cpp"],
+}
+
+cc_test {
+ name: "quat_test",
+ srcs: ["quat_test.cpp"],
+}
+
+cc_test {
+ name: "colorspace_test",
+ shared_libs: ["libui"],
+ srcs: ["colorspace_test.cpp"],
+}
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
new file mode 100644
index 0000000..e5c2633
--- /dev/null
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ColorSpaceTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <ui/ColorSpace.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class ColorSpaceTest : public testing::Test {
+protected:
+};
+
+TEST_F(ColorSpaceTest, XYZ) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ mat3 XYZtoSRGB(inverse(sRGBToXYZ));
+
+ ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+ EXPECT_EQ(sRGBToXYZ, sRGB.getRGBtoXYZ());
+ EXPECT_EQ(XYZtoSRGB, sRGB.getXYZtoRGB());
+}
+
+TEST_F(ColorSpaceTest, XYZPrimaries) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+ EXPECT_NEAR(0.640f, sRGB.getPrimaries()[0].x, 1e-5f);
+ EXPECT_NEAR(0.330f, sRGB.getPrimaries()[0].y, 1e-5f);
+
+ EXPECT_NEAR(0.300f, sRGB.getPrimaries()[1].x, 1e-5f);
+ EXPECT_NEAR(0.600f, sRGB.getPrimaries()[1].y, 1e-5f);
+
+ EXPECT_NEAR(0.150f, sRGB.getPrimaries()[2].x, 1e-5f);
+ EXPECT_NEAR(0.060f, sRGB.getPrimaries()[2].y, 1e-5f);
+}
+
+TEST_F(ColorSpaceTest, XYZWhitePoint) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+ EXPECT_NEAR(0.3127f, sRGB.getWhitePoint().x, 1e-5f);
+ EXPECT_NEAR(0.3290f, sRGB.getWhitePoint().y, 1e-5f);
+}
+
+TEST_F(ColorSpaceTest, XYZFromPrimaries) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ ColorSpace sRGB1("sRGB", sRGBToXYZ);
+ ColorSpace sRGB2(
+ "sRGB",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f}
+ );
+
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j= 0; j < 3; j++) {
+ ASSERT_NEAR(sRGB1.getRGBtoXYZ()[i][j], sRGB2.getRGBtoXYZ()[i][j], 1e-5f);
+ }
+ }
+
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j= 0; j < 3; j++) {
+ ASSERT_NEAR(sRGB2.getXYZtoRGB()[i][j], sRGB2.getXYZtoRGB()[i][j], 1e-5f);
+ }
+ }
+}
+
+TEST_F(ColorSpaceTest, TransferFunctions) {
+ ColorSpace sRGB = ColorSpace::sRGB();
+
+ EXPECT_NEAR(0.0f, sRGB.getEOTF()(0.0f), 1e-6f);
+ EXPECT_NEAR(0.0f, sRGB.getOETF()(0.0f), 1e-6f);
+ EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f);
+ EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f);
+
+ for (float v = 0.0f; v <= 0.5f; v += 1e-3f) {
+ ASSERT_TRUE(v >= sRGB.getEOTF()(v));
+ ASSERT_TRUE(v <= sRGB.getOETF()(v));
+ }
+
+ float previousEOTF = std::numeric_limits<float>::lowest();
+ float previousOETF = std::numeric_limits<float>::lowest();
+ for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
+ ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v));
+ previousEOTF = sRGB.getEOTF()(v);
+ ASSERT_TRUE(previousOETF < sRGB.getOETF()(v));
+ previousOETF = sRGB.getOETF()(v);
+ }
+
+ ColorSpace sRGB2(
+ "sRGB",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f}
+ // linear transfer functions
+ );
+ for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
+ ASSERT_EQ(v, sRGB2.getEOTF()(v));
+ ASSERT_EQ(v, sRGB2.getOETF()(v));
+ }
+}
+
+TEST_F(ColorSpaceTest, Clamping) {
+ // Pick a color outside of sRGB
+ float3 c(ColorSpace::BT2020().rgbToXYZ(float3{0, 1, 0}));
+
+ // The color will be clamped
+ float3 sRGB(ColorSpace::sRGB().xyzToRGB(c));
+ EXPECT_TRUE(sRGB > float3{0.0} && sRGB < float3{1.0});
+
+ // The color will not be clamped
+ float3 extendedSRGB(ColorSpace::linearExtendedSRGB().xyzToRGB(c));
+ EXPECT_TRUE(extendedSRGB.g > 1.0f);
+}
+
+}; // namespace android
diff --git a/libs/ui/tests/half_test.cpp b/libs/ui/tests/half_test.cpp
new file mode 100644
index 0000000..b2a5e5c
--- /dev/null
+++ b/libs/ui/tests/half_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HalfTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <ui/half.h>
+#include <ui/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HalfTest : public testing::Test {
+protected:
+};
+
+TEST_F(HalfTest, Basics) {
+
+ EXPECT_EQ(2UL, sizeof(half));
+
+ // test +/- zero
+ EXPECT_EQ(0x0000, half( 0.0f).getBits());
+ EXPECT_EQ(0x8000, half(-0.0f).getBits());
+
+ // test nan
+ EXPECT_EQ(0x7e00, half(NAN).getBits());
+
+ // test +/- infinity
+ EXPECT_EQ(0x7C00, half( std::numeric_limits<float>::infinity()).getBits());
+ EXPECT_EQ(0xFC00, half(-std::numeric_limits<float>::infinity()).getBits());
+
+ // test a few known values
+ EXPECT_EQ(0x3C01, half(1.0009765625).getBits());
+ EXPECT_EQ(0xC000, half(-2).getBits());
+ EXPECT_EQ(0x0400, half(6.10352e-5).getBits());
+ EXPECT_EQ(0x7BFF, half(65504).getBits());
+ EXPECT_EQ(0x3555, half(1.0f/3).getBits());
+
+ // numeric limits
+ EXPECT_EQ(0x7C00, std::numeric_limits<half>::infinity().getBits());
+ EXPECT_EQ(0x0400, std::numeric_limits<half>::min().getBits());
+ EXPECT_EQ(0x7BFF, std::numeric_limits<half>::max().getBits());
+ EXPECT_EQ(0xFBFF, std::numeric_limits<half>::lowest().getBits());
+
+ // denormals (flushed to zero)
+ EXPECT_EQ(0x0000, half( 6.09756e-5).getBits()); // if handled, should be: 0x03FF
+ EXPECT_EQ(0x0000, half( 5.96046e-8).getBits()); // if handled, should be: 0x0001
+ EXPECT_EQ(0x8000, half(-6.09756e-5).getBits()); // if handled, should be: 0x83FF
+ EXPECT_EQ(0x8000, half(-5.96046e-8).getBits()); // if handled, should be: 0x8001
+
+ // test all exactly representable integers
+ for (int i=-2048 ; i<= 2048 ; ++i) {
+ half h = i;
+ EXPECT_EQ(i, float(h));
+ }
+}
+
+TEST_F(HalfTest, Literals) {
+ half one = 1.0_hf;
+ half pi = 3.1415926_hf;
+ half minusTwo = -2.0_hf;
+
+ EXPECT_EQ(half(1.0f), one);
+ EXPECT_EQ(half(3.1415926), pi);
+ EXPECT_EQ(half(-2.0f), minusTwo);
+}
+
+
+TEST_F(HalfTest, Vec) {
+ float4 f4(1,2,3,4);
+ half4 h4(f4);
+ half3 h3(f4.xyz);
+ half2 h2(f4.xy);
+
+ EXPECT_EQ(f4, h4);
+ EXPECT_EQ(f4.xyz, h3);
+ EXPECT_EQ(f4.xy, h2);
+}
+
+}; // namespace android
diff --git a/libs/ui/tests/mat_test.cpp b/libs/ui/tests/mat_test.cpp
index a2c63ac..0f8e631 100644
--- a/libs/ui/tests/mat_test.cpp
+++ b/libs/ui/tests/mat_test.cpp
@@ -14,13 +14,17 @@
* limitations under the License.
*/
-#define LOG_TAG "RegionTest"
+#define LOG_TAG "MatTest"
#include <stdlib.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
+
+#include <limits>
+#include <random>
+#include <functional>
+
#include <gtest/gtest.h>
+#include <ui/mat2.h>
#include <ui/mat4.h>
namespace android {
@@ -99,11 +103,13 @@
const mat4 identity;
mat4 m0;
- ++m0;
- EXPECT_EQ(mat4( vec4(2,1,1,1), vec4(1,2,1,1), vec4(1,1,2,1), vec4(1,1,1,2) ), m0);
- EXPECT_EQ(mat4( -vec4(2,1,1,1), -vec4(1,2,1,1), -vec4(1,1,2,1), -vec4(1,1,1,2) ), -m0);
+ m0 = -m0;
+ EXPECT_EQ(mat4(vec4(-1, 0, 0, 0),
+ vec4(0, -1, 0, 0),
+ vec4(0, 0, -1, 0),
+ vec4(0, 0, 0, -1)), m0);
- --m0;
+ m0 = -m0;
EXPECT_EQ(identity, m0);
}
@@ -112,19 +118,19 @@
mat4 m0;
EXPECT_EQ(4, trace(m0));
- mat4 m1(vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16));
- mat4 m2(vec4(1,5,9,13), vec4(2,6,10,14), vec4(3,7,11,15), vec4(4,8,12,16));
+ mat4 m1(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+ mat4 m2(vec4(1, 5, 9, 13), vec4(2, 6, 10, 14), vec4(3, 7, 11, 15), vec4(4, 8, 12, 16));
EXPECT_EQ(m1, transpose(m2));
EXPECT_EQ(m2, transpose(m1));
- EXPECT_EQ(vec4(1,6,11,16), diag(m1));
+ EXPECT_EQ(vec4(1, 6, 11, 16), diag(m1));
EXPECT_EQ(identity, inverse(identity));
- mat4 m3(vec4(4,3,0,0), vec4(3,2,0,0), vec4(0,0,1,0), vec4(0,0,0,1));
+ mat4 m3(vec4(4, 3, 0, 0), vec4(3, 2, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1));
mat4 m3i(inverse(m3));
EXPECT_FLOAT_EQ(-2, m3i[0][0]);
- EXPECT_FLOAT_EQ( 3, m3i[0][1]);
- EXPECT_FLOAT_EQ( 3, m3i[1][0]);
+ EXPECT_FLOAT_EQ(3, m3i[0][1]);
+ EXPECT_FLOAT_EQ(3, m3i[1][0]);
EXPECT_FLOAT_EQ(-4, m3i[1][1]);
mat4 m3ii(inverse(m3i));
@@ -134,6 +140,553 @@
EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
EXPECT_EQ(m1, m1*identity);
+
+
+ for (size_t c=0 ; c<4 ; c++) {
+ for (size_t r=0 ; r<4 ; r++) {
+ EXPECT_FLOAT_EQ(m1[c][r], m1(r, c));
+ }
+ }
}
+TEST_F(MatTest, ElementAccess) {
+ mat4 m(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+ for (size_t c=0 ; c<4 ; c++) {
+ for (size_t r=0 ; r<4 ; r++) {
+ EXPECT_FLOAT_EQ(m[c][r], m(r, c));
+ }
+ }
+
+ m(3,2) = 100;
+ EXPECT_FLOAT_EQ(m[2][3], 100);
+ EXPECT_FLOAT_EQ(m(3, 2), 100);
+}
+
+//------------------------------------------------------------------------------
+// MAT 3
+//------------------------------------------------------------------------------
+
+class Mat3Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat3Test, Basics) {
+ mat3 m0;
+ EXPECT_EQ(sizeof(mat3), sizeof(float)*9);
+}
+
+TEST_F(Mat3Test, ComparisonOps) {
+ mat3 m0;
+ mat3 m1(2);
+
+ EXPECT_TRUE(m0 == m0);
+ EXPECT_TRUE(m0 != m1);
+ EXPECT_FALSE(m0 != m0);
+ EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat3Test, Constructors) {
+ mat3 m0;
+ ASSERT_EQ(m0[0].x, 1);
+ ASSERT_EQ(m0[0].y, 0);
+ ASSERT_EQ(m0[0].z, 0);
+ ASSERT_EQ(m0[1].x, 0);
+ ASSERT_EQ(m0[1].y, 1);
+ ASSERT_EQ(m0[1].z, 0);
+ ASSERT_EQ(m0[2].x, 0);
+ ASSERT_EQ(m0[2].y, 0);
+ ASSERT_EQ(m0[2].z, 1);
+
+ mat3 m1(2);
+ mat3 m2(vec3(2));
+ mat3 m3(m2);
+
+ EXPECT_EQ(m1, m2);
+ EXPECT_EQ(m2, m3);
+ EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat3Test, ArithmeticOps) {
+ mat3 m0;
+ mat3 m1(2);
+ mat3 m2(vec3(2));
+
+ m1 += m2;
+ EXPECT_EQ(mat3(4), m1);
+
+ m2 -= m1;
+ EXPECT_EQ(mat3(-2), m2);
+
+ m1 *= 2;
+ EXPECT_EQ(mat3(8), m1);
+
+ m1 /= 2;
+ EXPECT_EQ(mat3(4), m1);
+
+ m0 = -m0;
+ EXPECT_EQ(mat3(-1), m0);
+}
+
+TEST_F(Mat3Test, UnaryOps) {
+ const mat3 identity;
+ mat3 m0;
+
+ m0 = -m0;
+ EXPECT_EQ(mat3(vec3(-1, 0, 0),
+ vec3(0, -1, 0),
+ vec3(0, 0, -1)), m0);
+
+ m0 = -m0;
+ EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat3Test, MiscOps) {
+ const mat3 identity;
+ mat3 m0;
+ EXPECT_EQ(3, trace(m0));
+
+ mat3 m1(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 8, 9));
+ mat3 m2(vec3(1, 4, 7), vec3(2, 5, 8), vec3(3, 6, 9));
+ EXPECT_EQ(m1, transpose(m2));
+ EXPECT_EQ(m2, transpose(m1));
+ EXPECT_EQ(vec3(1, 5, 9), diag(m1));
+
+ EXPECT_EQ(identity, inverse(identity));
+
+ mat3 m3(vec3(4, 3, 0), vec3(3, 2, 0), vec3(0, 0, 1));
+ mat3 m3i(inverse(m3));
+ EXPECT_FLOAT_EQ(-2, m3i[0][0]);
+ EXPECT_FLOAT_EQ(3, m3i[0][1]);
+ EXPECT_FLOAT_EQ(3, m3i[1][0]);
+ EXPECT_FLOAT_EQ(-4, m3i[1][1]);
+
+ mat3 m3ii(inverse(m3i));
+ EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
+ EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
+ EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
+ EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
+
+ EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MAT 2
+//------------------------------------------------------------------------------
+
+class Mat2Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat2Test, Basics) {
+ mat2 m0;
+ EXPECT_EQ(sizeof(mat2), sizeof(float)*4);
+}
+
+TEST_F(Mat2Test, ComparisonOps) {
+ mat2 m0;
+ mat2 m1(2);
+
+ EXPECT_TRUE(m0 == m0);
+ EXPECT_TRUE(m0 != m1);
+ EXPECT_FALSE(m0 != m0);
+ EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat2Test, Constructors) {
+ mat2 m0;
+ ASSERT_EQ(m0[0].x, 1);
+ ASSERT_EQ(m0[0].y, 0);
+ ASSERT_EQ(m0[1].x, 0);
+ ASSERT_EQ(m0[1].y, 1);
+
+ mat2 m1(2);
+ mat2 m2(vec2(2));
+ mat2 m3(m2);
+
+ EXPECT_EQ(m1, m2);
+ EXPECT_EQ(m2, m3);
+ EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat2Test, ArithmeticOps) {
+ mat2 m0;
+ mat2 m1(2);
+ mat2 m2(vec2(2));
+
+ m1 += m2;
+ EXPECT_EQ(mat2(4), m1);
+
+ m2 -= m1;
+ EXPECT_EQ(mat2(-2), m2);
+
+ m1 *= 2;
+ EXPECT_EQ(mat2(8), m1);
+
+ m1 /= 2;
+ EXPECT_EQ(mat2(4), m1);
+
+ m0 = -m0;
+ EXPECT_EQ(mat2(-1), m0);
+}
+
+TEST_F(Mat2Test, UnaryOps) {
+ const mat2 identity;
+ mat2 m0;
+
+ m0 = -m0;
+ EXPECT_EQ(mat2(vec2(-1, 0),
+ vec2(0, -1)), m0);
+
+ m0 = -m0;
+ EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat2Test, MiscOps) {
+ const mat2 identity;
+ mat2 m0;
+ EXPECT_EQ(2, trace(m0));
+
+ mat2 m1(vec2(1, 2), vec2(3, 4));
+ mat2 m2(vec2(1, 3), vec2(2, 4));
+ EXPECT_EQ(m1, transpose(m2));
+ EXPECT_EQ(m2, transpose(m1));
+ EXPECT_EQ(vec2(1, 4), diag(m1));
+
+ EXPECT_EQ(identity, inverse(identity));
+
+ EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MORE MATRIX TESTS
+//------------------------------------------------------------------------------
+
+template <typename T>
+class MatTestT : public ::testing::Test {
+public:
+};
+
+typedef ::testing::Types<float,float> TestMatrixValueTypes;
+
+TYPED_TEST_CASE(MatTestT, TestMatrixValueTypes);
+
+#define TEST_MATRIX_INVERSE(MATRIX, EPSILON) \
+{ \
+ typedef decltype(MATRIX) MatrixType; \
+ MatrixType inv1 = inverse(MATRIX); \
+ MatrixType ident1 = MATRIX * inv1; \
+ static const MatrixType IDENTITY; \
+ for (size_t row = 0; row < MatrixType::ROW_SIZE; ++row) { \
+ for (size_t col = 0; col < MatrixType::COL_SIZE; ++col) { \
+ EXPECT_NEAR(ident1[row][col], IDENTITY[row][col], EPSILON); \
+ } \
+ } \
+}
+
+TYPED_TEST(MatTestT, Inverse4) {
+ typedef ::android::details::TMat44<TypeParam> M44T;
+
+ M44T m1(1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ M44T m2(0, -1, 0, 0,
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ M44T m3(1, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 0, 1,
+ 0, 0, -1, 0);
+
+ M44T m4(
+ 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+ -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00,
+ 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00,
+ 0.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+ M44T m5(
+ 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+ -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00,
+ 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00,
+ 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00);
+
+ TEST_MATRIX_INVERSE(m1, 0);
+ TEST_MATRIX_INVERSE(m2, 0);
+ TEST_MATRIX_INVERSE(m3, 0);
+ TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+ TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse3) {
+ typedef ::android::details::TMat33<TypeParam> M33T;
+
+ M33T m1(1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1);
+
+ M33T m2(0, -1, 0,
+ 1, 0, 0,
+ 0, 0, 1);
+
+ M33T m3(2, 0, 0,
+ 0, 0, 1,
+ 0, -1, 0);
+
+ M33T m4(
+ 4.683281e-01, 1.251189e-02, 0.000000e+00,
+ -8.749647e-01, 1.456563e-01, 0.000000e+00,
+ 0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+ M33T m5(
+ 4.683281e-01, 1.251189e-02, -8.834660e-01,
+ -8.749647e-01, 1.456563e-01, -4.617587e-01,
+ 1.229049e-01, 9.892561e-01, 7.916244e-02);
+
+ TEST_MATRIX_INVERSE(m1, 0);
+ TEST_MATRIX_INVERSE(m2, 0);
+ TEST_MATRIX_INVERSE(m3, 0);
+ TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+ TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse2) {
+ typedef ::android::details::TMat22<TypeParam> M22T;
+
+ M22T m1(1, 0,
+ 0, 1);
+
+ M22T m2(0, -1,
+ 1, 0);
+
+ M22T m3(
+ 4.683281e-01, 1.251189e-02,
+ -8.749647e-01, 1.456563e-01);
+
+ M22T m4(
+ 4.683281e-01, 1.251189e-02,
+ -8.749647e-01, 1.456563e-01);
+
+ TEST_MATRIX_INVERSE(m1, 0);
+ TEST_MATRIX_INVERSE(m2, 0);
+ TEST_MATRIX_INVERSE(m3, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+ TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+// A macro to help with vector comparisons within floating point range.
+#define EXPECT_VEC_EQ(VEC1, VEC2) \
+do { \
+ const decltype(VEC1) v1 = VEC1; \
+ const decltype(VEC2) v2 = VEC2; \
+ if (std::is_same<TypeParam,float>::value) { \
+ for (size_t i = 0; i < v1.size(); ++i) { \
+ EXPECT_FLOAT_EQ(v1[i], v2[i]); \
+ } \
+ } else if (std::is_same<TypeParam,float>::value) { \
+ for (size_t i = 0; i < v1.size(); ++i) { \
+ EXPECT_DOUBLE_EQ(v1[i], v2[i]); \
+ } \
+ } else { \
+ for (size_t i = 0; i < v1.size(); ++i) { \
+ EXPECT_EQ(v1[i], v2[i]); \
+ } \
+ } \
+} while(0)
+
+//------------------------------------------------------------------------------
+// A macro to help with type comparisons within floating point range.
+#define ASSERT_TYPE_EQ(T1, T2) \
+do { \
+ const decltype(T1) t1 = T1; \
+ const decltype(T2) t2 = T2; \
+ if (std::is_same<TypeParam,float>::value) { \
+ ASSERT_FLOAT_EQ(t1, t2); \
+ } else if (std::is_same<TypeParam,float>::value) { \
+ ASSERT_DOUBLE_EQ(t1, t2); \
+ } else { \
+ ASSERT_EQ(t1, t2); \
+ } \
+} while(0)
+
+//------------------------------------------------------------------------------
+// Test some translation stuff.
+TYPED_TEST(MatTestT, Translation4) {
+ typedef ::android::details::TMat44<TypeParam> M44T;
+ typedef ::android::details::TVec4<TypeParam> V4T;
+
+ V4T translateBy(-7.3, 1.1, 14.4, 0.0);
+ V4T translation(translateBy[0], translateBy[1], translateBy[2], 1.0);
+ M44T translation_matrix = M44T::translate(translation);
+
+ V4T p1(9.9, 3.1, 41.1, 1.0);
+ V4T p2(-18.0, 0.0, 1.77, 1.0);
+ V4T p3(0, 0, 0, 1);
+ V4T p4(-1000, -1000, 1000, 1.0);
+
+ EXPECT_VEC_EQ(translation_matrix * p1, translateBy + p1);
+ EXPECT_VEC_EQ(translation_matrix * p2, translateBy + p2);
+ EXPECT_VEC_EQ(translation_matrix * p3, translateBy + p3);
+ EXPECT_VEC_EQ(translation_matrix * p4, translateBy + p4);
+}
+
+//------------------------------------------------------------------------------
+template <typename MATRIX>
+static void verifyOrthonormal(const MATRIX& A) {
+ typedef typename MATRIX::value_type T;
+
+ static constexpr T value_eps = T(100) * std::numeric_limits<T>::epsilon();
+
+ const MATRIX prod = A * transpose(A);
+ for (size_t i = 0; i < MATRIX::NUM_COLS; ++i) {
+ for (size_t j = 0; j < MATRIX::NUM_ROWS; ++j) {
+ if (i == j) {
+ ASSERT_NEAR(prod[i][j], T(1), value_eps);
+ } else {
+ ASSERT_NEAR(prod[i][j], T(0), value_eps);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_44) {
+ typedef ::android::details::TMat44<TypeParam> M44T;
+
+ std::default_random_engine generator(82828);
+ std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M44T m = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ verifyOrthonormal(m);
+ }
+
+ M44T m = M44T::eulerZYX(1, 2, 3);
+ verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_33) {
+
+ typedef ::android::details::TMat33<TypeParam> M33T;
+
+ std::default_random_engine generator(112233);
+ std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M33T m = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ verifyOrthonormal(m);
+ }
+
+ M33T m = M33T::eulerZYX(1, 2, 3);
+ verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPostTranslation) {
+
+ typedef ::android::details::TMat44<TypeParam> M44T;
+ typedef ::android::details::TVec4<TypeParam> V4T;
+ typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+ std::default_random_engine generator(112233);
+ std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ M44T t = M44T::translate(V4T(rand_gen(), rand_gen(), rand_gen(), 1));
+ QuatT qr = r.toQuaternion();
+ M44T tr = t * r;
+ QuatT qtr = tr.toQuaternion();
+
+ ASSERT_TYPE_EQ(qr.x, qtr.x);
+ ASSERT_TYPE_EQ(qr.y, qtr.y);
+ ASSERT_TYPE_EQ(qr.z, qtr.z);
+ ASSERT_TYPE_EQ(qr.w, qtr.w);
+ }
+
+ M44T r = M44T::eulerZYX(1, 2, 3);
+ M44T t = M44T::translate(V4T(20, -15, 2, 1));
+ QuatT qr = r.toQuaternion();
+ M44T tr = t * r;
+ QuatT qtr = tr.toQuaternion();
+
+ ASSERT_TYPE_EQ(qr.x, qtr.x);
+ ASSERT_TYPE_EQ(qr.y, qtr.y);
+ ASSERT_TYPE_EQ(qr.z, qtr.z);
+ ASSERT_TYPE_EQ(qr.w, qtr.w);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation33) {
+ static constexpr TypeParam value_eps =
+ TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+ typedef ::android::details::TMat33<TypeParam> M33T;
+ typedef ::android::details::TVec3<TypeParam> V3T;
+ typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+ std::default_random_engine generator(112233);
+ std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M33T r = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ QuatT qr = r.toQuaternion();
+ V3T p(rand_gen(), rand_gen(), rand_gen());
+
+ V3T pr = r * p;
+ V3T pq = qr * p;
+
+ ASSERT_NEAR(pr.x, pq.x, value_eps);
+ ASSERT_NEAR(pr.y, pq.y, value_eps);
+ ASSERT_NEAR(pr.z, pq.z, value_eps);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation44) {
+ static constexpr TypeParam value_eps =
+ TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+ typedef ::android::details::TMat44<TypeParam> M44T;
+ typedef ::android::details::TVec4<TypeParam> V4T;
+ typedef ::android::details::TVec3<TypeParam> V3T;
+ typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+ std::default_random_engine generator(992626);
+ std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ QuatT qr = r.toQuaternion();
+ V3T p(rand_gen(), rand_gen(), rand_gen());
+
+ V4T pr = r * V4T(p.x, p.y, p.z, 1);
+ pr.x /= pr.w;
+ pr.y /= pr.w;
+ pr.z /= pr.w;
+ V3T pq = qr * p;
+
+ ASSERT_NEAR(pr.x, pq.x, value_eps);
+ ASSERT_NEAR(pr.y, pq.y, value_eps);
+ ASSERT_NEAR(pr.z, pq.z, value_eps);
+ }
+}
+
+#undef TEST_MATRIX_INVERSE
+
}; // namespace android
diff --git a/libs/ui/tests/quat_test.cpp b/libs/ui/tests/quat_test.cpp
new file mode 100644
index 0000000..f5cb659
--- /dev/null
+++ b/libs/ui/tests/quat_test.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "QuatTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <random>
+#include <functional>
+
+#include <ui/quat.h>
+#include <ui/mat4.h>
+#include <ui/vec3.h>
+#include <ui/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class QuatTest : public testing::Test {
+protected:
+};
+
+TEST_F(QuatTest, Basics) {
+ quatd q;
+ double4& v(q.xyzw);
+
+ EXPECT_EQ(sizeof(quatd), sizeof(double)*4);
+ EXPECT_EQ(reinterpret_cast<void*>(&q), reinterpret_cast<void*>(&v));
+}
+
+TEST_F(QuatTest, Constructors) {
+ quatd q0;
+ EXPECT_EQ(q0.x, 0);
+ EXPECT_EQ(q0.y, 0);
+ EXPECT_EQ(q0.z, 0);
+ EXPECT_EQ(q0.w, 0);
+
+ quatd q1(1);
+ EXPECT_EQ(q1.x, 0);
+ EXPECT_EQ(q1.y, 0);
+ EXPECT_EQ(q1.z, 0);
+ EXPECT_EQ(q1.w, 1);
+
+ quatd q2(1, 2, 3, 4);
+ EXPECT_EQ(q2.x, 2);
+ EXPECT_EQ(q2.y, 3);
+ EXPECT_EQ(q2.z, 4);
+ EXPECT_EQ(q2.w, 1);
+
+ quatd q3(q2);
+ EXPECT_EQ(q3.x, 2);
+ EXPECT_EQ(q3.y, 3);
+ EXPECT_EQ(q3.z, 4);
+ EXPECT_EQ(q3.w, 1);
+
+ quatd q4(q3.xyz, 42);
+ EXPECT_EQ(q4.x, 2);
+ EXPECT_EQ(q4.y, 3);
+ EXPECT_EQ(q4.z, 4);
+ EXPECT_EQ(q4.w, 42);
+
+ quatd q5(double3(q2.xy, 42), 24);
+ EXPECT_EQ(q5.x, 2);
+ EXPECT_EQ(q5.y, 3);
+ EXPECT_EQ(q5.z, 42);
+ EXPECT_EQ(q5.w, 24);
+
+ quatd q6;
+ q6 = 12;
+ EXPECT_EQ(q6.x, 0);
+ EXPECT_EQ(q6.y, 0);
+ EXPECT_EQ(q6.z, 0);
+ EXPECT_EQ(q6.w, 12);
+
+ quatd q7 = 1 + 2_id + 3_jd + 4_kd;
+ EXPECT_EQ(q7.x, 2);
+ EXPECT_EQ(q7.y, 3);
+ EXPECT_EQ(q7.z, 4);
+ EXPECT_EQ(q7.w, 1);
+
+ quatf qf(2);
+ EXPECT_EQ(qf.x, 0);
+ EXPECT_EQ(qf.y, 0);
+ EXPECT_EQ(qf.z, 0);
+ EXPECT_EQ(qf.w, 2);
+}
+
+TEST_F(QuatTest, Access) {
+ quatd q0(1, 2, 3, 4);
+ q0.x = 10;
+ q0.y = 20;
+ q0.z = 30;
+ q0.w = 40;
+ EXPECT_EQ(q0.x, 10);
+ EXPECT_EQ(q0.y, 20);
+ EXPECT_EQ(q0.z, 30);
+ EXPECT_EQ(q0.w, 40);
+
+ q0[0] = 100;
+ q0[1] = 200;
+ q0[2] = 300;
+ q0[3] = 400;
+ EXPECT_EQ(q0.x, 100);
+ EXPECT_EQ(q0.y, 200);
+ EXPECT_EQ(q0.z, 300);
+ EXPECT_EQ(q0.w, 400);
+
+ q0.xyz = double3(1, 2, 3);
+ EXPECT_EQ(q0.x, 1);
+ EXPECT_EQ(q0.y, 2);
+ EXPECT_EQ(q0.z, 3);
+ EXPECT_EQ(q0.w, 400);
+}
+
+TEST_F(QuatTest, UnaryOps) {
+ quatd q0(1, 2, 3, 4);
+
+ q0 += 1;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 2);
+
+ q0 -= 1;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 1);
+
+ q0 *= 2;
+ EXPECT_EQ(q0.x, 4);
+ EXPECT_EQ(q0.y, 6);
+ EXPECT_EQ(q0.z, 8);
+ EXPECT_EQ(q0.w, 2);
+
+ q0 /= 2;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 1);
+
+ quatd q1(10, 20, 30, 40);
+
+ q0 += q1;
+ EXPECT_EQ(q0.x, 22);
+ EXPECT_EQ(q0.y, 33);
+ EXPECT_EQ(q0.z, 44);
+ EXPECT_EQ(q0.w, 11);
+
+ q0 -= q1;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 1);
+
+ q1 = -q1;
+ EXPECT_EQ(q1.x, -20);
+ EXPECT_EQ(q1.y, -30);
+ EXPECT_EQ(q1.z, -40);
+ EXPECT_EQ(q1.w, -10);
+
+ // TODO(mathias): multiplies
+}
+
+TEST_F(QuatTest, ComparisonOps) {
+ quatd q0(1, 2, 3, 4);
+ quatd q1(10, 20, 30, 40);
+
+ EXPECT_TRUE(q0 == q0);
+ EXPECT_TRUE(q0 != q1);
+ EXPECT_FALSE(q0 != q0);
+ EXPECT_FALSE(q0 == q1);
+}
+
+TEST_F(QuatTest, ArithmeticOps) {
+ quatd q0(1, 2, 3, 4);
+ quatd q1(10, 20, 30, 40);
+
+ quatd q2(q0 + q1);
+ EXPECT_EQ(q2.x, 22);
+ EXPECT_EQ(q2.y, 33);
+ EXPECT_EQ(q2.z, 44);
+ EXPECT_EQ(q2.w, 11);
+
+ q0 = q1 * 2;
+ EXPECT_EQ(q0.x, 40);
+ EXPECT_EQ(q0.y, 60);
+ EXPECT_EQ(q0.z, 80);
+ EXPECT_EQ(q0.w, 20);
+
+ q0 = 2 * q1;
+ EXPECT_EQ(q0.x, 40);
+ EXPECT_EQ(q0.y, 60);
+ EXPECT_EQ(q0.z, 80);
+ EXPECT_EQ(q0.w, 20);
+
+ quatf qf(2);
+ q0 = q1 * qf;
+ EXPECT_EQ(q0.x, 40);
+ EXPECT_EQ(q0.y, 60);
+ EXPECT_EQ(q0.z, 80);
+ EXPECT_EQ(q0.w, 20);
+
+ EXPECT_EQ(1_id * 1_id, quat(-1));
+ EXPECT_EQ(1_jd * 1_jd, quat(-1));
+ EXPECT_EQ(1_kd * 1_kd, quat(-1));
+ EXPECT_EQ(1_id * 1_jd * 1_kd, quat(-1));
+}
+
+TEST_F(QuatTest, ArithmeticFunc) {
+ quatd q(1, 2, 3, 4);
+ quatd qc(conj(q));
+ __attribute__((unused)) quatd qi(inverse(q));
+ quatd qn(normalize(q));
+
+ EXPECT_EQ(qc.x, -2);
+ EXPECT_EQ(qc.y, -3);
+ EXPECT_EQ(qc.z, -4);
+ EXPECT_EQ(qc.w, 1);
+
+ EXPECT_EQ(~q, qc);
+ EXPECT_EQ(length(q), length(qc));
+ EXPECT_EQ(sqrt(30), length(q));
+ EXPECT_FLOAT_EQ(1, length(qn));
+ EXPECT_FLOAT_EQ(1, dot(qn, qn));
+
+ quatd qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+ EXPECT_EQ(mat4d(qr).toQuaternion(), qr);
+ EXPECT_EQ(1_id, mat4d(1_id).toQuaternion());
+ EXPECT_EQ(1_jd, mat4d(1_jd).toQuaternion());
+ EXPECT_EQ(1_kd, mat4d(1_kd).toQuaternion());
+
+
+ EXPECT_EQ(qr, log(exp(qr)));
+
+ quatd qq = qr * qr;
+ quatd q2 = pow(qr, 2);
+ EXPECT_NEAR(qq.x, q2.x, 1e-15);
+ EXPECT_NEAR(qq.y, q2.y, 1e-15);
+ EXPECT_NEAR(qq.z, q2.z, 1e-15);
+ EXPECT_NEAR(qq.w, q2.w, 1e-15);
+
+ quatd qa = quatd::fromAxisAngle(double3(0, 0, 1), 0);
+ quatd qb = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+ quatd qs = slerp(qa, qb, 0.5);
+ qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 4);
+ EXPECT_FLOAT_EQ(qr.x, qs.x);
+ EXPECT_FLOAT_EQ(qr.y, qs.y);
+ EXPECT_FLOAT_EQ(qr.z, qs.z);
+ EXPECT_FLOAT_EQ(qr.w, qs.w);
+
+ qs = nlerp(qa, qb, 0.5);
+ EXPECT_FLOAT_EQ(qr.x, qs.x);
+ EXPECT_FLOAT_EQ(qr.y, qs.y);
+ EXPECT_FLOAT_EQ(qr.z, qs.z);
+ EXPECT_FLOAT_EQ(qr.w, qs.w);
+}
+
+TEST_F(QuatTest, MultiplicationExhaustive) {
+ static constexpr double value_eps = double(1000) * std::numeric_limits<double>::epsilon();
+
+ std::default_random_engine generator(171717);
+ std::uniform_real_distribution<double> distribution(-10.0, 10.0);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < (1024 * 1024); ++i) {
+ double3 axis_a = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+ double angle_a = rand_gen();
+ quatd a = quatd::fromAxisAngle(axis_a, angle_a);
+
+ double3 axis_b = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+ double angle_b = rand_gen();
+ quatd b = quatd::fromAxisAngle(axis_b, angle_b);
+
+ quatd ab = a * b;
+ quatd ab_other(a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz),
+ (a.w * b.w) - dot(a.xyz, b.xyz));
+
+ ASSERT_NEAR(ab.x, ab_other.x, value_eps);
+ ASSERT_NEAR(ab.y, ab_other.y, value_eps);
+ ASSERT_NEAR(ab.z, ab_other.z, value_eps);
+ ASSERT_NEAR(ab.w, ab_other.w, value_eps);
+ }
+}
+
+}; // namespace android
diff --git a/libs/ui/tests/vec_test.cpp b/libs/ui/tests/vec_test.cpp
index 454c999..586d1a8 100644
--- a/libs/ui/tests/vec_test.cpp
+++ b/libs/ui/tests/vec_test.cpp
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-#define LOG_TAG "RegionTest"
+#define LOG_TAG "VecTest"
#include <math.h>
#include <stdlib.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
#include <ui/vec4.h>
#include <gtest/gtest.h>
@@ -37,7 +35,7 @@
EXPECT_EQ(sizeof(vec4), sizeof(float)*4);
EXPECT_EQ(sizeof(vec3), sizeof(float)*3);
EXPECT_EQ(sizeof(vec2), sizeof(float)*2);
- EXPECT_EQ((void*)&v3, (void*)&v4);
+ EXPECT_EQ(reinterpret_cast<void*>(&v3), reinterpret_cast<void*>(&v4));
}
TEST_F(VecTest, Constructors) {
@@ -53,7 +51,7 @@
EXPECT_EQ(v1.z, 1);
EXPECT_EQ(v1.w, 1);
- vec4 v2(1,2,3,4);
+ vec4 v2(1, 2, 3, 4);
EXPECT_EQ(v2.x, 1);
EXPECT_EQ(v2.y, 2);
EXPECT_EQ(v2.z, 3);
@@ -77,15 +75,16 @@
EXPECT_EQ(v5.z, 42);
EXPECT_EQ(v5.w, 24);
- tvec4<double> vd(2);
- EXPECT_EQ(vd.x, 2);
- EXPECT_EQ(vd.y, 2);
- EXPECT_EQ(vd.z, 2);
- EXPECT_EQ(vd.w, 2);
+ float4 vf(2);
+ EXPECT_EQ(vf.x, 2);
+ EXPECT_EQ(vf.y, 2);
+ EXPECT_EQ(vf.z, 2);
+ EXPECT_EQ(vf.w, 2);
}
TEST_F(VecTest, Access) {
- vec4 v0(1,2,3,4);
+ vec4 v0(1, 2, 3, 4);
+
v0.x = 10;
v0.y = 20;
v0.z = 30;
@@ -104,7 +103,7 @@
EXPECT_EQ(v0.z, 300);
EXPECT_EQ(v0.w, 400);
- v0.xyz = vec3(1,2,3);
+ v0.xyz = vec3(1, 2, 3);
EXPECT_EQ(v0.x, 1);
EXPECT_EQ(v0.y, 2);
EXPECT_EQ(v0.z, 3);
@@ -112,7 +111,7 @@
}
TEST_F(VecTest, UnaryOps) {
- vec4 v0(1,2,3,4);
+ vec4 v0(1, 2, 3, 4);
v0 += 1;
EXPECT_EQ(v0.x, 2);
@@ -164,41 +163,23 @@
EXPECT_EQ(v0.z, 3);
EXPECT_EQ(v0.w, 4);
- ++v0;
- EXPECT_EQ(v0.x, 2);
- EXPECT_EQ(v0.y, 3);
- EXPECT_EQ(v0.z, 4);
- EXPECT_EQ(v0.w, 5);
-
- ++++v0;
- EXPECT_EQ(v0.x, 4);
- EXPECT_EQ(v0.y, 5);
- EXPECT_EQ(v0.z, 6);
- EXPECT_EQ(v0.w, 7);
-
- --v1;
- EXPECT_EQ(v1.x, 9);
- EXPECT_EQ(v1.y, 19);
- EXPECT_EQ(v1.z, 29);
- EXPECT_EQ(v1.w, 39);
-
v1 = -v1;
- EXPECT_EQ(v1.x, -9);
- EXPECT_EQ(v1.y, -19);
- EXPECT_EQ(v1.z, -29);
- EXPECT_EQ(v1.w, -39);
+ EXPECT_EQ(v1.x, -10);
+ EXPECT_EQ(v1.y, -20);
+ EXPECT_EQ(v1.z, -30);
+ EXPECT_EQ(v1.w, -40);
- tvec4<double> dv(1,2,3,4);
- v1 += dv;
- EXPECT_EQ(v1.x, -8);
- EXPECT_EQ(v1.y, -17);
- EXPECT_EQ(v1.z, -26);
- EXPECT_EQ(v1.w, -35);
+ float4 fv(1, 2, 3, 4);
+ v1 += fv;
+ EXPECT_EQ(v1.x, -9);
+ EXPECT_EQ(v1.y, -18);
+ EXPECT_EQ(v1.z, -27);
+ EXPECT_EQ(v1.w, -36);
}
TEST_F(VecTest, ComparisonOps) {
- vec4 v0(1,2,3,4);
- vec4 v1(10,20,30,40);
+ vec4 v0(1, 2, 3, 4);
+ vec4 v1(10, 20, 30, 40);
EXPECT_TRUE(v0 == v0);
EXPECT_TRUE(v0 != v1);
@@ -207,8 +188,8 @@
}
TEST_F(VecTest, ArithmeticOps) {
- vec4 v0(1,2,3,4);
- vec4 v1(10,20,30,40);
+ vec4 v0(1, 2, 3, 4);
+ vec4 v1(10, 20, 30, 40);
vec4 v2(v0 + v1);
EXPECT_EQ(v2.x, 11);
@@ -228,8 +209,8 @@
EXPECT_EQ(v0.z, 60);
EXPECT_EQ(v0.w, 80);
- tvec4<double> vd(2);
- v0 = v1 * vd;
+ float4 vf(2);
+ v0 = v1 * vf;
EXPECT_EQ(v0.x, 20);
EXPECT_EQ(v0.y, 40);
EXPECT_EQ(v0.z, 60);
@@ -239,19 +220,19 @@
TEST_F(VecTest, ArithmeticFunc) {
vec3 east(1, 0, 0);
vec3 north(0, 1, 0);
- vec3 up( cross(east, north) );
- EXPECT_EQ(up, vec3(0,0,1));
+ vec3 up(cross(east, north));
+ EXPECT_EQ(up, vec3(0, 0, 1));
EXPECT_EQ(dot(east, north), 0);
EXPECT_EQ(length(east), 1);
EXPECT_EQ(distance(east, north), sqrtf(2));
- vec3 v0(1,2,3);
+ vec3 v0(1, 2, 3);
vec3 vn(normalize(v0));
EXPECT_FLOAT_EQ(1, length(vn));
EXPECT_FLOAT_EQ(length(v0), dot(v0, vn));
- tvec3<double> vd(east);
- EXPECT_EQ(length(vd), 1);
+ float3 vf(east);
+ EXPECT_EQ(length(vf), 1);
}
}; // namespace android
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 8b48032..8b754d5 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -607,6 +607,7 @@
#ifndef EGL_ANDROID_create_native_client_buffer
#define EGL_ANDROID_create_native_client_buffer 1
+#define EGL_LAYER_COUNT_ANDROID 0x3434
#define EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
#define EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
#define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
@@ -631,12 +632,16 @@
#ifndef EGL_ANDROID_get_frame_timestamps
#define EGL_ANDROID_get_frame_timestamps 1
#define EGL_TIMESTAMPS_ANDROID 0x314D
-#define EGL_QUEUE_TIME_ANDROID 0x314E
+#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E
#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
-#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430
-#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
-#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
-#define EGL_READS_DONE_TIME_ANDROID 0x3433
+#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150
+#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151
+#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152
+#define EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153
+#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155
+#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3156
+#define EGL_READS_DONE_TIME_ANDROID 0x3157
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 218ab35..ac455cd 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -98,24 +98,6 @@
// ----------------------------------------------------------------------------
-static char const * getProcessCmdline() {
- long pid = getpid();
- char procPath[128];
- snprintf(procPath, 128, "/proc/%ld/cmdline", pid);
- FILE * file = fopen(procPath, "r");
- if (file) {
- static char cmdline[256];
- char *str = fgets(cmdline, sizeof(cmdline) - 1, file);
- fclose(file);
- if (str) {
- return cmdline;
- }
- }
- return NULL;
-}
-
-// ----------------------------------------------------------------------------
-
Loader::driver_t::driver_t(void* gles)
{
dso[0] = gles;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index a42b3f1..97343a1 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -476,6 +476,7 @@
// modify the EGLconfig's format before setting the native window's
// format.
+ // TODO: Add support for HAL_PIXEL_FORMAT_RGBA_FP16
// by default, just pick RGBA_8888
EGLint format = HAL_PIXEL_FORMAT_RGBA_8888;
android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN;
@@ -1140,6 +1141,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;
@@ -1195,6 +1206,9 @@
egl_surface_t * const s = get_surface(surface);
if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+ if (!s->win.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
int err = native_window_set_auto_refresh(s->win.get(),
value ? true : false);
return (err == NO_ERROR) ? EGL_TRUE :
@@ -1203,8 +1217,13 @@
#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
if (attribute == EGL_TIMESTAMPS_ANDROID) {
- s->enableTimestamps = value;
- return EGL_TRUE;
+ if (!s->win.get()) {
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+ int err = native_window_enable_frame_timestamps(
+ s->win.get(), value ? true : false);
+ return (err == NO_ERROR) ? EGL_TRUE :
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
}
#endif
@@ -1793,6 +1812,7 @@
uint32_t width = 0;
uint32_t height = 0;
uint32_t format = 0;
+ uint32_t layer_count = 1;
uint32_t red_size = 0;
uint32_t green_size = 0;
uint32_t blue_size = 0;
@@ -1818,6 +1838,7 @@
GET_NONNEGATIVE_VALUE(EGL_GREEN_SIZE, green_size);
GET_NONNEGATIVE_VALUE(EGL_BLUE_SIZE, blue_size);
GET_NONNEGATIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
+ GET_NONNEGATIVE_VALUE(EGL_LAYER_COUNT_ANDROID, layer_count);
case EGL_NATIVE_BUFFER_USAGE_ANDROID:
if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
usage |= GRALLOC_USAGE_PROTECTED;
@@ -1847,7 +1868,7 @@
alpha_size == 0) {
format = HAL_PIXEL_FORMAT_RGB_565;
} else {
- ALOGE("Invalid native pixel format { r=%d, g=%d, b=%d, a=%d }",
+ ALOGE("Invalid native pixel format { r=%u, g=%u, b=%u, a=%u }",
red_size, green_size, blue_size, alpha_size);
return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
}
@@ -1892,7 +1913,9 @@
CHECK_ERROR_CONDITION("Unable to write height");
err = data.writeInt32(static_cast<int32_t>(format));
CHECK_ERROR_CONDITION("Unable to write format");
- err = data.writeUint32(usage);
+ err = data.writeUint32(layer_count);
+ CHECK_ERROR_CONDITION("Unable to write layer count");
+ err = data.writeUint32(usage);
CHECK_ERROR_CONDITION("Unable to write usage");
err = data.writeUtf8AsUtf16(
std::string("[eglCreateNativeClientBufferANDROID pid ") +
@@ -1909,12 +1932,13 @@
err = gBuffer->initCheck();
if (err != NO_ERROR) {
- ALOGE("Unable to create native buffer { w=%d, h=%d, f=%d, u=%#x }: %#x",
- width, height, format, usage, err);
+ ALOGE("Unable to create native buffer "
+ "{ w=%u, h=%u, f=%u, u=%#x, lc=%u}: %#x", width, height, format,
+ usage, layer_count, err);
goto error_condition;
}
- ALOGD("Created new native buffer %p { w=%d, h=%d, f=%d, u=%#x }",
- gBuffer, width, height, format, usage);
+ ALOGV("Created new native buffer %p { w=%u, h=%u, f=%u, u=%#x, lc=%u}",
+ gBuffer, width, height, format, usage, layer_count);
return static_cast<EGLClientBuffer>(gBuffer->getNativeBuffer());
#undef CHECK_ERROR_CONDITION
@@ -2001,66 +2025,87 @@
const egl_display_ptr dp = validate_display(dpy);
if (!dp) {
- setError(EGL_BAD_DISPLAY, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
}
SurfaceRef _s(dp.get(), surface);
if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
egl_surface_t const * const s = get_surface(surface);
- if (!s->enableTimestamps) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
+ if (!s->win.get()) {
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
- nsecs_t* postedTime = nullptr;
+ nsecs_t* requestedPresentTime = nullptr;
nsecs_t* acquireTime = nullptr;
- nsecs_t* refreshStartTime = nullptr;
+ nsecs_t* latchTime = nullptr;
+ nsecs_t* firstRefreshStartTime = nullptr;
nsecs_t* GLCompositionDoneTime = nullptr;
+ nsecs_t* lastRefreshStartTime = nullptr;
+ nsecs_t* displayPresentTime = nullptr;
nsecs_t* displayRetireTime = nullptr;
+ nsecs_t* dequeueReadyTime = nullptr;
nsecs_t* releaseTime = nullptr;
for (int i = 0; i < numTimestamps; i++) {
switch (timestamps[i]) {
- case EGL_QUEUE_TIME_ANDROID:
- postedTime = &values[i];
+ case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+ requestedPresentTime = &values[i];
break;
case EGL_RENDERING_COMPLETE_TIME_ANDROID:
acquireTime = &values[i];
break;
- case EGL_COMPOSITION_START_TIME_ANDROID:
- refreshStartTime = &values[i];
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ latchTime = &values[i];
break;
- case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ firstRefreshStartTime = &values[i];
+ break;
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ lastRefreshStartTime = &values[i];
+ break;
+ case EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID:
GLCompositionDoneTime = &values[i];
break;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+ displayPresentTime = &values[i];
+ break;
case EGL_DISPLAY_RETIRE_TIME_ANDROID:
displayRetireTime = &values[i];
break;
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
+ dequeueReadyTime = &values[i];
+ break;
case EGL_READS_DONE_TIME_ANDROID:
releaseTime = &values[i];
break;
default:
- setError(EGL_BAD_PARAMETER, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
}
}
status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
- postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime,
- displayRetireTime, releaseTime);
+ requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+ lastRefreshStartTime, GLCompositionDoneTime, displayPresentTime,
+ displayRetireTime, dequeueReadyTime, releaseTime);
- if (ret != NO_ERROR) {
- setError(EGL_BAD_ACCESS, EGL_FALSE);
- return EGL_FALSE;
+ switch (ret) {
+ case NO_ERROR:
+ return EGL_TRUE;
+ case NAME_NOT_FOUND:
+ return setError(EGL_BAD_ACCESS, EGL_FALSE);
+ case INVALID_OPERATION:
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ case BAD_VALUE:
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
}
-
- return EGL_TRUE;
}
EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
@@ -2070,25 +2115,44 @@
const egl_display_ptr dp = validate_display(dpy);
if (!dp) {
- setError(EGL_BAD_DISPLAY, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
}
SurfaceRef _s(dp.get(), surface);
if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ ANativeWindow* window = s->win.get();
+ if (!window) {
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
switch (timestamp) {
#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
- case EGL_QUEUE_TIME_ANDROID:
+ case EGL_REQUESTED_PRESENT_TIME_ANDROID:
case EGL_RENDERING_COMPLETE_TIME_ANDROID:
- case EGL_COMPOSITION_START_TIME_ANDROID:
- case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
- case EGL_DISPLAY_RETIRE_TIME_ANDROID:
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID:
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
case EGL_READS_DONE_TIME_ANDROID:
return EGL_TRUE;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+ int value = 0;
+ window->query(window,
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+ return value == 0 ? EGL_FALSE : EGL_TRUE;
+ }
+ case EGL_DISPLAY_RETIRE_TIME_ANDROID: {
+ int value = 0;
+ window->query(window,
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &value);
+ return value == 0 ? EGL_FALSE : EGL_TRUE;
+ }
#endif
default:
return EGL_FALSE;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 6a76737..7fc5609 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -68,7 +68,7 @@
EGLNativeWindowType win, EGLSurface surface,
egl_connection_t const* cnx) :
egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx),
- enableTimestamps(false), connected(true)
+ connected(true)
{}
egl_surface_t::~egl_surface_t() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 3150ba6..8ceba1d 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -139,7 +139,6 @@
EGLConfig config;
sp<ANativeWindow> win;
egl_connection_t const* cnx;
- bool enableTimestamps;
private:
bool connected;
void disconnect();
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
index 51c6c61..4c5551c 100644
--- a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
+++ b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
@@ -20,7 +20,7 @@
Version
- Version 1, January 19, 2016
+ Version 1.1, October 26, 2016
Number
@@ -53,6 +53,7 @@
New Tokens
+ EGL_NATIVE_BUFFER_LAYER_COUNT_ANDROID 0x3434
EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
@@ -103,6 +104,8 @@
| EGL_ALPHA_SIZE | The bits of Alpha in | 0 |
| | the color buffer | |
| | buffer data | |
+ | EGL_LAYER_COUNT_ANDROID | The number of image | 1 |
+ | | layers in the buffer | |
| EGL_NATIVE_BUFFER_USAGE_ANDROID | The usage bits of | 0 |
| | the buffer data | |
+---------------------------------+----------------------+---------------+
@@ -114,8 +117,10 @@
EGL_RED_SIZE, EGL_GREEN_SIZE, and EGL_BLUE_SIZE must be non-zero and
correspond to a valid pixel format for the implementation. If EGL_ALPHA_SIZE
is non-zero then the combination of all four sizes must correspond to a
- valid pixel format for the implementation. The
- EGL_NATIVE_BUFFER_USAGE_ANDROID flag may include any of the following bits:
+ valid pixel format for the implementation. The value of
+ EGL_LAYER_COUNT_ANDROID must be a valid number of image layers for the
+ implementation. The EGL_NATIVE_BUFFER_USAGE_ANDROID flag may include any of
+ the following bits:
EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID: Indicates that the
created buffer must have a hardware-protected path to external display
@@ -182,6 +187,10 @@
Revision History
+#3 (Craig Donner, October 26, 2016)
+ - Added EGL_LAYER_COUNT_ANDROID for creating buffers that back texture
+ arrays.
+
#2 (Craig Donner, April 15, 2016)
- Set color formats and usage bits explicitly using additional attributes,
and add value for new token EGL_NATIVE_BUFFER_USAGE_ANDROID.
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index 30337ad..7aa0d30 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -38,8 +38,8 @@
and display of window surfaces.
Some examples of how this might be used:
- - The display retire time can be used to calculate end-to-end latency of
- the entire graphics pipeline.
+ - The display present or retire time can be used to calculate end-to-end
+ latency of the entire graphics pipeline.
- The queue time and rendering complete time can be used to determine
how long the application's rendering took to complete. Likewise, the
composition start time and finish time can be used to determine how
@@ -67,12 +67,16 @@
New Tokens
EGL_TIMESTAMPS_ANDROID 0x314D
- EGL_QUEUE_TIME_ANDROID 0x314E
+ EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E
EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
- EGL_COMPOSITION_START_TIME_ANDROID 0x3430
- EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
- EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
- EGL_READS_DONE_TIME_ANDROID 0x3433
+ EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150
+ EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151
+ EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152
+ EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153
+ EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154
+ EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155
+ EGL_DEQUEUE_READY_TIME_ANDROID 0x3156
+ EGL_READS_DONE_TIME_ANDROID 0x3157
Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
"Surface Attributes", page 43:
@@ -98,9 +102,9 @@
allows querying various timestamps related to the composition and display of
a window surface.
- The framesAgo parameter indicates how many frames before the last posted
+ The framesAgo parameter indicates how many frames before the last queued
frame to query. So a value of zero would indicate that the query is for the
- last posted frame. Note that the implementation maintains a limited history
+ last queued frame. Note that the implementation maintains a limited history
of timestamp data. If a query is made for a frame whose timestamp history
no longer exists then EGL_BAD_ACCESS is generated. If timestamp collection
has not been enabled for the surface then EGL_BAD_SURFACE is generated.
@@ -112,22 +116,41 @@
The eglGetFrameTimestampsANDROID function takes an array of timestamps to
query and returns timestamps in the corresponding indices of the values
array. The possible timestamps that can be queried are:
- - EGL_QUEUE_TIME_ANDROID - The time this frame was queued by the
- application.
+ - EGL_REQUESTED_PRESENT_TIME_ANDROID - The time the application
+ requested this frame be presented. See EGL_ANDROID_presentation_time.
+ If the application does not request a presentation time explicitly,
+ this will correspond to buffer's queue time.
- EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the
application's rendering to the surface was completed.
- - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor
- began preparing composition for this frame.
- - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
+ - EGL_COMPOSITION_LATCH_TIME_ANDROID - The time when the compositor
+ selected this frame as the one to use for the next composition. The
+ latch is the earliest indication that the frame was submitted in time
+ to be composited.
+ - EGL_FIRST_COMPOSITION_START_TIME_ANDROID - The first time at which
+ the compositor began preparing composition for this frame.
+ - EGL_LAST_COMPOSITION_START_TIME_ANDROID - The last time at which the
+ compositor began preparing composition for this frame. If this frame
+ is composited only once, it will have the same value as
+ EGL_FIRST_COMPOSITION_START_TIME_ANDROID. If the value is not equal,
+ that indicates the subsequent frame was not submitted in time to be
+ latched by the compositor. Note: The value may not be updated for
+ every display refresh if the compositor becomes idle.
+ - EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
compositor's rendering work for this frame finished. This will be zero
if composition was handled by the display and the compositor didn't do
any rendering.
+ - EGL_DISPLAY_PRESENT_TIME_ANDROID - The time at which this frame
+ started to scan out to the physical display.
- EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
replaced by the next frame on-screen.
+ - EGL_DEQUEUE_READY_TIME_ANDROID - The time when the buffer became
+ available for reuse as a buffer the client can target without
+ blocking. This is generally the point when all read commands of the
+ buffer have been submitted, but not necessarily completed.
- EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the
purpose of display/composition were completed for this frame.
- Not all implementations may support all off the above timestamp queries. The
+ Not all implementations may support all of the above timestamp queries. The
function
EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
@@ -143,3 +166,12 @@
#1 (Pablo Ceballos, May 31, 2016)
- Initial draft.
+
+#2 (Brian Anderson, July 22, 2016)
+ - Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID.
+ - Add DISPLAY_PRESENT_TIME_ANDROID.
+
+#3 (Brian Anderson, November 30, 2016)
+ - Add EGL_COMPOSITION_LATCH_TIME_ANDROID,
+ EGL_LAST_COMPOSITION_START_TIME_ANDROID, and
+ EGL_DEQUEUE_READY_TIME_ANDROID.
diff --git a/opengl/specs/README b/opengl/specs/README
index f0c024e..8a3a7aa 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -20,10 +20,14 @@
0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
0x314D EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x314E EGL_QUEUE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x314E EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3430 EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3431 EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3432 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3433 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3434 - 0x343F (unused)
+0x3150 EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3151 EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3152 EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3153 EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3154 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3155 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3156 EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3157 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3158 - 0x315F (unused)
diff --git a/opengl/tests/EGLTest/Android.mk b/opengl/tests/EGLTest/Android.mk
index 80e4867..b772450 100644
--- a/opengl/tests/EGLTest/Android.mk
+++ b/opengl/tests/EGLTest/Android.mk
@@ -17,6 +17,7 @@
libbinder \
libutils \
libgui \
+ libbase \
LOCAL_C_INCLUDES := \
bionic/libc/private \
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index d69a275..2b9c38e 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -24,6 +24,8 @@
namespace android {
+#define EGL_UNSIGNED_TRUE static_cast<EGLBoolean>(EGL_TRUE)
+
class EGLTest : public ::testing::Test {
protected:
EGLDisplay mEglDisplay;
@@ -48,7 +50,7 @@
virtual void TearDown() {
EGLBoolean success = eglTerminate(mEglDisplay);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
}
};
@@ -65,20 +67,20 @@
};
success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_GE(numConfigs, 1);
EGLint components[3];
success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
EXPECT_GE(components[0], 8);
@@ -139,23 +141,23 @@
};
success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_GE(numConfigs, 1);
EGLint components[4];
success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
success = eglGetConfigAttrib(mEglDisplay, config, EGL_ALPHA_SIZE, &components[3]);
- ASSERT_EQ(EGL_TRUE, success);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
EXPECT_GE(components[0], 8);
diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp
index c5bf296..c974f63 100644
--- a/opengl/tests/EGLTest/egl_cache_test.cpp
+++ b/opengl/tests/EGLTest/egl_cache_test.cpp
@@ -21,9 +21,13 @@
#include <utils/Log.h>
+#include <android-base/test_utils.h>
+
#include "egl_cache.h"
#include "egl_display.h"
+#include <memory>
+
namespace android {
class EGLCacheTest : public ::testing::Test {
@@ -79,23 +83,20 @@
virtual void SetUp() {
EGLCacheTest::SetUp();
-
- char* tn = tempnam("/sdcard", "EGL_test-cache-");
- mFilename = tn;
- free(tn);
+ mTempFile.reset(new TemporaryFile());
}
virtual void TearDown() {
- unlink(mFilename.string());
+ mTempFile.reset(nullptr);
EGLCacheTest::TearDown();
}
- String8 mFilename;
+ std::unique_ptr<TemporaryFile> mTempFile;
};
TEST_F(EGLCacheSerializationTest, ReinitializedCacheContainsValues) {
uint8_t buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mCache->setCacheFilename(mFilename);
+ mCache->setCacheFilename(&mTempFile->path[0]);
mCache->initialize(egl_display_t::get(EGL_DEFAULT_DISPLAY));
mCache->setBlob("abcd", 4, "efgh", 4);
mCache->terminate();
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 6caf076..e8691bb 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -985,6 +985,7 @@
boolean emitExceptionCheck = ((numArrays > 0 || numStrings > 0)
&& (hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs)
|| (cfunc.hasPointerArg() && numArrays > 0))
+ || (numBufferArgs > 0)
|| hasCheckTest(cfunc)
|| hasIfTest(cfunc))
|| (stringArgs.size() > 0);
@@ -1308,6 +1309,8 @@
out.println();
} else if (jfunc.getArgType(idx).isBuffer()) {
+ needsExit = needsExit || (!nullAllowed && !isPointerFunc);
+
String array = numBufferArgs <= 1 ? "_array" :
"_" + cfunc.getArgName(cIndex) + "Array";
String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
@@ -1318,6 +1321,17 @@
out.println(indent + "if (" + cname + "_buf) {");
out.print(indent);
}
+ else
+ {
+ out.println(indent + "if (!" + cname + "_buf) {");
+ out.println(indent + indent + "_exception = 1;");
+ out.println(indent + indent + "_exceptionType = " +
+ "\"java/lang/IllegalArgumentException\";");
+ out.println(indent + indent + "_exceptionMessage = \"" +
+ cname +" == null\";");
+ out.println(indent + indent + "goto exit;");
+ out.println(indent + "}");
+ }
if (isPointerFunc) {
out.println(indent +
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
index a5a8968..523bc57 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
@@ -28,6 +28,7 @@
public static final int EGL_CONTEXT_MINOR_VERSION_KHR = 0x30FB;
public static final int EGL_CONTEXT_FLAGS_KHR = 0x30FC;
public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
+ public static final int EGL_RECORDABLE_ANDROID = 0x3142;
native private static void _nativeClassInit();
static {
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
new file mode 100644
index 0000000..3eacf3c
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
@@ -0,0 +1,9 @@
+/* EGLSurface eglCreatePixmapSurface ( EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list ) */
+static jobject
+android_eglCreatePixmapSurface
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jint pixmap, jintArray attrib_list_ref, jint offset) {
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
+ "eglCreatePixmapSurface");
+ return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
new file mode 100644
index 0000000..1750b32
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
@@ -0,0 +1,11 @@
+ // C function EGLSurface eglCreatePixmapSurface ( EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list )
+
+ @Deprecated
+ public static native EGLSurface eglCreatePixmapSurface(
+ EGLDisplay dpy,
+ EGLConfig config,
+ int pixmap,
+ int[] attrib_list,
+ int offset
+ );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg
new file mode 100644
index 0000000..fa260d8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg
@@ -0,0 +1 @@
+{"eglCreatePixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;I[II)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePixmapSurface },
diff --git a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
index c966e11..57338c7 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
+++ b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
@@ -1,5 +1,9 @@
// C function void glGetBufferPointerv ( GLenum target, GLenum pname, GLvoid** params )
+ /**
+ * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+ * to be an instance of {@link java.nio.ByteBuffer}.
+ */
public static native java.nio.Buffer glGetBufferPointerv(
int target,
int pname
diff --git a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
index dd656b6..6d42e56 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
@@ -5,15 +5,16 @@
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (!infoLen) {
- return _env->NewStringUTF("");
+ infoLen = 512;
}
char* buf = (char*) malloc(infoLen);
if (buf == NULL) {
jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
return NULL;
}
- glGetShaderInfoLog(shader, infoLen, NULL, buf);
- jstring result = _env->NewStringUTF(buf);
+ GLsizei outLen = 0;
+ glGetShaderInfoLog(shader, infoLen, &outLen, buf);
+ jstring result = _env->NewStringUTF(outLen == 0 ? "" : buf);
free(buf);
return result;
}
diff --git a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
index 482ea99..7b1966b 100644
--- a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
+++ b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
@@ -1,5 +1,9 @@
// C function GLvoid * glMapBufferRange ( GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access )
+ /**
+ * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+ * to be an instance of {@link java.nio.ByteBuffer}.
+ */
public static native java.nio.Buffer glMapBufferRange(
int target,
int offset,
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
new file mode 100644
index 0000000..04dd967
--- /dev/null
+++ b/services/audiomanager/Android.bp
@@ -0,0 +1,18 @@
+cc_library_shared {
+ name: "libaudiomanager",
+
+ srcs: ["IAudioManager.cpp"],
+
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ "liblog",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
new file mode 100644
index 0000000..a41804f
--- /dev/null
+++ b/services/audiomanager/IAudioManager.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 "IAudioManager"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <audiomanager/AudioManager.h>
+#include <audiomanager/IAudioManager.h>
+
+namespace android {
+
+class BpAudioManager : public BpInterface<IAudioManager>
+{
+public:
+ explicit BpAudioManager(const sp<IBinder>& impl)
+ : BpInterface<IAudioManager>(impl)
+ {
+ }
+
+ virtual audio_unique_id_t trackPlayer(int playerType, int usage, int content) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeInt32(1); // non-null PlayerIdCard parcelable
+ // marshall PlayerIdCard data
+ data.writeInt32((int32_t) playerType);
+ // write attributes of PlayerIdCard
+ data.writeInt32(usage);
+ data.writeInt32(content);
+ data.writeInt32(0 /*source: none here, this is a player*/);
+ data.writeInt32(0 /*flags*/);
+ // write attributes' tags
+ data.writeInt32(1 /*FLATTEN_TAGS*/);
+ data.writeString16(String16("")); // no tags
+ // write attributes' bundle
+ data.writeInt32(-1977 /*ATTR_PARCEL_IS_NULL_BUNDLE*/); // no bundle
+ // get new PIId in reply
+ const status_t res = remote()->transact(TRACK_PLAYER, data, &reply, 0);
+ if (res != OK || reply.readExceptionCode() != 0) {
+ ALOGE("trackPlayer() failed, piid is %d", PLAYER_PIID_INVALID);
+ return PLAYER_PIID_INVALID;
+ } else {
+ const audio_unique_id_t piid = (audio_unique_id_t) reply.readInt32();
+ ALOGV("trackPlayer() returned piid %d", piid);
+ return piid;
+ }
+ }
+
+ virtual status_t playerAttributes(audio_unique_id_t piid, int usage, int content) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeInt32(piid);
+ data.writeInt32(1); // non-null AudioAttributes parcelable
+ data.writeInt32(usage);
+ data.writeInt32(content);
+ data.writeInt32(0 /*source: none here, this is a player*/);
+ data.writeInt32(0 /*flags*/);
+ // write attributes' tags
+ data.writeInt32(1 /*FLATTEN_TAGS*/);
+ data.writeString16(String16("")); // no tags
+ // write attributes' bundle
+ data.writeInt32(-1977 /*ATTR_PARCEL_IS_NULL_BUNDLE*/); // no bundle
+ return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t playerEvent(int piid, int event) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeInt32(piid);
+ data.writeInt32(event);
+ return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t releasePlayer(int piid) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeInt32(piid);
+ return remote()->transact(RELEASE_PLAYER, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AudioManager, "android.media.IAudioService");
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/batteryservice/BatteryProperties.cpp b/services/batteryservice/BatteryProperties.cpp
index d89d4c9..8fa111d 100644
--- a/services/batteryservice/BatteryProperties.cpp
+++ b/services/batteryservice/BatteryProperties.cpp
@@ -41,6 +41,7 @@
batteryLevel = p->readInt32();
batteryVoltage = p->readInt32();
batteryTemperature = p->readInt32();
+ batteryFullCharge = p->readInt32();
batteryChargeCounter = p->readInt32();
batteryTechnology = String8((p->readString16()).string());
return OK;
@@ -58,6 +59,7 @@
p->writeInt32(batteryLevel);
p->writeInt32(batteryVoltage);
p->writeInt32(batteryTemperature);
+ p->writeInt32(batteryFullCharge);
p->writeInt32(batteryChargeCounter);
p->writeString16(String16(batteryTechnology));
return OK;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index f12320d..a7fe4f6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2078,6 +2078,25 @@
ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+ ASSERT_EQ(0, args.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
+ ASSERT_EQ(0, args.edgeFlags);
+ ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
// Button release. Should have same down time.
process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
@@ -2086,6 +2105,25 @@
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_EQ(0, args.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(0, args.buttonState);
+ ASSERT_EQ(0, args.edgeFlags);
+ ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
ASSERT_EQ(0, args.flags);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
@@ -2140,10 +2178,20 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
// Button release.
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2167,6 +2215,12 @@
1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
// Move X, Y a bit while pressed.
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 2);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 1);
@@ -2181,6 +2235,11 @@
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2277,19 +2336,33 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2306,23 +2379,57 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
@@ -2336,19 +2443,35 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
@@ -2361,21 +2484,37 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -2386,21 +2525,37 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2411,21 +2566,37 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(0, mFakePointerController->getButtonState());
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -3312,11 +3483,19 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
processKey(mapper, BTN_LEFT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
processKey(mapper, BTN_RIGHT, 1);
@@ -3327,17 +3506,34 @@
ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+
processKey(mapper, BTN_RIGHT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
processKey(mapper, BTN_MIDDLE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_BACK, release BTN_BACK
processKey(mapper, BTN_BACK, 1);
@@ -3345,15 +3541,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_BACK, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3364,15 +3570,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_SIDE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3383,15 +3599,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_FORWARD, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -3402,44 +3628,72 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_EXTRA, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+
// press BTN_STYLUS, release BTN_STYLUS
processKey(mapper, BTN_STYLUS, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_STYLUS2, release BTN_STYLUS2
processKey(mapper, BTN_STYLUS2, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS2, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// release touch
processUp(mapper);
@@ -4725,11 +4979,19 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
processKey(mapper, BTN_LEFT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
processKey(mapper, BTN_RIGHT, 1);
@@ -4740,17 +5002,34 @@
ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+
processKey(mapper, BTN_RIGHT, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
processKey(mapper, BTN_MIDDLE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_BACK, release BTN_BACK
processKey(mapper, BTN_BACK, 1);
@@ -4758,15 +5037,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_BACK, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4777,15 +5066,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
processKey(mapper, BTN_SIDE, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4796,15 +5095,25 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_FORWARD, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -4815,44 +5124,72 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
processKey(mapper, BTN_EXTRA, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+
// press BTN_STYLUS, release BTN_STYLUS
processKey(mapper, BTN_STYLUS, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// press BTN_STYLUS2, release BTN_STYLUS2
processKey(mapper, BTN_STYLUS2, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
processKey(mapper, BTN_STYLUS2, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
// release touch
processId(mapper, -1);
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 7b10319..86af0ef 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -10,7 +10,6 @@
OrientationSensor.cpp \
RecentEventLogger.cpp \
RotationVectorSensor.cpp \
- SensorDevice.cpp \
SensorEventConnection.cpp \
SensorFusion.cpp \
SensorInterface.cpp \
@@ -19,13 +18,19 @@
SensorService.cpp \
SensorServiceUtils.cpp \
-
LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
LOCAL_CFLAGS += -Wall -Werror -Wextra
LOCAL_CFLAGS += -fvisibility=hidden
+ifeq ($(ENABLE_TREBLE), true)
+LOCAL_SRC_FILES += SensorDeviceTreble.cpp
+LOCAL_CFLAGS += -DENABLE_TREBLE=1
+else
+LOCAL_SRC_FILES += SensorDevice.cpp
+endif
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libhardware \
@@ -35,7 +40,21 @@
libbinder \
libui \
libgui \
- libcrypto
+ libcrypto \
+
+ifeq ($(ENABLE_TREBLE), true)
+
+LOCAL_SHARED_LIBRARIES += \
+ libbase \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
+ android.hardware.sensors@1.0
+
+LOCAL_STATIC_LIBRARIES := \
+ android.hardware.sensors@1.0-convert
+
+endif # ENABLE_TREBLE
LOCAL_MODULE:= libsensorservice
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index ac03742..0245b26 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -53,13 +53,15 @@
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
if (mSensorDevice) {
- if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
- mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {
- ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");
- }
sensor_t const* list;
ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
+
+ if (mSensorDevice->common.version < SENSORS_DEVICE_API_VERSION_1_3) {
+ ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3, ignoring sensors reported by this device");
+ count = 0;
+ }
+
mActivationCount.setCapacity(count);
Info model;
for (size_t i=0 ; i<size_t(count) ; i++) {
@@ -164,6 +166,8 @@
ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
if (isClientDisabledLocked(ident)) {
+ ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+ ident, handle);
return INVALID_OPERATION;
}
@@ -347,6 +351,7 @@
void SensorDevice::enableAllSensors() {
Mutex::Autolock _l(mLock);
mDisabledClients.clear();
+ ALOGI("cleared mDisabledClients");
const int halVersion = getHalDeviceVersion();
for (size_t i = 0; i< mActivationCount.size(); ++i) {
Info& info = mActivationCount.editValueAt(i);
@@ -395,6 +400,7 @@
// clients list.
for (size_t j = 0; j < info.batchParams.size(); ++j) {
mDisabledClients.add(info.batchParams.keyAt(j));
+ ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
}
}
}
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d340da3..0bb0752 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -27,19 +27,29 @@
#include <stdint.h>
#include <sys/types.h>
+#ifdef ENABLE_TREBLE
+#include <map>
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
+
// ---------------------------------------------------------------------------
using SensorServiceUtil::Dumpable;
class SensorDevice : public Singleton<SensorDevice>, public Dumpable {
public:
ssize_t getSensorList(sensor_t const** list);
+
void handleDynamicSensorConnection(int handle, bool connected);
status_t initCheck() const;
int getHalDeviceVersion() const;
+
ssize_t poll(sensors_event_t* buffer, size_t count);
+
status_t activate(void* ident, int handle, int enabled);
status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs);
@@ -50,6 +60,7 @@
void disableAllSensors();
void enableAllSensors();
void autoDisable(void *ident, int handle);
+
status_t injectSensorData(const sensors_event_t *event);
void notifyConnectionDestroyed(void *ident);
@@ -57,8 +68,15 @@
virtual std::string dump() const;
private:
friend class Singleton<SensorDevice>;
+#ifdef ENABLE_TREBLE
+ sp<android::hardware::sensors::V1_0::ISensors> mSensors;
+ Vector<sensor_t> mSensorList;
+ std::map<int32_t, sensor_t*> mConnectedDynamicSensors;
+#else
sensors_poll_device_1_t* mSensorDevice;
struct sensors_module_t* mSensorModule;
+#endif
+
static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
mutable Mutex mLock; // protect mActivationCount[].batchParams
// fixed-size array after construction
@@ -111,6 +129,18 @@
bool isClientDisabled(void* ident);
bool isClientDisabledLocked(void* ident);
+
+#ifdef ENABLE_TREBLE
+ using Event = hardware::sensors::V1_0::Event;
+ using SensorInfo = hardware::sensors::V1_0::SensorInfo;
+
+ void convertToSensorEvent(const Event &src, sensors_event_t *dst);
+
+ void convertToSensorEvents(
+ const hardware::hidl_vec<Event> &src,
+ const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
+ sensors_event_t *dst);
+#endif // ENABLE_TREBLE
};
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDeviceTreble.cpp b/services/sensorservice/SensorDeviceTreble.cpp
new file mode 100644
index 0000000..37f0f6c
--- /dev/null
+++ b/services/sensorservice/SensorDeviceTreble.cpp
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+
+#include "SensorDevice.h"
+#include "SensorService.h"
+
+#include <sensors/convert.h>
+
+using android::hardware::sensors::V1_0::ISensors;
+using android::hardware::hidl_vec;
+
+using Event = android::hardware::sensors::V1_0::Event;
+using SensorInfo = android::hardware::sensors::V1_0::SensorInfo;
+using SensorType = android::hardware::sensors::V1_0::SensorType;
+using DynamicSensorInfo = android::hardware::sensors::V1_0::DynamicSensorInfo;
+using SensorInfo = android::hardware::sensors::V1_0::SensorInfo;
+using Result = android::hardware::sensors::V1_0::Result;
+
+using namespace android::hardware::sensors::V1_0::implementation;
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SensorDevice)
+
+static status_t StatusFromResult(Result result) {
+ switch (result) {
+ case Result::OK:
+ return OK;
+ case Result::BAD_VALUE:
+ return BAD_VALUE;
+ case Result::PERMISSION_DENIED:
+ return PERMISSION_DENIED;
+ case Result::INVALID_OPERATION:
+ return INVALID_OPERATION;
+ case Result::NO_MEMORY:
+ return NO_MEMORY;
+ }
+}
+
+SensorDevice::SensorDevice() {
+ mSensors = ISensors::getService("sensors");
+
+ if (mSensors == NULL) {
+ return;
+ }
+
+ mSensors->getSensorsList(
+ [&](const auto &list) {
+ const size_t count = list.size();
+
+ mActivationCount.setCapacity(count);
+ Info model;
+ for (size_t i=0 ; i < count; i++) {
+ sensor_t sensor;
+ convertToSensor(list[i], &sensor);
+ mSensorList.push_back(sensor);
+
+ mActivationCount.add(list[i].sensorHandle, model);
+
+ mSensors->activate(list[i].sensorHandle, 0 /* enabled */);
+ }
+ });
+}
+
+void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
+ if (connected) {
+ Info model;
+ mActivationCount.add(handle, model);
+ mSensors->activate(handle, 0 /* enabled */);
+ } else {
+ mActivationCount.removeItem(handle);
+ }
+}
+
+std::string SensorDevice::dump() const {
+ if (mSensors == NULL) return "HAL not initialized\n";
+
+ String8 result;
+ mSensors->getSensorsList([&](const auto &list) {
+ const size_t count = list.size();
+
+ result.appendFormat(
+ "Total %zu h/w sensors, %zu running:\n",
+ count,
+ mActivationCount.size());
+
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0 ; i < count ; i++) {
+ const Info& info = mActivationCount.valueFor(
+ list[i].sensorHandle);
+
+ if (info.batchParams.isEmpty()) continue;
+ result.appendFormat(
+ "0x%08x) active-count = %zu; ",
+ list[i].sensorHandle,
+ info.batchParams.size());
+
+ result.append("sampling_period(ms) = {");
+ for (size_t j = 0; j < info.batchParams.size(); j++) {
+ const BatchParams& params = info.batchParams.valueAt(j);
+ result.appendFormat(
+ "%.1f%s",
+ params.batchDelay / 1e6f,
+ j < info.batchParams.size() - 1 ? ", " : "");
+ }
+ result.appendFormat(
+ "}, selected = %.1f ms; ",
+ info.bestBatchParams.batchDelay / 1e6f);
+
+ result.append("batching_period(ms) = {");
+ for (size_t j = 0; j < info.batchParams.size(); j++) {
+ BatchParams params = info.batchParams.valueAt(j);
+
+ result.appendFormat(
+ "%.1f%s",
+ params.batchTimeout / 1e6f,
+ j < info.batchParams.size() - 1 ? ", " : "");
+ }
+
+ result.appendFormat(
+ "}, selected = %.1f ms\n",
+ info.bestBatchParams.batchTimeout / 1e6f);
+ }
+ });
+
+ return result.string();
+}
+
+ssize_t SensorDevice::getSensorList(sensor_t const** list) {
+ *list = &mSensorList[0];
+
+ return mSensorList.size();
+}
+
+status_t SensorDevice::initCheck() const {
+ return mSensors != NULL ? NO_ERROR : NO_INIT;
+}
+
+ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
+ if (mSensors == NULL) return NO_INIT;
+
+ ssize_t err;
+
+ mSensors->poll(
+ count,
+ [&](auto result,
+ const auto &events,
+ const auto &dynamicSensorsAdded) {
+ if (result == Result::OK) {
+ convertToSensorEvents(events, dynamicSensorsAdded, buffer);
+ err = (ssize_t)events.size();
+ } else {
+ err = StatusFromResult(result);
+ }
+ });
+
+ return err;
+}
+
+void SensorDevice::autoDisable(void *ident, int handle) {
+ Info& info( mActivationCount.editValueFor(handle) );
+ Mutex::Autolock _l(mLock);
+ info.removeBatchParamsForIdent(ident);
+}
+
+status_t SensorDevice::activate(void* ident, int handle, int enabled) {
+ if (mSensors == NULL) return NO_INIT;
+
+ status_t err(NO_ERROR);
+ bool actuateHardware = false;
+
+ Mutex::Autolock _l(mLock);
+ Info& info( mActivationCount.editValueFor(handle) );
+
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",
+ ident, handle, enabled, info.batchParams.size());
+
+ if (enabled) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
+
+ if (isClientDisabledLocked(ident)) {
+ ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+ ident, handle);
+ return INVALID_OPERATION;
+ }
+
+ if (info.batchParams.indexOfKey(ident) >= 0) {
+ if (info.numActiveClients() == 1) {
+ // This is the first connection, we need to activate the underlying h/w sensor.
+ actuateHardware = true;
+ }
+ } else {
+ // Log error. Every activate call should be preceded by a batch() call.
+ ALOGE("\t >>>ERROR: activate called without batch");
+ }
+ } else {
+ ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%zd", info.batchParams.indexOfKey(ident));
+
+ // If a connected dynamic sensor is deactivated, remove it from the
+ // dictionary.
+ auto it = mConnectedDynamicSensors.find(handle);
+ if (it != mConnectedDynamicSensors.end()) {
+ delete it->second;
+ mConnectedDynamicSensors.erase(it);
+ }
+
+ if (info.removeBatchParamsForIdent(ident) >= 0) {
+ if (info.numActiveClients() == 0) {
+ // This is the last connection, we need to de-activate the underlying h/w sensor.
+ actuateHardware = true;
+ } else {
+ // Call batch for this sensor with the previously calculated best effort
+ // batch_rate and timeout. One of the apps has unregistered for sensor
+ // events, and the best effort batch parameters might have changed.
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "\t>>> actuating h/w batch %d %d %" PRId64 " %" PRId64, handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ mSensors->batch(
+ handle,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ }
+ } else {
+ // sensor wasn't enabled for this ident
+ }
+
+ if (isClientDisabledLocked(ident)) {
+ return NO_ERROR;
+ }
+ }
+
+ if (actuateHardware) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
+ enabled);
+ err = StatusFromResult(mSensors->activate(handle, enabled));
+ ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
+ strerror(-err));
+
+ if (err != NO_ERROR && enabled) {
+ // Failure when enabling the sensor. Clean up on failure.
+ info.removeBatchParamsForIdent(ident);
+ }
+ }
+
+ return err;
+}
+
+status_t SensorDevice::batch(
+ void* ident,
+ int handle,
+ int flags,
+ int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
+ if (mSensors == NULL) return NO_INIT;
+
+ if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
+ samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
+ }
+
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
+ ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+
+ Mutex::Autolock _l(mLock);
+ Info& info(mActivationCount.editValueFor(handle));
+
+ if (info.batchParams.indexOfKey(ident) < 0) {
+ BatchParams params(flags, samplingPeriodNs, maxBatchReportLatencyNs);
+ info.batchParams.add(ident, params);
+ } else {
+ // A batch has already been called with this ident. Update the batch parameters.
+ info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+ }
+
+ BatchParams prevBestBatchParams = info.bestBatchParams;
+ // Find the minimum of all timeouts and batch_rates for this sensor.
+ info.selectBatchParams();
+
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "\t>>> curr_period=%" PRId64 " min_period=%" PRId64
+ " curr_timeout=%" PRId64 " min_timeout=%" PRId64,
+ prevBestBatchParams.batchDelay, info.bestBatchParams.batchDelay,
+ prevBestBatchParams.batchTimeout, info.bestBatchParams.batchTimeout);
+
+ status_t err(NO_ERROR);
+ // If the min period or min timeout has changed since the last batch call, call batch.
+ if (prevBestBatchParams != info.bestBatchParams) {
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout);
+ err = StatusFromResult(
+ mSensors->batch(
+ handle,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout));
+ if (err != NO_ERROR) {
+ ALOGE("sensor batch failed %p %d %d %" PRId64 " %" PRId64 " err=%s",
+ mSensors.get(), handle,
+ info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout, strerror(-err));
+ info.removeBatchParamsForIdent(ident);
+ }
+ }
+ return err;
+}
+
+status_t SensorDevice::setDelay(void* ident, int handle, int64_t samplingPeriodNs) {
+ if (mSensors == NULL) return NO_INIT;
+ if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
+ samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
+ }
+ Mutex::Autolock _l(mLock);
+ if (isClientDisabledLocked(ident)) return INVALID_OPERATION;
+ Info& info( mActivationCount.editValueFor(handle) );
+ // If the underlying sensor is NOT in continuous mode, setDelay() should return an error.
+ // Calling setDelay() in batch mode is an invalid operation.
+ if (info.bestBatchParams.batchTimeout != 0) {
+ return INVALID_OPERATION;
+ }
+ ssize_t index = info.batchParams.indexOfKey(ident);
+ if (index < 0) {
+ return BAD_INDEX;
+ }
+ BatchParams& params = info.batchParams.editValueAt(index);
+ params.batchDelay = samplingPeriodNs;
+ info.selectBatchParams();
+
+ return StatusFromResult(
+ mSensors->batch(handle, info.bestBatchParams.batchDelay, 0));
+}
+
+int SensorDevice::getHalDeviceVersion() const {
+ if (mSensors == NULL) return -1;
+ return SENSORS_DEVICE_API_VERSION_1_4;
+}
+
+status_t SensorDevice::flush(void* ident, int handle) {
+ if (isClientDisabled(ident)) return INVALID_OPERATION;
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
+ return StatusFromResult(mSensors->flush(handle));
+}
+
+bool SensorDevice::isClientDisabled(void* ident) {
+ Mutex::Autolock _l(mLock);
+ return isClientDisabledLocked(ident);
+}
+
+bool SensorDevice::isClientDisabledLocked(void* ident) {
+ return mDisabledClients.indexOf(ident) >= 0;
+}
+
+void SensorDevice::enableAllSensors() {
+ Mutex::Autolock _l(mLock);
+ mDisabledClients.clear();
+ ALOGI("cleared mDisabledClients");
+ for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ Info& info = mActivationCount.editValueAt(i);
+ if (info.batchParams.isEmpty()) continue;
+ info.selectBatchParams();
+ const int sensor_handle = mActivationCount.keyAt(i);
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
+ sensor_handle);
+ status_t err = StatusFromResult(
+ mSensors->batch(
+ sensor_handle,
+ info.bestBatchParams.batchDelay,
+ info.bestBatchParams.batchTimeout));
+ ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
+
+ if (err == NO_ERROR) {
+ err = StatusFromResult(
+ mSensors->activate(sensor_handle, 1 /* enabled */));
+ ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
+ }
+ }
+}
+
+void SensorDevice::disableAllSensors() {
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ const Info& info = mActivationCount.valueAt(i);
+ // Check if this sensor has been activated previously and disable it.
+ if (info.batchParams.size() > 0) {
+ const int sensor_handle = mActivationCount.keyAt(i);
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
+ sensor_handle);
+ mSensors->activate(sensor_handle, 0 /* enabled */);
+
+ // Add all the connections that were registered for this sensor to the disabled
+ // clients list.
+ for (size_t j = 0; j < info.batchParams.size(); ++j) {
+ mDisabledClients.add(info.batchParams.keyAt(j));
+ ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
+ }
+ }
+ }
+}
+
+status_t SensorDevice::injectSensorData(
+ const sensors_event_t *injected_sensor_event) {
+ ALOGD_IF(DEBUG_CONNECTIONS,
+ "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
+ injected_sensor_event->sensor,
+ injected_sensor_event->timestamp, injected_sensor_event->data[0],
+ injected_sensor_event->data[1], injected_sensor_event->data[2],
+ injected_sensor_event->data[3], injected_sensor_event->data[4],
+ injected_sensor_event->data[5]);
+
+ Event ev;
+ convertFromSensorEvent(*injected_sensor_event, &ev);
+
+ return StatusFromResult(mSensors->injectSensorData(ev));
+}
+
+status_t SensorDevice::setMode(uint32_t mode) {
+
+ return StatusFromResult(
+ mSensors->setOperationMode(
+ static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+}
+
+// ---------------------------------------------------------------------------
+
+int SensorDevice::Info::numActiveClients() {
+ SensorDevice& device(SensorDevice::getInstance());
+ int num = 0;
+ for (size_t i = 0; i < batchParams.size(); ++i) {
+ if (!device.isClientDisabledLocked(batchParams.keyAt(i))) {
+ ++num;
+ }
+ }
+ return num;
+}
+
+status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int flags,
+ int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
+ ssize_t index = batchParams.indexOfKey(ident);
+ if (index < 0) {
+ ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64 ") failed (%s)",
+ ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index));
+ return BAD_INDEX;
+ }
+ BatchParams& params = batchParams.editValueAt(index);
+ params.flags = flags;
+ params.batchDelay = samplingPeriodNs;
+ params.batchTimeout = maxBatchReportLatencyNs;
+ return NO_ERROR;
+}
+
+void SensorDevice::Info::selectBatchParams() {
+ BatchParams bestParams(0, -1, -1);
+ SensorDevice& device(SensorDevice::getInstance());
+
+ for (size_t i = 0; i < batchParams.size(); ++i) {
+ if (device.isClientDisabledLocked(batchParams.keyAt(i))) continue;
+ BatchParams params = batchParams.valueAt(i);
+ if (bestParams.batchDelay == -1 || params.batchDelay < bestParams.batchDelay) {
+ bestParams.batchDelay = params.batchDelay;
+ }
+ if (bestParams.batchTimeout == -1 || params.batchTimeout < bestParams.batchTimeout) {
+ bestParams.batchTimeout = params.batchTimeout;
+ }
+ }
+ bestBatchParams = bestParams;
+}
+
+ssize_t SensorDevice::Info::removeBatchParamsForIdent(void* ident) {
+ ssize_t idx = batchParams.removeItem(ident);
+ if (idx >= 0) {
+ selectBatchParams();
+ }
+ return idx;
+}
+
+void SensorDevice::notifyConnectionDestroyed(void* ident) {
+ Mutex::Autolock _l(mLock);
+ mDisabledClients.remove(ident);
+}
+
+void SensorDevice::convertToSensorEvent(
+ const Event &src, sensors_event_t *dst) {
+ ::android::hardware::sensors::V1_0::implementation::convertToSensorEvent(
+ src, dst);
+
+ if (src.sensorType == SensorType::DYNAMIC_SENSOR_META) {
+ const DynamicSensorInfo &dyn = src.u.dynamic;
+
+ dst->dynamic_sensor_meta.connected = dyn.connected;
+ dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
+ if (dyn.connected) {
+ auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
+ CHECK(it != mConnectedDynamicSensors.end());
+
+ dst->dynamic_sensor_meta.sensor = it->second;
+
+ memcpy(dst->dynamic_sensor_meta.uuid,
+ dyn.uuid.data(),
+ sizeof(dst->dynamic_sensor_meta.uuid));
+ }
+ }
+}
+
+void SensorDevice::convertToSensorEvents(
+ const hidl_vec<Event> &src,
+ const hidl_vec<SensorInfo> &dynamicSensorsAdded,
+ sensors_event_t *dst) {
+ // Allocate a sensor_t structure for each dynamic sensor added and insert
+ // it into the dictionary of connected dynamic sensors keyed by handle.
+ for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
+ const SensorInfo &info = dynamicSensorsAdded[i];
+
+ auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+ CHECK(it == mConnectedDynamicSensors.end());
+
+ sensor_t *sensor = new sensor_t;
+ convertToSensor(info, sensor);
+
+ mConnectedDynamicSensors.insert(
+ std::make_pair(sensor->handle, sensor));
+ }
+
+ for (size_t i = 0; i < src.size(); ++i) {
+ convertToSensorEvent(src[i], &dst[i]);
+ }
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 7b47709..2e44736 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -868,7 +868,7 @@
}
}
-Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) {
+Vector<Sensor> SensorService::getSensorList(const String16& /* opPackageName */) {
char value[PROPERTY_VALUE_MAX];
property_get("debug.sensors", value, "0");
const Vector<Sensor>& initialSensorList = (atoi(value)) ?
@@ -876,14 +876,7 @@
Vector<Sensor> accessibleSensorList;
for (size_t i = 0; i < initialSensorList.size(); i++) {
Sensor sensor = initialSensorList[i];
- if (canAccessSensor(sensor, "getSensorList", opPackageName)) {
- accessibleSensorList.add(sensor);
- } else {
- ALOGI("Skipped sensor %s because it requires permission %s and app op %d",
- sensor.getName().string(),
- sensor.getRequiredPermission().string(),
- sensor.getRequiredAppOp());
- }
+ accessibleSensorList.add(sensor);
}
makeUuidsIntoIdsForSensorList(accessibleSensorList);
return accessibleSensorList;
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 170faa8..fcc9241 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -10,15 +10,17 @@
DispSync.cpp \
EventControlThread.cpp \
EventThread.cpp \
- FenceTracker.cpp \
FrameTracker.cpp \
GpuService.cpp \
Layer.cpp \
LayerDim.cpp \
+ LayerRejecter.cpp \
MessageQueue.cpp \
MonitoredProducer.cpp \
SurfaceFlingerConsumer.cpp \
+ SurfaceInterceptor.cpp \
Transform.cpp \
+ DisplayHardware/ComposerHal.cpp \
DisplayHardware/FramebufferSurface.cpp \
DisplayHardware/HWC2.cpp \
DisplayHardware/HWC2On1Adapter.cpp \
@@ -34,13 +36,13 @@
RenderEngine/GLExtensions.cpp \
RenderEngine/RenderEngine.cpp \
RenderEngine/Texture.cpp \
- RenderEngine/GLES10RenderEngine.cpp \
- RenderEngine/GLES11RenderEngine.cpp \
- RenderEngine/GLES20RenderEngine.cpp
+ RenderEngine/GLES20RenderEngine.cpp \
+LOCAL_MODULE := libsurfaceflinger
LOCAL_C_INCLUDES := \
- frameworks/native/vulkan/include \
- external/vulkan-validation-layers/libs/vkjson
+ frameworks/native/vulkan/include \
+ external/vulkan-validation-layers/libs/vkjson \
+ system/libhwbinder/fast_msgq/include \
LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
@@ -50,7 +52,11 @@
LOCAL_SRC_FILES += \
SurfaceFlinger.cpp \
DisplayHardware/HWComposer.cpp
+ ifeq ($(TARGET_USES_HWC2ON1ADAPTER), true)
+ LOCAL_CFLAGS += -DBYPASS_IHWC
+ endif
else
+ LOCAL_CFLAGS += -DBYPASS_IHWC
LOCAL_SRC_FILES += \
SurfaceFlinger_hwc1.cpp \
DisplayHardware/HWComposer_hwc1.cpp
@@ -63,10 +69,6 @@
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
endif
-ifeq ($(TARGET_DISABLE_TRIPLE_BUFFERING),true)
- LOCAL_CFLAGS += -DTARGET_DISABLE_TRIPLE_BUFFERING
-endif
-
ifeq ($(TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS),true)
LOCAL_CFLAGS += -DFORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS
endif
@@ -125,12 +127,18 @@
LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
-LOCAL_STATIC_LIBRARIES := libvkjson
+LOCAL_STATIC_LIBRARIES := libhwcomposer-command-buffer libtrace_proto libvkjson
LOCAL_SHARED_LIBRARIES := \
+ android.hardware.graphics.allocator@2.0 \
+ android.hardware.graphics.composer@2.1 \
libcutils \
liblog \
libdl \
+ libfmq \
libhardware \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
libutils \
libEGL \
libGLESv1_CM \
@@ -139,9 +147,18 @@
libui \
libgui \
libpowermanager \
- libvulkan
+ libvulkan \
+ libsync \
+ libprotobuf-cpp-lite \
+ libbase \
+ android.hardware.power@1.0
-LOCAL_MODULE := libsurfaceflinger
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+ android.hardware.graphics.allocator@2.0 \
+ android.hardware.graphics.composer@2.1 \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
@@ -179,6 +196,7 @@
libdl
LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_STATIC_LIBRARIES := libtrace_proto
LOCAL_MODULE := surfaceflinger
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index f2e6491..d56b1c8 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_SURFACE_FLINGER_COLORIZER_H
#define ANDROID_SURFACE_FLINGER_COLORIZER_H
+#include <utils/String8.h>
+
namespace android {
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DdmConnection.h b/services/surfaceflinger/DdmConnection.h
index b6b088b..938d14b 100644
--- a/services/surfaceflinger/DdmConnection.h
+++ b/services/surfaceflinger/DdmConnection.h
@@ -24,6 +24,9 @@
class DdmConnection {
public:
+ // Creates a JVM and registers all handlers to DDMS.
+ // This allows tools relying on DDMS to find surfaceflinger
+ // (e.g: Memory Leak finder, heap analyzer, ...)
static void start(const char* name);
};
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 52c2b84..86cf17d 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -63,7 +63,7 @@
class DispSyncThread: public Thread {
public:
- DispSyncThread(const char* name):
+ explicit DispSyncThread(const char* name):
mName(name),
mStop(false),
mPeriod(0),
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5c2c0ad..6c18ef7 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -613,3 +613,17 @@
mDisplaySurface->dumpAsString(surfaceDump);
result.append(surfaceDump);
}
+
+std::atomic<int32_t> DisplayDeviceState::nextDisplayId(1);
+
+DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure)
+ : type(type),
+ layerStack(DisplayDevice::NO_LAYER_STACK),
+ orientation(0),
+ width(0),
+ height(0),
+ isSecure(isSecure)
+{
+ viewport.makeInvalid();
+ frame.makeInvalid();
+}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 105e980..92ede08 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -263,6 +263,28 @@
#endif
};
+struct DisplayDeviceState {
+ DisplayDeviceState() = default;
+ DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
+
+ bool isValid() const { return type >= 0; }
+ bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
+ bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
+
+ static std::atomic<int32_t> nextDisplayId;
+ int32_t displayId = nextDisplayId++;
+ DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_ID_INVALID;
+ sp<IGraphicBufferProducer> surface;
+ uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
+ Rect viewport;
+ Rect frame;
+ uint8_t orientation = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ String8 displayName;
+ bool isSecure = false;
+};
+
}; // namespace android
#endif // ANDROID_DISPLAY_DEVICE_H
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
new file mode 100644
index 0000000..b952fd7
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -0,0 +1,946 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HwcComposer"
+
+#include <inttypes.h>
+#include <log/log.h>
+
+#include "ComposerHal.h"
+
+namespace android {
+
+using hardware::Return;
+using hardware::hidl_vec;
+using hardware::hidl_handle;
+
+namespace Hwc2 {
+
+namespace {
+
+class BufferHandle {
+public:
+ BufferHandle(const native_handle_t* buffer)
+ {
+ // nullptr is not a valid handle to HIDL
+ mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
+ }
+
+ operator const hidl_handle&() const
+ {
+ return mHandle;
+ }
+
+private:
+ NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 0, 0);
+ hidl_handle mHandle;
+};
+
+class FenceHandle
+{
+public:
+ FenceHandle(int fd, bool owned)
+ : mOwned(owned)
+ {
+ native_handle_t* handle;
+ if (fd >= 0) {
+ handle = native_handle_init(mStorage, 1, 0);
+ handle->data[0] = fd;
+ } else {
+ // nullptr is not a valid handle to HIDL
+ handle = native_handle_init(mStorage, 0, 0);
+ }
+ mHandle = handle;
+ }
+
+ ~FenceHandle()
+ {
+ if (mOwned) {
+ native_handle_close(mHandle);
+ }
+ }
+
+ operator const hidl_handle&() const
+ {
+ return mHandle;
+ }
+
+private:
+ bool mOwned;
+ NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 1, 0);
+ hidl_handle mHandle;
+};
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+template<typename T, typename U>
+T unwrapRet(Return<T>& ret, const U& default_val)
+{
+ return (ret.isOk()) ? static_cast<T>(ret) :
+ static_cast<T>(default_val);
+}
+
+Error unwrapRet(Return<Error>& ret)
+{
+ return unwrapRet(ret, kDefaultError);
+}
+
+} // anonymous namespace
+
+Composer::Composer()
+ : mWriter(kWriterInitialSize)
+{
+ mComposer = IComposer::getService("hwcomposer");
+ if (mComposer == nullptr) {
+ LOG_ALWAYS_FATAL("failed to get hwcomposer service");
+ }
+
+ mComposer->createClient(
+ [&](const auto& tmpError, const auto& tmpClient)
+ {
+ if (tmpError == Error::NONE) {
+ mClient = tmpClient;
+ }
+ });
+ if (mClient == nullptr) {
+ LOG_ALWAYS_FATAL("failed to create composer client");
+ }
+}
+
+std::vector<IComposer::Capability> Composer::getCapabilities()
+{
+ std::vector<IComposer::Capability> capabilities;
+ mComposer->getCapabilities(
+ [&](const auto& tmpCapabilities) {
+ capabilities = tmpCapabilities;
+ });
+
+ return capabilities;
+}
+
+std::string Composer::dumpDebugInfo()
+{
+ std::string info;
+ mComposer->dumpDebugInfo([&](const auto& tmpInfo) {
+ info = tmpInfo.c_str();
+ });
+
+ return info;
+}
+
+void Composer::registerCallback(const sp<IComposerCallback>& callback)
+{
+ auto ret = mClient->registerCallback(callback);
+ if (!ret.isOk()) {
+ ALOGE("failed to register IComposerCallback");
+ }
+}
+
+uint32_t Composer::getMaxVirtualDisplayCount()
+{
+ auto ret = mClient->getMaxVirtualDisplayCount();
+ return unwrapRet(ret, 0);
+}
+
+Error Composer::createVirtualDisplay(uint32_t width, uint32_t height,
+ PixelFormat* format, Display* outDisplay)
+{
+ const uint32_t bufferSlotCount = 1;
+ Error error = kDefaultError;
+ mClient->createVirtualDisplay(width, height, *format, bufferSlotCount,
+ [&](const auto& tmpError, const auto& tmpDisplay,
+ const auto& tmpFormat) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outDisplay = tmpDisplay;
+ *format = tmpFormat;
+ });
+
+ return error;
+}
+
+Error Composer::destroyVirtualDisplay(Display display)
+{
+ auto ret = mClient->destroyVirtualDisplay(display);
+ return unwrapRet(ret);
+}
+
+Error Composer::acceptDisplayChanges(Display display)
+{
+ mWriter.selectDisplay(display);
+ mWriter.acceptDisplayChanges();
+ return Error::NONE;
+}
+
+Error Composer::createLayer(Display display, Layer* outLayer)
+{
+ const uint32_t bufferSlotCount = 1;
+ Error error = kDefaultError;
+ mClient->createLayer(display, bufferSlotCount,
+ [&](const auto& tmpError, const auto& tmpLayer) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outLayer = tmpLayer;
+ });
+
+ return error;
+}
+
+Error Composer::destroyLayer(Display display, Layer layer)
+{
+ auto ret = mClient->destroyLayer(display, layer);
+ return unwrapRet(ret);
+}
+
+Error Composer::getActiveConfig(Display display, Config* outConfig)
+{
+ Error error = kDefaultError;
+ mClient->getActiveConfig(display,
+ [&](const auto& tmpError, const auto& tmpConfig) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outConfig = tmpConfig;
+ });
+
+ return error;
+}
+
+Error Composer::getChangedCompositionTypes(Display display,
+ std::vector<Layer>* outLayers,
+ std::vector<IComposerClient::Composition>* outTypes)
+{
+ mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
+ return Error::NONE;
+}
+
+Error Composer::getColorModes(Display display,
+ std::vector<ColorMode>* outModes)
+{
+ Error error = kDefaultError;
+ mClient->getColorModes(display,
+ [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outModes = tmpModes;
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayAttribute(Display display, Config config,
+ IComposerClient::Attribute attribute, int32_t* outValue)
+{
+ Error error = kDefaultError;
+ mClient->getDisplayAttribute(display, config, attribute,
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outValue = tmpValue;
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayConfigs(Display display,
+ std::vector<Config>* outConfigs)
+{
+ Error error = kDefaultError;
+ mClient->getDisplayConfigs(display,
+ [&](const auto& tmpError, const auto& tmpConfigs) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outConfigs = tmpConfigs;
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayName(Display display, std::string* outName)
+{
+ Error error = kDefaultError;
+ mClient->getDisplayName(display,
+ [&](const auto& tmpError, const auto& tmpName) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outName = tmpName.c_str();
+ });
+
+ return error;
+}
+
+Error Composer::getDisplayRequests(Display display,
+ uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks)
+{
+ mReader.takeDisplayRequests(display, outDisplayRequestMask,
+ outLayers, outLayerRequestMasks);
+ return Error::NONE;
+}
+
+Error Composer::getDisplayType(Display display,
+ IComposerClient::DisplayType* outType)
+{
+ Error error = kDefaultError;
+ mClient->getDisplayType(display,
+ [&](const auto& tmpError, const auto& tmpType) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outType = tmpType;
+ });
+
+ return error;
+}
+
+Error Composer::getDozeSupport(Display display, bool* outSupport)
+{
+ Error error = kDefaultError;
+ mClient->getDozeSupport(display,
+ [&](const auto& tmpError, const auto& tmpSupport) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outSupport = tmpSupport;
+ });
+
+ return error;
+}
+
+Error Composer::getHdrCapabilities(Display display,
+ std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ float* outMaxAverageLuminance, float* outMinLuminance)
+{
+ Error error = kDefaultError;
+ mClient->getHdrCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpTypes,
+ const auto& tmpMaxLuminance,
+ const auto& tmpMaxAverageLuminance,
+ const auto& tmpMinLuminance) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outTypes = tmpTypes;
+ *outMaxLuminance = tmpMaxLuminance;
+ *outMaxAverageLuminance = tmpMaxAverageLuminance;
+ *outMinLuminance = tmpMinLuminance;
+ });
+
+ return error;
+}
+
+Error Composer::getReleaseFences(Display display,
+ std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
+{
+ mReader.takeReleaseFences(display, outLayers, outReleaseFences);
+ return Error::NONE;
+}
+
+Error Composer::presentDisplay(Display display, int* outPresentFence)
+{
+ mWriter.selectDisplay(display);
+ mWriter.presentDisplay();
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.takePresentFence(display, outPresentFence);
+
+ return Error::NONE;
+}
+
+Error Composer::setActiveConfig(Display display, Config config)
+{
+ auto ret = mClient->setActiveConfig(display, config);
+ return unwrapRet(ret);
+}
+
+Error Composer::setClientTarget(Display display, const native_handle_t* target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposerClient::Rect>& damage)
+{
+ mWriter.selectDisplay(display);
+ mWriter.setClientTarget(0, target, acquireFence, dataspace, damage);
+ return Error::NONE;
+}
+
+Error Composer::setColorMode(Display display, ColorMode mode)
+{
+ auto ret = mClient->setColorMode(display, mode);
+ return unwrapRet(ret);
+}
+
+Error Composer::setColorTransform(Display display, const float* matrix,
+ ColorTransform hint)
+{
+ mWriter.selectDisplay(display);
+ mWriter.setColorTransform(matrix, hint);
+ return Error::NONE;
+}
+
+Error Composer::setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence)
+{
+ mWriter.selectDisplay(display);
+ mWriter.setOutputBuffer(0, buffer, dup(releaseFence));
+ return Error::NONE;
+}
+
+Error Composer::setPowerMode(Display display, IComposerClient::PowerMode mode)
+{
+ auto ret = mClient->setPowerMode(display, mode);
+ return unwrapRet(ret);
+}
+
+Error Composer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled)
+{
+ auto ret = mClient->setVsyncEnabled(display, enabled);
+ return unwrapRet(ret);
+}
+
+Error Composer::setClientTargetSlotCount(Display display)
+{
+ const uint32_t bufferSlotCount = 1;
+ auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
+ return unwrapRet(ret);
+}
+
+Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests)
+{
+ mWriter.selectDisplay(display);
+ mWriter.validateDisplay();
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.hasChanges(display, outNumTypes, outNumRequests);
+
+ return Error::NONE;
+}
+
+Error Composer::setCursorPosition(Display display, Layer layer,
+ int32_t x, int32_t y)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerCursorPosition(x, y);
+ return Error::NONE;
+}
+
+Error Composer::setLayerBuffer(Display display, Layer layer,
+ const native_handle_t* buffer, int acquireFence)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerBuffer(0, buffer, acquireFence);
+ return Error::NONE;
+}
+
+Error Composer::setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& damage)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerSurfaceDamage(damage);
+ return Error::NONE;
+}
+
+Error Composer::setLayerBlendMode(Display display, Layer layer,
+ IComposerClient::BlendMode mode)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerBlendMode(mode);
+ return Error::NONE;
+}
+
+Error Composer::setLayerColor(Display display, Layer layer,
+ const IComposerClient::Color& color)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerColor(color);
+ return Error::NONE;
+}
+
+Error Composer::setLayerCompositionType(Display display, Layer layer,
+ IComposerClient::Composition type)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerCompositionType(type);
+ return Error::NONE;
+}
+
+Error Composer::setLayerDataspace(Display display, Layer layer,
+ Dataspace dataspace)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerDataspace(dataspace);
+ return Error::NONE;
+}
+
+Error Composer::setLayerDisplayFrame(Display display, Layer layer,
+ const IComposerClient::Rect& frame)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerDisplayFrame(frame);
+ return Error::NONE;
+}
+
+Error Composer::setLayerPlaneAlpha(Display display, Layer layer,
+ float alpha)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerPlaneAlpha(alpha);
+ return Error::NONE;
+}
+
+Error Composer::setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerSidebandStream(stream);
+ return Error::NONE;
+}
+
+Error Composer::setLayerSourceCrop(Display display, Layer layer,
+ const IComposerClient::FRect& crop)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerSourceCrop(crop);
+ return Error::NONE;
+}
+
+Error Composer::setLayerTransform(Display display, Layer layer,
+ Transform transform)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerTransform(transform);
+ return Error::NONE;
+}
+
+Error Composer::setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& visible)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerVisibleRegion(visible);
+ return Error::NONE;
+}
+
+Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z)
+{
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerZOrder(z);
+ return Error::NONE;
+}
+
+Error Composer::execute()
+{
+ // prepare input command queue
+ bool queueChanged = false;
+ uint32_t commandLength = 0;
+ hidl_vec<hidl_handle> commandHandles;
+ if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
+ mWriter.reset();
+ return Error::NO_RESOURCES;
+ }
+
+ // set up new input command queue if necessary
+ if (queueChanged) {
+ auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
+ auto error = unwrapRet(ret);
+ if (error != Error::NONE) {
+ mWriter.reset();
+ return error;
+ }
+ }
+
+ Error error = kDefaultError;
+ mClient->executeCommands(commandLength, commandHandles,
+ [&](const auto& tmpError, const auto& tmpOutChanged,
+ const auto& tmpOutLength, const auto& tmpOutHandles)
+ {
+ error = tmpError;
+
+ // set up new output command queue if necessary
+ if (error == Error::NONE && tmpOutChanged) {
+ error = kDefaultError;
+ mClient->getOutputCommandQueue(
+ [&](const auto& tmpError,
+ const auto& tmpDescriptor)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ mReader.setMQDescriptor(tmpDescriptor);
+ });
+ }
+
+ if (error != Error::NONE) {
+ return;
+ }
+
+ if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
+ error = mReader.parse();
+ mReader.reset();
+ } else {
+ error = Error::NO_RESOURCES;
+ }
+ });
+
+ if (error == Error::NONE) {
+ std::vector<CommandReader::CommandError> commandErrors =
+ mReader.takeErrors();
+
+ for (const auto& cmdErr : commandErrors) {
+ auto command = mWriter.getCommand(cmdErr.location);
+
+ if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
+ command == IComposerClient::Command::PRESENT_DISPLAY) {
+ error = cmdErr.error;
+ } else {
+ ALOGW("command 0x%x generated error %d",
+ command, cmdErr.error);
+ }
+ }
+ }
+
+ mWriter.reset();
+
+ return error;
+}
+
+CommandReader::~CommandReader()
+{
+ resetData();
+}
+
+Error CommandReader::parse()
+{
+ resetData();
+
+ IComposerClient::Command command;
+ uint16_t length = 0;
+
+ while (!isEmpty()) {
+ if (!beginCommand(&command, &length)) {
+ break;
+ }
+
+ bool parsed = false;
+ switch (command) {
+ case IComposerClient::Command::SELECT_DISPLAY:
+ parsed = parseSelectDisplay(length);
+ break;
+ case IComposerClient::Command::SET_ERROR:
+ parsed = parseSetError(length);
+ break;
+ case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
+ parsed = parseSetChangedCompositionTypes(length);
+ break;
+ case IComposerClient::Command::SET_DISPLAY_REQUESTS:
+ parsed = parseSetDisplayRequests(length);
+ break;
+ case IComposerClient::Command::SET_PRESENT_FENCE:
+ parsed = parseSetPresentFence(length);
+ break;
+ case IComposerClient::Command::SET_RELEASE_FENCES:
+ parsed = parseSetReleaseFences(length);
+ break;
+ default:
+ parsed = false;
+ break;
+ }
+
+ endCommand();
+
+ if (!parsed) {
+ ALOGE("failed to parse command 0x%x length %" PRIu16,
+ command, length);
+ break;
+ }
+ }
+
+ return isEmpty() ? Error::NONE : Error::NO_RESOURCES;
+}
+
+bool CommandReader::parseSelectDisplay(uint16_t length)
+{
+ if (length != CommandWriter::kSelectDisplayLength) {
+ return false;
+ }
+
+ mCurrentReturnData = &mReturnData[read64()];
+
+ return true;
+}
+
+bool CommandReader::parseSetError(uint16_t length)
+{
+ if (length != CommandWriter::kSetErrorLength) {
+ return false;
+ }
+
+ auto location = read();
+ auto error = static_cast<Error>(readSigned());
+
+ mErrors.emplace_back(CommandError{location, error});
+
+ return true;
+}
+
+bool CommandReader::parseSetChangedCompositionTypes(uint16_t length)
+{
+ // (layer id, composition type) pairs
+ if (length % 3 != 0 || !mCurrentReturnData) {
+ return false;
+ }
+
+ uint32_t count = length / 3;
+ mCurrentReturnData->changedLayers.reserve(count);
+ mCurrentReturnData->compositionTypes.reserve(count);
+ while (count > 0) {
+ auto layer = read64();
+ auto type = static_cast<IComposerClient::Composition>(readSigned());
+
+ mCurrentReturnData->changedLayers.push_back(layer);
+ mCurrentReturnData->compositionTypes.push_back(type);
+
+ count--;
+ }
+
+ return true;
+}
+
+bool CommandReader::parseSetDisplayRequests(uint16_t length)
+{
+ // display requests followed by (layer id, layer requests) pairs
+ if (length % 3 != 1 || !mCurrentReturnData) {
+ return false;
+ }
+
+ mCurrentReturnData->displayRequests = read();
+
+ uint32_t count = (length - 1) / 3;
+ mCurrentReturnData->requestedLayers.reserve(count);
+ mCurrentReturnData->requestMasks.reserve(count);
+ while (count > 0) {
+ auto layer = read64();
+ auto layerRequestMask = read();
+
+ mCurrentReturnData->requestedLayers.push_back(layer);
+ mCurrentReturnData->requestMasks.push_back(layerRequestMask);
+
+ count--;
+ }
+
+ return true;
+}
+
+bool CommandReader::parseSetPresentFence(uint16_t length)
+{
+ if (length != CommandWriter::kSetPresentFenceLength ||
+ !mCurrentReturnData) {
+ return false;
+ }
+
+ if (mCurrentReturnData->presentFence >= 0) {
+ close(mCurrentReturnData->presentFence);
+ }
+ mCurrentReturnData->presentFence = readFence();
+
+ return true;
+}
+
+bool CommandReader::parseSetReleaseFences(uint16_t length)
+{
+ // (layer id, release fence index) pairs
+ if (length % 3 != 0 || !mCurrentReturnData) {
+ return false;
+ }
+
+ uint32_t count = length / 3;
+ mCurrentReturnData->releasedLayers.reserve(count);
+ mCurrentReturnData->releaseFences.reserve(count);
+ while (count > 0) {
+ auto layer = read64();
+ auto fence = readFence();
+
+ mCurrentReturnData->releasedLayers.push_back(layer);
+ mCurrentReturnData->releaseFences.push_back(fence);
+
+ count--;
+ }
+
+ return true;
+}
+
+void CommandReader::resetData()
+{
+ mErrors.clear();
+
+ for (auto& data : mReturnData) {
+ if (data.second.presentFence >= 0) {
+ close(data.second.presentFence);
+ }
+ for (auto fence : data.second.releaseFences) {
+ if (fence >= 0) {
+ close(fence);
+ }
+ }
+ }
+
+ mReturnData.clear();
+ mCurrentReturnData = nullptr;
+}
+
+std::vector<CommandReader::CommandError> CommandReader::takeErrors()
+{
+ return std::move(mErrors);
+}
+
+bool CommandReader::hasChanges(Display display,
+ uint32_t* outNumChangedCompositionTypes,
+ uint32_t* outNumLayerRequestMasks) const
+{
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ *outNumChangedCompositionTypes = 0;
+ *outNumLayerRequestMasks = 0;
+ return false;
+ }
+
+ const ReturnData& data = found->second;
+
+ *outNumChangedCompositionTypes = data.compositionTypes.size();
+ *outNumLayerRequestMasks = data.requestMasks.size();
+
+ return !(data.compositionTypes.empty() && data.requestMasks.empty());
+}
+
+void CommandReader::takeChangedCompositionTypes(Display display,
+ std::vector<Layer>* outLayers,
+ std::vector<IComposerClient::Composition>* outTypes)
+{
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ outLayers->clear();
+ outTypes->clear();
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outLayers = std::move(data.changedLayers);
+ *outTypes = std::move(data.compositionTypes);
+}
+
+void CommandReader::takeDisplayRequests(Display display,
+ uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks)
+{
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ *outDisplayRequestMask = 0;
+ outLayers->clear();
+ outLayerRequestMasks->clear();
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outDisplayRequestMask = data.displayRequests;
+ *outLayers = std::move(data.requestedLayers);
+ *outLayerRequestMasks = std::move(data.requestMasks);
+}
+
+void CommandReader::takeReleaseFences(Display display,
+ std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
+{
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ outLayers->clear();
+ outReleaseFences->clear();
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outLayers = std::move(data.releasedLayers);
+ *outReleaseFences = std::move(data.releaseFences);
+}
+
+void CommandReader::takePresentFence(Display display, int* outPresentFence)
+{
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ *outPresentFence = -1;
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outPresentFence = data.presentFence;
+ data.presentFence = -1;
+}
+
+} // namespace Hwc2
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
new file mode 100644
index 0000000..6e42ba0
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_COMPOSER_HAL_H
+#define ANDROID_SF_COMPOSER_HAL_H
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <utils/StrongPointer.h>
+#include <IComposerCommandBuffer.h>
+#include <MessageQueue.h>
+
+namespace android {
+
+namespace Hwc2 {
+
+using android::hardware::graphics::common::V1_0::ColorMode;
+using android::hardware::graphics::common::V1_0::ColorTransform;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::Hdr;
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::common::V1_0::Transform;
+
+using android::hardware::graphics::composer::V2_1::IComposer;
+using android::hardware::graphics::composer::V2_1::IComposerCallback;
+using android::hardware::graphics::composer::V2_1::IComposerClient;
+using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_1::Display;
+using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_1::Config;
+
+using android::hardware::graphics::composer::V2_1::CommandWriter;
+using android::hardware::graphics::composer::V2_1::CommandReaderBase;
+
+using android::hardware::kSynchronizedReadWrite;
+using android::hardware::MessageQueue;
+using android::hardware::MQDescriptorSync;
+using android::hardware::hidl_vec;
+using android::hardware::hidl_handle;
+
+class CommandReader : public CommandReaderBase {
+public:
+ ~CommandReader();
+
+ // Parse and execute commands from the command queue. The commands are
+ // actually return values from the server and will be saved in ReturnData.
+ Error parse();
+
+ // Get and clear saved errors.
+ struct CommandError {
+ uint32_t location;
+ Error error;
+ };
+ std::vector<CommandError> takeErrors();
+
+ bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
+ uint32_t* outNumLayerRequestMasks) const;
+
+ // Get and clear saved changed composition types.
+ void takeChangedCompositionTypes(Display display,
+ std::vector<Layer>* outLayers,
+ std::vector<IComposerClient::Composition>* outTypes);
+
+ // Get and clear saved display requests.
+ void takeDisplayRequests(Display display,
+ uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks);
+
+ // Get and clear saved release fences.
+ void takeReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences);
+
+ // Get and clear saved present fence.
+ void takePresentFence(Display display, int* outPresentFence);
+
+private:
+ void resetData();
+
+ bool parseSelectDisplay(uint16_t length);
+ bool parseSetError(uint16_t length);
+ bool parseSetChangedCompositionTypes(uint16_t length);
+ bool parseSetDisplayRequests(uint16_t length);
+ bool parseSetPresentFence(uint16_t length);
+ bool parseSetReleaseFences(uint16_t length);
+
+ struct ReturnData {
+ uint32_t displayRequests = 0;
+
+ std::vector<Layer> changedLayers;
+ std::vector<IComposerClient::Composition> compositionTypes;
+
+ std::vector<Layer> requestedLayers;
+ std::vector<uint32_t> requestMasks;
+
+ int presentFence = -1;
+
+ std::vector<Layer> releasedLayers;
+ std::vector<int> releaseFences;
+ };
+
+ std::vector<CommandError> mErrors;
+ std::unordered_map<Display, ReturnData> mReturnData;
+
+ // When SELECT_DISPLAY is parsed, this is updated to point to the
+ // display's return data in mReturnData. We use it to avoid repeated
+ // map lookups.
+ ReturnData* mCurrentReturnData;
+};
+
+// Composer is a wrapper to IComposer, a proxy to server-side composer.
+class Composer {
+public:
+ Composer();
+
+ std::vector<IComposer::Capability> getCapabilities();
+ std::string dumpDebugInfo();
+
+ void registerCallback(const sp<IComposerCallback>& callback);
+
+ uint32_t getMaxVirtualDisplayCount();
+ Error createVirtualDisplay(uint32_t width, uint32_t height,
+ PixelFormat* format, Display* outDisplay);
+ Error destroyVirtualDisplay(Display display);
+
+ Error acceptDisplayChanges(Display display);
+
+ Error createLayer(Display display, Layer* outLayer);
+ Error destroyLayer(Display display, Layer layer);
+
+ Error getActiveConfig(Display display, Config* outConfig);
+ Error getChangedCompositionTypes(Display display,
+ std::vector<Layer>* outLayers,
+ std::vector<IComposerClient::Composition>* outTypes);
+ Error getColorModes(Display display, std::vector<ColorMode>* outModes);
+ Error getDisplayAttribute(Display display, Config config,
+ IComposerClient::Attribute attribute, int32_t* outValue);
+ Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+ Error getDisplayName(Display display, std::string* outName);
+
+ Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks);
+
+ Error getDisplayType(Display display,
+ IComposerClient::DisplayType* outType);
+ Error getDozeSupport(Display display, bool* outSupport);
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+ float* outMaxLuminance, float* outMaxAverageLuminance,
+ float* outMinLuminance);
+
+ Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences);
+
+ Error presentDisplay(Display display, int* outPresentFence);
+
+ Error setActiveConfig(Display display, Config config);
+ Error setClientTarget(Display display, const native_handle_t* target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposerClient::Rect>& damage);
+ Error setColorMode(Display display, ColorMode mode);
+ Error setColorTransform(Display display, const float* matrix,
+ ColorTransform hint);
+ Error setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence);
+ Error setPowerMode(Display display, IComposerClient::PowerMode mode);
+ Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled);
+
+ Error setClientTargetSlotCount(Display display);
+
+ Error validateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests);
+
+ Error setCursorPosition(Display display, Layer layer,
+ int32_t x, int32_t y);
+ Error setLayerBuffer(Display display, Layer layer,
+ const native_handle_t* buffer, int acquireFence);
+ Error setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& damage);
+ Error setLayerBlendMode(Display display, Layer layer,
+ IComposerClient::BlendMode mode);
+ Error setLayerColor(Display display, Layer layer,
+ const IComposerClient::Color& color);
+ Error setLayerCompositionType(Display display, Layer layer,
+ IComposerClient::Composition type);
+ Error setLayerDataspace(Display display, Layer layer,
+ Dataspace dataspace);
+ Error setLayerDisplayFrame(Display display, Layer layer,
+ const IComposerClient::Rect& frame);
+ Error setLayerPlaneAlpha(Display display, Layer layer,
+ float alpha);
+ Error setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream);
+ Error setLayerSourceCrop(Display display, Layer layer,
+ const IComposerClient::FRect& crop);
+ Error setLayerTransform(Display display, Layer layer,
+ Transform transform);
+ Error setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& visible);
+ Error setLayerZOrder(Display display, Layer layer, uint32_t z);
+
+private:
+ // Many public functions above simply write a command into the command
+ // queue to batch the calls. validateDisplay and presentDisplay will call
+ // this function to execute the command queue.
+ Error execute();
+
+ sp<IComposer> mComposer;
+ sp<IComposerClient> mClient;
+
+ // 64KiB minus a small space for metadata such as read/write pointers
+ static constexpr size_t kWriterInitialSize =
+ 64 * 1024 / sizeof(uint32_t) - 16;
+ CommandWriter mWriter;
+ CommandReader mReader;
+};
+
+} // namespace Hwc2
+
+} // namespace android
+
+#endif // ANDROID_SF_COMPOSER_HAL_H
diff --git a/services/surfaceflinger/DisplayHardware/FloatRect.h b/services/surfaceflinger/DisplayHardware/FloatRect.h
deleted file mode 100644
index 151eaaa..0000000
--- a/services/surfaceflinger/DisplayHardware/FloatRect.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_FLOAT_RECT
-#define ANDROID_SF_FLOAT_RECT
-
-#include <ui/Rect.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-class FloatRect
-{
-public:
- float left;
- float top;
- float right;
- float bottom;
-
- inline FloatRect()
- : left(0), top(0), right(0), bottom(0) { }
- inline FloatRect(const Rect& other) // NOLINT(implicit)
- : left(other.left), top(other.top), right(other.right), bottom(other.bottom) { }
-
- inline float getWidth() const { return right - left; }
- inline float getHeight() const { return bottom - top; }
-};
-
-}; // namespace android
-
-#endif // ANDROID_SF_FLOAT_RECT
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index cdfe34a..1998edf 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -204,7 +204,7 @@
void FramebufferSurface::onFrameCommitted() {
#ifdef USE_HWC2
if (mHasPendingRelease) {
- sp<Fence> fence = mHwc.getRetireFence(mDisplayType);
+ sp<Fence> fence = mHwc.getPresentFence(mDisplayType);
if (fence->isValid()) {
status_t result = addReleaseFence(mPreviousBufferSlot,
mPreviousBuffer, fence);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 4fe3cfd..c89ca83 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -21,8 +21,9 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "HWC2.h"
+#include "ComposerHal.h"
-#include "FloatRect.h"
+#include <gfx/FloatRect.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
@@ -73,17 +74,22 @@
}
using android::Fence;
-using android::FloatRect;
using android::GraphicBuffer;
using android::HdrCapabilities;
using android::Rect;
using android::Region;
using android::sp;
+using android::gfx::FloatRect;
+using android::hardware::Return;
+using android::hardware::Void;
namespace HWC2 {
+namespace Hwc2 = android::Hwc2;
+
// Device methods
+#ifdef BYPASS_IHWC
Device::Device(hwc2_device_t* device)
: mHwcDevice(device),
mCreateVirtualDisplay(nullptr),
@@ -128,6 +134,10 @@
mSetLayerTransform(nullptr),
mSetLayerVisibleRegion(nullptr),
mSetLayerZOrder(nullptr),
+#else
+Device::Device()
+ : mComposer(std::make_unique<Hwc2::Composer>()),
+#endif // BYPASS_IHWC
mCapabilities(),
mDisplays(),
mHotplug(),
@@ -144,9 +154,11 @@
Device::~Device()
{
+#ifdef BYPASS_IHWC
if (mHwcDevice == nullptr) {
return;
}
+#endif
for (auto element : mDisplays) {
auto display = element.second.lock();
@@ -175,13 +187,16 @@
}
}
+#ifdef BYPASS_IHWC
hwc2_close(mHwcDevice);
+#endif
}
// Required by HWC2 device
std::string Device::dump() const
{
+#ifdef BYPASS_IHWC
uint32_t numBytes = 0;
mDump(mHwcDevice, &numBytes, nullptr);
@@ -189,11 +204,18 @@
mDump(mHwcDevice, &numBytes, buffer.data());
return std::string(buffer.data(), buffer.size());
+#else
+ return mComposer->dumpDebugInfo();
+#endif
}
uint32_t Device::getMaxVirtualDisplayCount() const
{
+#ifdef BYPASS_IHWC
return mGetMaxVirtualDisplayCount(mHwcDevice);
+#else
+ return mComposer->getMaxVirtualDisplayCount();
+#endif
}
Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
@@ -202,9 +224,15 @@
ALOGI("Creating virtual display");
hwc2_display_t displayId = 0;
+#ifdef BYPASS_IHWC
int32_t intFormat = static_cast<int32_t>(*format);
int32_t intError = mCreateVirtualDisplay(mHwcDevice, width, height,
&intFormat, &displayId);
+#else
+ auto intFormat = static_cast<Hwc2::PixelFormat>(*format);
+ auto intError = mComposer->createVirtualDisplay(width, height,
+ &intFormat, &displayId);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -258,6 +286,9 @@
{
if (connected == Connection::Connected) {
if (!display->isConnected()) {
+#ifndef BYPASS_IHWC
+ mComposer->setClientTargetSlotCount(display->getId());
+#endif
display->loadConfigs();
display->setConnected(true);
}
@@ -315,6 +346,7 @@
{
static_assert(sizeof(Capability) == sizeof(int32_t),
"Capability size has changed");
+#ifdef BYPASS_IHWC
uint32_t numCapabilities = 0;
mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
std::vector<Capability> capabilities(numCapabilities);
@@ -323,6 +355,12 @@
for (auto capability : capabilities) {
mCapabilities.emplace(capability);
}
+#else
+ auto capabilities = mComposer->getCapabilities();
+ for (auto capability : capabilities) {
+ mCapabilities.emplace(static_cast<Capability>(capability));
+ }
+#endif
}
bool Device::hasCapability(HWC2::Capability capability) const
@@ -333,6 +371,7 @@
void Device::loadFunctionPointers()
{
+#ifdef BYPASS_IHWC
// For all of these early returns, we log an error message inside
// loadFunctionPointer specifying which function failed to load
@@ -426,13 +465,48 @@
mSetLayerVisibleRegion)) return;
if (!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
mSetLayerZOrder)) return;
+#endif // BYPASS_IHWC
}
+namespace {
+class ComposerCallback : public Hwc2::IComposerCallback {
+public:
+ ComposerCallback(Device* device) : mDevice(device) {}
+
+ Return<void> onHotplug(Hwc2::Display display,
+ Connection connected) override
+ {
+ hotplug_hook(mDevice, display, static_cast<int32_t>(connected));
+ return Void();
+ }
+
+ Return<void> onRefresh(Hwc2::Display display) override
+ {
+ refresh_hook(mDevice, display);
+ return Void();
+ }
+
+ Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
+ {
+ vsync_hook(mDevice, display, timestamp);
+ return Void();
+ }
+
+private:
+ Device* mDevice;
+};
+} // namespace anonymous
+
void Device::registerCallbacks()
{
+#ifdef BYPASS_IHWC
registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
+#else
+ sp<ComposerCallback> callback = new ComposerCallback(this);
+ mComposer->registerCallback(callback);
+#endif
}
@@ -441,7 +515,11 @@
void Device::destroyVirtualDisplay(hwc2_display_t display)
{
ALOGI("Destroying virtual display");
+#ifdef BYPASS_IHWC
int32_t intError = mDestroyVirtualDisplay(mHwcDevice, display);
+#else
+ auto intError = mComposer->destroyVirtualDisplay(display);
+#endif
auto error = static_cast<Error>(intError);
ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:"
" %s (%d)", display, to_string(error).c_str(), intError);
@@ -498,14 +576,22 @@
Error Display::acceptChanges()
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mAcceptDisplayChanges(mDevice.mHwcDevice, mId);
+#else
+ auto intError = mDevice.mComposer->acceptDisplayChanges(mId);
+#endif
return static_cast<Error>(intError);
}
Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
{
hwc2_layer_t layerId = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
+#else
+ auto intError = mDevice.mComposer->createLayer(mId, &layerId);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -522,11 +608,17 @@
{
ALOGV("[%" PRIu64 "] getActiveConfig", mId);
hwc2_config_t configId = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mGetActiveConfig(mDevice.mHwcDevice, mId,
&configId);
+#else
+ auto intError = mDevice.mComposer->getActiveConfig(mId, &configId);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
+ ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
+ *outConfig = nullptr;
return error;
}
@@ -546,6 +638,7 @@
Error Display::getChangedCompositionTypes(
std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes)
{
+#ifdef BYPASS_IHWC
uint32_t numElements = 0;
int32_t intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice,
mId, &numElements, nullptr, nullptr);
@@ -558,6 +651,14 @@
std::vector<int32_t> types(numElements);
intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice, mId,
&numElements, layerIds.data(), types.data());
+#else
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<Hwc2::IComposerClient::Composition> types;
+ auto intError = mDevice.mComposer->getChangedCompositionTypes(mId,
+ &layerIds, &types);
+ uint32_t numElements = layerIds.size();
+ auto error = static_cast<Error>(intError);
+#endif
error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -583,6 +684,7 @@
Error Display::getColorModes(std::vector<android_color_mode_t>* outModes) const
{
+#ifdef BYPASS_IHWC
uint32_t numModes = 0;
int32_t intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId,
&numModes, nullptr);
@@ -595,6 +697,12 @@
intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId, &numModes,
modes.data());
error = static_cast<Error>(intError);
+#else
+ std::vector<Hwc2::ColorMode> modes;
+ auto intError = mDevice.mComposer->getColorModes(mId, &modes);
+ uint32_t numModes = modes.size();
+ auto error = static_cast<Error>(intError);
+#endif
if (error != Error::None) {
return error;
}
@@ -617,6 +725,7 @@
Error Display::getName(std::string* outName) const
{
+#ifdef BYPASS_IHWC
uint32_t size;
int32_t intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size,
nullptr);
@@ -635,12 +744,17 @@
*outName = std::string(rawName.cbegin(), rawName.cend());
return Error::None;
+#else
+ auto intError = mDevice.mComposer->getDisplayName(mId, outName);
+ return static_cast<Error>(intError);
+#endif
}
Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
std::unordered_map<std::shared_ptr<Layer>, LayerRequest>*
outLayerRequests)
{
+#ifdef BYPASS_IHWC
int32_t intDisplayRequests = 0;
uint32_t numElements = 0;
int32_t intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId,
@@ -656,6 +770,15 @@
&intDisplayRequests, &numElements, layerIds.data(),
layerRequests.data());
error = static_cast<Error>(intError);
+#else
+ uint32_t intDisplayRequests;
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<uint32_t> layerRequests;
+ auto intError = mDevice.mComposer->getDisplayRequests(mId,
+ &intDisplayRequests, &layerIds, &layerRequests);
+ uint32_t numElements = layerIds.size();
+ auto error = static_cast<Error>(intError);
+#endif
if (error != Error::None) {
return error;
}
@@ -680,9 +803,15 @@
Error Display::getType(DisplayType* outType) const
{
+#ifdef BYPASS_IHWC
int32_t intType = 0;
int32_t intError = mDevice.mGetDisplayType(mDevice.mHwcDevice, mId,
&intType);
+#else
+ Hwc2::IComposerClient::DisplayType intType =
+ Hwc2::IComposerClient::DisplayType::INVALID;
+ auto intError = mDevice.mComposer->getDisplayType(mId, &intType);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -694,9 +823,14 @@
Error Display::supportsDoze(bool* outSupport) const
{
+#ifdef BYPASS_IHWC
int32_t intSupport = 0;
int32_t intError = mDevice.mGetDozeSupport(mDevice.mHwcDevice, mId,
&intSupport);
+#else
+ bool intSupport = false;
+ auto intError = mDevice.mComposer->getDozeSupport(mId, &intSupport);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
@@ -712,6 +846,7 @@
float maxLuminance = -1.0f;
float maxAverageLuminance = -1.0f;
float minLuminance = -1.0f;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId,
&numTypes, nullptr, &maxLuminance, &maxAverageLuminance,
&minLuminance);
@@ -724,6 +859,18 @@
intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId, &numTypes,
types.data(), &maxLuminance, &maxAverageLuminance, &minLuminance);
error = static_cast<HWC2::Error>(intError);
+#else
+ std::vector<Hwc2::Hdr> intTypes;
+ auto intError = mDevice.mComposer->getHdrCapabilities(mId, &intTypes,
+ &maxLuminance, &maxAverageLuminance, &minLuminance);
+ auto error = static_cast<HWC2::Error>(intError);
+
+ std::vector<int32_t> types;
+ for (auto type : intTypes) {
+ types.push_back(static_cast<int32_t>(type));
+ }
+ numTypes = types.size();
+#endif
if (error != Error::None) {
return error;
}
@@ -736,6 +883,7 @@
Error Display::getReleaseFences(
std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const
{
+#ifdef BYPASS_IHWC
uint32_t numElements = 0;
int32_t intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId,
&numElements, nullptr, nullptr);
@@ -749,6 +897,14 @@
intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId, &numElements,
layerIds.data(), fenceFds.data());
error = static_cast<Error>(intError);
+#else
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<int> fenceFds;
+ auto intError = mDevice.mComposer->getReleaseFences(mId,
+ &layerIds, &fenceFds);
+ auto error = static_cast<Error>(intError);
+ uint32_t numElements = layerIds.size();
+#endif
if (error != Error::None) {
return error;
}
@@ -771,17 +927,21 @@
return Error::None;
}
-Error Display::present(sp<Fence>* outRetireFence)
+Error Display::present(sp<Fence>* outPresentFence)
{
- int32_t retireFenceFd = 0;
+ int32_t presentFenceFd = -1;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
- &retireFenceFd);
+ &presentFenceFd);
+#else
+ auto intError = mDevice.mComposer->presentDisplay(mId, &presentFenceFd);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
}
- *outRetireFence = new Fence(retireFenceFd);
+ *outPresentFence = new Fence(presentFenceFd);
return Error::None;
}
@@ -793,8 +953,12 @@
config->getDisplayId(), mId);
return Error::BadConfig;
}
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetActiveConfig(mDevice.mHwcDevice, mId,
config->getId());
+#else
+ auto intError = mDevice.mComposer->setActiveConfig(mId, config->getId());
+#endif
return static_cast<Error>(intError);
}
@@ -803,22 +967,38 @@
{
// TODO: Properly encode client target surface damage
int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target,
fenceFd, static_cast<int32_t>(dataspace), {0, nullptr});
+#else
+ auto intError = mDevice.mComposer->setClientTarget(mId, target, fenceFd,
+ static_cast<Hwc2::Dataspace>(dataspace),
+ std::vector<Hwc2::IComposerClient::Rect>());
+#endif
return static_cast<Error>(intError);
}
Error Display::setColorMode(android_color_mode_t mode)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode);
+#else
+ auto intError = mDevice.mComposer->setColorMode(mId,
+ static_cast<Hwc2::ColorMode>(mode));
+#endif
return static_cast<Error>(intError);
}
Error Display::setColorTransform(const android::mat4& matrix,
android_color_transform_t hint)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetColorTransform(mDevice.mHwcDevice, mId,
matrix.asArray(), static_cast<int32_t>(hint));
+#else
+ auto intError = mDevice.mComposer->setColorTransform(mId,
+ matrix.asArray(), static_cast<Hwc2::ColorTransform>(hint));
+#endif
return static_cast<Error>(intError);
}
@@ -827,24 +1007,38 @@
{
int32_t fenceFd = releaseFence->dup();
auto handle = buffer->getNativeBuffer()->handle;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle,
fenceFd);
+#else
+ auto intError = mDevice.mComposer->setOutputBuffer(mId, handle, fenceFd);
+#endif
close(fenceFd);
return static_cast<Error>(intError);
}
Error Display::setPowerMode(PowerMode mode)
{
+#ifdef BYPASS_IHWC
auto intMode = static_cast<int32_t>(mode);
int32_t intError = mDevice.mSetPowerMode(mDevice.mHwcDevice, mId, intMode);
+#else
+ auto intMode = static_cast<Hwc2::IComposerClient::PowerMode>(mode);
+ auto intError = mDevice.mComposer->setPowerMode(mId, intMode);
+#endif
return static_cast<Error>(intError);
}
Error Display::setVsyncEnabled(Vsync enabled)
{
+#ifdef BYPASS_IHWC
auto intEnabled = static_cast<int32_t>(enabled);
int32_t intError = mDevice.mSetVsyncEnabled(mDevice.mHwcDevice, mId,
intEnabled);
+#else
+ auto intEnabled = static_cast<Hwc2::IComposerClient::Vsync>(enabled);
+ auto intError = mDevice.mComposer->setVsyncEnabled(mId, intEnabled);
+#endif
return static_cast<Error>(intError);
}
@@ -852,8 +1046,13 @@
{
uint32_t numTypes = 0;
uint32_t numRequests = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mValidateDisplay(mDevice.mHwcDevice, mId,
&numTypes, &numRequests);
+#else
+ auto intError = mDevice.mComposer->validateDisplay(mId,
+ &numTypes, &numRequests);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None && error != Error::HasChanges) {
return error;
@@ -869,8 +1068,14 @@
int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
{
int32_t value = 0;
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mGetDisplayAttribute(mDevice.mHwcDevice, mId,
configId, static_cast<int32_t>(attribute), &value);
+#else
+ auto intError = mDevice.mComposer->getDisplayAttribute(mId, configId,
+ static_cast<Hwc2::IComposerClient::Attribute>(attribute),
+ &value);
+#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
@@ -899,6 +1104,7 @@
{
ALOGV("[%" PRIu64 "] loadConfigs", mId);
+#ifdef BYPASS_IHWC
uint32_t numConfigs = 0;
int32_t intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId,
&numConfigs, nullptr);
@@ -913,6 +1119,11 @@
intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId, &numConfigs,
configIds.data());
error = static_cast<Error>(intError);
+#else
+ std::vector<Hwc2::Config> configIds;
+ auto intError = mDevice.mComposer->getDisplayConfigs(mId, &configIds);
+ auto error = static_cast<Error>(intError);
+#endif
if (error != Error::None) {
ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
to_string(error).c_str(), intError);
@@ -928,7 +1139,11 @@
void Display::destroyLayer(hwc2_layer_t layerId)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mDestroyLayer(mDevice.mHwcDevice, mId, layerId);
+#else
+ auto intError =mDevice.mComposer->destroyLayer(mId, layerId);
+#endif
auto error = static_cast<Error>(intError);
ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")"
" failed: %s (%d)", mId, layerId, to_string(error).c_str(),
@@ -970,8 +1185,13 @@
Error Layer::setCursorPosition(int32_t x, int32_t y)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetCursorPosition(mDevice.mHwcDevice,
mDisplayId, mId, x, y);
+#else
+ auto intError = mDevice.mComposer->setCursorPosition(mDisplayId,
+ mId, x, y);
+#endif
return static_cast<Error>(intError);
}
@@ -979,8 +1199,13 @@
const sp<Fence>& acquireFence)
{
int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
mId, buffer, fenceFd);
+#else
+ auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId,
+ mId, buffer, fenceFd);
+#endif
return static_cast<Error>(intError);
}
@@ -988,26 +1213,44 @@
{
// We encode default full-screen damage as INVALID_RECT upstream, but as 0
// rects for HWC
+#ifdef BYPASS_IHWC
int32_t intError = 0;
+#else
+ Hwc2::Error intError = Hwc2::Error::NONE;
+#endif
if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) {
+#ifdef BYPASS_IHWC
intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
mDisplayId, mId, {0, nullptr});
+#else
+ intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+ mId, std::vector<Hwc2::IComposerClient::Rect>());
+#endif
} else {
size_t rectCount = 0;
auto rectArray = damage.getArray(&rectCount);
+#ifdef BYPASS_IHWC
std::vector<hwc_rect_t> hwcRects;
+#else
+ std::vector<Hwc2::IComposerClient::Rect> hwcRects;
+#endif
for (size_t rect = 0; rect < rectCount; ++rect) {
hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
rectArray[rect].right, rectArray[rect].bottom});
}
+#ifdef BYPASS_IHWC
hwc_region_t hwcRegion = {};
hwcRegion.numRects = rectCount;
hwcRegion.rects = hwcRects.data();
intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
mDisplayId, mId, hwcRegion);
+#else
+ intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+ mId, hwcRects);
+#endif
}
return static_cast<Error>(intError);
@@ -1015,47 +1258,83 @@
Error Layer::setBlendMode(BlendMode mode)
{
+#ifdef BYPASS_IHWC
auto intMode = static_cast<int32_t>(mode);
int32_t intError = mDevice.mSetLayerBlendMode(mDevice.mHwcDevice,
mDisplayId, mId, intMode);
+#else
+ auto intMode = static_cast<Hwc2::IComposerClient::BlendMode>(mode);
+ auto intError = mDevice.mComposer->setLayerBlendMode(mDisplayId,
+ mId, intMode);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setColor(hwc_color_t color)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerColor(mDevice.mHwcDevice, mDisplayId,
mId, color);
+#else
+ Hwc2::IComposerClient::Color hwcColor{color.r, color.g, color.b, color.a};
+ auto intError = mDevice.mComposer->setLayerColor(mDisplayId,
+ mId, hwcColor);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setCompositionType(Composition type)
{
+#ifdef BYPASS_IHWC
auto intType = static_cast<int32_t>(type);
int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,
mDisplayId, mId, intType);
+#else
+ auto intType = static_cast<Hwc2::IComposerClient::Composition>(type);
+ auto intError = mDevice.mComposer->setLayerCompositionType(mDisplayId,
+ mId, intType);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setDataspace(android_dataspace_t dataspace)
{
+#ifdef BYPASS_IHWC
auto intDataspace = static_cast<int32_t>(dataspace);
int32_t intError = mDevice.mSetLayerDataspace(mDevice.mHwcDevice,
mDisplayId, mId, intDataspace);
+#else
+ auto intDataspace = static_cast<Hwc2::Dataspace>(dataspace);
+ auto intError = mDevice.mComposer->setLayerDataspace(mDisplayId,
+ mId, intDataspace);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setDisplayFrame(const Rect& frame)
{
+#ifdef BYPASS_IHWC
hwc_rect_t hwcRect{frame.left, frame.top, frame.right, frame.bottom};
int32_t intError = mDevice.mSetLayerDisplayFrame(mDevice.mHwcDevice,
mDisplayId, mId, hwcRect);
+#else
+ Hwc2::IComposerClient::Rect hwcRect{frame.left, frame.top,
+ frame.right, frame.bottom};
+ auto intError = mDevice.mComposer->setLayerDisplayFrame(mDisplayId,
+ mId, hwcRect);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setPlaneAlpha(float alpha)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerPlaneAlpha(mDevice.mHwcDevice,
mDisplayId, mId, alpha);
+#else
+ auto intError = mDevice.mComposer->setLayerPlaneAlpha(mDisplayId,
+ mId, alpha);
+#endif
return static_cast<Error>(intError);
}
@@ -1066,24 +1345,42 @@
"device supports sideband streams");
return Error::Unsupported;
}
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice,
mDisplayId, mId, stream);
+#else
+ auto intError = mDevice.mComposer->setLayerSidebandStream(mDisplayId,
+ mId, stream);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setSourceCrop(const FloatRect& crop)
{
+#ifdef BYPASS_IHWC
hwc_frect_t hwcRect{crop.left, crop.top, crop.right, crop.bottom};
int32_t intError = mDevice.mSetLayerSourceCrop(mDevice.mHwcDevice,
mDisplayId, mId, hwcRect);
+#else
+ Hwc2::IComposerClient::FRect hwcRect{
+ crop.left, crop.top, crop.right, crop.bottom};
+ auto intError = mDevice.mComposer->setLayerSourceCrop(mDisplayId,
+ mId, hwcRect);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setTransform(Transform transform)
{
+#ifdef BYPASS_IHWC
auto intTransform = static_cast<int32_t>(transform);
int32_t intError = mDevice.mSetLayerTransform(mDevice.mHwcDevice,
mDisplayId, mId, intTransform);
+#else
+ auto intTransform = static_cast<Hwc2::Transform>(transform);
+ auto intError = mDevice.mComposer->setLayerTransform(mDisplayId,
+ mId, intTransform);
+#endif
return static_cast<Error>(intError);
}
@@ -1092,25 +1389,38 @@
size_t rectCount = 0;
auto rectArray = region.getArray(&rectCount);
+#ifdef BYPASS_IHWC
std::vector<hwc_rect_t> hwcRects;
+#else
+ std::vector<Hwc2::IComposerClient::Rect> hwcRects;
+#endif
for (size_t rect = 0; rect < rectCount; ++rect) {
hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
rectArray[rect].right, rectArray[rect].bottom});
}
+#ifdef BYPASS_IHWC
hwc_region_t hwcRegion = {};
hwcRegion.numRects = rectCount;
hwcRegion.rects = hwcRects.data();
int32_t intError = mDevice.mSetLayerVisibleRegion(mDevice.mHwcDevice,
mDisplayId, mId, hwcRegion);
+#else
+ auto intError = mDevice.mComposer->setLayerVisibleRegion(mDisplayId,
+ mId, hwcRects);
+#endif
return static_cast<Error>(intError);
}
Error Layer::setZOrder(uint32_t z)
{
+#ifdef BYPASS_IHWC
int32_t intError = mDevice.mSetLayerZOrder(mDevice.mHwcDevice, mDisplayId,
mId, z);
+#else
+ auto intError = mDevice.mComposer->setLayerZOrder(mDisplayId, mId, z);
+#endif
return static_cast<Error>(intError);
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 32a9de0..5b894ba 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,10 +38,15 @@
namespace android {
class Fence;
- class FloatRect;
class GraphicBuffer;
class Rect;
class Region;
+ namespace gfx {
+ class FloatRect;
+ }
+ namespace Hwc2 {
+ class Composer;
+ }
}
namespace HWC2 {
@@ -54,10 +59,16 @@
typedef std::function<void(std::shared_ptr<Display>)> RefreshCallback;
typedef std::function<void(std::shared_ptr<Display>, nsecs_t)> VsyncCallback;
+// C++ Wrapper around hwc2_device_t. Load all functions pointers
+// and handle callback registration.
class Device
{
public:
+#ifdef BYPASS_IHWC
explicit Device(hwc2_device_t* device);
+#else
+ Device();
+#endif
~Device();
friend class HWC2::Display;
@@ -98,6 +109,7 @@
private:
// Initialization methods
+#ifdef BYPASS_IHWC
template <typename PFN>
[[clang::warn_unused_result]] bool loadFunctionPointer(
FunctionDescriptor desc, PFN& outPFN) {
@@ -121,6 +133,7 @@
auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
}
+#endif
void loadCapabilities();
void loadFunctionPointers();
@@ -132,6 +145,7 @@
// Member variables
+#ifdef BYPASS_IHWC
hwc2_device_t* mHwcDevice;
// Device function pointers
@@ -181,6 +195,9 @@
HWC2_PFN_SET_LAYER_TRANSFORM mSetLayerTransform;
HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
+#else
+ std::unique_ptr<android::Hwc2::Composer> mComposer;
+#endif // BYPASS_IHWC
std::unordered_set<Capability> mCapabilities;
std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
@@ -194,6 +211,7 @@
std::vector<std::pair<std::shared_ptr<Display>, nsecs_t>> mPendingVsyncs;
};
+// Convenience C++ class to access hwc2_device_t Display functions directly.
class Display : public std::enable_shared_from_this<Display>
{
public:
@@ -300,7 +318,7 @@
std::unordered_map<std::shared_ptr<Layer>,
android::sp<android::Fence>>* outFences) const;
[[clang::warn_unused_result]] Error present(
- android::sp<android::Fence>* outRetireFence);
+ android::sp<android::Fence>* outPresentFence);
[[clang::warn_unused_result]] Error setActiveConfig(
const std::shared_ptr<const Config>& config);
[[clang::warn_unused_result]] Error setClientTarget(
@@ -355,6 +373,7 @@
std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
};
+// Convenience C++ class to access hwc2_device_t Layer functions directly.
class Layer
{
public:
@@ -381,7 +400,7 @@
[[clang::warn_unused_result]] Error setSidebandStream(
const native_handle_t* stream);
[[clang::warn_unused_result]] Error setSourceCrop(
- const android::FloatRect& crop);
+ const android::gfx::FloatRect& crop);
[[clang::warn_unused_result]] Error setTransform(Transform transform);
[[clang::warn_unused_result]] Error setVisibleRegion(
const android::Region& region);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 617ea9f..7322c13 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -87,6 +87,7 @@
for (size_t l = 0; l < contents->numHwLayers; ++l) {
auto& layer = contents->hwLayers[l];
std::free(const_cast<hwc_rect_t*>(layer.visibleRegionScreen.rects));
+ std::free(const_cast<hwc_rect_t*>(layer.surfaceDamage.rects));
}
}
std::free(contents);
@@ -94,7 +95,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;
@@ -133,6 +134,7 @@
mHwc1Device(hwc1Device),
mHwc1MinorVersion(getMinorVersion(hwc1Device)),
mHwc1SupportsVirtualDisplays(false),
+ mHwc1SupportsBackgroundColor(false),
mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
mCapabilities(),
mLayers(),
@@ -561,6 +563,7 @@
mHwc1Id(-1),
mConfigs(),
mActiveConfig(nullptr),
+ mActiveColorMode(static_cast<android_color_mode_t>(-1)),
mName(),
mType(type),
mPowerMode(PowerMode::Off),
@@ -807,7 +810,10 @@
return Error::None;
}
- *outDisplayRequests = mChanges->getDisplayRequests();
+ // Display requests (HWC2::DisplayRequest) are not supported by hwc1:
+ // A hwc1 has always zero requests for the client.
+ *outDisplayRequests = 0;
+
uint32_t numWritten = 0;
for (const auto& request : mChanges->getLayerRequests()) {
if (numWritten == *outNumElements) {
@@ -1841,29 +1847,39 @@
auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
mDevice.mHwc1Device, mHwc1Id);
- if (activeConfig >= 0) {
- for (const auto& config : mConfigs) {
- if (config->hasHwc1Id(activeConfig)) {
- ALOGV("Setting active config to %d for HWC1 config %u",
- config->getId(), activeConfig);
- mActiveConfig = config;
- if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
- // This should never happen since we checked for the config's presence before
- // setting it as active.
- ALOGE("Unable to find color mode for active HWC1 config %d",
- config->getId());
- mActiveColorMode = HAL_COLOR_MODE_NATIVE;
- }
- break;
+
+ // Some devices startup without an activeConfig:
+ // We need to set one ourselves.
+ if (activeConfig == HWC_ERROR) {
+ ALOGV("There is no active configuration: Picking the first one: 0.");
+ const int defaultIndex = 0;
+ mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, defaultIndex);
+ activeConfig = defaultIndex;
+ }
+
+ for (const auto& config : mConfigs) {
+ if (config->hasHwc1Id(activeConfig)) {
+ ALOGE("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig);
+ mActiveConfig = config;
+ if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
+ // This should never happen since we checked for the config's presence before
+ // setting it as active.
+ ALOGE("Unable to find color mode for active HWC1 config %d", config->getId());
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
}
- }
- if (!mActiveConfig) {
- ALOGV("Unable to find active HWC1 config %u, defaulting to "
- "config 0", activeConfig);
- mActiveConfig = mConfigs[0];
- mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ break;
}
}
+ if (!mActiveConfig) {
+ ALOGV("Unable to find active HWC1 config %u, defaulting to "
+ "config 0", activeConfig);
+ mActiveConfig = mConfigs[0];
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ }
+
+
+
+
}
void HWC2On1Adapter::Display::reallocateHwc1Contents()
@@ -2262,7 +2278,18 @@
bool applyAllState)
{
if (applyAllState || mColor.isDirty()) {
- hwc1Layer.backgroundColor = mColor.getPendingValue();
+ // If the device does not support background color it is likely to make
+ // assumption regarding backgroundColor and handle (both fields occupy
+ // the same location in hwc_layer_1_t union).
+ // To not confuse these devices we don't set background color and we
+ // make sure handle is a null pointer.
+ if (mDisplay.getDevice().supportsBackgroundColor()) {
+ hwc1Layer.backgroundColor = mColor.getPendingValue();
+ mHasUnsupportedBackgroundColor = false;
+ } else {
+ hwc1Layer.handle = nullptr;
+ mHasUnsupportedBackgroundColor = true;
+ }
mColor.latch();
}
}
@@ -2289,7 +2316,7 @@
// supports plane alpha (depending on the version). These require us to drop
// some or all layers to client composition.
if (mHasUnsupportedDataspace || mHasUnsupportedPlaneAlpha ||
- mDisplay.hasColorTransform()) {
+ mDisplay.hasColorTransform() || mHasUnsupportedBackgroundColor) {
hwc1Layer.compositionType = HWC_FRAMEBUFFER;
hwc1Layer.flags = HWC_SKIP_LAYER;
return;
@@ -2359,6 +2386,18 @@
if (mHwc1MinorVersion >= 4U) {
mCapabilities.insert(Capability::SidebandStream);
}
+
+ // Check for HWC background color layer support.
+ if (mHwc1MinorVersion >= 1U) {
+ int backgroundColorSupported = 0;
+ auto result = mHwc1Device->query(mHwc1Device,
+ HWC_BACKGROUND_LAYER_SUPPORTED,
+ &backgroundColorSupported);
+ if ((result == 0) && (backgroundColorSupported == 1)) {
+ ALOGV("Found support for HWC background color");
+ mHwc1SupportsBackgroundColor = true;
+ }
+ }
}
HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index 962361e..9abdc38 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -40,6 +40,10 @@
namespace android {
+// For devices unable to provide an implementation of HWC2 (see hwcomposer2.h),
+// we provide an adapter able to talk to HWC1 (see hwcomposer.h). It translates
+// streamed function calls ala HWC2 model to batched array of structs calls ala
+// HWC1 model.
class HWC2On1Adapter : public hwc2_device_t
{
public:
@@ -63,6 +67,10 @@
getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
}
+ bool supportsBackgroundColor() {
+ return mHwc1SupportsBackgroundColor;
+ }
+
// getFunction
hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor);
@@ -131,11 +139,28 @@
void operator()(struct hwc_display_contents_1* contents);
};
+ // The semantics of the fences returned by the device differ between
+ // hwc1.set() and hwc2.present(). Read hwcomposer.h and hwcomposer2.h
+ // for more information.
+ //
+ // Release fences in hwc1 are obtained on set() for a frame n and signaled
+ // when the layer buffer is not needed for read operations anymore
+ // (typically on frame n+1). In HWC2, release fences are obtained with a
+ // special call after present() for frame n. These fences signal
+ // on frame n: More specifically, the fence for a given buffer provided in
+ // frame n will signal when the prior buffer is no longer required.
+ //
+ // A retire fence (HWC1) is signaled when a composition is replaced
+ // on the panel whereas a present fence (HWC2) is signaled when a
+ // composition starts to be displayed on a panel.
+ //
+ // The HWC2to1Adapter emulates the new fence semantics for a frame
+ // n by returning the fence from frame n-1. For frame 0, the adapter
+ // returns NO_FENCE.
class DeferredFence {
public:
DeferredFence()
- : mMutex(),
- mFences({Fence::NO_FENCE, Fence::NO_FENCE}) {}
+ : mFences({Fence::NO_FENCE, Fence::NO_FENCE}) {}
void add(int32_t fenceFd) {
mFences.emplace(new Fence(fenceFd));
@@ -147,7 +172,7 @@
}
private:
- mutable std::mutex mMutex;
+ // There are always two fences in this queue.
std::queue<sp<Fence>> mFences;
};
@@ -232,7 +257,10 @@
bool prepare();
HWC1Contents cloneRequestedContents() const;
+
+ // Called after hwc.prepare() with responses from the device.
void setReceivedContents(HWC1Contents contents);
+
bool hasChanges() const;
HWC2::Error set(hwc_display_contents_1& hwcContents);
void addRetireFence(int fenceFd);
@@ -247,6 +275,7 @@
public:
Config(Display& display)
: mDisplay(display),
+ mId(0),
mAttributes() {}
bool isOnDisplay(const Display& display) const {
@@ -285,6 +314,10 @@
std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
};
+ // Store changes requested from the device upon calling prepare().
+ // Handles change request to:
+ // - Layer composition type.
+ // - Layer hints.
class Changes {
public:
uint32_t getNumTypes() const {
@@ -305,14 +338,6 @@
return mLayerRequests;
}
- int32_t getDisplayRequests() const {
- int32_t requests = 0;
- for (auto request : mDisplayRequests) {
- requests |= static_cast<int32_t>(request);
- }
- return requests;
- }
-
void addTypeChange(hwc2_layer_t layerId,
HWC2::Composition type) {
mTypeChanges.insert({layerId, type});
@@ -330,7 +355,6 @@
mTypeChanges;
std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
mLayerRequests;
- std::unordered_set<HWC2::DisplayRequest> mDisplayRequests;
};
std::shared_ptr<const Config>
@@ -342,8 +366,13 @@
void reallocateHwc1Contents();
void assignHwc1LayerIds();
+ // Called after a response to prepare() has been received:
+ // Ingest composition type changes requested by the device.
void updateTypeChanges(const struct hwc_layer_1& hwc1Layer,
const Layer& layer);
+
+ // Called after a response to prepare() has been received:
+ // Ingest layer hint changes requested by the device.
void updateLayerRequests(const struct hwc_layer_1& hwc1Layer,
const Layer& layer);
@@ -367,8 +396,11 @@
mutable std::recursive_mutex mStateMutex;
bool mZIsDirty;
- HWC1Contents mHwc1RequestedContents;
- HWC1Contents mHwc1ReceivedContents;
+
+ // Array of structs exchanged between client and hwc1 device.
+ HWC1Contents mHwc1RequestedContents; // Sent to device upon calling prepare().
+ HWC1Contents mHwc1ReceivedContents; // Returned by device after prepare().
+
DeferredFence mRetireFence;
// Will only be non-null after the layer has been validated but
@@ -575,6 +607,7 @@
size_t mHwc1Id;
bool mHasUnsupportedDataspace;
bool mHasUnsupportedPlaneAlpha;
+ bool mHasUnsupportedBackgroundColor;
};
template <typename ...Args>
@@ -655,6 +688,7 @@
struct hwc_composer_device_1* const mHwc1Device;
const uint8_t mHwc1MinorVersion;
bool mHwc1SupportsVirtualDisplays;
+ bool mHwc1SupportsBackgroundColor;
class Callbacks;
const std::unique_ptr<Callbacks> mHwc1Callbacks;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f0ded39..7914770 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -109,6 +109,7 @@
{
ALOGV("loadHwcModule");
+#ifdef BYPASS_IHWC
hw_module_t const* module;
if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
@@ -140,6 +141,9 @@
mHwcDevice = std::make_unique<HWC2::Device>(
static_cast<hwc2_device_t*>(mAdapter.get()));
}
+#else
+ mHwcDevice = std::make_unique<HWC2::Device>();
+#endif
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}
@@ -305,22 +309,22 @@
return layer;
}
-nsecs_t HWComposer::getRefreshTimestamp(int32_t disp) const {
+nsecs_t HWComposer::getRefreshTimestamp(int32_t displayId) 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);
- auto vsyncPeriod = getActiveConfig(disp)->getVsyncPeriod();
- return now - ((now - mLastHwVSync[disp]) % vsyncPeriod);
+ auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
+ return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod);
}
-bool HWComposer::isConnected(int32_t disp) const {
- if (!isValidDisplay(disp)) {
- ALOGE("isConnected: Attempted to access invalid display %d", disp);
+bool HWComposer::isConnected(int32_t displayId) const {
+ if (!isValidDisplay(displayId)) {
+ ALOGE("isConnected: Attempted to access invalid display %d", displayId);
return false;
}
- return mDisplayData[disp].hwcDisplay->isConnected();
+ return mDisplayData[displayId].hwcDisplay->isConnected();
}
std::vector<std::shared_ptr<const HWC2::Display::Config>>
@@ -342,14 +346,14 @@
std::shared_ptr<const HWC2::Display::Config>
HWComposer::getActiveConfig(int32_t displayId) const {
if (!isValidDisplay(displayId)) {
- ALOGE("getActiveConfigs: Attempted to access invalid display %d",
+ ALOGV("getActiveConfigs: Attempted to access invalid display %d",
displayId);
return nullptr;
}
std::shared_ptr<const HWC2::Display::Config> config;
auto error = mDisplayData[displayId].hwcDisplay->getActiveConfig(&config);
if (error == HWC2::Error::BadConfig) {
- ALOGV("getActiveConfig: No config active, returning null");
+ ALOGE("getActiveConfig: No config active, returning null");
return nullptr;
} else if (error != HWC2::Error::None) {
ALOGE("getActiveConfig failed for display %d: %s (%d)", displayId,
@@ -404,14 +408,15 @@
}
-void HWComposer::setVsyncEnabled(int32_t disp, HWC2::Vsync enabled) {
- if (disp < 0 || disp >= HWC_DISPLAY_VIRTUAL) {
- ALOGD("setVsyncEnabled: Ignoring for virtual display %d", disp);
+void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) {
+ if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) {
+ ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId);
return;
}
- if (!isValidDisplay(disp)) {
- ALOGE("setVsyncEnabled: Attempted to access invalid display %d", disp);
+ if (!isValidDisplay(displayId)) {
+ ALOGE("setVsyncEnabled: Attempted to access invalid display %d",
+ displayId);
return;
}
@@ -420,7 +425,7 @@
// that even if HWC blocks (which it shouldn't), it won't
// affect other threads.
Mutex::Autolock _l(mVsyncLock);
- auto& displayData = mDisplayData[disp];
+ auto& displayData = mDisplayData[displayId];
if (enabled != displayData.vsyncEnabled) {
ATRACE_CALL();
auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
@@ -428,12 +433,12 @@
displayData.vsyncEnabled = enabled;
char tag[16];
- snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp);
+ snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
} else {
ALOGE("setVsyncEnabled: Failed to set vsync to %s on %d/%" PRIu64
- ": %s (%d)", to_string(enabled).c_str(), disp,
- mDisplayData[disp].hwcDisplay->getId(),
+ ": %s (%d)", to_string(enabled).c_str(), displayId,
+ mDisplayData[displayId].hwcDisplay->getId(),
to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -589,12 +594,16 @@
return mDisplayData[displayId].hasClientComposition;
}
-sp<Fence> HWComposer::getRetireFence(int32_t displayId) const {
+sp<Fence> HWComposer::getPresentFence(int32_t displayId) const {
if (!isValidDisplay(displayId)) {
- ALOGE("getRetireFence failed for invalid display %d", displayId);
+ ALOGE("getPresentFence failed for invalid display %d", displayId);
return Fence::NO_FENCE;
}
- return mDisplayData[displayId].lastRetireFence;
+ return mDisplayData[displayId].lastPresentFence;
+}
+
+bool HWComposer::presentFenceRepresentsStartOfScanout() const {
+ return mAdapter ? false : true;
}
sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId,
@@ -611,7 +620,7 @@
return displayFences[layer];
}
-status_t HWComposer::commit(int32_t displayId) {
+status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) {
ATRACE_CALL();
if (!isValidDisplay(displayId)) {
@@ -620,17 +629,18 @@
auto& displayData = mDisplayData[displayId];
auto& hwcDisplay = displayData.hwcDisplay;
- auto error = hwcDisplay->present(&displayData.lastRetireFence);
+ auto error = hwcDisplay->present(&displayData.lastPresentFence);
if (error != HWC2::Error::None) {
- ALOGE("commit: present failed for display %d: %s (%d)", displayId,
- to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)",
+ displayId, to_string(error).c_str(), static_cast<int32_t>(error));
return UNKNOWN_ERROR;
}
std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
error = hwcDisplay->getReleaseFences(&releaseFences);
if (error != HWC2::Error::None) {
- ALOGE("commit: Failed to get release fences for display %d: %s (%d)",
+ ALOGE("presentAndGetReleaseFences: Failed to get release fences "
+ "for display %d: %s (%d)",
displayId, to_string(error).c_str(),
static_cast<int32_t>(error));
return UNKNOWN_ERROR;
@@ -869,7 +879,7 @@
: hasClientComposition(false),
hasDeviceComposition(false),
hwcDisplay(),
- lastRetireFence(Fence::NO_FENCE),
+ lastPresentFence(Fence::NO_FENCE),
outbufHandle(nullptr),
outbufAcquireFence(Fence::NO_FENCE),
vsyncEnabled(HWC2::Vsync::Disable) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 41671f6..2713505 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -97,8 +97,8 @@
status_t setClientTarget(int32_t displayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, android_dataspace_t dataspace);
- // Finalize the layers and present them
- status_t commit(int32_t displayId);
+ // Present layers to the display and read releaseFences.
+ status_t presentAndGetReleaseFences(int32_t displayId);
// set power mode
status_t setPowerMode(int32_t displayId, int mode);
@@ -118,9 +118,13 @@
// does this display have layers handled by GLES
bool hasClientComposition(int32_t displayId) const;
- // get the retire fence for the previous frame (i.e., corresponding to the
- // last call to presentDisplay
- sp<Fence> getRetireFence(int32_t displayId) const;
+ // get the present fence received from the last call to present.
+ sp<Fence> getPresentFence(int32_t displayId) const;
+
+ // Returns true if the present fence represents the start of the display
+ // controller's scan out. This should be true for all HWC2 implementations,
+ // except for the wrapper around HWC1 implementations.
+ bool presentFenceRepresentsStartOfScanout() const;
// Get last release fence for the given layer
sp<Fence> getLayerReleaseFence(int32_t displayId,
@@ -140,12 +144,12 @@
// Events handling ---------------------------------------------------------
- void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled);
+ void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
// Query display parameters. Pass in a display index (e.g.
// HWC_DISPLAY_PRIMARY).
- nsecs_t getRefreshTimestamp(int32_t disp) const;
- bool isConnected(int32_t disp) const;
+ nsecs_t getRefreshTimestamp(int32_t displayId) const;
+ bool isConnected(int32_t displayId) const;
// Non-const because it can update configMap inside of mDisplayData
std::vector<std::shared_ptr<const HWC2::Display::Config>>
@@ -186,7 +190,7 @@
bool hasDeviceComposition;
std::shared_ptr<HWC2::Display> hwcDisplay;
HWC2::DisplayRequest displayRequests;
- sp<Fence> lastRetireFence; // signals when the last set op retires
+ sp<Fence> lastPresentFence; // signals when the last set op retires
std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>>
releaseFences;
buffer_handle_t outbufHandle;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index 52cbb34..cc5578d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -933,7 +933,7 @@
protected:
HWCTYPE* const mLayerList;
HWCTYPE* mCurrentLayer;
- Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
+ explicit Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
mIndex(0) { }
inline HWCTYPE const * getLayer() const { return mCurrentLayer; }
inline HWCTYPE* getLayer() { return mCurrentLayer; }
@@ -1031,7 +1031,7 @@
virtual void setFrame(const Rect& frame) {
getLayer()->displayFrame = reinterpret_cast<hwc_rect_t const&>(frame);
}
- virtual void setCrop(const FloatRect& crop) {
+ virtual void setCrop(const gfx::FloatRect& crop) {
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
getLayer()->sourceCropf = reinterpret_cast<hwc_frect_t const&>(crop);
} else {
@@ -1159,6 +1159,7 @@
switch (format) {
case PIXEL_FORMAT_RGBA_8888: return String8("RGBA_8888");
case PIXEL_FORMAT_RGBX_8888: return String8("RGBx_8888");
+ case PIXEL_FORMAT_RGBA_FP16: return String8("RGBA_FP16");
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");
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index 170e382..bca25ac 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -48,12 +48,14 @@
// ---------------------------------------------------------------------------
class Fence;
-class FloatRect;
class GraphicBuffer;
class NativeHandle;
class Region;
class String8;
class SurfaceFlinger;
+namespace gfx {
+ class FloatRect;
+}
class HWComposer
{
@@ -168,7 +170,7 @@
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 setCrop(const gfx::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;
diff --git a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
index a4a1f99..a6f076e 100644
--- a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/hardware/power/1.0/IPower.h>
#include <stdint.h>
#include <sys/types.h>
@@ -26,6 +27,7 @@
#include "PowerHAL.h"
+using android::hardware::power::V1_0::PowerHint;
namespace android {
// ---------------------------------------------------------------------------
@@ -39,7 +41,9 @@
}
mPowerManager = interface_cast<IPowerManager>(bs);
}
- status_t status = mPowerManager->powerHint(POWER_HINT_VSYNC, enabled ? 1 : 0);
+ status_t status;
+ status = mPowerManager->powerHint(static_cast<int>(PowerHint::VSYNC),
+ enabled ? 1 : 0);
if(status == DEAD_OBJECT) {
mPowerManager = NULL;
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 2190466..6a98f03 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -241,7 +241,7 @@
mDbgState = DBG_STATE_IDLE;
#ifdef USE_HWC2
- sp<Fence> retireFence = mHwc.getRetireFence(mDisplayId);
+ sp<Fence> retireFence = mHwc.getPresentFence(mDisplayId);
#else
sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
#endif
@@ -281,7 +281,7 @@
#endif
&qbo);
if (result == NO_ERROR) {
- updateQueueBufferOutput(qbo);
+ updateQueueBufferOutput(std::move(qbo));
}
} else {
// If the surface hadn't actually been updated, then we only went
@@ -303,13 +303,8 @@
}
void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
- uint32_t tmpW, tmpH, transformHint, numPendingBuffers;
- uint64_t nextFrameNumber;
- mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers,
- &nextFrameNumber);
- mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers,
- nextFrameNumber);
-
+ mQueueBufferOutput.width = w;
+ mQueueBufferOutput.height = h;
mSinkBufferWidth = w;
mSinkBufferHeight = h;
}
@@ -345,7 +340,7 @@
LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
status_t result = mSource[source]->dequeueBuffer(sslot, fence,
- mSinkBufferWidth, mSinkBufferHeight, format, usage);
+ mSinkBufferWidth, mSinkBufferHeight, format, usage, nullptr);
if (result < 0)
return result;
int pslot = mapSource2ProducerSlot(source, *sslot);
@@ -384,9 +379,12 @@
}
status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t usage) {
- if (mDisplayId < 0)
- return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage);
+ uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
+ if (mDisplayId < 0) {
+ return mSource[SOURCE_SINK]->dequeueBuffer(
+ pslot, fence, w, h, format, usage, outTimestamps);
+ }
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
"Unexpected dequeueBuffer() in %s state", dbgStateStr());
@@ -518,7 +516,8 @@
mOutputFence = mFbFence;
}
- *output = mQueueBufferOutput;
+ // This moves the frame timestamps and keeps a copy of all other fields.
+ *output = std::move(mQueueBufferOutput);
return NO_ERROR;
}
@@ -557,8 +556,9 @@
status_t result = mSource[SOURCE_SINK]->connect(listener, api,
producerControlledByApp, &qbo);
if (result == NO_ERROR) {
- updateQueueBufferOutput(qbo);
- *output = mQueueBufferOutput;
+ updateQueueBufferOutput(std::move(qbo));
+ // This moves the frame timestamps and keeps a copy of all other fields.
+ *output = std::move(mQueueBufferOutput);
}
return result;
}
@@ -617,11 +617,9 @@
}
void VirtualDisplaySurface::updateQueueBufferOutput(
- const QueueBufferOutput& qbo) {
- uint32_t w, h, transformHint, numPendingBuffers;
- uint64_t nextFrameNumber;
- qbo.deflate(&w, &h, &transformHint, &numPendingBuffers, &nextFrameNumber);
- mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers, nextFrameNumber);
+ QueueBufferOutput&& qbo) {
+ mQueueBufferOutput = std::move(qbo);
+ mQueueBufferOutput.transformHint = 0;
}
void VirtualDisplaySurface::resetPerFrameState() {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 70f717f..d37dc0a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -104,7 +104,8 @@
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual status_t setAsyncMode(bool async);
virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t usage);
+ uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta *outTimestamps);
virtual status_t detachBuffer(int slot);
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence);
@@ -135,7 +136,7 @@
static Source fbSourceForCompositionType(CompositionType type);
status_t dequeueBuffer(Source source, PixelFormat format, uint32_t usage,
int* sslot, sp<Fence>* fence);
- void updateQueueBufferOutput(const QueueBufferOutput& qbo);
+ void updateQueueBufferOutput(QueueBufferOutput&& qbo);
void resetPerFrameState();
status_t refreshOutputBuffer();
@@ -180,6 +181,8 @@
// The QueueBufferOutput with the latest info from the sink, and with the
// transform hint cleared. Since we defer queueBuffer from the GLES driver
// to the sink, we have to return the previous version.
+ // Moves instead of copies are performed to avoid duplicate
+ // FrameEventHistoryDeltas.
QueueBufferOutput mQueueBufferOutput;
// Details of the current sink buffer. These become valid when a buffer is
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
deleted file mode 100644
index 0e18a93..0000000
--- a/services/surfaceflinger/FenceTracker.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <inttypes.h>
-#include "FenceTracker.h"
-#include "Layer.h"
-#include <utils/Trace.h>
-
-namespace android {
-
-FenceTracker::FenceTracker() :
- mFrameCounter(0),
- mOffset(0),
- mFrames(),
- mMutex() {
-}
-
-void FenceTracker::dump(String8* outString) {
- Mutex::Autolock lock(mMutex);
- checkFencesForCompletion();
-
- for (size_t i = 0; i < MAX_FRAME_HISTORY; i++) {
- int index = (mOffset + i) % MAX_FRAME_HISTORY;
- const FrameRecord& frame = mFrames[index];
-
- outString->appendFormat("Frame %" PRIu64 "\n", frame.frameId);
- outString->appendFormat("- Refresh start\t%" PRId64 "\n",
- frame.refreshStartTime);
-
- if (frame.glesCompositionDoneTime) {
- outString->appendFormat("- GLES done\t%" PRId64 "\n",
- frame.glesCompositionDoneTime);
- } else if (frame.glesCompositionDoneFence != Fence::NO_FENCE) {
- outString->append("- GLES done\tNot signaled\n");
- }
- if (frame.retireTime) {
- outString->appendFormat("- Retire\t%" PRId64 "\n",
- frame.retireTime);
- } else {
- outString->append("- Retire\tNot signaled\n");
- }
- for (const auto& kv : frame.layers) {
- const LayerRecord& layer = kv.second;
- outString->appendFormat("-- %s\n", layer.name.string());
- outString->appendFormat("---- Frame # %" PRIu64 " (%s)\n",
- layer.frameNumber,
- layer.isGlesComposition ? "GLES" : "HWC");
- outString->appendFormat("---- Posted\t%" PRId64 "\n",
- layer.postedTime);
- if (layer.acquireTime) {
- outString->appendFormat("---- Acquire\t%" PRId64 "\n",
- layer.acquireTime);
- } else {
- outString->append("---- Acquire\tNot signaled\n");
- }
- if (layer.releaseTime) {
- outString->appendFormat("---- Release\t%" PRId64 "\n",
- layer.releaseTime);
- } else {
- outString->append("---- Release\tNot signaled\n");
- }
- }
- }
-}
-
-static inline bool isValidTimestamp(nsecs_t time) {
- return time > 0 && time < INT64_MAX;
-}
-
-void FenceTracker::checkFencesForCompletion() {
- ATRACE_CALL();
- for (auto& frame : mFrames) {
- if (frame.retireFence != Fence::NO_FENCE) {
- nsecs_t time = frame.retireFence->getSignalTime();
- if (isValidTimestamp(time)) {
- frame.retireTime = time;
- frame.retireFence = Fence::NO_FENCE;
- }
- }
- if (frame.glesCompositionDoneFence != Fence::NO_FENCE) {
- nsecs_t time = frame.glesCompositionDoneFence->getSignalTime();
- if (isValidTimestamp(time)) {
- frame.glesCompositionDoneTime = time;
- frame.glesCompositionDoneFence = Fence::NO_FENCE;
- }
- }
- for (auto& kv : frame.layers) {
- LayerRecord& layer = kv.second;
- if (layer.acquireFence != Fence::NO_FENCE) {
- nsecs_t time = layer.acquireFence->getSignalTime();
- if (isValidTimestamp(time)) {
- layer.acquireTime = time;
- layer.acquireFence = Fence::NO_FENCE;
- }
- }
- if (layer.releaseFence != Fence::NO_FENCE) {
- nsecs_t time = layer.releaseFence->getSignalTime();
- if (isValidTimestamp(time)) {
- layer.releaseTime = time;
- layer.releaseFence = Fence::NO_FENCE;
- }
- }
- }
- }
-}
-
-void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
- const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence) {
- ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
- FrameRecord& frame = mFrames[mOffset];
- FrameRecord& prevFrame = mFrames[(mOffset + MAX_FRAME_HISTORY - 1) %
- MAX_FRAME_HISTORY];
- frame.layers.clear();
-
- bool wasGlesCompositionDone = false;
- const size_t count = layers.size();
- for (size_t i = 0; i < count; i++) {
- String8 name;
- uint64_t frameNumber;
- bool glesComposition;
- nsecs_t postedTime;
- sp<Fence> acquireFence;
- sp<Fence> prevReleaseFence;
- int32_t layerId = layers[i]->getSequence();
-
- 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),
- std::forward_as_tuple(name, frameNumber, glesComposition,
- postedTime, 0, 0, acquireFence, prevReleaseFence));
- wasGlesCompositionDone = true;
- } else {
- frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(layerId),
- std::forward_as_tuple(name, frameNumber, glesComposition,
- postedTime, 0, 0, acquireFence, Fence::NO_FENCE));
- auto prevLayer = prevFrame.layers.find(layerId);
- if (prevLayer != prevFrame.layers.end()) {
- 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,
- postedTime, 0, 0, acquireFence, prevReleaseFence));
- }
-
- frame.frameId = mFrameCounter;
- frame.refreshStartTime = refreshStartTime;
- frame.retireTime = 0;
- frame.glesCompositionDoneTime = 0;
- prevFrame.retireFence = retireFence;
- frame.retireFence = Fence::NO_FENCE;
- frame.glesCompositionDoneFence = wasGlesCompositionDone ? glDoneFence :
- Fence::NO_FENCE;
-
- mOffset = (mOffset + 1) % MAX_FRAME_HISTORY;
- mFrameCounter++;
-}
-
-bool FenceTracker::getFrameTimestamps(const Layer& layer,
- uint64_t frameNumber, FrameTimestamps* outTimestamps) {
- Mutex::Autolock lock(mMutex);
- checkFencesForCompletion();
- int32_t layerId = layer.getSequence();
-
- size_t i = 0;
- for (; i < MAX_FRAME_HISTORY; i++) {
- if (mFrames[i].layers.count(layerId) &&
- mFrames[i].layers[layerId].frameNumber == frameNumber) {
- break;
- }
- }
- if (i == MAX_FRAME_HISTORY) {
- return false;
- }
-
- const FrameRecord& frameRecord = mFrames[i];
- const LayerRecord& layerRecord = mFrames[i].layers[layerId];
- outTimestamps->frameNumber = frameNumber;
- outTimestamps->postedTime = layerRecord.postedTime;
- outTimestamps->acquireTime = layerRecord.acquireTime;
- outTimestamps->refreshStartTime = frameRecord.refreshStartTime;
- outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime;
- outTimestamps->displayRetireTime = frameRecord.retireTime;
- outTimestamps->releaseTime = layerRecord.releaseTime;
- return true;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
deleted file mode 100644
index 4cb14a5..0000000
--- a/services/surfaceflinger/FenceTracker.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_FENCETRACKER_H
-#define ANDROID_FENCETRACKER_H
-
-#include <ui/Fence.h>
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <unordered_map>
-
-namespace android {
-
-class Layer;
-struct FrameTimestamps;
-/*
- * Keeps a circular buffer of fence/timestamp data for the last N frames in
- * SurfaceFlinger. Gets timestamps for fences after they have signaled.
- */
-class FenceTracker {
-public:
- FenceTracker();
- void dump(String8* outString);
- void addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
- const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence);
- bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
- FrameTimestamps* outTimestamps);
-
-protected:
- static constexpr size_t MAX_FRAME_HISTORY = 8;
-
- struct LayerRecord {
- String8 name; // layer name
- uint64_t frameNumber; // frame number for this layer
- bool isGlesComposition; // was GLES composition used for this layer?
- nsecs_t postedTime; // time when buffer was queued
- nsecs_t acquireTime; // timestamp from the acquire fence
- nsecs_t releaseTime; // timestamp from the release fence
- sp<Fence> acquireFence; // acquire fence
- sp<Fence> releaseFence; // release fence
-
- LayerRecord(const String8& name, uint64_t frameNumber,
- bool isGlesComposition, nsecs_t postedTime,
- nsecs_t acquireTime, nsecs_t releaseTime,
- sp<Fence> acquireFence, sp<Fence> releaseFence) :
- name(name), frameNumber(frameNumber),
- isGlesComposition(isGlesComposition), postedTime(postedTime),
- acquireTime(acquireTime), releaseTime(releaseTime),
- acquireFence(acquireFence), releaseFence(releaseFence) {};
- LayerRecord() : name("uninitialized"), frameNumber(0),
- isGlesComposition(false), postedTime(0), acquireTime(0),
- releaseTime(0), acquireFence(Fence::NO_FENCE),
- releaseFence(Fence::NO_FENCE) {};
- };
-
- struct FrameRecord {
- // global SurfaceFlinger frame counter
- uint64_t frameId;
- // layer data for this frame
- std::unordered_map<int32_t, LayerRecord> layers;
- // timestamp for when SurfaceFlinger::handleMessageRefresh() was called
- nsecs_t refreshStartTime;
- // timestamp from the retire fence
- nsecs_t retireTime;
- // timestamp from the GLES composition completion fence
- nsecs_t glesCompositionDoneTime;
- // primary display retire fence for this frame
- sp<Fence> retireFence;
- // if GLES composition was done, the fence for its completion
- sp<Fence> glesCompositionDoneFence;
-
- FrameRecord() : frameId(0), layers(), refreshStartTime(0),
- retireTime(0), glesCompositionDoneTime(0),
- retireFence(Fence::NO_FENCE),
- glesCompositionDoneFence(Fence::NO_FENCE) {}
- };
-
- uint64_t mFrameCounter;
- uint32_t mOffset;
- FrameRecord mFrames[MAX_FRAME_HISTORY];
- Mutex mMutex;
-
- void checkFencesForCompletion();
-};
-
-}
-
-#endif // ANDROID_FRAMETRACKER_H
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 99c4f62..99c4daa 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -22,7 +22,6 @@
#include <android/log.h>
#include <utils/String8.h>
-#include <ui/Fence.h>
#include <ui/FrameStats.h>
#include "FrameTracker.h"
@@ -47,9 +46,10 @@
mFrameRecords[mOffset].frameReadyTime = readyTime;
}
-void FrameTracker::setFrameReadyFence(const sp<Fence>& readyFence) {
+void FrameTracker::setFrameReadyFence(
+ std::shared_ptr<FenceTime>&& readyFence) {
Mutex::Autolock lock(mMutex);
- mFrameRecords[mOffset].frameReadyFence = readyFence;
+ mFrameRecords[mOffset].frameReadyFence = std::move(readyFence);
mNumFences++;
}
@@ -58,9 +58,10 @@
mFrameRecords[mOffset].actualPresentTime = presentTime;
}
-void FrameTracker::setActualPresentFence(const sp<Fence>& readyFence) {
+void FrameTracker::setActualPresentFence(
+ std::shared_ptr<FenceTime>&& readyFence) {
Mutex::Autolock lock(mMutex);
- mFrameRecords[mOffset].actualPresentFence = readyFence;
+ mFrameRecords[mOffset].actualPresentFence = std::move(readyFence);
mNumFences++;
}
@@ -94,10 +95,6 @@
mFrameRecords[mOffset].actualPresentFence = NULL;
mNumFences--;
}
-
- // Clean up the signaled fences to keep the number of open fence FDs in
- // this process reasonable.
- processFencesLocked();
}
void FrameTracker::clearStats() {
@@ -106,8 +103,8 @@
mFrameRecords[i].desiredPresentTime = 0;
mFrameRecords[i].frameReadyTime = 0;
mFrameRecords[i].actualPresentTime = 0;
- mFrameRecords[i].frameReadyFence.clear();
- mFrameRecords[i].actualPresentFence.clear();
+ mFrameRecords[i].frameReadyFence.reset();
+ mFrameRecords[i].actualPresentFence.reset();
}
mNumFences = 0;
mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
@@ -155,7 +152,7 @@
size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
bool updated = false;
- const sp<Fence>& rfence = records[idx].frameReadyFence;
+ const std::shared_ptr<FenceTime>& rfence = records[idx].frameReadyFence;
if (rfence != NULL) {
records[idx].frameReadyTime = rfence->getSignalTime();
if (records[idx].frameReadyTime < INT64_MAX) {
@@ -165,7 +162,8 @@
}
}
- const sp<Fence>& pfence = records[idx].actualPresentFence;
+ const std::shared_ptr<FenceTime>& pfence =
+ records[idx].actualPresentFence;
if (pfence != NULL) {
records[idx].actualPresentTime = pfence->getSignalTime();
if (records[idx].actualPresentTime < INT64_MAX) {
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index cd5e3f3..adcdfb5 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_FRAMETRACKER_H
#define ANDROID_FRAMETRACKER_H
+#include <ui/FenceTime.h>
+
#include <stddef.h>
#include <utils/Mutex.h>
@@ -26,7 +28,6 @@
namespace android {
class String8;
-class Fence;
// FrameTracker tracks information about the most recently rendered frames. It
// uses a circular buffer of frame records, and is *NOT* thread-safe -
@@ -60,7 +61,7 @@
// setFrameReadyFence sets the fence that is used to get the time at which
// the current frame became ready to be presented to the user.
- void setFrameReadyFence(const sp<Fence>& readyFence);
+ void setFrameReadyFence(std::shared_ptr<FenceTime>&& readyFence);
// setActualPresentTime sets the timestamp at which the current frame became
// visible to the user.
@@ -68,7 +69,7 @@
// setActualPresentFence sets the fence that is used to get the time
// at which the current frame became visible to the user.
- void setActualPresentFence(const sp<Fence>& fence);
+ void setActualPresentFence(std::shared_ptr<FenceTime>&& fence);
// setDisplayRefreshPeriod sets the display refresh period in nanoseconds.
// This is used to compute frame presentation duration statistics relative
@@ -100,8 +101,8 @@
nsecs_t desiredPresentTime;
nsecs_t frameReadyTime;
nsecs_t actualPresentTime;
- sp<Fence> frameReadyFence;
- sp<Fence> actualPresentFence;
+ std::shared_ptr<FenceTime> frameReadyFence;
+ std::shared_ptr<FenceTime> actualPresentFence;
};
// processFences iterates over all the frame records that have a fence set
diff --git a/services/surfaceflinger/GpuService.cpp b/services/surfaceflinger/GpuService.cpp
index 70d9682..71052fb 100644
--- a/services/surfaceflinger/GpuService.cpp
+++ b/services/surfaceflinger/GpuService.cpp
@@ -16,6 +16,7 @@
#include "GpuService.h"
+#include <binder/IResultReceiver.h>
#include <binder/Parcel.h>
#include <utils/String8.h>
#include <vkjson.h>
@@ -27,7 +28,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");
@@ -35,6 +36,7 @@
status_t BnGpuService::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
+ status_t status;
switch (code) {
case SHELL_COMMAND_TRANSACTION: {
int in = data.readFileDescriptor();
@@ -45,7 +47,16 @@
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(data.readString16());
}
- return shellCommand(in, out, err, args);
+ sp<IBinder> unusedCallback;
+ sp<IResultReceiver> resultReceiver;
+ if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK)
+ return status;
+ if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK)
+ return status;
+ status = shellCommand(in, out, err, args);
+ if (resultReceiver != nullptr)
+ resultReceiver->send(status);
+ return OK;
}
default:
@@ -71,12 +82,15 @@
for (size_t i = 0, n = args.size(); i < n; i++)
ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string());
- if (args[0] == String16("vkjson"))
- return cmd_vkjson(out, err);
- else if (args[0] == String16("help"))
- return cmd_help(out);
-
- return NO_ERROR;
+ if (args.size() >= 1) {
+ if (args[0] == String16("vkjson"))
+ return cmd_vkjson(out, err);
+ if (args[0] == String16("help"))
+ return cmd_help(out);
+ }
+ // no command, or unrecognized command
+ cmd_help(err);
+ return BAD_VALUE;
}
// ----------------------------------------------------------------------------
@@ -92,82 +106,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 1b90678..72f6a7e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -44,6 +44,7 @@
#include "Colorizer.h"
#include "DisplayDevice.h"
#include "Layer.h"
+#include "LayerRejecter.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
@@ -79,7 +80,9 @@
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mOverrideScalingMode(-1),
mCurrentOpacity(true),
+ mBufferLatched(false),
mCurrentFrameNumber(0),
+ mPreviousFrameNumber(-1U),
mRefreshPending(false),
mFrameLatencyNeeded(false),
mFiltering(false),
@@ -155,17 +158,16 @@
// 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);
-#ifndef TARGET_DISABLE_TRIPLE_BUFFERING
- mProducer->setMaxDequeuedBufferCount(2);
-#endif
+ if (mFlinger->isLayerTripleBufferingDisabled()) {
+ mProducer->setMaxDequeuedBufferCount(2);
+ }
const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
updateTransformHint(hw);
@@ -212,7 +214,8 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
Mutex::Autolock lock(mQueueItemLock);
-
+ mFlinger->mInterceptor.saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+ item.mGraphicBuffer->getHeight(), item.mFrameNumber);
// Reset the frame number tracker when we receive the first buffer after
// a frame number reset
if (item.mFrameNumber == 1) {
@@ -312,22 +315,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);
@@ -390,10 +377,10 @@
return reduce(win, activeTransparentRegion);
}
-FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
+gfx::FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
// the content crop is the area of the content that gets scaled to the
// layer's size.
- FloatRect crop(getContentCrop());
+ gfx::FloatRect crop = getContentCrop().toFloatRect();
// the crop is the area of the window that gets cropped, but not
// scaled in any ways.
@@ -598,7 +585,7 @@
hwcInfo.displayFrame = transformedFrame;
}
- FloatRect sourceCrop = computeCrop(displayDevice);
+ gfx::FloatRect sourceCrop = computeCrop(displayDevice);
error = hwcLayer->setSourceCrop(sourceCrop);
if (error != HWC2::Error::None) {
ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
@@ -919,7 +906,7 @@
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
- clearWithOpenGL(hw, holes, 0, 0, 0, 1);
+ clearWithOpenGL(hw, 0, 0, 0, 1);
}
return;
}
@@ -985,13 +972,13 @@
} else {
engine.setupLayerBlackedOut();
}
- drawWithOpenGL(hw, clip, useIdentityTransform);
+ drawWithOpenGL(hw, useIdentityTransform);
engine.disableTexturing();
}
void Layer::clearWithOpenGL(const sp<const DisplayDevice>& hw,
- const Region& /* clip */, float red, float green, float blue,
+ float red, float green, float blue,
float alpha) const
{
RenderEngine& engine(mFlinger->getRenderEngine());
@@ -1001,12 +988,12 @@
}
void Layer::clearWithOpenGL(
- const sp<const DisplayDevice>& hw, const Region& clip) const {
- clearWithOpenGL(hw, clip, 0,0,0,0);
+ const sp<const DisplayDevice>& hw) const {
+ clearWithOpenGL(hw, 0,0,0,0);
}
void Layer::drawWithOpenGL(const sp<const DisplayDevice>& hw,
- const Region& /* clip */, bool useIdentityTransform) const {
+ bool useIdentityTransform) const {
const State& s(getDrawingState());
computeGeometry(hw, mMesh, useIdentityTransform);
@@ -1202,6 +1189,7 @@
switch (format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
return false;
}
// in all other case, we have no blending (also for unknown formats)
@@ -1744,50 +1732,79 @@
return isDue || !isPlausible;
}
-bool Layer::onPreComposition() {
+bool Layer::onPreComposition(nsecs_t refreshStartTime) {
+ if (mBufferLatched) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
+ }
mRefreshPending = false;
return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
}
-bool Layer::onPostComposition() {
- bool frameLatencyNeeded = mFrameLatencyNeeded;
- if (mFrameLatencyNeeded) {
- nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
- mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+bool Layer::onPostComposition(
+ const std::shared_ptr<FenceTime>& glDoneFence,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const std::shared_ptr<FenceTime>& retireFence) {
+ mAcquireTimeline.updateSignalTimes();
+ mReleaseTimeline.updateSignalTimes();
- sp<Fence> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFence();
- if (frameReadyFence->isValid()) {
- mFrameTracker.setFrameReadyFence(frameReadyFence);
- } else {
- // There was no fence for this frame, so assume that it was ready
- // to be presented at the desired present time.
- mFrameTracker.setFrameReadyTime(desiredPresentTime);
- }
+ // mFrameLatencyNeeded is true when a new frame was latched for the
+ // composition.
+ if (!mFrameLatencyNeeded)
+ return false;
- 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 {
- // The HWC doesn't support present fences, so use the refresh
- // timestamp instead.
- nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
- mFrameTracker.setActualPresentTime(presentTime);
- }
-
- mFrameTracker.advanceFrame();
- mFrameLatencyNeeded = false;
+ // Update mFrameEventHistory.
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addPostComposition(mCurrentFrameNumber,
+ glDoneFence, presentFence);
+ mFrameEventHistory.addRetire(mPreviousFrameNumber,
+ retireFence);
}
- return frameLatencyNeeded;
+
+ // Update mFrameTracker.
+ nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
+ mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+ std::shared_ptr<FenceTime> frameReadyFence =
+ mSurfaceFlingerConsumer->getCurrentFenceTime();
+ if (frameReadyFence->isValid()) {
+ mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+ } else {
+ // There was no fence for this frame, so assume that it was ready
+ // to be presented at the desired present time.
+ mFrameTracker.setFrameReadyTime(desiredPresentTime);
+ }
+
+ if (presentFence->isValid()) {
+ mFrameTracker.setActualPresentFence(
+ std::shared_ptr<FenceTime>(presentFence));
+ } else if (retireFence->isValid()) {
+ mFrameTracker.setActualPresentFence(
+ std::shared_ptr<FenceTime>(retireFence));
+ } else {
+ // The HWC doesn't support present fences, so use the refresh
+ // timestamp instead.
+ mFrameTracker.setActualPresentTime(
+ mFlinger->getHwComposer().getRefreshTimestamp(
+ HWC_DISPLAY_PRIMARY));
+ }
+
+ mFrameTracker.advanceFrame();
+ mFrameLatencyNeeded = false;
+ return true;
}
#ifdef USE_HWC2
-void Layer::releasePendingBuffer() {
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
mSurfaceFlingerConsumer->releasePendingBuffer();
+ auto releaseFenceTime = std::make_shared<FenceTime>(
+ mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+ mReleaseTimeline.push(releaseFenceTime);
+
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addRelease(
+ mPreviousFrameNumber, dequeueReadyTime, std::move(releaseFenceTime));
}
#endif
@@ -1802,7 +1819,33 @@
#endif
}
-Region Layer::latchBuffer(bool& recomputeVisibleRegions)
+bool Layer::allTransactionsSignaled() {
+ auto headFrameNumber = getHeadFrameNumber();
+ bool matchingFramesFound = false;
+ bool allTransactionsApplied = true;
+ Mutex::Autolock lock(mLocalSyncPointMutex);
+
+ for (auto& point : mLocalSyncPoints) {
+ if (point->getFrameNumber() > headFrameNumber) {
+ break;
+ }
+ matchingFramesFound = true;
+
+ if (!point->frameIsAvailable()) {
+ // We haven't notified the remote layer that the frame for
+ // this point is available yet. Notify it now, and then
+ // abort this attempt to latch.
+ point->setFrameAvailable();
+ allTransactionsApplied = false;
+ break;
+ }
+
+ allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied();
+ }
+ return !matchingFramesFound || allTransactionsApplied;
+}
+
+Region Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime)
{
ATRACE_CALL();
@@ -1820,334 +1863,191 @@
}
Region outDirtyRegion;
- if (mQueuedFrames > 0 || mAutoRefresh) {
-
- // if we've already called updateTexImage() without going through
- // a composition step, we have to skip this layer at this point
- // because we cannot call updateTeximage() without a corresponding
- // compositionComplete() call.
- // we'll trigger an update in onPreComposition().
- if (mRefreshPending) {
- return outDirtyRegion;
- }
-
- // If the head buffer's acquire fence hasn't signaled yet, return and
- // try again later
- if (!headFenceHasSignaled()) {
- mFlinger->signalLayerUpdate();
- return outDirtyRegion;
- }
-
- // Capture the old state of the layer for comparisons later
- const State& s(getDrawingState());
- const bool oldOpacity = isOpaque(s);
- sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
-
- struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
- Layer::State& front;
- Layer::State& current;
- bool& recomputeVisibleRegions;
- bool stickyTransformSet;
- const char* name;
- int32_t overrideScalingMode;
- bool& freezePositionUpdates;
-
- Reject(Layer::State& front, Layer::State& current,
- bool& recomputeVisibleRegions, bool stickySet,
- const char* name,
- int32_t overrideScalingMode,
- bool& freezePositionUpdates)
- : front(front), current(current),
- recomputeVisibleRegions(recomputeVisibleRegions),
- stickyTransformSet(stickySet),
- name(name),
- overrideScalingMode(overrideScalingMode),
- freezePositionUpdates(freezePositionUpdates) {
- }
-
- virtual bool reject(const sp<GraphicBuffer>& buf,
- const BufferItem& item) {
- if (buf == NULL) {
- return false;
- }
-
- uint32_t bufWidth = buf->getWidth();
- uint32_t bufHeight = buf->getHeight();
-
- // check that we received a buffer of the right size
- // (Take the buffer's orientation into account)
- if (item.mTransform & Transform::ROT_90) {
- swap(bufWidth, bufHeight);
- }
-
- int actualScalingMode = overrideScalingMode >= 0 ?
- overrideScalingMode : item.mScalingMode;
- bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
- if (front.active != front.requested) {
-
- if (isFixedSize ||
- (bufWidth == front.requested.w &&
- bufHeight == front.requested.h))
- {
- // Here we pretend the transaction happened by updating the
- // current and drawing states. Drawing state is only accessed
- // in this thread, no need to have it locked
- front.active = front.requested;
-
- // We also need to update the current state so that
- // we don't end-up overwriting the drawing state with
- // this stale current state during the next transaction
- //
- // NOTE: We don't need to hold the transaction lock here
- // because State::active is only accessed from this thread.
- current.active = front.active;
- current.modified = true;
-
- // recompute visible region
- recomputeVisibleRegions = true;
- }
-
- ALOGD_IF(DEBUG_RESIZE,
- "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
- " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
- " requested={ wh={%4u,%4u} }}\n",
- name,
- bufWidth, bufHeight, item.mTransform, item.mScalingMode,
- front.active.w, front.active.h,
- front.crop.left,
- front.crop.top,
- front.crop.right,
- front.crop.bottom,
- front.crop.getWidth(),
- front.crop.getHeight(),
- front.requested.w, front.requested.h);
- }
-
- if (!isFixedSize && !stickyTransformSet) {
- if (front.active.w != bufWidth ||
- front.active.h != bufHeight) {
- // reject this buffer
- ALOGE("[%s] rejecting buffer: "
- "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
- name, bufWidth, bufHeight, front.active.w, front.active.h);
- return true;
- }
- }
-
- // if the transparent region has changed (this test is
- // conservative, but that's fine, worst case we're doing
- // a bit of extra work), we latch the new one and we
- // trigger a visible-region recompute.
- if (!front.activeTransparentRegion.isTriviallyEqual(
- front.requestedTransparentRegion)) {
- front.activeTransparentRegion = front.requestedTransparentRegion;
-
- // We also need to update the current state so that
- // we don't end-up overwriting the drawing state with
- // this stale current state during the next transaction
- //
- // NOTE: We don't need to hold the transaction lock here
- // because State::active is only accessed from this thread.
- current.activeTransparentRegion = front.activeTransparentRegion;
-
- // recompute visible region
- recomputeVisibleRegions = true;
- }
-
- if (front.crop != front.requestedCrop) {
- front.crop = front.requestedCrop;
- current.crop = front.requestedCrop;
- recomputeVisibleRegions = true;
- }
- freezePositionUpdates = false;
-
- return false;
- }
- };
-
- Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
- getProducerStickyTransform() != 0, mName.string(),
- mOverrideScalingMode, mFreezePositionUpdates);
-
-
- // Check all of our local sync points to ensure that all transactions
- // which need to have been applied prior to the frame which is about to
- // be latched have signaled
-
- auto headFrameNumber = getHeadFrameNumber();
- bool matchingFramesFound = false;
- bool allTransactionsApplied = true;
- {
- Mutex::Autolock lock(mLocalSyncPointMutex);
- for (auto& point : mLocalSyncPoints) {
- if (point->getFrameNumber() > headFrameNumber) {
- break;
- }
-
- matchingFramesFound = true;
-
- if (!point->frameIsAvailable()) {
- // We haven't notified the remote layer that the frame for
- // this point is available yet. Notify it now, and then
- // abort this attempt to latch.
- point->setFrameAvailable();
- allTransactionsApplied = false;
- break;
- }
-
- allTransactionsApplied &= point->transactionIsApplied();
- }
- }
-
- if (matchingFramesFound && !allTransactionsApplied) {
- mFlinger->signalLayerUpdate();
- return outDirtyRegion;
- }
-
- // This boolean is used to make sure that SurfaceFlinger's shadow copy
- // of the buffer queue isn't modified when the buffer queue is returning
- // BufferItem's that weren't actually queued. This can happen in shared
- // buffer mode.
- bool queuedBuffer = false;
- status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
- mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
- mLastFrameNumberReceived);
- if (updateResult == BufferQueue::PRESENT_LATER) {
- // Producer doesn't want buffer to be displayed yet. Signal a
- // layer update so we check again at the next opportunity.
- mFlinger->signalLayerUpdate();
- return outDirtyRegion;
- } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
- // If the buffer has been rejected, remove it from the shadow queue
- // and return early
- if (queuedBuffer) {
- Mutex::Autolock lock(mQueueItemLock);
- mQueueItems.removeAt(0);
- android_atomic_dec(&mQueuedFrames);
- }
- return outDirtyRegion;
- } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
- // This can occur if something goes wrong when trying to create the
- // EGLImage for this buffer. If this happens, the buffer has already
- // been released, so we need to clean up the queue and bug out
- // early.
- if (queuedBuffer) {
- Mutex::Autolock lock(mQueueItemLock);
- mQueueItems.clear();
- android_atomic_and(0, &mQueuedFrames);
- }
-
- // Once we have hit this state, the shadow queue may no longer
- // correctly reflect the incoming BufferQueue's contents, so even if
- // updateTexImage starts working, the only safe course of action is
- // to continue to ignore updates.
- mUpdateTexImageFailed = true;
-
- return outDirtyRegion;
- }
-
- if (queuedBuffer) {
- // Autolock scope
- auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
- Mutex::Autolock lock(mQueueItemLock);
-
- // Remove any stale buffers that have been dropped during
- // updateTexImage
- while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mQueueItems.removeAt(0);
- android_atomic_dec(&mQueuedFrames);
- }
-
- mQueueItems.removeAt(0);
- }
-
-
- // Decrement the queued-frames count. Signal another event if we
- // have more frames pending.
- if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
- || mAutoRefresh) {
- mFlinger->signalLayerUpdate();
- }
-
- if (updateResult != NO_ERROR) {
- // something happened!
- recomputeVisibleRegions = true;
- return outDirtyRegion;
- }
-
- // update the active buffer
- mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
- if (mActiveBuffer == NULL) {
- // this can only happen if the very first buffer was rejected.
- return outDirtyRegion;
- }
-
- mRefreshPending = true;
- mFrameLatencyNeeded = true;
- if (oldActiveBuffer == NULL) {
- // the first time we receive a buffer, we need to trigger a
- // geometry invalidation.
- recomputeVisibleRegions = true;
- }
-
- Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
- const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
- const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
- if ((crop != mCurrentCrop) ||
- (transform != mCurrentTransform) ||
- (scalingMode != mCurrentScalingMode))
- {
- mCurrentCrop = crop;
- mCurrentTransform = transform;
- mCurrentScalingMode = scalingMode;
- recomputeVisibleRegions = true;
- }
-
- if (oldActiveBuffer != NULL) {
- uint32_t bufWidth = mActiveBuffer->getWidth();
- uint32_t bufHeight = mActiveBuffer->getHeight();
- if (bufWidth != uint32_t(oldActiveBuffer->width) ||
- bufHeight != uint32_t(oldActiveBuffer->height)) {
- recomputeVisibleRegions = true;
- }
- }
-
- mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
- if (oldOpacity != isOpaque(s)) {
- recomputeVisibleRegions = true;
- }
-
- mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
- // Remove any sync points corresponding to the buffer which was just
- // latched
- {
- Mutex::Autolock lock(mLocalSyncPointMutex);
- auto point = mLocalSyncPoints.begin();
- while (point != mLocalSyncPoints.end()) {
- if (!(*point)->frameIsAvailable() ||
- !(*point)->transactionIsApplied()) {
- // This sync point must have been added since we started
- // latching. Don't drop it yet.
- ++point;
- continue;
- }
-
- if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
- point = mLocalSyncPoints.erase(point);
- } else {
- ++point;
- }
- }
- }
-
- // FIXME: postedRegion should be dirty & bounds
- Region dirtyRegion(Rect(s.active.w, s.active.h));
-
- // transform the dirty region to window-manager space
- outDirtyRegion = (s.active.transform.transform(dirtyRegion));
+ if (mQueuedFrames <= 0 && !mAutoRefresh) {
+ return outDirtyRegion;
}
+
+ // if we've already called updateTexImage() without going through
+ // a composition step, we have to skip this layer at this point
+ // because we cannot call updateTeximage() without a corresponding
+ // compositionComplete() call.
+ // we'll trigger an update in onPreComposition().
+ if (mRefreshPending) {
+ return outDirtyRegion;
+ }
+
+ // If the head buffer's acquire fence hasn't signaled yet, return and
+ // try again later
+ if (!headFenceHasSignaled()) {
+ mFlinger->signalLayerUpdate();
+ return outDirtyRegion;
+ }
+
+ // Capture the old state of the layer for comparisons later
+ const State& s(getDrawingState());
+ const bool oldOpacity = isOpaque(s);
+ sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
+
+ if (!allTransactionsSignaled()) {
+ mFlinger->signalLayerUpdate();
+ return outDirtyRegion;
+ }
+
+ // This boolean is used to make sure that SurfaceFlinger's shadow copy
+ // of the buffer queue isn't modified when the buffer queue is returning
+ // BufferItem's that weren't actually queued. This can happen in shared
+ // buffer mode.
+ bool queuedBuffer = false;
+ LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+ getProducerStickyTransform() != 0, mName.string(),
+ mOverrideScalingMode, mFreezePositionUpdates);
+ status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
+ mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
+ mLastFrameNumberReceived);
+ if (updateResult == BufferQueue::PRESENT_LATER) {
+ // Producer doesn't want buffer to be displayed yet. Signal a
+ // layer update so we check again at the next opportunity.
+ mFlinger->signalLayerUpdate();
+ return outDirtyRegion;
+ } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
+ // If the buffer has been rejected, remove it from the shadow queue
+ // and return early
+ if (queuedBuffer) {
+ Mutex::Autolock lock(mQueueItemLock);
+ mQueueItems.removeAt(0);
+ android_atomic_dec(&mQueuedFrames);
+ }
+ return outDirtyRegion;
+ } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
+ // This can occur if something goes wrong when trying to create the
+ // EGLImage for this buffer. If this happens, the buffer has already
+ // been released, so we need to clean up the queue and bug out
+ // early.
+ if (queuedBuffer) {
+ Mutex::Autolock lock(mQueueItemLock);
+ mQueueItems.clear();
+ android_atomic_and(0, &mQueuedFrames);
+ }
+
+ // Once we have hit this state, the shadow queue may no longer
+ // correctly reflect the incoming BufferQueue's contents, so even if
+ // updateTexImage starts working, the only safe course of action is
+ // to continue to ignore updates.
+ mUpdateTexImageFailed = true;
+
+ return outDirtyRegion;
+ }
+
+ if (queuedBuffer) {
+ // Autolock scope
+ auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+ Mutex::Autolock lock(mQueueItemLock);
+
+ // Remove any stale buffers that have been dropped during
+ // updateTexImage
+ while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+ mQueueItems.removeAt(0);
+ android_atomic_dec(&mQueuedFrames);
+ }
+
+ mQueueItems.removeAt(0);
+ }
+
+
+ // Decrement the queued-frames count. Signal another event if we
+ // have more frames pending.
+ if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
+ || mAutoRefresh) {
+ mFlinger->signalLayerUpdate();
+ }
+
+ // update the active buffer
+ mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
+ if (mActiveBuffer == NULL) {
+ // this can only happen if the very first buffer was rejected.
+ return outDirtyRegion;
+ }
+
+ mBufferLatched = true;
+ mPreviousFrameNumber = mCurrentFrameNumber;
+ mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+#ifndef USE_HWC2
+ auto releaseFenceTime = std::make_shared<FenceTime>(
+ mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+ mReleaseTimeline.push(releaseFenceTime);
+ mFrameEventHistory.addRelease(
+ mPreviousFrameNumber, latchTime, std::move(releaseFenceTime));
+#endif
+ }
+
+ mRefreshPending = true;
+ mFrameLatencyNeeded = true;
+ if (oldActiveBuffer == NULL) {
+ // the first time we receive a buffer, we need to trigger a
+ // geometry invalidation.
+ recomputeVisibleRegions = true;
+ }
+
+ Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
+ const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
+ const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
+ if ((crop != mCurrentCrop) ||
+ (transform != mCurrentTransform) ||
+ (scalingMode != mCurrentScalingMode))
+ {
+ mCurrentCrop = crop;
+ mCurrentTransform = transform;
+ mCurrentScalingMode = scalingMode;
+ recomputeVisibleRegions = true;
+ }
+
+ if (oldActiveBuffer != NULL) {
+ uint32_t bufWidth = mActiveBuffer->getWidth();
+ uint32_t bufHeight = mActiveBuffer->getHeight();
+ if (bufWidth != uint32_t(oldActiveBuffer->width) ||
+ bufHeight != uint32_t(oldActiveBuffer->height)) {
+ recomputeVisibleRegions = true;
+ }
+ }
+
+ mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
+ if (oldOpacity != isOpaque(s)) {
+ recomputeVisibleRegions = true;
+ }
+
+ // Remove any sync points corresponding to the buffer which was just
+ // latched
+ {
+ Mutex::Autolock lock(mLocalSyncPointMutex);
+ auto point = mLocalSyncPoints.begin();
+ while (point != mLocalSyncPoints.end()) {
+ if (!(*point)->frameIsAvailable() ||
+ !(*point)->transactionIsApplied()) {
+ // This sync point must have been added since we started
+ // latching. Don't drop it yet.
+ ++point;
+ continue;
+ }
+
+ if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
+ point = mLocalSyncPoints.erase(point);
+ } else {
+ ++point;
+ }
+ }
+ }
+
+ // FIXME: postedRegion should be dirty & bounds
+ Region dirtyRegion(Rect(s.active.w, s.active.h));
+
+ // transform the dirty region to window-manager space
+ outDirtyRegion = (s.active.transform.transform(dirtyRegion));
+
return outDirtyRegion;
}
@@ -2279,7 +2179,7 @@
const Rect& frame = hwcInfo.displayFrame;
result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top,
frame.right, frame.bottom);
- const FloatRect& crop = hwcInfo.sourceCrop;
+ const gfx::FloatRect& crop = hwcInfo.sourceCrop;
result.appendFormat("%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top,
crop.right, crop.bottom);
@@ -2304,22 +2204,25 @@
mFrameTracker.getStats(outStats);
}
-void Layer::getFenceData(String8* outName, uint64_t* outFrameNumber,
- bool* outIsGlesComposition, nsecs_t* outPostedTime,
- sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const {
- *outName = mName;
- *outFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+void Layer::dumpFrameEvents(String8& result) {
+ result.appendFormat("- Layer %s (%s, %p)\n",
+ getName().string(), getTypeId(), this);
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.checkFencesForCompletion();
+ mFrameEventHistory.dump(result);
+}
-#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();
+void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta *outDelta) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ if (newTimestamps) {
+ mAcquireTimeline.push(newTimestamps->acquireFence);
+ mFrameEventHistory.addQueue(*newTimestamps);
+ }
+
+ if (outDelta) {
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ }
}
std::vector<OccupancyTracker::Segment> Layer::getOccupancyHistory(
@@ -2341,17 +2244,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..92d2292 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -27,6 +27,8 @@
#include <utils/String8.h>
#include <utils/Timers.h>
+#include <gfx/FloatRect.h>
+
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
@@ -46,7 +48,6 @@
#include "Transform.h"
#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/FloatRect.h"
#include "RenderEngine/Mesh.h"
#include "RenderEngine/Texture.h"
@@ -121,9 +122,11 @@
int32_t sequence; // changes when visible regions can change
bool modified;
+ // Crop is expressed in layer space coordinate.
Rect crop;
Rect requestedCrop;
+ // finalCrop is expressed in display space coordinate.
Rect finalCrop;
// If set, defers this state update until the Layer identified by handle
@@ -180,11 +183,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; }
// -----------------------------------------------------------------------
@@ -275,17 +273,20 @@
* called before composition.
* returns true if the layer has pending updates.
*/
- bool onPreComposition();
+ bool onPreComposition(nsecs_t refreshStartTime);
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
*/
- bool onPostComposition();
+ bool onPostComposition(
+ const std::shared_ptr<FenceTime>& glDoneFence,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const std::shared_ptr<FenceTime>& retireFence);
#ifdef USE_HWC2
// If a buffer was replaced this frame, release the former buffer
- void releasePendingBuffer();
+ void releasePendingBuffer(nsecs_t dequeueReadyTime);
#endif
/*
@@ -328,7 +329,7 @@
* operation, so this should be set only if needed). Typically this is used
* to figure out if the content or size of a surface has changed.
*/
- Region latchBuffer(bool& recomputeVisibleRegions);
+ Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime);
bool isPotentialCursor() const { return mPotentialCursor;}
@@ -388,7 +389,7 @@
#endif
// -----------------------------------------------------------------------
- void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip) const;
+ void clearWithOpenGL(const sp<const DisplayDevice>& hw) const;
void setFiltering(bool filtering);
bool getFiltering() const;
@@ -407,29 +408,21 @@
void miniDump(String8& result, int32_t hwcId) const;
#endif
void dumpFrameStats(String8& result) const;
+ void dumpFrameEvents(String8& result);
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
- void getFenceData(String8* outName, uint64_t* outFrameNumber,
- bool* outIsGlesComposition, nsecs_t* outPostedTime,
- sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const;
-
std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush);
- bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- return mFlinger->getFrameTimestamps(*this, frameNumber, outTimestamps);
- }
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
+ FrameEventHistoryDelta* outDelta);
bool getTransformToDisplayInverse() const;
protected:
// constant
sp<SurfaceFlinger> mFlinger;
-
- virtual void onFirstRef();
-
/*
* Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
* is called.
@@ -438,13 +431,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;
@@ -456,14 +460,14 @@
bool needsFiltering(const sp<const DisplayDevice>& hw) const;
uint32_t getEffectiveUsage(uint32_t usage) const;
- FloatRect computeCrop(const sp<const DisplayDevice>& hw) const;
+ gfx::FloatRect computeCrop(const sp<const DisplayDevice>& hw) const;
bool isCropped() const;
static bool getOpacityForFormat(uint32_t format);
// drawing
- void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
+ void clearWithOpenGL(const sp<const DisplayDevice>& hw,
float r, float g, float b, float alpha) const;
- void drawWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
+ void drawWithOpenGL(const sp<const DisplayDevice>& hw,
bool useIdentityTransform) const;
// Temporary - Used only for LEGACY camera mode.
@@ -531,11 +535,35 @@
// 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:
// -----------------------------------------------------------------------
+ // Check all of the local sync points to ensure that all transactions
+ // which need to have been applied prior to the frame which is about to
+ // be latched have signaled
+ bool allTransactionsSignaled();
+
// constants
sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer;
sp<IGraphicBufferProducer> mProducer;
@@ -556,8 +584,17 @@
// thread-safe
volatile int32_t mQueuedFrames;
volatile int32_t mSidebandStreamChanged; // used like an atomic boolean
+
+ // Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
+ // Timestamp history for the consumer to query.
+ // Accessed by both consumer and producer on main and binder threads.
+ Mutex mFrameEventHistoryMutex;
+ ConsumerFrameEventHistory mFrameEventHistory;
+ FenceTimeline mAcquireTimeline;
+ FenceTimeline mReleaseTimeline;
+
// main thread
sp<GraphicBuffer> mActiveBuffer;
sp<NativeHandle> mSidebandStream;
@@ -567,7 +604,9 @@
// We encode unset as -1.
int32_t mOverrideScalingMode;
bool mCurrentOpacity;
+ bool mBufferLatched = false; // TODO: Use mActiveBuffer?
std::atomic<uint64_t> mCurrentFrameNumber;
+ uint64_t mPreviousFrameNumber; // Only accessed on the main thread.
bool mRefreshPending;
bool mFrameLatencyNeeded;
// Whether filtering is forced on or not
@@ -593,7 +632,7 @@
HWC2::Composition compositionType;
bool clearClientTarget;
Rect displayFrame;
- FloatRect sourceCrop;
+ gfx::FloatRect sourceCrop;
};
std::unordered_map<int32_t, HWCInfo> mHwcLayers;
#else
@@ -617,7 +656,7 @@
Condition mQueueItemCondition;
Vector<BufferItem> mQueueItems;
std::atomic<uint64_t> mLastFrameNumberReceived;
- bool mUpdateTexImageFailed; // This is only modified from the main thread
+ bool mUpdateTexImageFailed; // This is only accessed on the main thread.
bool mAutoRefresh;
bool mFreezePositionUpdates;
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
new file mode 100644
index 0000000..2bc0605
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerRejecter.h"
+
+#include "clz.h"
+
+#define DEBUG_RESIZE 0
+
+namespace android {
+
+LayerRejecter::LayerRejecter(Layer::State& front,
+ Layer::State& current,
+ bool& recomputeVisibleRegions,
+ bool stickySet,
+ const char* name,
+ int32_t overrideScalingMode,
+ bool& freezePositionUpdates)
+ : mFront(front),
+ mCurrent(current),
+ mRecomputeVisibleRegions(recomputeVisibleRegions),
+ mStickyTransformSet(stickySet),
+ mName(name),
+ mOverrideScalingMode(overrideScalingMode),
+ mFreezePositionUpdates(freezePositionUpdates) {}
+
+bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
+ if (buf == NULL) {
+ return false;
+ }
+
+ uint32_t bufWidth = buf->getWidth();
+ uint32_t bufHeight = buf->getHeight();
+
+ // check that we received a buffer of the right size
+ // (Take the buffer's orientation into account)
+ if (item.mTransform & Transform::ROT_90) {
+ swap(bufWidth, bufHeight);
+ }
+
+ int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
+ bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+ if (mFront.active != mFront.requested) {
+ if (isFixedSize || (bufWidth == mFront.requested.w && bufHeight == mFront.requested.h)) {
+ // Here we pretend the transaction happened by updating the
+ // current and drawing states. Drawing state is only accessed
+ // in this thread, no need to have it locked
+ mFront.active = mFront.requested;
+
+ // We also need to update the current state so that
+ // we don't end-up overwriting the drawing state with
+ // this stale current state during the next transaction
+ //
+ // NOTE: We don't need to hold the transaction lock here
+ // because State::active is only accessed from this thread.
+ mCurrent.active = mFront.active;
+ mCurrent.modified = true;
+
+ // recompute visible region
+ mRecomputeVisibleRegions = true;
+ }
+
+ ALOGD_IF(DEBUG_RESIZE,
+ "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
+ " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) "
+ "}\n"
+ " requested={ wh={%4u,%4u} }}\n",
+ mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, mFront.active.w,
+ mFront.active.h, mFront.crop.left, mFront.crop.top, mFront.crop.right,
+ mFront.crop.bottom, mFront.crop.getWidth(), mFront.crop.getHeight(),
+ mFront.requested.w, mFront.requested.h);
+ }
+
+ if (!isFixedSize && !mStickyTransformSet) {
+ if (mFront.active.w != bufWidth || mFront.active.h != bufHeight) {
+ // reject this buffer
+ ALOGE("[%s] rejecting buffer: "
+ "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
+ mName, bufWidth, bufHeight, mFront.active.w, mFront.active.h);
+ return true;
+ }
+ }
+
+ // if the transparent region has changed (this test is
+ // conservative, but that's fine, worst case we're doing
+ // a bit of extra work), we latch the new one and we
+ // trigger a visible-region recompute.
+ if (!mFront.activeTransparentRegion.isTriviallyEqual(mFront.requestedTransparentRegion)) {
+ mFront.activeTransparentRegion = mFront.requestedTransparentRegion;
+
+ // We also need to update the current state so that
+ // we don't end-up overwriting the drawing state with
+ // this stale current state during the next transaction
+ //
+ // NOTE: We don't need to hold the transaction lock here
+ // because State::active is only accessed from this thread.
+ mCurrent.activeTransparentRegion = mFront.activeTransparentRegion;
+
+ // recompute visible region
+ mRecomputeVisibleRegions = true;
+ }
+
+ if (mFront.crop != mFront.requestedCrop) {
+ mFront.crop = mFront.requestedCrop;
+ mCurrent.crop = mFront.requestedCrop;
+ mRecomputeVisibleRegions = true;
+ }
+ mFreezePositionUpdates = false;
+
+ return false;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
new file mode 100644
index 0000000..c2a9483
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_REJECTER_H
+#define ANDROID_LAYER_REJECTER_H
+
+#include "Layer.h"
+#include "SurfaceFlingerConsumer.h"
+
+namespace android {
+ class LayerRejecter : public SurfaceFlingerConsumer::BufferRejecter {
+ public:
+ LayerRejecter(Layer::State &front,
+ Layer::State ¤t,
+ bool &recomputeVisibleRegions,
+ bool stickySet,
+ const char *name,
+ int32_t overrideScalingMode,
+ bool &freezePositionUpdates);
+
+ virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
+
+ private:
+ Layer::State &mFront;
+ Layer::State &mCurrent;
+ bool &mRecomputeVisibleRegions;
+ bool mStickyTransformSet;
+ const char *mName;
+ int32_t mOverrideScalingMode;
+ bool &mFreezePositionUpdates;
+ };
+} // namespace android
+
+#endif // ANDROID_LAYER_REJECTER_H
\ No newline at end of file
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index ffaee7a..8bf6e82 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -49,7 +49,7 @@
wp<IBinder> mProducer;
};
- mFlinger->postMessageAsync(new MessageCleanUpList(mFlinger, asBinder(this)));
+ mFlinger->postMessageAsync(new MessageCleanUpList(mFlinger, asBinder(mProducer)));
}
status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
@@ -66,8 +66,10 @@
}
status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t usage) {
- return mProducer->dequeueBuffer(slot, fence, w, h, format, usage);
+ uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
+ return mProducer->dequeueBuffer(
+ slot, fence, w, h, format, usage, outTimestamps);
}
status_t MonitoredProducer::detachBuffer(int slot) {
@@ -145,12 +147,16 @@
outTransformMatrix);
}
+void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ mProducer->getFrameTimestamps(outDelta);
+}
+
status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
return mProducer->getUniqueId(outId);
}
IBinder* MonitoredProducer::onAsBinder() {
- return IInterface::asBinder(mProducer).get();
+ return this;
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 66f6cf0..becc740 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -27,7 +27,7 @@
// MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will
// be notified upon its destruction
-class MonitoredProducer : public IGraphicBufferProducer {
+class MonitoredProducer : public BnGraphicBufferProducer {
public:
MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
const sp<SurfaceFlinger>& flinger);
@@ -38,7 +38,8 @@
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual status_t setAsyncMode(bool async);
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t usage);
+ uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps);
virtual status_t detachBuffer(int slot);
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence);
@@ -63,6 +64,7 @@
virtual IBinder* onAsBinder();
virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
virtual status_t setAutoRefresh(bool autoRefresh) override;
+ virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
virtual status_t getUniqueId(uint64_t* outId) const override;
private:
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
deleted file mode 100644
index 579affb..0000000
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES/gl.h>
-
-#include <cutils/compiler.h>
-
-#include "GLES10RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-GLES10RenderEngine::~GLES10RenderEngine() {
-}
-
-void GLES10RenderEngine::setupLayerBlending(
-#ifdef USE_HWC2
- bool premultipliedAlpha, bool opaque, float alpha) {
-#else
- bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
- // OpenGL ES 1.0 doesn't support texture combiners.
- // This path doesn't properly handle opaque layers that have non-opaque
- // alpha values. The alpha channel will be copied into the framebuffer or
- // screenshot, so if the framebuffer or screenshot is blended on top of
- // something else, whatever is below the window will incorrectly show
- // through.
-#ifdef USE_HWC2
- if (CC_UNLIKELY(alpha < 1.0f)) {
- if (premultipliedAlpha) {
- glColor4f(alpha, alpha, alpha, alpha);
- } else {
- glColor4f(1.0f, 1.0f, 1.0f, alpha);
- }
-#else
- if (CC_UNLIKELY(alpha < 0xFF)) {
- GLfloat floatAlpha = alpha * (1.0f / 255.0f);
- if (premultipliedAlpha) {
- glColor4f(floatAlpha, floatAlpha, floatAlpha, floatAlpha);
- } else {
- glColor4f(1.0f, 1.0f, 1.0f, floatAlpha);
- }
-#endif
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- } else {
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- }
-
-#ifdef USE_HWC2
- if (alpha < 1.0f || !opaque) {
-#else
- if (alpha < 0xFF || !opaque) {
-#endif
- glEnable(GL_BLEND);
- glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA,
- GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
deleted file mode 100644
index 61abd6a..0000000
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef SF_GLES10RENDERENGINE_H_
-#define SF_GLES10RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "GLES11RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class GLES10RenderEngine : public GLES11RenderEngine {
- virtual ~GLES10RenderEngine();
-protected:
-#ifdef USE_HWC2
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- float alpha) override;
-#else
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha);
-#endif
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES10RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
deleted file mode 100644
index 847cdb3..0000000
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-#include <ui/Rect.h>
-
-#include <utils/String8.h>
-#include <cutils/compiler.h>
-#include <gui/ISurfaceComposer.h>
-
-#include "GLES11RenderEngine.h"
-#include "Mesh.h"
-#include "Texture.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-GLES11RenderEngine::GLES11RenderEngine() {
-
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
- glEnableClientState(GL_VERTEX_ARRAY);
- glShadeModel(GL_FLAT);
- glDisable(GL_DITHER);
- glDisable(GL_CULL_FACE);
-
- const uint16_t protTexData[] = { 0 };
- glGenTextures(1, &mProtectedTexName);
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0,
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-}
-
-GLES11RenderEngine::~GLES11RenderEngine() {
-}
-
-
-size_t GLES11RenderEngine::getMaxTextureSize() const {
- return mMaxTextureSize;
-}
-
-size_t GLES11RenderEngine::getMaxViewportDims() const {
- return
- mMaxViewportDims[0] < mMaxViewportDims[1] ?
- mMaxViewportDims[0] : mMaxViewportDims[1];
-}
-
-void GLES11RenderEngine::setViewportAndProjection(
- size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap,
- Transform::orientation_flags rotation) {
- glViewport(0, 0, vpw, vph);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- size_t l = sourceCrop.left;
- size_t r = sourceCrop.right;
-
- // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
- size_t t = hwh - sourceCrop.top;
- size_t b = hwh - sourceCrop.bottom;
-
- if (yswap) {
- glOrthof(l, r, t, b, 0, 1);
- } else {
- glOrthof(l, r, b, t, 0, 1);
- }
-
- switch (rotation) {
- case Transform::ROT_0:
- break;
- case Transform::ROT_90:
- glRotatef(90, 0, 0, 1);
- break;
- case Transform::ROT_180:
- glRotatef(180, 0, 0, 1);
- break;
- case Transform::ROT_270:
- glRotatef(270, 0, 0, 1);
- break;
- default:
- break;
- }
-
- glMatrixMode(GL_MODELVIEW);
-}
-
-#ifdef USE_HWC2
-void GLES11RenderEngine::setupLayerBlending(bool premultipliedAlpha,
- bool opaque, float alpha) {
-#else
-void GLES11RenderEngine::setupLayerBlending(
- bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
- GLenum combineRGB;
- GLenum combineAlpha;
- GLenum src0Alpha;
- GLfloat envColor[4];
-
-#ifdef USE_HWC2
- if (CC_UNLIKELY(alpha < 1.0f)) {
-#else
- if (CC_UNLIKELY(alpha < 0xFF)) {
-#endif
- // Cv = premultiplied ? Cs*alpha : Cs
- // Av = !opaque ? As*alpha : As
- combineRGB = premultipliedAlpha ? GL_MODULATE : GL_REPLACE;
- combineAlpha = !opaque ? GL_MODULATE : GL_REPLACE;
- src0Alpha = GL_CONSTANT;
-#ifdef USE_HWC2
- envColor[0] = alpha;
-#else
- envColor[0] = alpha * (1.0f / 255.0f);
-#endif
- } else {
- // Cv = Cs
- // Av = opaque ? 1.0 : As
- combineRGB = GL_REPLACE;
- combineAlpha = GL_REPLACE;
- src0Alpha = opaque ? GL_CONSTANT : GL_TEXTURE;
- envColor[0] = 1.0f;
- }
-
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, combineRGB);
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
- if (combineRGB == GL_MODULATE) {
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
- }
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, combineAlpha);
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, src0Alpha);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
- if (combineAlpha == GL_MODULATE) {
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
- }
- if (combineRGB == GL_MODULATE || src0Alpha == GL_CONSTANT) {
- envColor[1] = envColor[0];
- envColor[2] = envColor[0];
- envColor[3] = envColor[0];
- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, envColor);
- }
-
-#ifdef USE_HWC2
- if (alpha < 1.0f || !opaque) {
-#else
- if (alpha < 0xFF || !opaque) {
-#endif
- glEnable(GL_BLEND);
- glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA,
- GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-#ifdef USE_HWC2
-void GLES11RenderEngine::setupDimLayerBlending(float alpha) {
-#else
-void GLES11RenderEngine::setupDimLayerBlending(int alpha) {
-#endif
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
-#ifdef USE_HWC2
- if (alpha == 1.0f) {
-#else
- if (alpha == 0xFF) {
-#endif
- glDisable(GL_BLEND);
- } else {
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- }
-#ifdef USE_HWC2
- glColor4f(0, 0, 0, alpha);
-#else
- glColor4f(0, 0, 0, alpha/255.0f);
-#endif
-}
-
-void GLES11RenderEngine::setupLayerTexturing(const Texture& texture) {
- GLuint target = texture.getTextureTarget();
- glBindTexture(target, texture.getTextureName());
- GLenum filter = GL_NEAREST;
- if (texture.getFiltering()) {
- filter = GL_LINEAR;
- }
- glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_MAG_FILTER, filter);
- glTexParameterx(target, GL_TEXTURE_MIN_FILTER, filter);
- glMatrixMode(GL_TEXTURE);
- glLoadMatrixf(texture.getMatrix().asArray());
- glMatrixMode(GL_MODELVIEW);
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_TEXTURE_EXTERNAL_OES);
-}
-
-void GLES11RenderEngine::setupLayerBlackedOut() {
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glEnable(GL_TEXTURE_2D);
-}
-
-void GLES11RenderEngine::disableTexturing() {
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
-}
-
-void GLES11RenderEngine::disableBlending() {
- glDisable(GL_BLEND);
-}
-
-void GLES11RenderEngine::bindImageAsFramebuffer(EGLImageKHR image,
- uint32_t* texName, uint32_t* fbName, uint32_t* status) {
- GLuint tname, name;
- // turn our EGLImage into a texture
- glGenTextures(1, &tname);
- glBindTexture(GL_TEXTURE_2D, tname);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
-
- // create a Framebuffer Object to render into
- glGenFramebuffersOES(1, &name);
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
- glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
- GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
-
- *status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
- *texName = tname;
- *fbName = name;
-}
-
-void GLES11RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) {
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- glDeleteFramebuffersOES(1, &fbName);
- glDeleteTextures(1, &texName);
-}
-
-void GLES11RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
- glColor4f(r, g, b, a);
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
-}
-
-void GLES11RenderEngine::drawMesh(const Mesh& mesh) {
- if (mesh.getTexCoordsSize()) {
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(mesh.getTexCoordsSize(),
- GL_FLOAT,
- mesh.getByteStride(),
- mesh.getTexCoords());
- }
-
- glVertexPointer(mesh.getVertexSize(),
- GL_FLOAT,
- mesh.getByteStride(),
- mesh.getPositions());
-
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
- if (mesh.getTexCoordsSize()) {
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- }
-}
-
-void GLES11RenderEngine::dump(String8& result) {
- RenderEngine::dump(result);
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#if defined(__gl2_h_)
-#error "don't include gl2/gl2.h in this file"
-#endif
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
deleted file mode 100644
index 4cd968d..0000000
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef SF_GLES11RENDERENGINE_H_
-#define SF_GLES11RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <GLES/gl.h>
-#include <Transform.h>
-
-#include "RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
-class Mesh;
-class Texture;
-
-class GLES11RenderEngine : public RenderEngine {
- GLuint mProtectedTexName;
- GLint mMaxViewportDims[2];
- GLint mMaxTextureSize;
-
- virtual void bindImageAsFramebuffer(EGLImageKHR image,
- uint32_t* texName, uint32_t* fbName, uint32_t* status);
- virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName);
-
-public:
- GLES11RenderEngine();
-
-protected:
- virtual ~GLES11RenderEngine();
-
- virtual void dump(String8& result);
- virtual void setViewportAndProjection(size_t vpw, size_t vph,
- Rect sourceCrop, size_t hwh, bool yswap,
- Transform::orientation_flags rotation);
-#ifdef USE_HWC2
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- float alpha) override;
- virtual void setupDimLayerBlending(float alpha) override;
-#else
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- int alpha);
- virtual void setupDimLayerBlending(int alpha);
-#endif
- virtual void setupLayerTexturing(const Texture& texture);
- virtual void setupLayerBlackedOut();
- virtual void setupFillWithColor(float r, float g, float b, float a) ;
- virtual void disableTexturing();
- virtual void disableBlending();
-
- virtual void drawMesh(const Mesh& mesh);
-
- virtual size_t getMaxTextureSize() const;
- virtual size_t getMaxViewportDims() const;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES11RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 2aec9e9..9909bf9 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -19,8 +19,6 @@
#include <ui/Region.h>
#include "RenderEngine.h"
-#include "GLES10RenderEngine.h"
-#include "GLES11RenderEngine.h"
#include "GLES20RenderEngine.h"
#include "GLExtensions.h"
#include "Mesh.h"
@@ -122,10 +120,8 @@
RenderEngine* engine = NULL;
switch (version) {
case GLES_VERSION_1_0:
- engine = new GLES10RenderEngine();
- break;
case GLES_VERSION_1_1:
- engine = new GLES11RenderEngine();
+ LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
break;
case GLES_VERSION_2_0:
case GLES_VERSION_3_0:
@@ -317,7 +313,7 @@
KeyedVector<Attribute, EGLint> mList;
struct Attribute {
Attribute() : v(0) {};
- Attribute(EGLint v) : v(v) { }
+ explicit Attribute(EGLint v) : v(v) { }
EGLint v;
bool operator < (const Attribute& other) const {
// this places EGL_NONE at the end
@@ -338,18 +334,18 @@
public:
void operator = (EGLint value) {
if (attribute != EGL_NONE) {
- v.mList.add(attribute, value);
+ v.mList.add(Attribute(attribute), value);
}
}
operator EGLint () const { return v.mList[attribute]; }
};
public:
EGLAttributeVector() {
- mList.add(EGL_NONE, EGL_NONE);
+ mList.add(Attribute(EGL_NONE), EGL_NONE);
}
void remove(EGLint attribute) {
if (attribute != EGL_NONE) {
- mList.removeItem(attribute);
+ mList.removeItem(Attribute(attribute));
}
}
Adder operator [] (EGLint attribute) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dabece2..5f25ef5 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>
@@ -149,6 +150,7 @@
mLastTransactionTime(0),
mBootFinished(false),
mForceFullDamage(false),
+ mInterceptor(),
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
@@ -187,6 +189,10 @@
property_get("debug.sf.disable_hwc_vds", value, "0");
mUseHwcVirtualDisplays = !atoi(value);
ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+
+ property_get("ro.sf.disable_triple_buffer", value, "0");
+ mLayerTripleBufferingDisabled = !atoi(value);
+ ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
}
void SurfaceFlinger::onFirstRef()
@@ -235,7 +241,7 @@
flinger->setTransactionFlags(eDisplayTransactionNeeded);
}
public:
- DisplayToken(const sp<SurfaceFlinger>& flinger)
+ explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
: flinger(flinger) {
}
};
@@ -246,7 +252,7 @@
DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
info.displayName = displayName;
mCurrentState.displays.add(token, info);
-
+ mInterceptor.saveDisplayCreation(info);
return token;
}
@@ -264,7 +270,7 @@
ALOGE("destroyDisplay called for non-virtual display");
return;
}
-
+ mInterceptor.saveDisplayDeletion(info.displayId);
mCurrentState.displays.removeItemsAt(idx);
setTransactionFlags(eDisplayTransactionNeeded);
}
@@ -277,6 +283,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) {
@@ -449,6 +456,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...");
@@ -463,10 +494,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
@@ -539,6 +570,23 @@
return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
}
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ getHwComposer().presentFenceRepresentsStartOfScanout() ?
+ FrameEvent::DISPLAY_PRESENT : FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE,
+ };
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
Vector<DisplayInfo>* configs) {
if ((configs == NULL) || (display.get() == NULL)) {
@@ -850,6 +898,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() {
@@ -1042,7 +1124,7 @@
}
bool SurfaceFlinger::handleMessageTransaction() {
- uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+ uint32_t transactionFlags = peekTransactionFlags();
if (transactionFlags) {
handleTransaction(transactionFlags);
return true;
@@ -1060,14 +1142,14 @@
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- preComposition();
+ preComposition(refreshStartTime);
rebuildLayerStacks();
setUpHWComposer();
doDebugFlashRegions();
doComposition();
- postComposition(refreshStartTime);
+ postComposition();
- mPreviousPresentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+ mPreviousPresentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
mHadClientComposition = false;
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
@@ -1076,10 +1158,6 @@
mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
}
- // Release any buffers which were replaced this frame
- for (auto& layer : mLayersWithQueuedFrames) {
- layer->releasePendingBuffer();
- }
mLayersWithQueuedFrames.clear();
}
@@ -1127,7 +1205,7 @@
}
}
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
{
ATRACE_CALL();
ALOGV("preComposition");
@@ -1136,7 +1214,7 @@
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- if (layers[i]->onPreComposition()) {
+ if (layers[i]->onPreComposition(refreshStartTime)) {
needExtraInvalidate = true;
}
}
@@ -1145,46 +1223,74 @@
}
}
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
{
ATRACE_CALL();
ALOGV("postComposition");
+ // Release any buffers which were replaced this frame
+ nsecs_t dequeueReadyTime = systemTime();
+ for (auto& layer : mLayersWithQueuedFrames) {
+ layer->releasePendingBuffer(dequeueReadyTime);
+ }
+
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+
+ std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
+ if (mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
+ glCompositionDoneFenceTime =
+ std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
+ mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
+ } else {
+ glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+ }
+ mGlCompositionDoneTimeline.updateSignalTimes();
+
+ sp<Fence> displayFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
+ auto displayFenceTime = std::make_shared<FenceTime>(displayFence);
+ mDisplayTimeline.push(displayFenceTime);
+ mDisplayTimeline.updateSignalTimes();
+
+ const std::shared_ptr<FenceTime>* presentFenceTime = &FenceTime::NO_FENCE;
+ const std::shared_ptr<FenceTime>* retireFenceTime = &FenceTime::NO_FENCE;
+ if (mHwc->presentFenceRepresentsStartOfScanout()) {
+ presentFenceTime = &displayFenceTime;
+ } else {
+ retireFenceTime = &displayFenceTime;
+ }
+
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- bool frameLatched = layers[i]->onPostComposition();
+ bool frameLatched =
+ layers[i]->onPostComposition(glCompositionDoneFenceTime,
+ *presentFenceTime, *retireFenceTime);
if (frameLatched) {
recordBufferingStats(layers[i]->getName().string(),
layers[i]->getOccupancyHistory(false));
}
}
- sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
-
- if (presentFence->isValid()) {
- if (mPrimaryDispSync.addPresentFence(presentFence)) {
+ if (displayFence->isValid()) {
+ if (mPrimaryDispSync.addPresentFence(displayFence)) {
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);
+ if (displayFenceTime->isValid()) {
+ mAnimFrameTracker.setActualPresentFence(
+ std::move(displayFenceTime));
} else {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
@@ -1402,16 +1508,10 @@
}
const auto hwcId = displayDevice->getHwcDisplayId();
if (hwcId >= 0) {
- mHwc->commit(hwcId);
+ mHwc->presentAndGetReleaseFences(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) {
@@ -1942,7 +2042,7 @@
{
ALOGV("handlePageFlip");
- Region dirtyRegion;
+ nsecs_t latchTime = systemTime();
bool visibleRegions = false;
const LayerVector& layers(mDrawingState.layersSortedByZ);
@@ -1971,7 +2071,7 @@
}
}
for (auto& layer : mLayersWithQueuedFrames) {
- const Region dirty(layer->latchBuffer(visibleRegions));
+ const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
layer->useSurfaceDamage();
const Layer::State& s(layer->getDrawingState());
invalidateLayerStack(s.layerStack, dirty);
@@ -1996,14 +2096,15 @@
}
-void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw,
+void SurfaceFlinger::doDisplayComposition(
+ const sp<const DisplayDevice>& displayDevice,
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;
+ bool isHwcDisplay = displayDevice->getHwcDisplayId() >= 0;
if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
ALOGV("Skipping display composition");
return;
@@ -2014,35 +2115,35 @@
Region dirtyRegion(inDirtyRegion);
// compute the invalid region
- hw->swapRegion.orSelf(dirtyRegion);
+ displayDevice->swapRegion.orSelf(dirtyRegion);
- uint32_t flags = hw->getFlags();
+ uint32_t flags = displayDevice->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());
+ dirtyRegion.set(displayDevice->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());
+ dirtyRegion.set(displayDevice->swapRegion.bounds());
} else {
// we need to redraw everything (the whole screen)
- dirtyRegion.set(hw->bounds());
- hw->swapRegion = dirtyRegion;
+ dirtyRegion.set(displayDevice->bounds());
+ displayDevice->swapRegion = dirtyRegion;
}
}
- if (!doComposeSurfaces(hw, dirtyRegion)) return;
+ if (!doComposeSurfaces(displayDevice, dirtyRegion)) return;
// update the swap region and clear the dirty region
- hw->swapRegion.orSelf(dirtyRegion);
+ displayDevice->swapRegion.orSelf(dirtyRegion);
// swap buffers (presentation)
- hw->swapBuffers(getHwComposer());
+ displayDevice->swapBuffers(getHwComposer());
}
bool SurfaceFlinger::doComposeSurfaces(
@@ -2150,7 +2251,7 @@
&& hasClientComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
- layer->clearWithOpenGL(displayDevice, clip);
+ layer->clearWithOpenGL(displayDevice);
}
break;
}
@@ -2186,8 +2287,8 @@
return true;
}
-void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const {
- const int32_t height = hw->getHeight();
+void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const {
+ const int32_t height = displayDevice->getHeight();
RenderEngine& engine(getRenderEngine());
engine.fillRegionWithColor(region, height, 0, 0, 0, 0);
}
@@ -2231,7 +2332,7 @@
return status_t(index);
}
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
return android_atomic_release_load(&mTransactionFlags);
}
@@ -2308,6 +2409,10 @@
}
if (transactionFlags) {
+ if (mInterceptor.isEnabled()) {
+ mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+ }
+
// this triggers the transaction
setTransactionFlags(transactionFlags);
@@ -2468,7 +2573,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));
@@ -2503,6 +2607,7 @@
if (result != NO_ERROR) {
return result;
}
+ mInterceptor.saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
return result;
@@ -2550,6 +2655,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));
@@ -2593,7 +2699,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;
@@ -2621,6 +2727,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);
@@ -2753,9 +2869,9 @@
}
if ((index < numArgs) &&
- (args[index] == String16("--fences"))) {
+ (args[index] == String16("--frame-events"))) {
index++;
- mFenceTracker.dump(&result);
+ dumpFrameEventsLocked(result);
dumpAll = false;
}
}
@@ -2844,21 +2960,18 @@
mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
}
-/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
+void SurfaceFlinger::appendSfConfigString(String8& result) const
{
- static const char* config =
- " [sf"
+ result.append(" [sf");
#ifdef HAS_CONTEXT_PRIORITY
- " HAS_CONTEXT_PRIORITY"
+ result.append(" HAS_CONTEXT_PRIORITY");
#endif
#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
- " NEVER_DEFAULT_TO_ASYNC_MODE"
+ result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
#endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
- " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
- "]";
- result.append(config);
+ if (isLayerTripleBufferingDisabled())
+ result.append(" DISABLE_TRIPLE_BUFFERING");
+ result.append("]");
}
void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
@@ -2896,6 +3009,16 @@
}
}
+void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
+ result.appendFormat("Layer frame timestamps:\n");
+
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ currentLayers[i]->dumpFrameEvents(result);
+ }
+}
+
void SurfaceFlinger::dumpBufferingStats(String8& result) const {
result.append("Buffering stats:\n");
result.append(" [Layer name] <Active time> <Two buffer> "
@@ -3136,9 +3259,7 @@
return true;
}
-status_t SurfaceFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
+status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
switch (code) {
case CREATE_CONNECTION:
case CREATE_DISPLAY:
@@ -3155,8 +3276,7 @@
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);
+ ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
break;
@@ -3169,13 +3289,22 @@
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);
+ ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
break;
}
}
+ return OK;
+}
+
+status_t SurfaceFlinger::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ status_t credentialCheck = CheckTransactCodeCredentials(code);
+ if (credentialCheck != OK) {
+ return credentialCheck;
+ }
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
@@ -3306,6 +3435,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;
@@ -3407,7 +3548,7 @@
}
public:
- GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+ explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
: impl(impl),
looper(new Looper(true)),
result(NO_ERROR),
@@ -3797,11 +3938,6 @@
}
}
-bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
- uint64_t frameNumber, FrameTimestamps* outTimestamps) {
- return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
-}
-
// ---------------------------------------------------------------------------
SurfaceFlinger::LayerVector::LayerVector() {
@@ -3831,31 +3967,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..f7f9ef5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -37,6 +37,7 @@
#include <binder/IMemory.h>
+#include <ui/FenceTime.h>
#include <ui/PixelFormat.h>
#include <ui/mat4.h>
@@ -53,9 +54,9 @@
#include "Barrier.h"
#include "DisplayDevice.h"
#include "DispSync.h"
-#include "FenceTracker.h"
#include "FrameTracker.h"
#include "MessageQueue.h"
+#include "SurfaceInterceptor.h"
#include "DisplayHardware/HWComposer.h"
#include "Effects/Daltonizer.h"
@@ -76,6 +77,8 @@
class Surface;
class RenderEngine;
class EventControlThread;
+class VSyncSource;
+class InjectVSyncSource;
// ---------------------------------------------------------------------------
@@ -148,6 +151,7 @@
private:
friend class Client;
friend class DisplayEventConnection;
+ friend class EventThread;
friend class Layer;
friend class MonitoredProducer;
@@ -171,23 +175,6 @@
virtual int do_compare(const void* lhs, const void* rhs) const;
};
- struct DisplayDeviceState {
- DisplayDeviceState();
- DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
- bool isValid() const { return type >= 0; }
- bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
- bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
- DisplayDevice::DisplayType type;
- sp<IGraphicBufferProducer> surface;
- uint32_t layerStack;
- Rect viewport;
- Rect frame;
- uint8_t orientation;
- uint32_t width, height;
- String8 displayName;
- bool isSecure;
- };
-
struct State {
LayerVector layersSortedByZ;
DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
@@ -213,6 +200,8 @@
virtual void bootFinished();
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const;
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const;
virtual sp<IDisplayEventConnection> createDisplayEventConnection();
virtual status_t captureScreen(const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
@@ -234,6 +223,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
@@ -292,7 +284,7 @@
* Transactions
*/
uint32_t getTransactionFlags(uint32_t flags);
- uint32_t peekTransactionFlags(uint32_t flags);
+ uint32_t peekTransactionFlags();
uint32_t setTransactionFlags(uint32_t flags);
void commitTransaction();
uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s);
@@ -409,20 +401,20 @@
const LayerVector& currentLayers, uint32_t layerStack,
Region& dirtyRegion, Region& opaqueRegion);
- void preComposition();
- void postComposition(nsecs_t refreshStartTime);
+ void preComposition(nsecs_t refreshStartTime);
+ void postComposition();
void rebuildLayerStacks();
void setUpHWComposer();
void doComposition();
void doDebugFlashRegions();
- void doDisplayComposition(const sp<const DisplayDevice>& hw, const Region& dirtyRegion);
+ void doDisplayComposition(const sp<const DisplayDevice>& displayDevice, const Region& dirtyRegion);
// compose surfaces for display hw. this fails if using GL and the surface
// has been destroyed and is no longer valid.
- bool doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty);
+ bool doComposeSurfaces(const sp<const DisplayDevice>& displayDevice, const Region& dirty);
void postFramebuffer();
- void drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const;
+ void drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const;
/* ------------------------------------------------------------------------
* Display management
@@ -446,7 +438,7 @@
void clearStatsLocked(const Vector<String16>& args, size_t& index, String8& result);
void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
bool startDdmConnection();
- static void appendSfConfigString(String8& result);
+ void appendSfConfigString(String8& result) const;
void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
const sp<const DisplayDevice>& hw,
uint32_t minLayerZ, uint32_t maxLayerZ);
@@ -454,14 +446,16 @@
void logFrameStats();
void dumpStaticScreenStats(String8& result) const;
+ // Not const because each Layer needs to query Fences and cache timestamps.
+ void dumpFrameEventsLocked(String8& result);
void recordBufferingStats(const char* layerName,
std::vector<OccupancyTracker::Segment>&& history);
void dumpBufferingStats(String8& result) const;
- bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
- FrameTimestamps* outTimestamps);
-
+ bool isLayerTripleBufferingDisabled() const {
+ return this->mLayerTripleBufferingDisabled;
+ }
/* ------------------------------------------------------------------------
* Attributes
*/
@@ -489,6 +483,8 @@
bool mGpuToCpuSupported;
sp<EventThread> mEventThread;
sp<EventThread> mSFEventThread;
+ sp<EventThread> mInjectorEventThread;
+ sp<InjectVSyncSource> mVSyncInjector;
sp<EventControlThread> mEventControlThread;
EGLContext mEGLContext;
EGLDisplay mEGLDisplay;
@@ -509,6 +505,8 @@
sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
bool mHadClientComposition = false;
#endif
+ FenceTimeline mGlCompositionDoneTimeline;
+ FenceTimeline mDisplayTimeline;
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
@@ -525,12 +523,15 @@
nsecs_t mLastTransactionTime;
bool mBootFinished;
bool mForceFullDamage;
- FenceTracker mFenceTracker;
#ifdef USE_HWC2
bool mPropagateBackpressure = true;
#endif
+ SurfaceInterceptor mInterceptor;
bool mUseHwcVirtualDisplays = true;
+ // Restrict layers to use two buffers in their bufferqueues.
+ bool mLayerTripleBufferingDisabled = false;
+
// these are thread safe
mutable MessageQueue mEventQueue;
FrameTracker mAnimFrameTracker;
@@ -549,6 +550,8 @@
* Feature prototyping
*/
+ bool mInjectVSyncs;
+
Daltonizer mDaltonizer;
#ifndef USE_HWC2
bool mDaltonize;
@@ -587,7 +590,11 @@
};
mutable Mutex mBufferingStatsMutex;
std::unordered_map<std::string, BufferingStats> mBufferingStats;
-};
+
+ // Verify that transaction is being called by an approved process:
+ // either AID_GRAPHICS or AID_SYSTEM.
+ status_t CheckTransactCodeCredentials(uint32_t code);
+ };
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 6f2520b..029937a 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -189,10 +189,14 @@
return nextRefresh + extraPadding;
}
+sp<Fence> SurfaceFlingerConsumer::getPrevFinalReleaseFence() const {
+ Mutex::Autolock lock(mMutex);
+ return ConsumerBase::mPrevFinalReleaseFence;
+}
+
#ifdef USE_HWC2
void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence)
{
- mPrevReleaseFence = fence;
if (!mPendingRelease.isPending) {
GLConsumer::setReleaseFence(fence);
return;
@@ -222,17 +226,8 @@
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;
-}
-
void SurfaceFlingerConsumer::setContentsChangedListener(
const wp<ContentsChangedListener>& listener) {
setFrameAvailableListener(listener);
@@ -253,10 +248,13 @@
}
}
-bool SurfaceFlingerConsumer::getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- sp<const Layer> l = mLayer.promote();
- return l.get() ? l->getFrameTimestamps(frameNumber, outTimestamps) : false;
+void SurfaceFlingerConsumer::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta *outDelta) {
+ sp<Layer> l = mLayer.promote();
+ if (l.get()) {
+ l->addAndGetFrameTimestamps(newTimestamps, outDelta);
+ }
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 4271039..d3f0070 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -37,10 +37,9 @@
};
SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
- uint32_t tex, const Layer* layer)
+ uint32_t tex, Layer* layer)
: GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
- mTransformToDisplayInverse(false), mSurfaceDamage(),
- mPrevReleaseFence(Fence::NO_FENCE), mLayer(layer)
+ mTransformToDisplayInverse(false), mSurfaceDamage(), mLayer(layer)
{}
class BufferRejecter {
@@ -61,7 +60,7 @@
// texture.
status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
bool* autoRefresh, bool* queuedBuffer,
- uint64_t maxFrameNumber = 0);
+ uint64_t maxFrameNumber);
// See GLConsumer::bindTextureImageLocked().
status_t bindTextureImage();
@@ -79,14 +78,15 @@
nsecs_t computeExpectedPresent(const DispSync& dispSync);
- virtual void setReleaseFence(const sp<Fence>& fence) override;
- sp<Fence> getPrevReleaseFence() const;
+ sp<Fence> getPrevFinalReleaseFence() const;
#ifdef USE_HWC2
+ virtual void setReleaseFence(const sp<Fence>& fence) override;
void releasePendingBuffer();
#endif
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ virtual void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override;
private:
virtual void onSidebandStreamChanged();
@@ -107,11 +107,8 @@
PendingRelease mPendingRelease;
#endif
- // The release fence of the already displayed buffer (previous frame).
- sp<Fence> mPrevReleaseFence;
-
// The layer for this SurfaceFlingerConsumer
- wp<const Layer> mLayer;
+ const wp<Layer> mLayer;
};
// ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 6710bca..fcf0185 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -24,6 +24,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <mutex>
+
#include <EGL/egl.h>
#include <cutils/properties.h>
@@ -150,6 +152,7 @@
mLastTransactionTime(0),
mBootFinished(false),
mForceFullDamage(false),
+ mInterceptor(),
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
@@ -162,7 +165,6 @@
{
ALOGI("SurfaceFlinger is starting");
- // debugging stuff...
char value[PROPERTY_VALUE_MAX];
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
@@ -185,6 +187,10 @@
property_get("debug.sf.disable_hwc_vds", value, "0");
mUseHwcVirtualDisplays = !atoi(value);
ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+
+ property_get("ro.sf.disable_triple_buffer", value, "0");
+ mLayerTripleBufferingDisabled = !atoi(value);
+ ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
}
void SurfaceFlinger::onFirstRef()
@@ -233,7 +239,7 @@
flinger->setTransactionFlags(eDisplayTransactionNeeded);
}
public:
- DisplayToken(const sp<SurfaceFlinger>& flinger)
+ explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
: flinger(flinger) {
}
};
@@ -244,7 +250,7 @@
DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
info.displayName = displayName;
mCurrentState.displays.add(token, info);
-
+ mInterceptor.saveDisplayCreation(info);
return token;
}
@@ -262,7 +268,7 @@
ALOGE("destroyDisplay called for non-virtual display");
return;
}
-
+ mInterceptor.saveDisplayDeletion(info.displayId);
mCurrentState.displays.removeItemsAt(idx);
setTransactionFlags(eDisplayTransactionNeeded);
}
@@ -274,6 +280,7 @@
// All non-virtual displays are currently considered secure.
DisplayDeviceState info(type, true);
mCurrentState.displays.add(mBuiltinDisplays[type], info);
+ mInterceptor.saveDisplayCreation(info);
}
sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
@@ -446,6 +453,30 @@
bool mEnabled;
};
+class InjectVSyncSource : public VSyncSource {
+public:
+ InjectVSyncSource() {}
+
+ virtual ~InjectVSyncSource() {}
+
+ virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ mCallback = callback;
+ }
+
+ virtual void onInjectSyncEvent(nsecs_t when) {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ mCallback->onVSyncEvent(when);
+ }
+
+ virtual void setVSyncEnabled(bool) {}
+ virtual void setPhaseOffset(nsecs_t) {}
+
+private:
+ std::mutex mCallbackMutex; // Protects the following
+ sp<VSyncSource::Callback> mCallback;
+};
+
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
@@ -459,10 +490,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
@@ -572,6 +603,22 @@
return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
}
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE,
+ };
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
Vector<DisplayInfo>* configs) {
if ((configs == NULL) || (display.get() == NULL)) {
@@ -809,6 +856,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() {
@@ -976,7 +1057,7 @@
}
bool SurfaceFlinger::handleMessageTransaction() {
- uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+ uint32_t transactionFlags = peekTransactionFlags();
if (transactionFlags) {
handleTransaction(transactionFlags);
return true;
@@ -994,12 +1075,12 @@
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- preComposition();
+ preComposition(refreshStartTime);
rebuildLayerStacks();
setUpHWComposer();
doDebugFlashRegions();
doComposition();
- postComposition(refreshStartTime);
+ postComposition();
}
void SurfaceFlinger::doDebugFlashRegions()
@@ -1042,13 +1123,13 @@
}
}
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
{
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()) {
+ if (layers[i]->onPreComposition(refreshStartTime)) {
needExtraInvalidate = true;
}
}
@@ -1057,44 +1138,57 @@
}
}
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
{
+ const HWComposer& hwc = getHwComposer();
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+
+ std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
+ if (getHwComposer().hasGlesComposition(hw->getHwcDisplayId())) {
+ glCompositionDoneFenceTime =
+ std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
+ mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
+ } else {
+ glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+ }
+ mGlCompositionDoneTimeline.updateSignalTimes();
+
+ sp<Fence> displayFence = mHwc->getDisplayFence(HWC_DISPLAY_PRIMARY);
+ const std::shared_ptr<FenceTime>& presentFenceTime = FenceTime::NO_FENCE;
+ auto retireFenceTime = std::make_shared<FenceTime>(displayFence);
+ mDisplayTimeline.push(retireFenceTime);
+ mDisplayTimeline.updateSignalTimes();
+
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- bool frameLatched = layers[i]->onPostComposition();
+ bool frameLatched = layers[i]->onPostComposition(
+ glCompositionDoneFenceTime, presentFenceTime, retireFenceTime);
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)) {
+ if (displayFence->isValid()) {
+ if (mPrimaryDispSync.addPresentFence(displayFence)) {
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);
+ if (retireFenceTime->isValid()) {
+ mAnimFrameTracker.setActualPresentFence(std::move(retireFenceTime));
} else {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
@@ -1857,6 +1951,7 @@
bool SurfaceFlinger::handlePageFlip()
{
+ nsecs_t latchTime = systemTime();
Region dirtyRegion;
bool visibleRegions = false;
@@ -1888,7 +1983,7 @@
}
for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) {
Layer* layer = layersWithQueuedFrames[i];
- const Region dirty(layer->latchBuffer(visibleRegions));
+ const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
layer->useSurfaceDamage();
const Layer::State& s(layer->getDrawingState());
invalidateLayerStack(s.layerStack, dirty);
@@ -2063,7 +2158,7 @@
&& hasGlesComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
- layer->clearWithOpenGL(hw, clip);
+ layer->clearWithOpenGL(hw);
}
break;
}
@@ -2143,7 +2238,7 @@
return status_t(index);
}
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
return android_atomic_release_load(&mTransactionFlags);
}
@@ -2220,6 +2315,10 @@
}
if (transactionFlags) {
+ if (mInterceptor.isEnabled()) {
+ mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+ }
+
// this triggers the transaction
setTransactionFlags(transactionFlags);
@@ -2380,7 +2479,6 @@
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
{
- //ALOGD("createLayer for (%d x %d), name=%s", w, h, name.string());
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -2415,6 +2513,7 @@
if (result != NO_ERROR) {
return result;
}
+ mInterceptor.saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
return result;
@@ -2462,6 +2561,7 @@
status_t err = NO_ERROR;
sp<Layer> l(client->getLayerUser(handle));
if (l != NULL) {
+ mInterceptor.saveSurfaceDeletion(l);
err = removeLayer(l);
ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
"error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -2505,7 +2605,7 @@
class MessageScreenInitialized : public MessageBase {
SurfaceFlinger* flinger;
public:
- MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
+ explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
virtual bool handler() {
flinger->onInitializeDisplays();
return true;
@@ -2533,6 +2633,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);
@@ -2665,9 +2775,9 @@
}
if ((index < numArgs) &&
- (args[index] == String16("--fences"))) {
+ (args[index] == String16("--frame-events"))) {
index++;
- mFenceTracker.dump(&result);
+ dumpFrameEventsLocked(result);
dumpAll = false;
}
}
@@ -2756,21 +2866,18 @@
mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
}
-/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
+void SurfaceFlinger::appendSfConfigString(String8& result) const
{
- static const char* config =
- " [sf"
+ result.append(" [sf");
#ifdef HAS_CONTEXT_PRIORITY
- " HAS_CONTEXT_PRIORITY"
+ result.append(" HAS_CONTEXT_PRIORITY");
#endif
#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
- " NEVER_DEFAULT_TO_ASYNC_MODE"
+ result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
#endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
- " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
- "]";
- result.append(config);
+ if (isLayerTripleBufferingDisabled())
+ result.append(" DISABLE_TRIPLE_BUFFERING");
+ result.append("]");
}
void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
@@ -2790,6 +2897,16 @@
NUM_BUCKETS - 1, bucketTimeSec, percent);
}
+void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
+ result.appendFormat("Layer frame timestamps:\n");
+
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ currentLayers[i]->dumpFrameEvents(result);
+ }
+}
+
void SurfaceFlinger::recordBufferingStats(const char* layerName,
std::vector<OccupancyTracker::Segment>&& history) {
Mutex::Autolock lock(mBufferingStatsMutex);
@@ -3195,6 +3312,18 @@
mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
return NO_ERROR;
}
+ case 1020: { // Layer updates interceptor
+ n = data.readInt32();
+ if (n) {
+ ALOGV("Interceptor enabled");
+ mInterceptor.enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
+ }
+ else{
+ ALOGV("Interceptor disabled");
+ mInterceptor.disable();
+ }
+ return NO_ERROR;
+ }
case 1021: { // Disable HWC virtual displays
n = data.readInt32();
mUseHwcVirtualDisplays = !n;
@@ -3296,7 +3425,7 @@
}
public:
- GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+ explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
: impl(impl),
looper(new Looper(true)),
result(NO_ERROR),
@@ -3652,11 +3781,6 @@
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) {
@@ -3714,31 +3838,6 @@
return l->sequence - r->sequence;
}
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState()
- : type(DisplayDevice::DISPLAY_ID_INVALID),
- layerStack(DisplayDevice::NO_LAYER_STACK),
- orientation(0),
- width(0),
- height(0),
- isSecure(false) {
-}
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(
- DisplayDevice::DisplayType type, bool isSecure)
- : type(type),
- layerStack(DisplayDevice::NO_LAYER_STACK),
- orientation(0),
- width(0),
- height(0),
- isSecure(isSecure) {
- viewport.makeInvalid();
- frame.makeInvalid();
-}
-
-// ---------------------------------------------------------------------------
-
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
new file mode 100644
index 0000000..60bc7f3
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -0,0 +1,580 @@
+/*
+ * 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 <fstream>
+
+#include <android-base/file.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+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::string output;
+
+ if (!mTrace.IsInitialized()) {
+ return NOT_ENOUGH_DATA;
+ }
+ if (!mTrace.SerializeToString(&output)) {
+ return PERMISSION_DENIED;
+ }
+ if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
+ 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..9af6e61
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -0,0 +1,136 @@
+/*
+ * 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>
+
+#include <utils/SortedVector.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class BufferItem;
+class Layer;
+struct DisplayState;
+struct layer_state_t;
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+/*
+ * SurfaceInterceptor intercepts and stores incoming streams of window
+ * properties on SurfaceFlinger.
+ */
+class SurfaceInterceptor {
+public:
+ // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
+ void enable(const SortedVector<sp<Layer>>& layers,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+ void disable();
+ bool isEnabled();
+
+ // Intercept display and surface transactions
+ void saveTransaction(const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t flags);
+
+ // Intercept surface data
+ void saveSurfaceCreation(const sp<const Layer>& layer);
+ void saveSurfaceDeletion(const sp<const Layer>& layer);
+ void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+ uint64_t frameNumber);
+
+ // Intercept display data
+ void saveDisplayCreation(const DisplayDeviceState& info);
+ void saveDisplayDeletion(int32_t displayId);
+ void savePowerModeUpdate(int32_t displayId, int32_t mode);
+ void saveVSyncEvent(nsecs_t timestamp);
+
+private:
+ // The creation increments of Surfaces and Displays do not contain enough information to capture
+ // the initial state of each object, so a transaction with all of the missing properties is
+ // performed at the initial snapshot for each display and surface.
+ void saveExistingDisplaysLocked(
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+ void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
+ void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
+ void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
+
+ status_t writeProtoFileLocked();
+ const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle);
+ const std::string getLayerName(const sp<const Layer>& layer);
+ int32_t getLayerId(const sp<const Layer>& layer);
+
+ Increment* createTraceIncrementLocked();
+ void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
+ void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
+ void addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+ uint32_t height, uint64_t frameNumber);
+ void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
+ void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
+ void addDisplayDeletionLocked(Increment* increment, int32_t displayId);
+ void addPowerModeUpdateLocked(Increment* increment, int32_t displayId, int32_t mode);
+
+ // Add surface transactions to the trace
+ SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
+ void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
+ void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
+ void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
+ void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
+ void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
+ void addMatrixLocked(Transaction* transaction, int32_t layerId,
+ const layer_state_t::matrix22_t& matrix);
+ void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
+ const Region& transRegion);
+ void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
+ void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+ void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+ const wp<const IBinder>& weakHandle, uint64_t frameNumber);
+ void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
+ int32_t overrideScalingMode);
+ void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
+ void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+
+ // Add display transactions to the trace
+ DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t displayId);
+ void addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+ const sp<const IGraphicBufferProducer>& surface);
+ void addDisplayLayerStackLocked(Transaction* transaction, int32_t displayId,
+ uint32_t layerStack);
+ void addDisplaySizeLocked(Transaction* transaction, int32_t displayId, uint32_t w,
+ uint32_t h);
+ void addDisplayProjectionLocked(Transaction* transaction, int32_t displayId,
+ int32_t orientation, const Rect& viewport, const Rect& frame);
+ void addDisplayChangesLocked(Transaction* transaction,
+ const DisplayState& state, int32_t displayId);
+
+
+ bool mEnabled {false};
+ std::string mOutputFileName {DEFAULT_FILENAME};
+ std::mutex mTraceMutex {};
+ Trace mTrace {};
+};
+
+}
+
+#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
index 979062e..4f1a8e6 100644
--- a/services/surfaceflinger/tests/Android.mk
+++ b/services/surfaceflinger/tests/Android.mk
@@ -9,15 +9,21 @@
LOCAL_SRC_FILES := \
Transaction_test.cpp \
+ SurfaceInterceptor_test.cpp
LOCAL_SHARED_LIBRARIES := \
- libEGL \
- libGLESv2 \
- libbinder \
- libcutils \
- libgui \
- libui \
- libutils \
+ libEGL \
+ libGLESv2 \
+ libbinder \
+ libcutils \
+ libgui \
+ libprotobuf-cpp-full \
+ libui \
+ libutils \
+
+LOCAL_STATIC_LIBRARIES := libtrace_proto
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
# to integrate with auto-test framework.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
new file mode 100644
index 0000000..1a3f89f
--- /dev/null
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <gtest/gtest.h>
+
+#include <android/native_window.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+#include <ui/DisplayInfo.h>
+
+#include <fstream>
+#include <random>
+#include <thread>
+
+namespace android {
+
+constexpr int32_t SCALING_UPDATE = 1;
+constexpr uint32_t BUFFER_UPDATES = 18;
+constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
+constexpr uint32_t SIZE_UPDATE = 134;
+constexpr uint32_t STACK_UPDATE = 1;
+constexpr uint64_t DEFERRED_UPDATE = 13;
+constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float POSITION_UPDATE = 121;
+const Rect CROP_UPDATE(16, 16, 32, 32);
+
+const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
+constexpr auto LAYER_NAME = "Layer Create and Delete Test";
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
+ ANativeWindow_Buffer outBuffer;
+ sp<Surface> s = sc->getSurface();
+ ASSERT_TRUE(s != nullptr);
+ ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+ uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+ for (int y = 0; y < outBuffer.height; y++) {
+ for (int x = 0; x < outBuffer.width; x++) {
+ uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ pixel[3] = 255;
+ }
+ }
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+}
+
+static status_t readProtoFile(Trace* trace) {
+ std::ifstream input(DEFAULT_FILENAME, std::ios::in | std::ios::binary);
+ if (input && !trace->ParseFromIstream(&input)) {
+ return PERMISSION_DENIED;
+ }
+ return NO_ERROR;
+}
+
+static void enableInterceptor() {
+ system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
+}
+
+static void disableInterceptor() {
+ system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
+}
+
+int32_t getSurfaceId(const std::string& surfaceName) {
+ enableInterceptor();
+ disableInterceptor();
+ Trace capturedTrace;
+ readProtoFile(&capturedTrace);
+ int32_t layerId = 0;
+ for (const auto& increment : *capturedTrace.mutable_increment()) {
+ if (increment.increment_case() == increment.kSurfaceCreation) {
+ if (increment.surface_creation().name() == surfaceName) {
+ layerId = increment.surface_creation().id();
+ break;
+ }
+ }
+ }
+ return layerId;
+}
+
+int32_t getDisplayId(const std::string& displayName) {
+ enableInterceptor();
+ disableInterceptor();
+ Trace capturedTrace;
+ readProtoFile(&capturedTrace);
+ int32_t displayId = 0;
+ for (const auto& increment : *capturedTrace.mutable_increment()) {
+ if (increment.increment_case() == increment.kDisplayCreation) {
+ if (increment.display_creation().name() == displayName) {
+ displayId = increment.display_creation().id();
+ break;
+ }
+ }
+ }
+ return displayId;
+}
+
+class SurfaceInterceptorTest : public ::testing::Test {
+protected:
+ virtual void SetUp() {
+ // Allow SurfaceInterceptor write to /data
+ system("setenforce 0");
+
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ssize_t displayWidth = info.w;
+ ssize_t displayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl = mComposerClient->createSurface(
+ String8("BG Interceptor Test Surface"), displayWidth, displayHeight,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != NULL);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ mBGLayerId = getSurfaceId("BG Interceptor Test Surface");
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mComposerClient->setDisplayLayerStack(display, 0);
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-3));
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ }
+
+ virtual void TearDown() {
+ mComposerClient->dispose();
+ mBGSurfaceControl.clear();
+ mComposerClient.clear();
+ }
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mBGSurfaceControl;
+ int32_t mBGLayerId;
+ // Used to verify creation and destruction of surfaces and displays
+ int32_t mTargetId;
+
+public:
+ void captureTest(void (SurfaceInterceptorTest::* action)(void),
+ bool (SurfaceInterceptorTest::* verification)(Trace *));
+ void captureTest(void (SurfaceInterceptorTest::* action)(void),
+ SurfaceChange::SurfaceChangeCase changeCase);
+ void captureTest(void (SurfaceInterceptorTest::* action)(void),
+ Increment::IncrementCase incrementCase);
+ void runInTransaction(void (SurfaceInterceptorTest::* action)(void), bool intercepted = false);
+
+ // Verification of changes to a surface
+ bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
+ bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
+ bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
+ bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
+ bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
+ bool finalCropUpdateFound(const SurfaceChange& change, bool foundFinalCrop);
+ bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
+ bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
+ bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
+ bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
+ bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
+ bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
+ bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
+ bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
+ bool surfaceUpdateFound(Trace* trace, SurfaceChange::SurfaceChangeCase changeCase);
+ void assertAllUpdatesFound(Trace* trace);
+
+ // Verification of creation and deletion of a surface
+ bool surfaceCreationFound(const Increment& increment, bool foundSurface);
+ bool surfaceDeletionFound(const Increment& increment, bool foundSurface);
+ bool displayCreationFound(const Increment& increment, bool foundDisplay);
+ bool displayDeletionFound(const Increment& increment, bool foundDisplay);
+ bool singleIncrementFound(Trace* trace, Increment::IncrementCase incrementCase);
+
+ // Verification of buffer updates
+ bool bufferUpdatesFound(Trace* trace);
+
+ // Perform each of the possible changes to a surface
+ void positionUpdate();
+ void sizeUpdate();
+ void alphaUpdate();
+ void layerUpdate();
+ void cropUpdate();
+ void finalCropUpdate();
+ void matrixUpdate();
+ void overrideScalingModeUpdate();
+ void transparentRegionHintUpdate();
+ void layerStackUpdate();
+ void hiddenFlagUpdate();
+ void opaqueFlagUpdate();
+ void secureFlagUpdate();
+ void deferredTransactionUpdate();
+ void runAllUpdates();
+ void surfaceCreation();
+ void nBufferUpdates();
+ void displayCreation();
+ void displayDeletion();
+};
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ bool (SurfaceInterceptorTest::* verification)(Trace *))
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE((this->*verification)(&capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ Increment::IncrementCase incrementCase)
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(&capturedTrace, incrementCase));
+}
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ SurfaceChange::SurfaceChangeCase changeCase)
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase));
+}
+
+void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(void),
+ bool intercepted)
+{
+ if (intercepted) {
+ enableInterceptor();
+ }
+ SurfaceComposerClient::openGlobalTransaction();
+ (this->*action)();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ if (intercepted) {
+ disableInterceptor();
+ }
+}
+
+void SurfaceInterceptorTest::positionUpdate() {
+ mBGSurfaceControl->setPosition(POSITION_UPDATE, POSITION_UPDATE);
+}
+
+void SurfaceInterceptorTest::sizeUpdate() {
+ mBGSurfaceControl->setSize(SIZE_UPDATE, SIZE_UPDATE);
+}
+
+void SurfaceInterceptorTest::alphaUpdate() {
+ mBGSurfaceControl->setAlpha(ALPHA_UPDATE);
+}
+
+void SurfaceInterceptorTest::layerUpdate() {
+ mBGSurfaceControl->setLayer(LAYER_UPDATE);
+}
+
+void SurfaceInterceptorTest::cropUpdate() {
+ mBGSurfaceControl->setCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::finalCropUpdate() {
+ mBGSurfaceControl->setFinalCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::matrixUpdate() {
+ mBGSurfaceControl->setMatrix(M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
+}
+
+void SurfaceInterceptorTest::overrideScalingModeUpdate() {
+ mBGSurfaceControl->setOverrideScalingMode(SCALING_UPDATE);
+}
+
+void SurfaceInterceptorTest::transparentRegionHintUpdate() {
+ Region region(CROP_UPDATE);
+ mBGSurfaceControl->setTransparentRegionHint(region);
+}
+
+void SurfaceInterceptorTest::layerStackUpdate() {
+ mBGSurfaceControl->setLayerStack(STACK_UPDATE);
+}
+
+void SurfaceInterceptorTest::hiddenFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
+}
+
+void SurfaceInterceptorTest::opaqueFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+}
+
+void SurfaceInterceptorTest::secureFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+}
+
+void SurfaceInterceptorTest::deferredTransactionUpdate() {
+ mBGSurfaceControl->deferTransactionUntil(mBGSurfaceControl->getHandle(), DEFERRED_UPDATE);
+}
+
+void SurfaceInterceptorTest::displayCreation() {
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+ SurfaceComposerClient::destroyDisplay(testDisplay);
+}
+
+void SurfaceInterceptorTest::displayDeletion() {
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
+ mTargetId = getDisplayId(DISPLAY_NAME.string());
+ SurfaceComposerClient::destroyDisplay(testDisplay);
+}
+
+void SurfaceInterceptorTest::runAllUpdates() {
+ runInTransaction(&SurfaceInterceptorTest::positionUpdate);
+ runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
+ runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+ runInTransaction(&SurfaceInterceptorTest::layerUpdate);
+ runInTransaction(&SurfaceInterceptorTest::cropUpdate);
+ runInTransaction(&SurfaceInterceptorTest::finalCropUpdate);
+ runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
+ runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
+ runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
+ runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
+ runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
+}
+
+void SurfaceInterceptorTest::surfaceCreation() {
+ mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
+ PIXEL_FORMAT_RGBA_8888, 0);
+}
+
+void SurfaceInterceptorTest::nBufferUpdates() {
+ std::random_device rd;
+ std::mt19937_64 gen(rd());
+ // This makes testing fun
+ std::uniform_int_distribution<uint8_t> dis;
+ for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
+ fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
+ }
+}
+
+bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
+ // There should only be one position transaction with x and y = POSITION_UPDATE
+ bool hasX(change.position().x() == POSITION_UPDATE);
+ bool hasY(change.position().y() == POSITION_UPDATE);
+ if (hasX && hasY && !foundPosition) {
+ foundPosition = true;
+ }
+ // Failed because the position update was found a second time
+ else if (hasX && hasY && foundPosition) {
+ [] () { FAIL(); }();
+ }
+ return foundPosition;
+}
+
+bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool foundSize) {
+ bool hasWidth(change.size().h() == SIZE_UPDATE);
+ bool hasHeight(change.size().w() == SIZE_UPDATE);
+ if (hasWidth && hasHeight && !foundSize) {
+ foundSize = true;
+ }
+ else if (hasWidth && hasHeight && foundSize) {
+ [] () { FAIL(); }();
+ }
+ return foundSize;
+}
+
+bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
+ bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
+ if (hasAlpha && !foundAlpha) {
+ foundAlpha = true;
+ }
+ else if (hasAlpha && foundAlpha) {
+ [] () { FAIL(); }();
+ }
+ return foundAlpha;
+}
+
+bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
+ bool hasLayer(change.layer().layer() == LAYER_UPDATE);
+ if (hasLayer && !foundLayer) {
+ foundLayer = true;
+ }
+ else if (hasLayer && foundLayer) {
+ [] () { FAIL(); }();
+ }
+ return foundLayer;
+}
+
+bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
+ bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
+ bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
+ bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
+ bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
+ foundCrop = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
+ [] () { FAIL(); }();
+ }
+ return foundCrop;
+}
+
+bool SurfaceInterceptorTest::finalCropUpdateFound(const SurfaceChange& change,
+ bool foundFinalCrop)
+{
+ bool hasLeft(change.final_crop().rectangle().left() == CROP_UPDATE.left);
+ bool hasTop(change.final_crop().rectangle().top() == CROP_UPDATE.top);
+ bool hasRight(change.final_crop().rectangle().right() == CROP_UPDATE.right);
+ bool hasBottom(change.final_crop().rectangle().bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundFinalCrop) {
+ foundFinalCrop = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundFinalCrop) {
+ [] () { FAIL(); }();
+ }
+ return foundFinalCrop;
+}
+
+bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
+ bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
+ bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
+ bool hasSy((float)change.matrix().dsdy() == (float)-M_SQRT1_2);
+ bool hasTy((float)change.matrix().dtdy() == (float)M_SQRT1_2);
+ if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
+ foundMatrix = true;
+ }
+ else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
+ [] () { FAIL(); }();
+ }
+ return foundMatrix;
+}
+
+bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
+ bool foundScalingMode)
+{
+ bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
+ if (hasScalingUpdate && !foundScalingMode) {
+ foundScalingMode = true;
+ }
+ else if (hasScalingUpdate && foundScalingMode) {
+ [] () { FAIL(); }();
+ }
+ return foundScalingMode;
+}
+
+bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
+ bool foundTransparentRegion)
+{
+ auto traceRegion = change.transparent_region_hint().region(0);
+ bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
+ bool hasTop(traceRegion.top() == CROP_UPDATE.top);
+ bool hasRight(traceRegion.right() == CROP_UPDATE.right);
+ bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
+ foundTransparentRegion = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
+ [] () { FAIL(); }();
+ }
+ return foundTransparentRegion;
+}
+
+bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
+ bool foundLayerStack)
+{
+ bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
+ if (hasLayerStackUpdate && !foundLayerStack) {
+ foundLayerStack = true;
+ }
+ else if (hasLayerStackUpdate && foundLayerStack) {
+ [] () { FAIL(); }();
+ }
+ return foundLayerStack;
+}
+
+bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
+ bool foundHiddenFlag)
+{
+ bool hasHiddenFlag(change.hidden_flag().hidden_flag());
+ if (hasHiddenFlag && !foundHiddenFlag) {
+ foundHiddenFlag = true;
+ }
+ else if (hasHiddenFlag && foundHiddenFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundHiddenFlag;
+}
+
+bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
+ bool foundOpaqueFlag)
+{
+ bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
+ if (hasOpaqueFlag && !foundOpaqueFlag) {
+ foundOpaqueFlag = true;
+ }
+ else if (hasOpaqueFlag && foundOpaqueFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundOpaqueFlag;
+}
+
+bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
+ bool foundSecureFlag)
+{
+ bool hasSecureFlag(change.secure_flag().secure_flag());
+ if (hasSecureFlag && !foundSecureFlag) {
+ foundSecureFlag = true;
+ }
+ else if (hasSecureFlag && foundSecureFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundSecureFlag;
+}
+
+bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
+ bool foundDeferred)
+{
+ bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
+ bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
+ if (hasId && hasFrameNumber && !foundDeferred) {
+ foundDeferred = true;
+ }
+ else if (hasId && hasFrameNumber && foundDeferred) {
+ [] () { FAIL(); }();
+ }
+ return foundDeferred;
+}
+
+bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace,
+ SurfaceChange::SurfaceChangeCase changeCase)
+{
+ bool foundUpdate = false;
+ for (const auto& increment : *trace->mutable_increment()) {
+ if (increment.increment_case() == increment.kTransaction) {
+ for (const auto& change : increment.transaction().surface_change()) {
+ if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
+ switch (changeCase) {
+ case SurfaceChange::SurfaceChangeCase::kPosition:
+ // foundUpdate is sent for the tests to fail on duplicated increments
+ foundUpdate = positionUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSize:
+ foundUpdate = sizeUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kAlpha:
+ foundUpdate = alphaUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayer:
+ foundUpdate = layerUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kCrop:
+ foundUpdate = cropUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+ foundUpdate = finalCropUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kMatrix:
+ foundUpdate = matrixUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+ foundUpdate = scalingModeUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+ foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayerStack:
+ foundUpdate = layerStackUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+ foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+ foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+ foundUpdate = secureFlagUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+ foundUpdate = deferredTransactionUpdateFound(change, foundUpdate);
+ break;
+ case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
+ break;
+ }
+ }
+ }
+ }
+ }
+ return foundUpdate;
+}
+
+void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) {
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kFinalCrop));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
+}
+
+bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
+ bool isMatch(increment.surface_creation().name() == LAYER_NAME &&
+ increment.surface_creation().w() == SIZE_UPDATE &&
+ increment.surface_creation().h() == SIZE_UPDATE);
+ if (isMatch && !foundSurface) {
+ foundSurface = true;
+ }
+ else if (isMatch && foundSurface) {
+ [] () { FAIL(); }();
+ }
+ return foundSurface;
+}
+
+bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, bool foundSurface) {
+ bool isMatch(increment.surface_deletion().id() == mTargetId);
+ if (isMatch && !foundSurface) {
+ foundSurface = true;
+ }
+ else if (isMatch && foundSurface) {
+ [] () { FAIL(); }();
+ }
+ return foundSurface;
+}
+
+bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
+ bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
+ increment.display_creation().is_secure());
+ if (isMatch && !foundDisplay) {
+ foundDisplay = true;
+ }
+ else if (isMatch && foundDisplay) {
+ [] () { FAIL(); }();
+ }
+ return foundDisplay;
+}
+
+bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, bool foundDisplay) {
+ bool isMatch(increment.display_deletion().id() == mTargetId);
+ if (isMatch && !foundDisplay) {
+ foundDisplay = true;
+ }
+ else if (isMatch && foundDisplay) {
+ [] () { FAIL(); }();
+ }
+ return foundDisplay;
+}
+
+bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace,
+ Increment::IncrementCase incrementCase)
+{
+ bool foundIncrement = false;
+ for (const auto& increment : *trace->mutable_increment()) {
+ if (increment.increment_case() == incrementCase) {
+ switch (incrementCase) {
+ case Increment::IncrementCase::kSurfaceCreation:
+ foundIncrement = surfaceCreationFound(increment, foundIncrement);
+ break;
+ case Increment::IncrementCase::kSurfaceDeletion:
+ foundIncrement = surfaceDeletionFound(increment, foundIncrement);
+ break;
+ case Increment::IncrementCase::kDisplayCreation:
+ foundIncrement = displayCreationFound(increment, foundIncrement);
+ break;
+ case Increment::IncrementCase::kDisplayDeletion:
+ foundIncrement = displayDeletionFound(increment, foundIncrement);
+ break;
+ default:
+ /* code */
+ break;
+ }
+ }
+ }
+ return foundIncrement;
+}
+
+bool SurfaceInterceptorTest::bufferUpdatesFound(Trace* trace) {
+ uint32_t updates = 0;
+ for (const auto& inc : *trace->mutable_increment()) {
+ if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
+ updates++;
+ }
+ }
+ return updates == BUFFER_UPDATES;
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::positionUpdate,
+ SurfaceChange::SurfaceChangeCase::kPosition);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptFinalCropUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::finalCropUpdate,
+ SurfaceChange::SurfaceChangeCase::kFinalCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
+ SurfaceChange::SurfaceChangeCase::kOverrideScalingMode);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
+ SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::layerStackUpdate,
+ SurfaceChange::SurfaceChangeCase::kLayerStack);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
+ SurfaceChange::SurfaceChangeCase::kHiddenFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
+ SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
+ SurfaceChange::SurfaceChangeCase::kSecureFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::deferredTransactionUpdate,
+ SurfaceChange::SurfaceChangeCase::kDeferredTransaction);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
+ enableInterceptor();
+ runAllUpdates();
+ disableInterceptor();
+
+ // Find all of the updates in the single trace
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ assertAllUpdatesFound(&capturedTrace);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
+ captureTest(&SurfaceInterceptorTest::surfaceCreation,
+ Increment::IncrementCase::kSurfaceCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSurfaceDeletionWorks) {
+ sp<SurfaceControl> layerToDelete = mComposerClient->createSurface(String8(LAYER_NAME),
+ SIZE_UPDATE, SIZE_UPDATE, PIXEL_FORMAT_RGBA_8888, 0);
+ this->mTargetId = getSurfaceId(LAYER_NAME);
+ enableInterceptor();
+ mComposerClient->destroySurface(layerToDelete->getHandle());
+ disableInterceptor();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceDeletion));
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
+ captureTest(&SurfaceInterceptorTest::displayCreation,
+ Increment::IncrementCase::kDisplayCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
+ captureTest(&SurfaceInterceptorTest::displayDeletion,
+ Increment::IncrementCase::kDisplayDeletion);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::nBufferUpdates,
+ &SurfaceInterceptorTest::bufferUpdatesFound);
+}
+
+// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
+// first create a snapshot of the existing displays and surfaces and then start capturing
+// the buffer updates
+TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
+ std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+ enableInterceptor();
+ disableInterceptor();
+ bufferUpdates.join();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ const auto& firstIncrement = capturedTrace.mutable_increment(0);
+ ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
+ enableInterceptor();
+ std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+ std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
+ runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
+ bufferUpdates.join();
+ surfaceUpdates.join();
+ disableInterceptor();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+
+ assertAllUpdatesFound(&capturedTrace);
+ ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceCreation));
+}
+
+}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index f8d4d13..79cd245 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -61,7 +61,6 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- IGraphicBufferProducer::QueueBufferOutput bufferOutput;
sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sp<IBinder> display(sf->getBuiltInDisplay(
diff --git a/vulkan/api/platform.api b/vulkan/api/platform.api
index 7aa19e7..fb8e3ce 100644
--- a/vulkan/api/platform.api
+++ b/vulkan/api/platform.api
@@ -48,3 +48,5 @@
// VK_USE_PLATFORM_WIN32_KHR
@internal type void* HINSTANCE
@internal type void* HWND
+@internal type void* HANDLE
+@internal class SECURITY_ATTRIBUTES {}
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 870f8eb..df45c20 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 38
// API limits
define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -75,10 +75,16 @@
@extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION 5
@extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME "VK_KHR_win32_surface"
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 5
+@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_NAME "VK_KHR_incremental_present"
+
+@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME "VK_ANDROID_native_buffer"
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 2
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_NAME "VK_GOOGLE_display_timing"
+
+@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 4
@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME "VK_EXT_debug_report"
@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION 1
@@ -93,9 +99,58 @@
@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"
+
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
+@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
+
+@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
+@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
+
+@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
+@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
+
+@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
+@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
+
+@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1
+@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
+
+@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
+
+@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
+@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
+
+@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
+
+@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
+
+@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
+@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
+
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+
+
/////////////
// Types //
@@ -144,6 +199,9 @@
@extension("VK_EXT_debug_report") @nonDispatchHandle type u64 VkDebugReportCallbackEXT
+@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX
+@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX
+
/////////////
// Enums //
@@ -584,6 +642,30 @@
VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+
+ //@extension("VK_IMG_format_pvrtc")
+ VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
}
/// Structure type enumerant
@@ -667,8 +749,15 @@
//@extension("VK_KHR_win32_surface")
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
+ //@extension("VK_KHR_incremental_present")
+ VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
+
//@extension("VK_ANDROID_native_buffer")
VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID = 1000010000,
+ VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID = 1000010001,
+
+ //@extension("VK_GOOGLE_display_timing")
+ VK_STRUCTURE_TYPE_PRESENT_TIMES_GOOGLE = 1000092000,
//@extension("VK_EXT_debug_report")
VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000,
@@ -684,6 +773,81 @@
//@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,
+
+ //@extension("VK_NV_external_memory")
+ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000,
+
+ //@extension("VK_NV_external_memory")
+ VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001,
+
+ //@extension("VK_NV_external_memory_win32")
+ VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000,
+
+ //@extension("VK_NV_external_memory_win32")
+ VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001,
+
+ //@extension("VK_NV_win32_keyed_mutex")
+ VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
+
+ //@extension("VK_KHR_get_physical_device_properties2")
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+
+ //@extension("VK_EXT_validation_flags")
+ VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
+
+ //@extension("VK_KHR_incremental_present")
+ VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
}
enum VkSubpassContents {
@@ -721,6 +885,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
@@ -797,6 +962,10 @@
VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26,
VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27,
VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28,
+ VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29,
+ VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30,
+ VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
+ VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
}
@extension("VK_EXT_debug_report")
@@ -811,6 +980,31 @@
VK_RASTERIZATION_ORDER_RELAXED_AMD = 1,
}
+@extension("VK_EXT_validation_flags")
+enum VkValidationCheckEXT {
+ VK_VALIDATION_CHECK_ALL_EXT = 0,
+}
+
+@extension("VK_NVX_device_generated_commands")
+enum VkIndirectCommandsTokenTypeNVX {
+ VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0,
+ VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1,
+ VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2,
+ VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3,
+ VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4,
+ VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5,
+ VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6,
+ VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7,
+}
+
+@extension("VK_NVX_device_generated_commands")
+enum VkObjectEntryTypeNVX {
+ VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0,
+ VK_OBJECT_ENTRY_PIPELINE_NVX = 1,
+ VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2,
+ VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3,
+ VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4,
+}
/////////////////
// Bitfields //
@@ -861,6 +1055,12 @@
VK_ACCESS_HOST_WRITE_BIT = 0x00004000,
VK_ACCESS_MEMORY_READ_BIT = 0x00008000,
VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000,
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000,
}
/// Buffer usage flags
@@ -1085,6 +1285,9 @@
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, /// All stages of the graphics pipeline
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, /// All graphics, compute, copy, and transition commands
+
+ //@extension("VK_NVX_device_generated_commands")
+ VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000,
}
/// Render pass attachment description flags
@@ -1354,6 +1557,50 @@
VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010,
}
+@extension("VK_ANDROID_native_buffer")
+type VkFlags VkSwapchainImageUsageFlagsANDROID
+@extension("VK_ANDROID_native_buffer")
+bitfield VkSwapchainImageUsageFlagBitsANDROID {
+ VK_SWAPCHAIN_IMAGE_USAGE_FLAGS_FRONT_BUFFER_BIT_ANDROID = 0x00000001,
+}
+
+@extension("VK_NV_external_memory_capabilities")
+type VkFlags VkExternalMemoryHandleTypeFlagsNV
+@extension("VK_NV_external_memory_capabilities")
+bitfield VkExternalMemoryHandleTypeFlagBitsNV {
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001,
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002,
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004,
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008,
+}
+
+@extension("VK_NV_external_memory_capabilities")
+type VkFlags VkExternalMemoryFeatureFlagsNV
+@extension("VK_NV_external_memory_capabilities")
+bitfield VkExternalMemoryFeatureFlagBitsNV {
+ VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001,
+ VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002,
+ VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004,
+}
+
+@extension("VK_NVX_device_generated_commands")
+type VkFlags VkIndirectCommandsLayoutUsageFlagsNVX
+@extension("VK_NVX_device_generated_commands")
+bitfield VkIndirectCommandsLayoutUsageFlagBitsNVX {
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001,
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002,
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004,
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008,
+}
+
+@extension("VK_NVX_device_generated_commands")
+type VkFlags VkObjectEntryUsageFlagsNVX
+@extension("VK_NVX_device_generated_commands")
+bitfield VkObjectEntryUsageFlagBitsNVX {
+ VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001,
+ VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002,
+}
+
//////////////////
// Structures //
@@ -2649,6 +2896,42 @@
int usage
}
+@extension("VK_ANDROID_native_buffer")
+class VkSwapchainImageCreateInfoANDROID {
+ VkStructureType sType
+ const void* pNext
+ VkSwapchainImageUsageFlagsANDROID flags
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkRefreshCycleDurationGOOGLE {
+ u64 minRefreshDuration
+ u64 maxRefreshDuration
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPastPresentationTimingGOOGLE {
+ u32 presentID
+ u64 desiredPresentTime
+ u64 actualPresentTime
+ u64 earliestPresentTime
+ u64 presentMargin
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPresentTimeGOOGLE {
+ u32 presentID
+ u64 desiredPresentTime
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPresentTimesInfoGOOGLE {
+ VkStructureType sType
+ const void* pNext
+ u32 swapchainCount
+ const VkPresentTimeGOOGLE* pTimes
+}
+
@extension("VK_EXT_debug_report")
class VkDebugReportCallbackCreateInfoEXT {
VkStructureType sType
@@ -2693,6 +2976,306 @@
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
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceFeatures2KHR {
+ VkStructureType sType
+ void* pNext
+ VkPhysicalDeviceFeatures features
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceProperties2KHR {
+ VkStructureType sType
+ void* pNext
+ VkPhysicalDeviceProperties properties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkFormatProperties2KHR {
+ VkStructureType sType
+ void* pNext
+ VkFormatProperties formatProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkImageFormatProperties2KHR {
+ VkStructureType sType
+ void* pNext
+ VkImageFormatProperties imageFormatProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceImageFormatInfo2KHR {
+ VkStructureType sType
+ const void* pNext
+ VkFormat format
+ VkImageType type
+ VkImageTiling tiling
+ VkImageUsageFlags usage
+ VkImageCreateFlags flags
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkQueueFamilyProperties2KHR {
+ VkStructureType sType
+ void* pNext
+ VkQueueFamilyProperties queueFamilyProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceMemoryProperties2KHR {
+ VkStructureType sType
+ void* pNext
+ VkPhysicalDeviceMemoryProperties memoryProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkSparseImageFormatProperties2KHR {
+ VkStructureType sType
+ void* pNext
+ VkSparseImageFormatProperties properties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceSparseImageFormatInfo2KHR {
+ VkStructureType sType
+ const void* pNext
+ VkFormat format
+ VkImageType type
+ VkSampleCountFlagBits samples
+ VkImageUsageFlags usage
+ VkImageTiling tiling
+}
+
+@extension("VK_KHR_incremental_present")
+class VkRectLayerKHR {
+ VkOffset2D offset
+ VkExtent2D extent
+ u32 layer
+}
+
+@extension("VK_KHR_incremental_present")
+class VkPresentRegionKHR {
+ u32 rectangleCount
+ const VkRectLayerKHR* pRectangles
+}
+
+@extension("VK_KHR_incremental_present")
+class VkPresentRegionsKHR {
+ VkStructureType sType
+ const void* pNext
+ u32 swapchainCount
+ const VkPresentRegionKHR* pRegions
+}
+
+@extension("VK_NV_external_memory_capabilities")
+class VkExternalImageFormatPropertiesNV {
+ VkImageFormatProperties imageFormatProperties
+ VkExternalMemoryFeatureFlagsNV externalMemoryFeatures
+ VkExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes
+ VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes
+}
+
+@extension("VK_NV_external_memory")
+class VkExternalMemoryImageCreateInfoNV {
+ VkStructureType sType
+ const void* pNext
+ VkExternalMemoryHandleTypeFlagsNV handleTypes
+}
+
+@extension("VK_NV_external_memory")
+class VkExportMemoryAllocateInfoNV {
+ VkStructureType sType
+ const void* pNext
+ VkExternalMemoryHandleTypeFlagsNV handleTypes
+}
+
+@extension("VK_NV_external_memory_win32")
+class VkImportMemoryWin32HandleInfoNV {
+ VkStructureType sType
+ const void* pNext
+ VkExternalMemoryHandleTypeFlagsNV handleType
+ platform.HANDLE handle
+}
+
+@extension("VK_NV_external_memory_win32")
+class VkExportMemoryWin32HandleInfoNV {
+ VkStructureType sType
+ const void* pNext
+ const platform.SECURITY_ATTRIBUTES* pAttributes
+ u32 dwAccess
+}
+
+@extension("VK_NV_win32_keyed_mutex")
+class VkWin32KeyedMutexAcquireReleaseInfoNV {
+ VkStructureType sType
+ const void* pNext
+ u32 acquireCount
+ const VkDeviceMemory* pAcquireSyncs
+ const u64* pAcquireKeys
+ const u32* pAcquireTimeoutMilliseconds
+ u32 releaseCount
+ const VkDeviceMemory* pReleaseSyncs
+ const u64* pReleaseKeys
+}
+
+@extension("VK_EXT_validation_flags")
+class VkValidationFlagsEXT {
+ VkStructureType sType
+ const void* pNext
+ u32 disabledValidationCheckCount
+ VkValidationCheckEXT* pDisabledValidationChecks
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkDeviceGeneratedCommandsFeaturesNVX {
+ VkStructureType sType
+ const void* pNext
+ VkBool32 computeBindingPointSupport
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkDeviceGeneratedCommandsLimitsNVX {
+ VkStructureType sType
+ const void* pNext
+ u32 maxIndirectCommandsLayoutTokenCount
+ u32 maxObjectEntryCounts
+ u32 minSequenceCountBufferOffsetAlignment
+ u32 minSequenceIndexBufferOffsetAlignment
+ u32 minCommandsTokenBufferOffsetAlignment
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkIndirectCommandsTokenNVX {
+ VkIndirectCommandsTokenTypeNVX tokenType
+ VkBuffer buffer
+ VkDeviceSize offset
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkIndirectCommandsLayoutTokenNVX {
+ VkIndirectCommandsTokenTypeNVX tokenType
+ u32 bindingUnit
+ u32 dynamicCount
+ u32 divisor
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkIndirectCommandsLayoutCreateInfoNVX {
+ VkStructureType sType
+ const void* pNext
+ VkPipelineBindPoint pipelineBindPoint
+ VkIndirectCommandsLayoutUsageFlagsNVX flags
+ u32 tokenCount
+ const VkIndirectCommandsLayoutTokenNVX* pTokens
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkCmdProcessCommandsInfoNVX {
+ VkStructureType sType
+ const void* pNext
+ VkObjectTableNVX objectTable
+ VkIndirectCommandsLayoutNVX indirectCommandsLayout
+ u32 indirectCommandsTokenCount
+ const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens
+ u32 maxSequencesCount
+ VkCommandBuffer targetCommandBuffer
+ VkBuffer sequencesCountBuffer
+ VkDeviceSize sequencesCountOffset
+ VkBuffer sequencesIndexBuffer
+ VkDeviceSize sequencesIndexOffset
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkCmdReserveSpaceForCommandsInfoNVX {
+ VkStructureType sType
+ const void* pNext
+ VkObjectTableNVX objectTable
+ VkIndirectCommandsLayoutNVX indirectCommandsLayout
+ u32 maxSequencesCount
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableCreateInfoNVX {
+ VkStructureType sType
+ const void* pNext
+ u32 objectCount
+ const VkObjectEntryTypeNVX* pObjectEntryTypes
+ const u32* pObjectEntryCounts
+ const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags
+ u32 maxUniformBuffersPerDescriptor
+ u32 maxStorageBuffersPerDescriptor
+ u32 maxStorageImagesPerDescriptor
+ u32 maxSampledImagesPerDescriptor
+ u32 maxPipelineLayouts
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableEntryNVX {
+ VkObjectEntryTypeNVX type
+ VkObjectEntryUsageFlagsNVX flags
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTablePipelineEntryNVX {
+ VkObjectEntryTypeNVX type
+ VkObjectEntryUsageFlagsNVX flags
+ VkPipeline pipeline
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableDescriptorSetEntryNVX {
+ VkObjectEntryTypeNVX type
+ VkObjectEntryUsageFlagsNVX flags
+ VkPipelineLayout pipelineLayout
+ VkDescriptorSet descriptorSet
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableVertexBufferEntryNVX {
+ VkObjectEntryTypeNVX type
+ VkObjectEntryUsageFlagsNVX flags
+ VkBuffer buffer
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableIndexBufferEntryNVX {
+ VkObjectEntryTypeNVX type
+ VkObjectEntryUsageFlagsNVX flags
+ VkBuffer buffer
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTablePushConstantEntryNVX {
+ VkObjectEntryTypeNVX type
+ VkObjectEntryUsageFlagsNVX flags
+ VkPipelineLayout pipelineLayout
+ VkShaderStageFlags stageFlags
+}
+
+
////////////////
// Commands //
@@ -4570,7 +5153,7 @@
VkBuffer dstBuffer,
VkDeviceSize dstOffset,
VkDeviceSize dataSize,
- const u32* pData) {
+ const void* pData) {
commandBufferObject := GetCommandBuffer(commandBuffer)
dstBufferObject := GetBuffer(dstBuffer)
assert(commandBufferObject.device == dstBufferObject.device)
@@ -5237,6 +5820,16 @@
}
@extension("VK_ANDROID_native_buffer")
+cmd VkResult vkGetSwapchainGrallocUsage2ANDROID(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+ int* grallocUsage) {
+ return ?
+}
+
+@extension("VK_ANDROID_native_buffer")
cmd VkResult vkAcquireImageANDROID(
VkDevice device,
VkImage image,
@@ -5256,6 +5849,29 @@
return ?
}
+@extension("VK_GOOGLE_display_timing")
+cmd VkResult vkGetRefreshCycleDurationGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ deviceObject := GetDevice(device)
+ swapchainObject := GetSwapchain(swapchain)
+
+ displayTimingProperties := ?
+ pDisplayTimingProperties[0] = displayTimingProperties
+
+ return ?
+}
+
+@extension("VK_GOOGLE_display_timing")
+cmd VkResult vkGetPastPresentationTimingGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ u32* pPresentationTimingCount,
+ VkPastPresentationTimingGOOGLE* pPresentationTimings) {
+ return ?
+}
+
@extension("VK_EXT_debug_report")
@external type void* PFN_vkDebugReportCallbackEXT
@extension("VK_EXT_debug_report")
@@ -5330,6 +5946,169 @@
VkDebugMarkerMarkerInfoEXT* pMarkerInfo) {
}
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceFeatures2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2KHR* pFeatures) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2KHR* pProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceFormatProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2KHR* pFormatProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd VkResult vkGetPhysicalDeviceImageFormatProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo,
+ VkImageFormatProperties2KHR* pImageFormatProperties) {
+ return ?
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceQueueFamilyProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ u32* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2KHR* pQueueFamilyProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceMemoryProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo,
+ u32* pPropertyCount,
+ VkSparseImageFormatProperties2KHR* pProperties) {
+}
+
+@extension("VK_AMD_draw_indirect_count")
+cmd void vkCmdDrawIndirectCountAMD(
+ VkCommandBuffer commandBuffer,
+ VkBuffer buffer,
+ VkDeviceSize offset,
+ VkBuffer countBuffer,
+ VkDeviceSize countBufferOffset,
+ u32 maxDrawCount,
+ u32 stride) {
+}
+
+@extension("VK_AMD_draw_indirect_count")
+cmd void vkCmdDrawIndexedIndirectCountAMD(
+ VkCommandBuffer commandBuffer,
+ VkBuffer buffer,
+ VkDeviceSize offset,
+ VkBuffer countBuffer,
+ VkDeviceSize countBufferOffset,
+ u32 maxDrawCount,
+ u32 stride) {
+}
+
+@extension("VK_NV_external_memory_capabilities")
+cmd VkResult vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkImageType type,
+ VkImageTiling tiling,
+ VkImageUsageFlags usage,
+ VkImageCreateFlags flags,
+ VkExternalMemoryHandleTypeFlagsNV externalHandleType,
+ VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties) {
+ return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult vkGetMemoryWin32HandleNV(
+ VkDevice device,
+ VkDeviceMemory memory,
+ VkExternalMemoryHandleTypeFlagsNV handleType,
+ platform.HANDLE* pHandle) {
+ return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void vkCmdProcessCommandsNVX(
+ VkCommandBuffer commandBuffer,
+ const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void vkCmdReserveSpaceForCommandsNVX(
+ VkCommandBuffer commandBuffer,
+ const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult vkCreateIndirectCommandsLayoutNVX(
+ VkDevice device,
+ const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout) {
+ return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void vkDestroyIndirectCommandsLayoutNVX(
+ VkDevice device,
+ VkIndirectCommandsLayoutNVX indirectCommandsLayout,
+ const VkAllocationCallbacks* pAllocator) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult vkCreateObjectTableNVX(
+ VkDevice device,
+ const VkObjectTableCreateInfoNVX* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkObjectTableNVX* pObjectTable) {
+ return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void vkDestroyObjectTableNVX(
+ VkDevice device,
+ VkObjectTableNVX objectTable,
+ const VkAllocationCallbacks* pAllocator) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult vkRegisterObjectsNVX(
+ VkDevice device,
+ VkObjectTableNVX objectTable,
+ u32 objectCount,
+ const VkObjectTableEntryNVX* const* ppObjectTableEntries,
+ const u32* pObjectIndices) {
+ return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult vkUnregisterObjectsNVX(
+ VkDevice device,
+ VkObjectTableNVX objectTable,
+ u32 objectCount,
+ const VkObjectEntryTypeNVX* pObjectEntryTypes,
+ const u32* pObjectIndices) {
+ return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
+ VkPhysicalDevice physicalDevice,
+ VkDeviceGeneratedCommandsFeaturesNVX* pFeatures,
+ VkDeviceGeneratedCommandsLimitsNVX* pLimits) {
+}
+
////////////////
// Validation //
diff --git a/vulkan/doc/implementors_guide/implementors_guide.adoc b/vulkan/doc/implementors_guide/implementors_guide.adoc
index 7ace777..dc18e9d 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.adoc
+++ b/vulkan/doc/implementors_guide/implementors_guide.adoc
@@ -59,6 +59,28 @@
----
The +format+ and +imageUsage+ parameters are taken from the +VkSwapchainCreateInfoKHR+ structure. The driver should fill +*grallocUsage+ with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.
+Implementations may further need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on +format+ and +imageUsage+, but also on the intended usage of the swapchain. The additional usage bits are defined as
+[source,c]
+----
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+ VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID = 0x00000001,
+ VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
+----
+
+If the driver provides the +vkGetSwapchainGrallocUsage2ANDROID+ function, the platform will use it in preference to +vkGetSwapchainGrallocUsageANDROID+ when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. +vkGetSwapchainGrallocUsage2ANDROID+ behaves in the same way as +vkGetSwapchainGrallocUsageANDROID+, and is declared as
+[source,c]
+----
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+ int* grallocUsage
+);
+----
+
+VkNativeBufferANDROID+ is a +vkCreateImage+ extension structure for creating an image backed by a gralloc buffer. This structure is provided to +vkCreateImage+ in the +VkImageCreateInfo+ structure chain. Calls to +vkCreateImage+ with this structure will happen during the first call to +vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)+. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a +VkImage+ for each one.
[source,c]
@@ -78,6 +100,7 @@
----
When creating a gralloc-backed image, the +VkImageCreateInfo+ will have:
+[source,txt]
----
.imageType = VK_IMAGE_TYPE_2D
.format = a VkFormat matching the format requested for the gralloc buffer
@@ -93,6 +116,17 @@
.pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices
----
+Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a +VkSwapchainImageCreateInfoANDROID+ extension structure in the +VkImageCreateInfo+ chain provided to +vkCreateImage+, containing the swapchain image usage flags:
+[source,c]
+----
+typedef struct {
+ VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+ const void* pNext;
+
+ VkSwapchainImageUsageFlagsANDROID usage;
+} VkSwapchainImageCreateInfoANDROID;
+----
+
+vkAcquireImageANDROID+ acquires ownership of a swapchain image and imports an
externally-signalled native fence into both an existing VkSemaphore object
and an existing VkFence object:
@@ -139,6 +173,8 @@
This will be called during +vkQueuePresentWSI+ on the provided queue. Effects are similar to +vkQueueSignalSemaphore+, except with a native fence instead of a semaphore. The native fence must: not signal until the +waitSemaphoreCount+ semaphores in +pWaitSemaphores+ have signaled. Unlike +vkQueueSignalSemaphore+, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set +*pNativeFenceFd+ to -1. The file descriptor returned in +*pNativeFenceFd+ is owned and will be closed by the caller. Many drivers will be able to ignore the +image+ parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to +VK_IMAGE_LAYOUT_PRESENT_SRC_KHR+.
+If +image+ was created with +VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID+, then the driver must tolerate +vkQueueSignalReleaseImageANDROID+ being called repeatedly without intervening calls to +vkAcquireImageANDROID+.
+
== History ==
. *2015-07-08* Initial version
@@ -158,4 +194,9 @@
. *2016-01-08*
* Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
. *2016-06-17*
- * Updates to reflect final behavior, closed some TBDs now that they've BDed.
\ No newline at end of file
+ * Updates to reflect final behavior, closed some TBDs now that they've BDed.
+. *2017-01-06*
+ * Extension version 6
+ * Added VkSwapchainImageUsageFlagBitsANDROID
+ * Added vkGetSwapchainGrallocUsage2ANDROID
+ * Added VkSwapchainImageCreateInfoANDROID
diff --git a/vulkan/doc/implementors_guide/implementors_guide.html b/vulkan/doc/implementors_guide/implementors_guide.html
index 0bfeb81..ce52c7f 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.html
+++ b/vulkan/doc/implementors_guide/implementors_guide.html
@@ -730,7 +730,7 @@
/*]]>*/
</script>
</head>
-<body class="book">
+<body class="article">
<div id="header">
<h1>Vulkan on Android Implementor’s Guide</h1>
<span id="revnumber">version 5</span>
@@ -795,7 +795,7 @@
<div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions are primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver’s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
<div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -806,9 +806,33 @@
<span style="color: #009900">int</span><span style="color: #990000">*</span> grallocUsage
<span style="color: #990000">);</span></tt></pre></div></div>
<div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
+<div class="paragraph"><p>Implementations may further need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span>, but also on the intended usage of the swapchain. The additional usage bits are defined as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">enum</span></span> VkSwapchainImageUsageFlagBitsANDROID <span style="color: #FF0000">{</span>
+ VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID <span style="color: #990000">=</span> <span style="color: #993399">0x00000001</span><span style="color: #990000">,</span>
+ VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM <span style="color: #990000">=</span> <span style="color: #993399">0x7FFFFFFF</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageUsageFlagBitsANDROID<span style="color: #990000">;</span>
+<span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="color: #008080">VkFlags</span> VkSwapchainImageUsageFlagsANDROID<span style="color: #990000">;</span></tt></pre></div></div>
+<div class="paragraph"><p>If the driver provides the <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> function, the platform will use it in preference to <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span> when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> behaves in the same way as <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span>, and is declared as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>VKAPI_ATTR <span style="color: #008080">VkResult</span> <span style="color: #008080">VKAPI_CALL</span> <span style="font-weight: bold"><span style="color: #000000">vkGetSwapchainGrallocUsage2ANDROID</span></span><span style="color: #990000">(</span>
+ <span style="color: #008080">VkDevice</span> device<span style="color: #990000">,</span>
+ <span style="color: #008080">VkFormat</span> format<span style="color: #990000">,</span>
+ <span style="color: #008080">VkImageUsageFlags</span> imageUsage<span style="color: #990000">,</span>
+ <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> swapchainImageUsage<span style="color: #990000">,</span>
+ <span style="color: #009900">int</span><span style="color: #990000">*</span> grallocUsage
+<span style="color: #990000">);</span></tt></pre></div></div>
<div class="paragraph"><p><span class="monospaced">VkNativeBufferANDROID</span> is a <span class="monospaced">vkCreateImage</span> extension structure for creating an image backed by a gralloc buffer. This structure is provided to <span class="monospaced">vkCreateImage</span> in the <span class="monospaced">VkImageCreateInfo</span> structure chain. Calls to <span class="monospaced">vkCreateImage</span> with this structure will happen during the first call to <span class="monospaced">vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)</span>. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a <span class="monospaced">VkImage</span> for each one.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -826,8 +850,11 @@
<span style="color: #FF0000">}</span> VkNativeBufferANDROID<span style="color: #990000">;</span></tt></pre></div></div>
<div class="paragraph"><p>When creating a gralloc-backed image, the <span class="monospaced">VkImageCreateInfo</span> will have:</p></div>
<div class="listingblock">
-<div class="content monospaced">
-<pre> .imageType = VK_IMAGE_TYPE_2D
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt> .imageType = VK_IMAGE_TYPE_2D
.format = a VkFormat matching the format requested for the gralloc buffer
.extent = the 2D dimensions requested for the gralloc buffer
.mipLevels = 1
@@ -838,13 +865,24 @@
.flags = 0
.sharingMode = VkSwapChainCreateInfoWSI::sharingMode
.queueFamilyCount = VkSwapChainCreateInfoWSI::queueFamilyCount
- .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</pre>
-</div></div>
+ .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</tt></pre></div></div>
+<div class="paragraph"><p>Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a <span class="monospaced">VkSwapchainImageCreateInfoANDROID</span> extension structure in the <span class="monospaced">VkImageCreateInfo</span> chain provided to <span class="monospaced">vkCreateImage</span>, containing the swapchain image usage flags:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
+ <span style="color: #008080">VkStructureType</span> sType<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID</span></span>
+ <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> <span style="color: #009900">void</span><span style="color: #990000">*</span> pNext<span style="color: #990000">;</span>
+
+ <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> usage<span style="color: #990000">;</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageCreateInfoANDROID<span style="color: #990000">;</span></tt></pre></div></div>
<div class="paragraph"><p><span class="monospaced">vkAcquireImageANDROID</span> acquires ownership of a swapchain image and imports an
externally-signalled native fence into both an existing VkSemaphore object
and an existing VkFence object:</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -872,7 +910,7 @@
is as if the native fence was already signalled.</p></div>
<div class="paragraph"><p><span class="monospaced">vkQueueSignalReleaseImageANDROID</span> prepares a swapchain image for external use, and creates a native fence and schedules it to be signalled when prior work on the queue has completed.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
@@ -884,6 +922,7 @@
<span style="color: #009900">int</span><span style="color: #990000">*</span> pNativeFenceFd
<span style="color: #990000">);</span></tt></pre></div></div>
<div class="paragraph"><p>This will be called during <span class="monospaced">vkQueuePresentWSI</span> on the provided queue. Effects are similar to <span class="monospaced">vkQueueSignalSemaphore</span>, except with a native fence instead of a semaphore. The native fence must: not signal until the <span class="monospaced">waitSemaphoreCount</span> semaphores in <span class="monospaced">pWaitSemaphores</span> have signaled. Unlike <span class="monospaced">vkQueueSignalSemaphore</span>, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set <span class="monospaced">*pNativeFenceFd</span> to -1. The file descriptor returned in <span class="monospaced">*pNativeFenceFd</span> is owned and will be closed by the caller. Many drivers will be able to ignore the <span class="monospaced">image</span> parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to <span class="monospaced">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>.</p></div>
+<div class="paragraph"><p>If <span class="monospaced">image</span> was created with <span class="monospaced">VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID</span>, then the driver must tolerate <span class="monospaced">vkQueueSignalReleaseImageANDROID</span> being called repeatedly without intervening calls to <span class="monospaced">vkAcquireImageANDROID</span>.</p></div>
</div>
</div>
<div class="sect1">
@@ -978,6 +1017,33 @@
</li>
</ul></div>
</li>
+<li>
+<p>
+<strong>2017-01-06</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Extension version 6
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageUsageFlagBitsANDROID
+</p>
+</li>
+<li>
+<p>
+Added vkGetSwapchainGrallocUsage2ANDROID
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageCreateInfoANDROID
+</p>
+</li>
+</ul></div>
+</li>
</ol></div>
</div>
</div>
@@ -986,7 +1052,7 @@
<div id="footer">
<div id="footer-text">
Version 5<br>
-Last updated 2016-06-17 13:54:25 PDT
+Last updated 2017-01-12 14:25:30 NZDT
</div>
</div>
</body>
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index d0ebf81..a2ab07b 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -27,11 +27,18 @@
#define VK_ANDROID_native_buffer 1
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 5
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id) ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
+#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
+
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+ VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID = 0x00000001,
+ VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
typedef struct {
VkStructureType sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
@@ -46,7 +53,15 @@
int usage;
} VkNativeBufferANDROID;
+typedef struct {
+ VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+ const void* pNext;
+
+ VkSwapchainImageUsageFlagsANDROID usage;
+} VkSwapchainImageCreateInfoANDROID;
+
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, int* grallocUsage);
typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
@@ -57,6 +72,13 @@
VkImageUsageFlags imageUsage,
int* grallocUsage
);
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+ int* grallocUsage
+);
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
VkDevice device,
VkImage image,
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..6ddce8f 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 38
#define VK_NULL_HANDLE 0
@@ -53,11 +53,13 @@
#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(VK_DEFINE_NON_DISPATCHABLE_HANDLE)
+#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;
#endif
+#endif
@@ -135,6 +137,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 +145,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 +217,32 @@
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_PRESENT_REGIONS_KHR = 1000084000,
+ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000,
+ VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001,
+ VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000,
+ VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001,
+ VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001,
+ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002,
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004,
+ VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006,
+ VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+ VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
+ VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000,
+ VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001,
+ VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002,
+ VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
+ VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
+ VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
+ VK_STRUCTURE_TYPE_PRESENT_TIMES_GOOGLE = 1000092000,
VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -426,6 +455,14 @@
VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
+ VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+ VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+ VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+ VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+ VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+ VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+ VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+ VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED,
VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1),
@@ -898,6 +935,7 @@
VK_PIPELINE_STAGE_HOST_BIT = 0x00004000,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000,
+ VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000,
VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkPipelineStageFlagBits;
typedef VkFlags VkPipelineStageFlags;
@@ -1072,6 +1110,8 @@
VK_ACCESS_HOST_WRITE_BIT = 0x00004000,
VK_ACCESS_MEMORY_READ_BIT = 0x00008000,
VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000,
+ VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000,
+ VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000,
VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkAccessFlagBits;
typedef VkFlags VkAccessFlags;
@@ -2347,7 +2387,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 +3072,7 @@
VkBuffer dstBuffer,
VkDeviceSize dstOffset,
VkDeviceSize dataSize,
- const uint32_t* pData);
+ const void* pData);
VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer(
VkCommandBuffer commandBuffer,
@@ -3712,10 +3752,142 @@
#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge"
+#define VK_KHR_get_physical_device_properties2 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+typedef struct VkPhysicalDeviceFeatures2KHR {
+ VkStructureType sType;
+ void* pNext;
+ VkPhysicalDeviceFeatures features;
+} VkPhysicalDeviceFeatures2KHR;
+
+typedef struct VkPhysicalDeviceProperties2KHR {
+ VkStructureType sType;
+ void* pNext;
+ VkPhysicalDeviceProperties properties;
+} VkPhysicalDeviceProperties2KHR;
+
+typedef struct VkFormatProperties2KHR {
+ VkStructureType sType;
+ void* pNext;
+ VkFormatProperties formatProperties;
+} VkFormatProperties2KHR;
+
+typedef struct VkImageFormatProperties2KHR {
+ VkStructureType sType;
+ void* pNext;
+ VkImageFormatProperties imageFormatProperties;
+} VkImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceImageFormatInfo2KHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkFormat format;
+ VkImageType type;
+ VkImageTiling tiling;
+ VkImageUsageFlags usage;
+ VkImageCreateFlags flags;
+} VkPhysicalDeviceImageFormatInfo2KHR;
+
+typedef struct VkQueueFamilyProperties2KHR {
+ VkStructureType sType;
+ void* pNext;
+ VkQueueFamilyProperties queueFamilyProperties;
+} VkQueueFamilyProperties2KHR;
+
+typedef struct VkPhysicalDeviceMemoryProperties2KHR {
+ VkStructureType sType;
+ void* pNext;
+ VkPhysicalDeviceMemoryProperties memoryProperties;
+} VkPhysicalDeviceMemoryProperties2KHR;
+
+typedef struct VkSparseImageFormatProperties2KHR {
+ VkStructureType sType;
+ void* pNext;
+ VkSparseImageFormatProperties properties;
+} VkSparseImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceSparseImageFormatInfo2KHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkFormat format;
+ VkImageType type;
+ VkSampleCountFlagBits samples;
+ VkImageUsageFlags usage;
+ VkImageTiling tiling;
+} VkPhysicalDeviceSparseImageFormatInfo2KHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2KHR* pFeatures);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2KHR* pProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2KHR* pFormatProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo,
+ VkImageFormatProperties2KHR* pImageFormatProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ uint32_t* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo,
+ uint32_t* pPropertyCount,
+ VkSparseImageFormatProperties2KHR* pProperties);
+#endif
+
+#define VK_KHR_incremental_present 1
+#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+#define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
+
+typedef struct VkRectLayerKHR {
+ VkOffset2D offset;
+ VkExtent2D extent;
+ uint32_t layer;
+} VkRectLayerKHR;
+
+typedef struct VkPresentRegionKHR {
+ uint32_t rectangleCount;
+ const VkRectLayerKHR* pRectangles;
+} VkPresentRegionKHR;
+
+typedef struct VkPresentRegionsKHR {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t swapchainCount;
+ const VkPresentRegionKHR* pRegions;
+} VkPresentRegionsKHR;
+
#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 4
#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
@@ -3750,9 +3922,13 @@
VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26,
VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27,
VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28,
+ VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29,
+ VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30,
+ VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
+ VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
- VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
- VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
+ VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT,
+ VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
} VkDebugReportObjectTypeEXT;
@@ -3855,6 +4031,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 +4098,490 @@
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;
+
+
+#define VK_GOOGLE_display_timing 1
+#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+typedef struct VkRefreshCycleDurationGOOGLE {
+ uint64_t minRefreshDuration;
+ uint64_t maxRefreshDuration;
+} VkRefreshCycleDurationGOOGLE;
+
+typedef struct VkPastPresentationTimingGOOGLE {
+ uint32_t presentID;
+ uint64_t desiredPresentTime;
+ uint64_t actualPresentTime;
+ uint64_t earliestPresentTime;
+ uint64_t presentMargin;
+} VkPastPresentationTimingGOOGLE;
+
+typedef struct VkPresentTimeGOOGLE {
+ uint32_t presentID;
+ uint64_t desiredPresentTime;
+} VkPresentTimeGOOGLE;
+
+typedef struct VkPresentTimesInfoGOOGLE {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t swapchainCount;
+ const VkPresentTimeGOOGLE* pTimes;
+} VkPresentTimesInfoGOOGLE;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ uint32_t* pPresentationTimingCount,
+ VkPastPresentationTimingGOOGLE* pPresentationTimings);
+#endif
+
+
+#define VK_AMD_draw_indirect_count 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
+
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD(
+ VkCommandBuffer commandBuffer,
+ VkBuffer buffer,
+ VkDeviceSize offset,
+ VkBuffer countBuffer,
+ VkDeviceSize countBufferOffset,
+ uint32_t maxDrawCount,
+ uint32_t stride);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD(
+ VkCommandBuffer commandBuffer,
+ VkBuffer buffer,
+ VkDeviceSize offset,
+ VkBuffer countBuffer,
+ VkDeviceSize countBufferOffset,
+ uint32_t maxDrawCount,
+ uint32_t stride);
+#endif
+
+#define VK_AMD_negative_viewport_height 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
+
+
+#define VK_AMD_gpu_shader_half_float 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
+
+
+#define VK_AMD_shader_ballot 1
+#define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
+#define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
+
+
+#define VK_IMG_format_pvrtc 1
+#define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1
+#define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
+
+
+#define VK_NV_external_memory_capabilities 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
+
+
+typedef enum VkExternalMemoryHandleTypeFlagBitsNV {
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001,
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002,
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004,
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008,
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryHandleTypeFlagBitsNV;
+typedef VkFlags VkExternalMemoryHandleTypeFlagsNV;
+
+typedef enum VkExternalMemoryFeatureFlagBitsNV {
+ VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001,
+ VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002,
+ VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004,
+ VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryFeatureFlagBitsNV;
+typedef VkFlags VkExternalMemoryFeatureFlagsNV;
+
+typedef struct VkExternalImageFormatPropertiesNV {
+ VkImageFormatProperties imageFormatProperties;
+ VkExternalMemoryFeatureFlagsNV externalMemoryFeatures;
+ VkExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes;
+ VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes;
+} VkExternalImageFormatPropertiesNV;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkImageType type,
+ VkImageTiling tiling,
+ VkImageUsageFlags usage,
+ VkImageCreateFlags flags,
+ VkExternalMemoryHandleTypeFlagsNV externalHandleType,
+ VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties);
+#endif
+
+#define VK_NV_external_memory 1
+#define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
+
+typedef struct VkExternalMemoryImageCreateInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ VkExternalMemoryHandleTypeFlagsNV handleTypes;
+} VkExternalMemoryImageCreateInfoNV;
+
+typedef struct VkExportMemoryAllocateInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ VkExternalMemoryHandleTypeFlagsNV handleTypes;
+} VkExportMemoryAllocateInfoNV;
+
+
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_NV_external_memory_win32 1
+#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
+
+typedef struct VkImportMemoryWin32HandleInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ VkExternalMemoryHandleTypeFlagsNV handleType;
+ HANDLE handle;
+} VkImportMemoryWin32HandleInfoNV;
+
+typedef struct VkExportMemoryWin32HandleInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ const SECURITY_ATTRIBUTES* pAttributes;
+ DWORD dwAccess;
+} VkExportMemoryWin32HandleInfoNV;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV(
+ VkDevice device,
+ VkDeviceMemory memory,
+ VkExternalMemoryHandleTypeFlagsNV handleType,
+ HANDLE* pHandle);
+#endif
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_NV_win32_keyed_mutex 1
+#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
+
+typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t acquireCount;
+ const VkDeviceMemory* pAcquireSyncs;
+ const uint64_t* pAcquireKeys;
+ const uint32_t* pAcquireTimeoutMilliseconds;
+ uint32_t releaseCount;
+ const VkDeviceMemory* pReleaseSyncs;
+ const uint64_t* pReleaseKeys;
+} VkWin32KeyedMutexAcquireReleaseInfoNV;
+
+
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#define VK_EXT_validation_flags 1
+#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
+#define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
+
+
+typedef enum VkValidationCheckEXT {
+ VK_VALIDATION_CHECK_ALL_EXT = 0,
+ VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+ VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+ VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_ALL_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1),
+ VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkValidationCheckEXT;
+
+typedef struct VkValidationFlagsEXT {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t disabledValidationCheckCount;
+ VkValidationCheckEXT* pDisabledValidationChecks;
+} VkValidationFlagsEXT;
+
+
+
+#define VK_NVX_device_generated_commands 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX)
+
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+
+
+typedef enum VkIndirectCommandsTokenTypeNVX {
+ VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0,
+ VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1,
+ VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2,
+ VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3,
+ VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4,
+ VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5,
+ VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6,
+ VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7,
+ VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX,
+ VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX,
+ VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX + 1),
+ VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsTokenTypeNVX;
+
+typedef enum VkObjectEntryTypeNVX {
+ VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0,
+ VK_OBJECT_ENTRY_PIPELINE_NVX = 1,
+ VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2,
+ VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3,
+ VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4,
+ VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX,
+ VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX,
+ VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX + 1),
+ VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryTypeNVX;
+
+
+typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX {
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001,
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002,
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004,
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008,
+ VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsLayoutUsageFlagBitsNVX;
+typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX;
+
+typedef enum VkObjectEntryUsageFlagBitsNVX {
+ VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001,
+ VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002,
+ VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryUsageFlagBitsNVX;
+typedef VkFlags VkObjectEntryUsageFlagsNVX;
+
+typedef struct VkDeviceGeneratedCommandsFeaturesNVX {
+ VkStructureType sType;
+ const void* pNext;
+ VkBool32 computeBindingPointSupport;
+} VkDeviceGeneratedCommandsFeaturesNVX;
+
+typedef struct VkDeviceGeneratedCommandsLimitsNVX {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t maxIndirectCommandsLayoutTokenCount;
+ uint32_t maxObjectEntryCounts;
+ uint32_t minSequenceCountBufferOffsetAlignment;
+ uint32_t minSequenceIndexBufferOffsetAlignment;
+ uint32_t minCommandsTokenBufferOffsetAlignment;
+} VkDeviceGeneratedCommandsLimitsNVX;
+
+typedef struct VkIndirectCommandsTokenNVX {
+ VkIndirectCommandsTokenTypeNVX tokenType;
+ VkBuffer buffer;
+ VkDeviceSize offset;
+} VkIndirectCommandsTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutTokenNVX {
+ VkIndirectCommandsTokenTypeNVX tokenType;
+ uint32_t bindingUnit;
+ uint32_t dynamicCount;
+ uint32_t divisor;
+} VkIndirectCommandsLayoutTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutCreateInfoNVX {
+ VkStructureType sType;
+ const void* pNext;
+ VkPipelineBindPoint pipelineBindPoint;
+ VkIndirectCommandsLayoutUsageFlagsNVX flags;
+ uint32_t tokenCount;
+ const VkIndirectCommandsLayoutTokenNVX* pTokens;
+} VkIndirectCommandsLayoutCreateInfoNVX;
+
+typedef struct VkCmdProcessCommandsInfoNVX {
+ VkStructureType sType;
+ const void* pNext;
+ VkObjectTableNVX objectTable;
+ VkIndirectCommandsLayoutNVX indirectCommandsLayout;
+ uint32_t indirectCommandsTokenCount;
+ const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens;
+ uint32_t maxSequencesCount;
+ VkCommandBuffer targetCommandBuffer;
+ VkBuffer sequencesCountBuffer;
+ VkDeviceSize sequencesCountOffset;
+ VkBuffer sequencesIndexBuffer;
+ VkDeviceSize sequencesIndexOffset;
+} VkCmdProcessCommandsInfoNVX;
+
+typedef struct VkCmdReserveSpaceForCommandsInfoNVX {
+ VkStructureType sType;
+ const void* pNext;
+ VkObjectTableNVX objectTable;
+ VkIndirectCommandsLayoutNVX indirectCommandsLayout;
+ uint32_t maxSequencesCount;
+} VkCmdReserveSpaceForCommandsInfoNVX;
+
+typedef struct VkObjectTableCreateInfoNVX {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t objectCount;
+ const VkObjectEntryTypeNVX* pObjectEntryTypes;
+ const uint32_t* pObjectEntryCounts;
+ const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags;
+ uint32_t maxUniformBuffersPerDescriptor;
+ uint32_t maxStorageBuffersPerDescriptor;
+ uint32_t maxStorageImagesPerDescriptor;
+ uint32_t maxSampledImagesPerDescriptor;
+ uint32_t maxPipelineLayouts;
+} VkObjectTableCreateInfoNVX;
+
+typedef struct VkObjectTableEntryNVX {
+ VkObjectEntryTypeNVX type;
+ VkObjectEntryUsageFlagsNVX flags;
+} VkObjectTableEntryNVX;
+
+typedef struct VkObjectTablePipelineEntryNVX {
+ VkObjectEntryTypeNVX type;
+ VkObjectEntryUsageFlagsNVX flags;
+ VkPipeline pipeline;
+} VkObjectTablePipelineEntryNVX;
+
+typedef struct VkObjectTableDescriptorSetEntryNVX {
+ VkObjectEntryTypeNVX type;
+ VkObjectEntryUsageFlagsNVX flags;
+ VkPipelineLayout pipelineLayout;
+ VkDescriptorSet descriptorSet;
+} VkObjectTableDescriptorSetEntryNVX;
+
+typedef struct VkObjectTableVertexBufferEntryNVX {
+ VkObjectEntryTypeNVX type;
+ VkObjectEntryUsageFlagsNVX flags;
+ VkBuffer buffer;
+} VkObjectTableVertexBufferEntryNVX;
+
+typedef struct VkObjectTableIndexBufferEntryNVX {
+ VkObjectEntryTypeNVX type;
+ VkObjectEntryUsageFlagsNVX flags;
+ VkBuffer buffer;
+} VkObjectTableIndexBufferEntryNVX;
+
+typedef struct VkObjectTablePushConstantEntryNVX {
+ VkObjectEntryTypeNVX type;
+ VkObjectEntryUsageFlagsNVX flags;
+ VkPipelineLayout pipelineLayout;
+ VkShaderStageFlags stageFlags;
+} VkObjectTablePushConstantEntryNVX;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout);
+typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable);
+typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices);
+typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX(
+ VkCommandBuffer commandBuffer,
+ const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX(
+ VkCommandBuffer commandBuffer,
+ const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX(
+ VkDevice device,
+ const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX(
+ VkDevice device,
+ VkIndirectCommandsLayoutNVX indirectCommandsLayout,
+ const VkAllocationCallbacks* pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX(
+ VkDevice device,
+ const VkObjectTableCreateInfoNVX* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkObjectTableNVX* pObjectTable);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX(
+ VkDevice device,
+ VkObjectTableNVX objectTable,
+ const VkAllocationCallbacks* pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX(
+ VkDevice device,
+ VkObjectTableNVX objectTable,
+ uint32_t objectCount,
+ const VkObjectTableEntryNVX* const* ppObjectTableEntries,
+ const uint32_t* pObjectIndices);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX(
+ VkDevice device,
+ VkObjectTableNVX objectTable,
+ uint32_t objectCount,
+ const VkObjectEntryTypeNVX* pObjectEntryTypes,
+ const uint32_t* pObjectIndices);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
+ VkPhysicalDevice physicalDevice,
+ VkDeviceGeneratedCommandsFeaturesNVX* pFeatures,
+ VkDeviceGeneratedCommandsLimitsNVX* pLimits);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 147cc56..ba883f7 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -47,6 +47,7 @@
"-Wno-c99-extensions",
"-Wno-c++98-compat-pedantic",
"-Wno-exit-time-destructors",
+ "-Wno-float-equal",
"-Wno-global-constructors",
"-Wno-zero-length-array",
],
@@ -66,9 +67,9 @@
export_static_lib_headers: ["vulkan_headers"],
static_libs: [
"vulkan_headers",
- "libziparchive",
],
shared_libs: [
+ "libziparchive",
"libhardware",
"libsync",
"libbase",
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 36755a2..e05ca5a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1050,7 +1050,8 @@
VkResult LayerChain::CreateInstance(const VkInstanceCreateInfo* create_info,
const VkAllocationCallbacks* allocator,
VkInstance* instance_out) {
- LayerChain chain(true, driver::DebugReportLogger(*create_info),
+ const driver::DebugReportLogger logger(*create_info);
+ LayerChain chain(true, logger,
(allocator) ? *allocator : driver::GetDefaultAllocator());
VkResult result = chain.ActivateLayers(create_info->ppEnabledLayerNames,
@@ -1075,8 +1076,9 @@
const VkDeviceCreateInfo* create_info,
const VkAllocationCallbacks* allocator,
VkDevice* dev_out) {
+ const driver::DebugReportLogger logger = driver::Logger(physical_dev);
LayerChain chain(
- false, driver::Logger(physical_dev),
+ false, logger,
(allocator) ? *allocator : driver::GetData(physical_dev).allocator);
VkResult result = chain.ActivateLayers(
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index b72aa0a..b8b7e94 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -31,11 +31,11 @@
#define UNLIKELY(expr) __builtin_expect((expr), 0)
-#define INIT_PROC(obj, proc) \
+#define INIT_PROC(required, obj, proc) \
do { \
data.dispatch.proc = \
reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \
- if (UNLIKELY(!data.dispatch.proc)) { \
+ if (UNLIKELY(required && !data.dispatch.proc)) { \
ALOGE("missing " #obj " proc: vk" #proc); \
success = false; \
} \
@@ -43,10 +43,10 @@
// Exported extension functions may be invoked even when their extensions
// are disabled. Dispatch to stubs when that happens.
-#define INIT_PROC_EXT(ext, obj, proc) \
+#define INIT_PROC_EXT(ext, required, obj, proc) \
do { \
if (extensions[driver::ProcHook::ext]) \
- INIT_PROC(obj, proc); \
+ INIT_PROC(required, obj, proc); \
else \
data.dispatch.proc = disabled##proc; \
} while (0)
@@ -120,24 +120,24 @@
bool success = true;
// clang-format off
- INIT_PROC(instance, DestroyInstance);
- INIT_PROC(instance, EnumeratePhysicalDevices);
- INIT_PROC(instance, GetInstanceProcAddr);
- INIT_PROC(instance, GetPhysicalDeviceProperties);
- INIT_PROC(instance, GetPhysicalDeviceQueueFamilyProperties);
- INIT_PROC(instance, GetPhysicalDeviceMemoryProperties);
- INIT_PROC(instance, GetPhysicalDeviceFeatures);
- INIT_PROC(instance, GetPhysicalDeviceFormatProperties);
- INIT_PROC(instance, GetPhysicalDeviceImageFormatProperties);
- INIT_PROC(instance, CreateDevice);
- INIT_PROC(instance, EnumerateDeviceExtensionProperties);
- INIT_PROC(instance, GetPhysicalDeviceSparseImageFormatProperties);
- INIT_PROC_EXT(KHR_surface, instance, DestroySurfaceKHR);
- INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceSupportKHR);
- INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
- INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceFormatsKHR);
- INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfacePresentModesKHR);
- INIT_PROC_EXT(KHR_android_surface, instance, CreateAndroidSurfaceKHR);
+ INIT_PROC(true, instance, DestroyInstance);
+ INIT_PROC(true, instance, EnumeratePhysicalDevices);
+ INIT_PROC(true, instance, GetInstanceProcAddr);
+ INIT_PROC(true, instance, GetPhysicalDeviceProperties);
+ INIT_PROC(true, instance, GetPhysicalDeviceQueueFamilyProperties);
+ INIT_PROC(true, instance, GetPhysicalDeviceMemoryProperties);
+ INIT_PROC(true, instance, GetPhysicalDeviceFeatures);
+ INIT_PROC(true, instance, GetPhysicalDeviceFormatProperties);
+ INIT_PROC(true, instance, GetPhysicalDeviceImageFormatProperties);
+ INIT_PROC(true, instance, CreateDevice);
+ INIT_PROC(true, instance, EnumerateDeviceExtensionProperties);
+ INIT_PROC(true, instance, GetPhysicalDeviceSparseImageFormatProperties);
+ INIT_PROC_EXT(KHR_surface, true, instance, DestroySurfaceKHR);
+ INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceSupportKHR);
+ INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
+ INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceFormatsKHR);
+ INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfacePresentModesKHR);
+ INIT_PROC_EXT(KHR_android_surface, true, instance, CreateAndroidSurfaceKHR);
// clang-format on
return success;
@@ -151,132 +151,132 @@
bool success = true;
// clang-format off
- INIT_PROC(dev, GetDeviceProcAddr);
- INIT_PROC(dev, DestroyDevice);
- INIT_PROC(dev, GetDeviceQueue);
- INIT_PROC(dev, QueueSubmit);
- INIT_PROC(dev, QueueWaitIdle);
- INIT_PROC(dev, DeviceWaitIdle);
- INIT_PROC(dev, AllocateMemory);
- INIT_PROC(dev, FreeMemory);
- INIT_PROC(dev, MapMemory);
- INIT_PROC(dev, UnmapMemory);
- INIT_PROC(dev, FlushMappedMemoryRanges);
- INIT_PROC(dev, InvalidateMappedMemoryRanges);
- INIT_PROC(dev, GetDeviceMemoryCommitment);
- INIT_PROC(dev, GetBufferMemoryRequirements);
- INIT_PROC(dev, BindBufferMemory);
- INIT_PROC(dev, GetImageMemoryRequirements);
- INIT_PROC(dev, BindImageMemory);
- INIT_PROC(dev, GetImageSparseMemoryRequirements);
- INIT_PROC(dev, QueueBindSparse);
- INIT_PROC(dev, CreateFence);
- INIT_PROC(dev, DestroyFence);
- INIT_PROC(dev, ResetFences);
- INIT_PROC(dev, GetFenceStatus);
- INIT_PROC(dev, WaitForFences);
- INIT_PROC(dev, CreateSemaphore);
- INIT_PROC(dev, DestroySemaphore);
- INIT_PROC(dev, CreateEvent);
- INIT_PROC(dev, DestroyEvent);
- INIT_PROC(dev, GetEventStatus);
- INIT_PROC(dev, SetEvent);
- INIT_PROC(dev, ResetEvent);
- INIT_PROC(dev, CreateQueryPool);
- INIT_PROC(dev, DestroyQueryPool);
- INIT_PROC(dev, GetQueryPoolResults);
- INIT_PROC(dev, CreateBuffer);
- INIT_PROC(dev, DestroyBuffer);
- INIT_PROC(dev, CreateBufferView);
- INIT_PROC(dev, DestroyBufferView);
- INIT_PROC(dev, CreateImage);
- INIT_PROC(dev, DestroyImage);
- INIT_PROC(dev, GetImageSubresourceLayout);
- INIT_PROC(dev, CreateImageView);
- INIT_PROC(dev, DestroyImageView);
- INIT_PROC(dev, CreateShaderModule);
- INIT_PROC(dev, DestroyShaderModule);
- INIT_PROC(dev, CreatePipelineCache);
- INIT_PROC(dev, DestroyPipelineCache);
- INIT_PROC(dev, GetPipelineCacheData);
- INIT_PROC(dev, MergePipelineCaches);
- INIT_PROC(dev, CreateGraphicsPipelines);
- INIT_PROC(dev, CreateComputePipelines);
- INIT_PROC(dev, DestroyPipeline);
- INIT_PROC(dev, CreatePipelineLayout);
- INIT_PROC(dev, DestroyPipelineLayout);
- INIT_PROC(dev, CreateSampler);
- INIT_PROC(dev, DestroySampler);
- INIT_PROC(dev, CreateDescriptorSetLayout);
- INIT_PROC(dev, DestroyDescriptorSetLayout);
- INIT_PROC(dev, CreateDescriptorPool);
- INIT_PROC(dev, DestroyDescriptorPool);
- INIT_PROC(dev, ResetDescriptorPool);
- INIT_PROC(dev, AllocateDescriptorSets);
- INIT_PROC(dev, FreeDescriptorSets);
- INIT_PROC(dev, UpdateDescriptorSets);
- INIT_PROC(dev, CreateFramebuffer);
- INIT_PROC(dev, DestroyFramebuffer);
- INIT_PROC(dev, CreateRenderPass);
- INIT_PROC(dev, DestroyRenderPass);
- INIT_PROC(dev, GetRenderAreaGranularity);
- INIT_PROC(dev, CreateCommandPool);
- INIT_PROC(dev, DestroyCommandPool);
- INIT_PROC(dev, ResetCommandPool);
- INIT_PROC(dev, AllocateCommandBuffers);
- INIT_PROC(dev, FreeCommandBuffers);
- INIT_PROC(dev, BeginCommandBuffer);
- INIT_PROC(dev, EndCommandBuffer);
- INIT_PROC(dev, ResetCommandBuffer);
- INIT_PROC(dev, CmdBindPipeline);
- INIT_PROC(dev, CmdSetViewport);
- INIT_PROC(dev, CmdSetScissor);
- INIT_PROC(dev, CmdSetLineWidth);
- INIT_PROC(dev, CmdSetDepthBias);
- INIT_PROC(dev, CmdSetBlendConstants);
- INIT_PROC(dev, CmdSetDepthBounds);
- INIT_PROC(dev, CmdSetStencilCompareMask);
- INIT_PROC(dev, CmdSetStencilWriteMask);
- INIT_PROC(dev, CmdSetStencilReference);
- INIT_PROC(dev, CmdBindDescriptorSets);
- INIT_PROC(dev, CmdBindIndexBuffer);
- INIT_PROC(dev, CmdBindVertexBuffers);
- INIT_PROC(dev, CmdDraw);
- INIT_PROC(dev, CmdDrawIndexed);
- INIT_PROC(dev, CmdDrawIndirect);
- INIT_PROC(dev, CmdDrawIndexedIndirect);
- INIT_PROC(dev, CmdDispatch);
- INIT_PROC(dev, CmdDispatchIndirect);
- INIT_PROC(dev, CmdCopyBuffer);
- INIT_PROC(dev, CmdCopyImage);
- INIT_PROC(dev, CmdBlitImage);
- INIT_PROC(dev, CmdCopyBufferToImage);
- INIT_PROC(dev, CmdCopyImageToBuffer);
- INIT_PROC(dev, CmdUpdateBuffer);
- INIT_PROC(dev, CmdFillBuffer);
- INIT_PROC(dev, CmdClearColorImage);
- INIT_PROC(dev, CmdClearDepthStencilImage);
- INIT_PROC(dev, CmdClearAttachments);
- INIT_PROC(dev, CmdResolveImage);
- INIT_PROC(dev, CmdSetEvent);
- INIT_PROC(dev, CmdResetEvent);
- INIT_PROC(dev, CmdWaitEvents);
- INIT_PROC(dev, CmdPipelineBarrier);
- INIT_PROC(dev, CmdBeginQuery);
- INIT_PROC(dev, CmdEndQuery);
- INIT_PROC(dev, CmdResetQueryPool);
- INIT_PROC(dev, CmdWriteTimestamp);
- INIT_PROC(dev, CmdCopyQueryPoolResults);
- INIT_PROC(dev, CmdPushConstants);
- INIT_PROC(dev, CmdBeginRenderPass);
- INIT_PROC(dev, CmdNextSubpass);
- INIT_PROC(dev, CmdEndRenderPass);
- INIT_PROC(dev, CmdExecuteCommands);
- INIT_PROC_EXT(KHR_swapchain, dev, CreateSwapchainKHR);
- INIT_PROC_EXT(KHR_swapchain, dev, DestroySwapchainKHR);
- INIT_PROC_EXT(KHR_swapchain, dev, GetSwapchainImagesKHR);
- INIT_PROC_EXT(KHR_swapchain, dev, AcquireNextImageKHR);
- INIT_PROC_EXT(KHR_swapchain, dev, QueuePresentKHR);
+ INIT_PROC(true, dev, GetDeviceProcAddr);
+ INIT_PROC(true, dev, DestroyDevice);
+ INIT_PROC(true, dev, GetDeviceQueue);
+ INIT_PROC(true, dev, QueueSubmit);
+ INIT_PROC(true, dev, QueueWaitIdle);
+ INIT_PROC(true, dev, DeviceWaitIdle);
+ INIT_PROC(true, dev, AllocateMemory);
+ INIT_PROC(true, dev, FreeMemory);
+ INIT_PROC(true, dev, MapMemory);
+ INIT_PROC(true, dev, UnmapMemory);
+ INIT_PROC(true, dev, FlushMappedMemoryRanges);
+ INIT_PROC(true, dev, InvalidateMappedMemoryRanges);
+ INIT_PROC(true, dev, GetDeviceMemoryCommitment);
+ INIT_PROC(true, dev, GetBufferMemoryRequirements);
+ INIT_PROC(true, dev, BindBufferMemory);
+ INIT_PROC(true, dev, GetImageMemoryRequirements);
+ INIT_PROC(true, dev, BindImageMemory);
+ INIT_PROC(true, dev, GetImageSparseMemoryRequirements);
+ INIT_PROC(true, dev, QueueBindSparse);
+ INIT_PROC(true, dev, CreateFence);
+ INIT_PROC(true, dev, DestroyFence);
+ INIT_PROC(true, dev, ResetFences);
+ INIT_PROC(true, dev, GetFenceStatus);
+ INIT_PROC(true, dev, WaitForFences);
+ INIT_PROC(true, dev, CreateSemaphore);
+ INIT_PROC(true, dev, DestroySemaphore);
+ INIT_PROC(true, dev, CreateEvent);
+ INIT_PROC(true, dev, DestroyEvent);
+ INIT_PROC(true, dev, GetEventStatus);
+ INIT_PROC(true, dev, SetEvent);
+ INIT_PROC(true, dev, ResetEvent);
+ INIT_PROC(true, dev, CreateQueryPool);
+ INIT_PROC(true, dev, DestroyQueryPool);
+ INIT_PROC(true, dev, GetQueryPoolResults);
+ INIT_PROC(true, dev, CreateBuffer);
+ INIT_PROC(true, dev, DestroyBuffer);
+ INIT_PROC(true, dev, CreateBufferView);
+ INIT_PROC(true, dev, DestroyBufferView);
+ INIT_PROC(true, dev, CreateImage);
+ INIT_PROC(true, dev, DestroyImage);
+ INIT_PROC(true, dev, GetImageSubresourceLayout);
+ INIT_PROC(true, dev, CreateImageView);
+ INIT_PROC(true, dev, DestroyImageView);
+ INIT_PROC(true, dev, CreateShaderModule);
+ INIT_PROC(true, dev, DestroyShaderModule);
+ INIT_PROC(true, dev, CreatePipelineCache);
+ INIT_PROC(true, dev, DestroyPipelineCache);
+ INIT_PROC(true, dev, GetPipelineCacheData);
+ INIT_PROC(true, dev, MergePipelineCaches);
+ INIT_PROC(true, dev, CreateGraphicsPipelines);
+ INIT_PROC(true, dev, CreateComputePipelines);
+ INIT_PROC(true, dev, DestroyPipeline);
+ INIT_PROC(true, dev, CreatePipelineLayout);
+ INIT_PROC(true, dev, DestroyPipelineLayout);
+ INIT_PROC(true, dev, CreateSampler);
+ INIT_PROC(true, dev, DestroySampler);
+ INIT_PROC(true, dev, CreateDescriptorSetLayout);
+ INIT_PROC(true, dev, DestroyDescriptorSetLayout);
+ INIT_PROC(true, dev, CreateDescriptorPool);
+ INIT_PROC(true, dev, DestroyDescriptorPool);
+ INIT_PROC(true, dev, ResetDescriptorPool);
+ INIT_PROC(true, dev, AllocateDescriptorSets);
+ INIT_PROC(true, dev, FreeDescriptorSets);
+ INIT_PROC(true, dev, UpdateDescriptorSets);
+ INIT_PROC(true, dev, CreateFramebuffer);
+ INIT_PROC(true, dev, DestroyFramebuffer);
+ INIT_PROC(true, dev, CreateRenderPass);
+ INIT_PROC(true, dev, DestroyRenderPass);
+ INIT_PROC(true, dev, GetRenderAreaGranularity);
+ INIT_PROC(true, dev, CreateCommandPool);
+ INIT_PROC(true, dev, DestroyCommandPool);
+ INIT_PROC(true, dev, ResetCommandPool);
+ INIT_PROC(true, dev, AllocateCommandBuffers);
+ INIT_PROC(true, dev, FreeCommandBuffers);
+ INIT_PROC(true, dev, BeginCommandBuffer);
+ INIT_PROC(true, dev, EndCommandBuffer);
+ INIT_PROC(true, dev, ResetCommandBuffer);
+ INIT_PROC(true, dev, CmdBindPipeline);
+ INIT_PROC(true, dev, CmdSetViewport);
+ INIT_PROC(true, dev, CmdSetScissor);
+ INIT_PROC(true, dev, CmdSetLineWidth);
+ INIT_PROC(true, dev, CmdSetDepthBias);
+ INIT_PROC(true, dev, CmdSetBlendConstants);
+ INIT_PROC(true, dev, CmdSetDepthBounds);
+ INIT_PROC(true, dev, CmdSetStencilCompareMask);
+ INIT_PROC(true, dev, CmdSetStencilWriteMask);
+ INIT_PROC(true, dev, CmdSetStencilReference);
+ INIT_PROC(true, dev, CmdBindDescriptorSets);
+ INIT_PROC(true, dev, CmdBindIndexBuffer);
+ INIT_PROC(true, dev, CmdBindVertexBuffers);
+ INIT_PROC(true, dev, CmdDraw);
+ INIT_PROC(true, dev, CmdDrawIndexed);
+ INIT_PROC(true, dev, CmdDrawIndirect);
+ INIT_PROC(true, dev, CmdDrawIndexedIndirect);
+ INIT_PROC(true, dev, CmdDispatch);
+ INIT_PROC(true, dev, CmdDispatchIndirect);
+ INIT_PROC(true, dev, CmdCopyBuffer);
+ INIT_PROC(true, dev, CmdCopyImage);
+ INIT_PROC(true, dev, CmdBlitImage);
+ INIT_PROC(true, dev, CmdCopyBufferToImage);
+ INIT_PROC(true, dev, CmdCopyImageToBuffer);
+ INIT_PROC(true, dev, CmdUpdateBuffer);
+ INIT_PROC(true, dev, CmdFillBuffer);
+ INIT_PROC(true, dev, CmdClearColorImage);
+ INIT_PROC(true, dev, CmdClearDepthStencilImage);
+ INIT_PROC(true, dev, CmdClearAttachments);
+ INIT_PROC(true, dev, CmdResolveImage);
+ INIT_PROC(true, dev, CmdSetEvent);
+ INIT_PROC(true, dev, CmdResetEvent);
+ INIT_PROC(true, dev, CmdWaitEvents);
+ INIT_PROC(true, dev, CmdPipelineBarrier);
+ INIT_PROC(true, dev, CmdBeginQuery);
+ INIT_PROC(true, dev, CmdEndQuery);
+ INIT_PROC(true, dev, CmdResetQueryPool);
+ INIT_PROC(true, dev, CmdWriteTimestamp);
+ INIT_PROC(true, dev, CmdCopyQueryPoolResults);
+ INIT_PROC(true, dev, CmdPushConstants);
+ INIT_PROC(true, dev, CmdBeginRenderPass);
+ INIT_PROC(true, dev, CmdNextSubpass);
+ INIT_PROC(true, dev, CmdEndRenderPass);
+ INIT_PROC(true, dev, CmdExecuteCommands);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, CreateSwapchainKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, DestroySwapchainKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, GetSwapchainImagesKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImageKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, QueuePresentKHR);
// clang-format on
return success;
@@ -396,7 +396,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);
@@ -453,13 +453,22 @@
"vkEnumerateInstanceLayerProperties",
"vkEnumeratePhysicalDevices",
"vkGetInstanceProcAddr",
+ "vkGetPhysicalDeviceExternalImageFormatPropertiesNV",
"vkGetPhysicalDeviceFeatures",
+ "vkGetPhysicalDeviceFeatures2KHR",
"vkGetPhysicalDeviceFormatProperties",
+ "vkGetPhysicalDeviceFormatProperties2KHR",
+ "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
"vkGetPhysicalDeviceImageFormatProperties",
+ "vkGetPhysicalDeviceImageFormatProperties2KHR",
"vkGetPhysicalDeviceMemoryProperties",
+ "vkGetPhysicalDeviceMemoryProperties2KHR",
"vkGetPhysicalDeviceProperties",
+ "vkGetPhysicalDeviceProperties2KHR",
"vkGetPhysicalDeviceQueueFamilyProperties",
+ "vkGetPhysicalDeviceQueueFamilyProperties2KHR",
"vkGetPhysicalDeviceSparseImageFormatProperties",
+ "vkGetPhysicalDeviceSparseImageFormatProperties2KHR",
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
"vkGetPhysicalDeviceSurfaceFormatsKHR",
"vkGetPhysicalDeviceSurfacePresentModesKHR",
@@ -1077,7 +1086,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);
}
@@ -1797,7 +1806,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/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 46333ec..33c5719 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -393,10 +393,10 @@
{{define "C++.DefineInitProcMacro"}}
#define UNLIKELY(expr) __builtin_expect((expr), 0)
¶
- #define INIT_PROC(obj, proc) do { \
+ #define INIT_PROC(required, obj, proc) do { \
data.{{$}}.proc = reinterpret_cast<PFN_vk ## proc>( \
get_proc(obj, "vk" # proc)); \
- if (UNLIKELY(!data.{{$}}.proc)) { \
+ if (UNLIKELY(required && !data.{{$}}.proc)) { \
ALOGE("missing " # obj " proc: vk" # proc); \
success = false; \
} \
@@ -413,10 +413,11 @@
{{AssertType $ "Function"}}
{{$ext := GetAnnotation $ "extension"}}
+ {{$required := (Macro "IsRequiredFunction" $)}}
{{if $ext}}
- INIT_PROC_EXT({{Macro "BaseName" $ext}}, §
+ INIT_PROC_EXT({{Macro "BaseName" $ext}}, {{$required}}, §
{{else}}
- INIT_PROC(§
+ INIT_PROC({{$required}}, §
{{end}}
{{if (Macro "IsInstanceDispatched" $)}}
@@ -431,6 +432,25 @@
{{/*
------------------------------------------------------------------------------
+ Emits true if a function /must/ be resolved. The only time this is not
+ the case is for extension-added functions added in a later revision of the
+ extension, and where we have to cope with drivers written against an older
+ revision.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsRequiredFunction"}}
+ {{AssertType $ "Function"}}
+
+ {{if eq $.Name "vkGetSwapchainGrallocUsage2ANDROID"}}
+ false
+ {{else}}
+ true
+ {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
Emits true if a function is exported and instance-dispatched.
------------------------------------------------------------------------------
*/}}
@@ -494,9 +514,9 @@
{{define "api.C++.DefineInitProcExtMacro"}}
// Exported extension functions may be invoked even when their extensions
// are disabled. Dispatch to stubs when that happens.
- #define INIT_PROC_EXT(ext, obj, proc) do { \
+ #define INIT_PROC_EXT(ext, required, obj, proc) do { \
if (extensions[driver::ProcHook::ext]) \
- INIT_PROC(obj, proc); \
+ INIT_PROC(required, obj, proc); \
else \
data.dispatch.proc = disabled ## proc; \
} while(0)
@@ -681,8 +701,10 @@
VK_ANDROID_native_buffer
VK_EXT_debug_report
VK_KHR_android_surface
+VK_KHR_incremental_present
VK_KHR_surface
VK_KHR_swapchain
+VK_GOOGLE_display_timing
{{end}}
@@ -796,9 +818,9 @@
-------------------------------------------------------------------------------
*/}}
{{define "driver.C++.DefineInitProcExtMacro"}}
- #define INIT_PROC_EXT(ext, obj, proc) do { \
+ #define INIT_PROC_EXT(ext, required, obj, proc) do { \
if (extensions[ProcHook::ext]) \
- INIT_PROC(obj, proc); \
+ INIT_PROC(required, obj, proc); \
} while(0)
{{end}}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 56396f4..800e474 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -663,26 +663,49 @@
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties) {
const InstanceData& data = GetData(physicalDevice);
+ static const std::array<VkExtensionProperties, 1> loader_extensions = {{
+ // WSI extensions
+ {VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
+ VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION},
+ }};
+
+ // enumerate our extensions first
+ if (!pLayerName && pProperties) {
+ uint32_t count = std::min(
+ *pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
+
+ std::copy_n(loader_extensions.begin(), count, pProperties);
+
+ if (count < loader_extensions.size()) {
+ *pPropertyCount = count;
+ return VK_INCOMPLETE;
+ }
+
+ pProperties += count;
+ *pPropertyCount -= count;
+ }
VkResult result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
- if (result != VK_SUCCESS && result != VK_INCOMPLETE)
- return result;
- if (!pProperties)
- return result;
+ if (pProperties) {
+ // map VK_ANDROID_native_buffer to VK_KHR_swapchain
+ for (uint32_t i = 0; i < *pPropertyCount; i++) {
+ auto& prop = pProperties[i];
- // map VK_ANDROID_native_buffer to VK_KHR_swapchain
- for (uint32_t i = 0; i < *pPropertyCount; i++) {
- auto& prop = pProperties[i];
+ if (strcmp(prop.extensionName,
+ VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+ continue;
- if (strcmp(prop.extensionName,
- VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
- continue;
+ memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
+ prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+ }
+ }
- memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
- sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
- prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+ // restore loader extension count
+ if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
+ *pPropertyCount += loader_extensions.size();
}
return result;
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 8cbd398..cfa9e30 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -75,6 +75,24 @@
}
}
+VKAPI_ATTR VkResult checkedGetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+ return GetRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties);
+ } else {
+ Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetRefreshCycleDurationGOOGLE not executed.");
+ return VK_SUCCESS;
+ }
+}
+
+VKAPI_ATTR VkResult checkedGetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) {
+ if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+ return GetPastPresentationTimingGOOGLE(device, swapchain, pPresentationTimingCount, pPresentationTimings);
+ } else {
+ Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetPastPresentationTimingGOOGLE not executed.");
+ return VK_SUCCESS;
+ }
+}
+
// clang-format on
const ProcHook g_proc_hooks[] = {
@@ -220,6 +238,13 @@
nullptr,
},
{
+ "vkGetPastPresentationTimingGOOGLE",
+ ProcHook::DEVICE,
+ ProcHook::GOOGLE_display_timing,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPastPresentationTimingGOOGLE),
+ reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
+ },
+ {
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
ProcHook::INSTANCE,
ProcHook::KHR_surface,
@@ -248,6 +273,20 @@
nullptr,
},
{
+ "vkGetRefreshCycleDurationGOOGLE",
+ ProcHook::DEVICE,
+ ProcHook::GOOGLE_display_timing,
+ reinterpret_cast<PFN_vkVoidFunction>(GetRefreshCycleDurationGOOGLE),
+ reinterpret_cast<PFN_vkVoidFunction>(checkedGetRefreshCycleDurationGOOGLE),
+ },
+ {
+ "vkGetSwapchainGrallocUsage2ANDROID",
+ ProcHook::DEVICE,
+ ProcHook::ANDROID_native_buffer,
+ nullptr,
+ nullptr,
+ },
+ {
"vkGetSwapchainGrallocUsageANDROID",
ProcHook::DEVICE,
ProcHook::ANDROID_native_buffer,
@@ -295,28 +334,30 @@
if (strcmp(name, "VK_ANDROID_native_buffer") == 0) return ProcHook::ANDROID_native_buffer;
if (strcmp(name, "VK_EXT_debug_report") == 0) return ProcHook::EXT_debug_report;
if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
+ if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
+ if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
// clang-format on
return ProcHook::EXTENSION_UNKNOWN;
}
#define UNLIKELY(expr) __builtin_expect((expr), 0)
-#define INIT_PROC(obj, proc) \
+#define INIT_PROC(required, obj, proc) \
do { \
data.driver.proc = \
reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \
- if (UNLIKELY(!data.driver.proc)) { \
+ if (UNLIKELY(required && !data.driver.proc)) { \
ALOGE("missing " #obj " proc: vk" #proc); \
success = false; \
} \
} while (0)
-#define INIT_PROC_EXT(ext, obj, proc) \
- do { \
- if (extensions[ProcHook::ext]) \
- INIT_PROC(obj, proc); \
+#define INIT_PROC_EXT(ext, required, obj, proc) \
+ do { \
+ if (extensions[ProcHook::ext]) \
+ INIT_PROC(required, obj, proc); \
} while (0)
bool InitDriverTable(VkInstance instance,
@@ -326,14 +367,14 @@
bool success = true;
// clang-format off
- INIT_PROC(instance, DestroyInstance);
- INIT_PROC(instance, EnumeratePhysicalDevices);
- INIT_PROC(instance, GetInstanceProcAddr);
- INIT_PROC(instance, CreateDevice);
- INIT_PROC(instance, EnumerateDeviceExtensionProperties);
- INIT_PROC_EXT(EXT_debug_report, instance, CreateDebugReportCallbackEXT);
- INIT_PROC_EXT(EXT_debug_report, instance, DestroyDebugReportCallbackEXT);
- INIT_PROC_EXT(EXT_debug_report, instance, DebugReportMessageEXT);
+ INIT_PROC(true, instance, DestroyInstance);
+ INIT_PROC(true, instance, EnumeratePhysicalDevices);
+ INIT_PROC(true, instance, GetInstanceProcAddr);
+ INIT_PROC(true, instance, CreateDevice);
+ INIT_PROC(true, instance, EnumerateDeviceExtensionProperties);
+ INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
+ INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
+ INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
// clang-format on
return success;
@@ -346,15 +387,16 @@
bool success = true;
// clang-format off
- INIT_PROC(dev, GetDeviceProcAddr);
- INIT_PROC(dev, DestroyDevice);
- INIT_PROC(dev, GetDeviceQueue);
- INIT_PROC(dev, CreateImage);
- INIT_PROC(dev, DestroyImage);
- INIT_PROC(dev, AllocateCommandBuffers);
- INIT_PROC_EXT(ANDROID_native_buffer, dev, GetSwapchainGrallocUsageANDROID);
- INIT_PROC_EXT(ANDROID_native_buffer, dev, AcquireImageANDROID);
- INIT_PROC_EXT(ANDROID_native_buffer, dev, QueueSignalReleaseImageANDROID);
+ INIT_PROC(true, dev, GetDeviceProcAddr);
+ INIT_PROC(true, dev, DestroyDevice);
+ INIT_PROC(true, dev, GetDeviceQueue);
+ INIT_PROC(true, dev, CreateImage);
+ INIT_PROC(true, dev, DestroyImage);
+ INIT_PROC(true, dev, AllocateCommandBuffers);
+ INIT_PROC_EXT(ANDROID_native_buffer, true, dev, GetSwapchainGrallocUsageANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
// clang-format on
return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index a60b2fe..fa81958 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -36,8 +36,10 @@
ANDROID_native_buffer,
EXT_debug_report,
KHR_android_surface,
+ KHR_incremental_present,
KHR_surface,
KHR_swapchain,
+ GOOGLE_display_timing,
EXTENSION_CORE, // valid bit
EXTENSION_COUNT,
@@ -74,6 +76,7 @@
PFN_vkDestroyImage DestroyImage;
PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
+ PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
PFN_vkAcquireImageANDROID AcquireImageANDROID;
PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
// clang-format on
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 6e44126..05856d3 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -69,7 +69,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/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index bfe7aa7..296be2d 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -122,10 +122,13 @@
struct Swapchain {
Swapchain(Surface& surface_, uint32_t num_images_)
- : surface(surface_), num_images(num_images_) {}
+ : surface(surface_),
+ num_images(num_images_),
+ frame_timestamps_enabled(false) {}
Surface& surface;
uint32_t num_images;
+ bool frame_timestamps_enabled;
struct Image {
Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
@@ -327,7 +330,6 @@
// TODO(jessehall): I think these are right, but haven't thought hard about
// it. Do we need to query the driver for support of any of these?
// Currently not included:
- // - VK_IMAGE_USAGE_GENERAL: maybe? does this imply cpu mappable?
// - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
// - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
capabilities->supportedUsageFlags =
@@ -584,9 +586,17 @@
return VK_ERROR_INITIALIZATION_FAILED;
}
+ VkSwapchainImageUsageFlagsANDROID swapchain_image_usage = 0;
int gralloc_usage = 0;
- // TODO(jessehall): Remove conditional once all drivers have been updated
- if (dispatch.GetSwapchainGrallocUsageANDROID) {
+ if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+ result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ swapchain_image_usage, &gralloc_usage);
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
result = dispatch.GetSwapchainGrallocUsageANDROID(
device, create_info->imageFormat, create_info->imageUsage,
&gralloc_usage);
@@ -629,12 +639,20 @@
// -- Dequeue all buffers and create a VkImage for each --
// Any failures during or after this must cancel the dequeued buffers.
+ VkSwapchainImageCreateInfoANDROID swapchain_image_create = {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
+#pragma clang diagnostic pop
+ .pNext = nullptr,
+ .usage = swapchain_image_usage,
+ };
VkNativeBufferANDROID image_native_buffer = {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
.sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
#pragma clang diagnostic pop
- .pNext = nullptr,
+ .pNext = &swapchain_image_create,
};
VkImageCreateInfo image_create = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -728,6 +746,9 @@
bool active = swapchain->surface.swapchain_handle == swapchain_handle;
ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
+ if (swapchain->frame_timestamps_enabled) {
+ native_window_enable_frame_timestamps(window, false);
+ }
for (uint32_t i = 0; i < swapchain->num_images; i++)
ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
if (active)
@@ -862,17 +883,55 @@
ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
"vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
present_info->sType);
- ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
VkDevice device = GetData(queue).driver_device;
const auto& dispatch = GetData(queue).driver;
VkResult final_result = VK_SUCCESS;
+ // Look at the pNext chain for supported extension structs:
+ const VkPresentRegionsKHR* present_regions = nullptr;
+ const VkPresentTimesInfoGOOGLE* present_times = nullptr;
+ const VkPresentRegionsKHR* next =
+ reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext);
+ while (next) {
+ switch (next->sType) {
+ case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
+ present_regions = next;
+ break;
+ case VK_STRUCTURE_TYPE_PRESENT_TIMES_GOOGLE:
+ present_times =
+ reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next);
+ break;
+ default:
+ ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x",
+ next->sType);
+ break;
+ }
+ next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext);
+ }
+ ALOGV_IF(
+ present_regions &&
+ present_regions->swapchainCount != present_info->swapchainCount,
+ "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount");
+ ALOGV_IF(present_times &&
+ present_times->swapchainCount != present_info->swapchainCount,
+ "VkPresentTimesInfoGOOGLE::swapchainCount != "
+ "VkPresentInfo::swapchainCount");
+ const VkPresentRegionKHR* regions =
+ (present_regions) ? present_regions->pRegions : nullptr;
+ const VkPresentTimeGOOGLE* times =
+ (present_times) ? present_times->pTimes : nullptr;
+ const VkAllocationCallbacks* allocator = &GetData(device).allocator;
+ android_native_rect_t* rects = nullptr;
+ uint32_t nrects = 0;
+
for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
Swapchain& swapchain =
*SwapchainFromHandle(present_info->pSwapchains[sc]);
uint32_t image_idx = present_info->pImageIndices[sc];
Swapchain::Image& img = swapchain.images[image_idx];
+ const VkPresentRegionKHR* region = (regions) ? ®ions[sc] : nullptr;
+ const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr;
VkResult swapchain_result = VK_SUCCESS;
VkResult result;
int err;
@@ -890,6 +949,58 @@
present_info->pSwapchains[sc]) {
ANativeWindow* window = swapchain.surface.window.get();
if (swapchain_result == VK_SUCCESS) {
+ if (region) {
+ // Process the incremental-present hint for this swapchain:
+ uint32_t rcount = region->rectangleCount;
+ if (rcount > nrects) {
+ android_native_rect_t* new_rects =
+ static_cast<android_native_rect_t*>(
+ allocator->pfnReallocation(
+ allocator->pUserData, rects,
+ sizeof(android_native_rect_t) * rcount,
+ alignof(android_native_rect_t),
+ VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
+ if (new_rects) {
+ rects = new_rects;
+ nrects = rcount;
+ } else {
+ rcount = 0; // Ignore the hint for this swapchain
+ }
+ }
+ for (uint32_t r = 0; r < rcount; ++r) {
+ if (region->pRectangles[r].layer > 0) {
+ ALOGV(
+ "vkQueuePresentKHR ignoring invalid layer "
+ "(%u); using layer 0 instead",
+ region->pRectangles[r].layer);
+ }
+ int x = region->pRectangles[r].offset.x;
+ int y = region->pRectangles[r].offset.y;
+ int width = static_cast<int>(
+ region->pRectangles[r].extent.width);
+ int height = static_cast<int>(
+ region->pRectangles[r].extent.height);
+ android_native_rect_t* cur_rect = &rects[r];
+ cur_rect->left = x;
+ cur_rect->top = y + height;
+ cur_rect->right = x + width;
+ cur_rect->bottom = y;
+ }
+ native_window_set_surface_damage(window, rects, rcount);
+ }
+ if (time) {
+ if (!swapchain.frame_timestamps_enabled) {
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+ // TODO(ianelliott): need to store the presentID (and
+ // desiredPresentTime), so it can be later correlated to
+ // this present. Probably modify the following function
+ // (and below) to plumb a path to store it in FrameEvents
+ // code, on the producer side.
+ native_window_set_buffers_timestamp(
+ window, static_cast<int64_t>(time->desiredPresentTime));
+ }
err = window->queueBuffer(window, img.buffer.get(), fence);
// queueBuffer always closes fence, even on error
if (err != 0) {
@@ -920,9 +1031,51 @@
if (swapchain_result != final_result)
final_result = WorstPresentResult(final_result, swapchain_result);
}
+ if (rects) {
+ allocator->pfnFree(allocator->pUserData, rects);
+ }
return final_result;
}
+VKAPI_ATTR
+VkResult GetRefreshCycleDurationGOOGLE(
+ VkDevice,
+ VkSwapchainKHR,
+ VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ VkResult result = VK_SUCCESS;
+
+ // TODO(ianelliott): FULLY IMPLEMENT THIS FUNCTION!!!
+ pDisplayTimingProperties->minRefreshDuration = 16666666666;
+ pDisplayTimingProperties->maxRefreshDuration = 16666666666;
+
+ return result;
+}
+
+VKAPI_ATTR
+VkResult GetPastPresentationTimingGOOGLE(
+ VkDevice,
+ VkSwapchainKHR swapchain_handle,
+ uint32_t* count,
+ VkPastPresentationTimingGOOGLE* timings) {
+ Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+ ANativeWindow* window = swapchain.surface.window.get();
+ VkResult result = VK_SUCCESS;
+
+ if (!swapchain.frame_timestamps_enabled) {
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+
+ // TODO(ianelliott): FULLY IMPLEMENT THIS FUNCTION!!!
+ if (timings) {
+ *count = 0;
+ } else {
+ *count = 0;
+ }
+
+ return result;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
index 2c60c49..8aac427 100644
--- a/vulkan/libvulkan/swapchain.h
+++ b/vulkan/libvulkan/swapchain.h
@@ -34,6 +34,8 @@
VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint32_t* count, VkImage* images);
VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* image_index);
VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info);
+VKAPI_ATTR VkResult GetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+VKAPI_ATTR VkResult GetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
// clang-format on
} // namespace driver
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 4ac994b..89c65af 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -887,6 +887,16 @@
return VK_SUCCESS;
}
+VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice,
+ VkFormat,
+ VkImageUsageFlags,
+ VkSwapchainImageUsageFlagsANDROID,
+ int* grallocUsage) {
+ // The null driver never reads or writes the gralloc buffer
+ *grallocUsage = 0;
+ return VK_SUCCESS;
+}
+
VkResult AcquireImageANDROID(VkDevice,
VkImage,
int fence,
@@ -1335,7 +1345,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..5ae31a2 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,13 @@
{"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))},
+ {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
+ {"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 +210,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..25a4371 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,10 @@
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 GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, 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);
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 801eca8..89bc926 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -176,7 +176,7 @@
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = 0,
.queueCount = 1,
- queue_priorities
+ .pQueuePriorities = queue_priorities
};
// clang-format off
const char *kValidationLayers[] = {