Merge "Add native interface for activity manager service."
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index a217c5d..b6f6adc 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -525,14 +525,10 @@
static void pokeHalServices()
{
using ::android::hidl::manager::V1_0::IServiceManager;
- using ::android::hardware::IBinder;
using ::android::hardware::hidl_string;
- using ::android::hardware::Parcel;
-
- Parcel data;
sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
- sm->list([&](const auto &interfaces) {
+ auto listRet = sm->list([&](const auto &interfaces) {
for (size_t i = 0; i < interfaces.size(); i++) {
string fqInstanceName = interfaces[i];
string::size_type n = fqInstanceName.find("/");
@@ -540,13 +536,24 @@
continue;
hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
- sm->get(fqInterfaceName, instanceName, [&](const auto &interface) {
- // TODO(b/32756130)
- // Once IServiceManager returns IBase, use interface->notifySyspropsChanged() here
- interface->transact(IBinder::SYSPROPS_TRANSACTION, data, nullptr, 0, nullptr);
+ auto getRet = sm->get(fqInterfaceName, instanceName, [&](const auto &interface) {
+ auto notifyRet = interface->notifySyspropsChanged();
+ if (!notifyRet.isOk()) {
+ fprintf(stderr, "failed to notifySyspropsChanged on service %s: %s\n",
+ fqInstanceName.c_str(),
+ notifyRet.getStatus().toString8().string());
+ }
});
+ if (!getRet.isOk()) {
+ fprintf(stderr, "failed to get service %s: %s\n",
+ fqInstanceName.c_str(),
+ getRet.getStatus().toString8().string());
+ }
}
});
+ if (!listRet.isOk()) {
+ fprintf(stderr, "failed to list services: %s\n", listRet.getStatus().toString8().string());
+ }
}
// Set the trace tags that userland tracing uses, and poke the running
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 7edc7de..17b411f 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -4,7 +4,7 @@
# Common settings #
# ================#
# ZipArchive support, the order matters here to get all symbols.
-COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto_static
+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
@@ -12,6 +12,7 @@
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 \
@@ -22,10 +23,10 @@
libhardware_legacy \
liblog \
libselinux \
- libutils
-COMMON_STATIC_LIBRARIES := \
- libdumpstateutil \
+ libutils \
$(COMMON_ZIP_LIBRARIES)
+COMMON_STATIC_LIBRARIES := \
+ libdumpstateutil
# ====================#
# libdumpstateutil #
@@ -38,10 +39,10 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
LOCAL_SRC_FILES := \
- utils.cpp # TODO: temporary, until functions are moved to DumpstateUtil.cpp
-# TODO: include just what it uses (libbase, libcutils, etc...) once split from utils.cpp
-LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
-LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES)
+ DumpstateInternal.cpp \
+ DumpstateUtil.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbase
include $(BUILD_STATIC_LIBRARY)
@@ -151,19 +152,32 @@
LOCAL_SRC_FILES := \
tests/dumpstate_test_fixture.cpp
-dist_zip_root := $(TARGET_OUT_DATA)
+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 := $(dist_zip_root)/$(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)
-LOCAL_PICKUP_FILES := $(dist_zip_root)
+
+# 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..2d74377
--- /dev/null
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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_WAKELOCK, 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) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective =
+ (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ 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
index f2dcf2b..5430956 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -22,6 +22,8 @@
#include "android/os/BnDumpstate.h"
+#include "DumpstateInternal.h"
+
namespace android {
namespace os {
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
new file mode 100644
index 0000000..9faa63e
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -0,0 +1,344 @@
+/*
+ * 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 <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/properties.h>
+#include <cutils/log.h>
+
+#include "DumpstateInternal.h"
+
+// TODO: move to unnamed namespace
+static constexpr const char* kSuPath = "/system/xbin/su";
+
+// TODO: move to unnamed namespace
+static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
+ sigset_t child_mask, old_mask;
+ sigemptyset(&child_mask);
+ 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;
+}
+
+CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
+CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build();
+CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
+
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
+}
+
+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;
+}
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index 42a91db..8bda6f2 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -16,12 +16,15 @@
#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
+#include <cstdint>
+#include <string>
+
// TODO: use android::os::dumpstate (must wait until device code is refactored)
/*
- * Defines the Linux user that should be executing a command.
+ * Defines the Linux account that should be executing a command.
*/
-enum RootMode {
+enum PrivilegeMode {
/* Explicitly change the `uid` and `gid` to be `shell`.*/
DROP_ROOT,
/* Don't change the `uid` and `gid`. */
@@ -31,12 +34,12 @@
};
/*
- * Defines what should happen with the `stdout` stream of a command.
+ * Defines what should happen with the main output stream (`stdout` or fd) of a command.
*/
-enum StdoutMode {
- /* Don't change `stdout`. */
- NORMAL_STDOUT,
- /* Redirect `stdout` to `stderr`. */
+enum OutputMode {
+ /* Don't change main output. */
+ NORMAL_OUTPUT,
+ /* Redirect main output to `stderr`. */
REDIRECT_TO_STDERR
};
@@ -65,8 +68,8 @@
int64_t timeout_;
bool always_;
- RootMode root_mode_;
- StdoutMode stdout_mode_;
+ PrivilegeMode account_mode_;
+ OutputMode output_mode_;
std::string logging_message_;
friend class CommandOptions;
@@ -82,11 +85,11 @@
public:
/* Sets the command to always run, even on `dry-run` mode. */
CommandOptionsBuilder& Always();
- /* Sets the command's RootMode as `SU_ROOT` */
+ /* Sets the command's PrivilegeMode as `SU_ROOT` */
CommandOptionsBuilder& AsRoot();
- /* Sets the command's RootMode as `DROP_ROOT` */
+ /* Sets the command's PrivilegeMode as `DROP_ROOT` */
CommandOptionsBuilder& DropRoot();
- /* Sets the command's StdoutMode `REDIRECT_TO_STDERR` */
+ /* 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`. */
@@ -104,10 +107,10 @@
int64_t Timeout() const;
/* Checks whether the command should always be run, even on dry-run mode. */
bool Always() const;
- /** Gets the RootMode of the command. */
- RootMode RootMode() const;
- /** Gets the StdoutMode of the command. */
- StdoutMode StdoutMode() const;
+ /** Gets the 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;
@@ -116,31 +119,59 @@
// Common options.
static CommandOptions DEFAULT;
+ static CommandOptions AS_ROOT;
+
+ // TODO: temporary, until device implementations use AS_ROOT
static CommandOptions AS_ROOT_5;
- static CommandOptions AS_ROOT_10;
- static CommandOptions AS_ROOT_20;
+};
+
+/*
+ * 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.
+ * Must contain at least one element.
* |options| optional argument defining the command's behavior.
- * |description| optional description of the command to be used on log messages. If empty,
- * the command path (without arguments) will be used instead.
*/
-int RunCommandToFd(int fd, const std::vector<const char*>& full_command,
- const CommandOptions& options = CommandOptions::DEFAULT,
- const std::string& description = "");
+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& path);
+int DumpFileToFd(int fd, const std::string& title, const std::string& path);
#endif // FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8879ab8..a0b0426 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>
@@ -49,6 +50,7 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "DumpstateInternal.h"
#include "DumpstateService.h"
#include "dumpstate.h"
@@ -99,9 +101,6 @@
static int DumpFile(const std::string& title, const std::string& path) {
return ds.DumpFile(title, path);
}
-bool IsUserBuild() {
- return ds.IsUserBuild();
-}
// Relative directory (inside the zip) for all files copied as-is into the bugreport.
static const std::string ZIP_ROOT_DIR = "FS";
@@ -110,6 +109,8 @@
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]) {
@@ -159,7 +160,7 @@
if (!ds.IsZipping()) return;
std::string title = "MOUNT INFO";
mount_points.clear();
- DurationReporter duration_reporter(title, nullptr);
+ 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());
}
@@ -359,7 +360,7 @@
}
static void dump_raft() {
- if (IsUserBuild()) {
+ if (PropertiesHelper::IsUserBuild()) {
return;
}
@@ -430,7 +431,7 @@
static void DumpModemLogs() {
DurationReporter durationReporter("DUMP MODEM LOGS");
- if (IsUserBuild()) {
+ if (PropertiesHelper::IsUserBuild()) {
return;
}
@@ -638,11 +639,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 */
@@ -685,12 +685,11 @@
printf("Network: %s\n", network.c_str());
printf("Kernel: ");
- fflush(stdout);
- DumpFileToFd(STDOUT_FILENO, "/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=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
- dry_run_, args_.c_str(), extra_options_.c_str());
+ PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
printf("\n");
}
@@ -778,7 +777,7 @@
return;
}
MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
- DurationReporter duration_reporter(dir, nullptr);
+ DurationReporter duration_reporter(dir, true);
dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
}
@@ -915,7 +914,7 @@
DumpFile("MEMORY INFO", "/proc/meminfo");
RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
"pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
- RunCommand("PROCRANK", {"procrank"}, CommandOptions::AS_ROOT_20);
+ RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20);
DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
DumpFile("SLAB INFO", "/proc/slabinfo");
@@ -930,7 +929,7 @@
RunCommand("PROCESSES AND THREADS",
{"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
- RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT_10);
+ RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
RunCommand("PRINTENV", {"printenv"});
RunCommand("NETSTAT", {"netstat", "-nW"});
@@ -943,7 +942,7 @@
do_dmesg();
- RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT_10);
+ 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)");
@@ -1046,7 +1045,7 @@
#ifdef FWDUMP_bcmdhd
RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT_5);
- RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
+ RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, AS_ROOT_20);
RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
@@ -1057,9 +1056,9 @@
CommandOptions::WithTimeout(10).Build());
#ifdef FWDUMP_bcmdhd
- RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, CommandOptions::AS_ROOT_20);
+ RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, AS_ROOT_20);
- RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
+ RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, AS_ROOT_20);
RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
#endif
@@ -1104,7 +1103,7 @@
// root can run on user builds.
CommandOptions::CommandOptionsBuilder options =
CommandOptions::WithTimeout(rilDumpstateTimeout);
- if (!IsUserBuild()) {
+ if (!PropertiesHelper::IsUserBuild()) {
options.AsRoot();
}
RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
@@ -1164,7 +1163,6 @@
printf("========================================================\n");
printf("== Board\n");
printf("========================================================\n");
- fflush(stdout);
android::sp<android::hardware::dumpstate::V1_0::IDumpstateDevice> dumpstate_device(
android::hardware::dumpstate::V1_0::IDumpstateDevice::getService("DumpstateDevice"));
@@ -1198,9 +1196,11 @@
}
handle->data[0] = fd;
+ // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call.
dumpstate_device->dumpstateBoard(handle);
AddZipEntry("dumpstate-board.txt", path);
+ printf("*** See dumpstate-board.txt entry ***\n");
native_handle_close(handle);
native_handle_delete(handle);
@@ -1495,7 +1495,7 @@
}
}
- if (ds.IsDryRun()) {
+ if (PropertiesHelper::IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
@@ -1627,6 +1627,10 @@
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.
@@ -1657,7 +1661,7 @@
ds.AddDir(RECOVERY_DIR, true);
ds.AddDir(RECOVERY_DATA_DIR, true);
ds.AddDir(LOGPERSIST_DATA_DIR, false);
- if (!IsUserBuild()) {
+ if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(PROFILE_DATA_DIR_CUR, true);
ds.AddDir(PROFILE_DATA_DIR_REF, true);
}
@@ -1671,7 +1675,7 @@
// Run ss as root so we can see socket marks.
RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build());
- if (!drop_root_user()) {
+ if (!DropRootUser()) {
return -1;
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 969348f..fcf0683 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -17,18 +17,6 @@
#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
-#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
-
#include <time.h>
#include <unistd.h>
#include <stdbool.h>
@@ -63,18 +51,13 @@
*/
class DurationReporter {
public:
- DurationReporter(const std::string& title);
- DurationReporter(const std::string& title, FILE* out);
+ DurationReporter(const std::string& title, bool log_only = false);
~DurationReporter();
- static uint64_t Nanotime();
-
private:
- // TODO: use std::string for title, once dump_files() and other places that pass a char* are
- // refactored as well.
std::string title_;
- FILE* out_;
+ bool log_only_;
uint64_t started_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
@@ -173,19 +156,8 @@
static Dumpstate& GetInstance();
- /*
- * When running in dry-run mode, skips the real dumps and just print the section headers.
- *
- * Useful when debugging dumpstate or other bugreport-related activities.
- *
- * Dry-run mode is enabled by setting the system property dumpstate.dry_run to true.
- */
- bool IsDryRun() const;
-
- /*
- * Gets whether device is running a `user` build.
- */
- bool IsUserBuild() const;
+ // TODO: temporary function until device code uses PropertiesHelper::IsUserBuild()
+ bool IsUserBuild();
/* Checkes whether dumpstate is generating a zipped bugreport. */
bool IsZipping() const;
@@ -351,14 +323,7 @@
private:
// Used by GetInstance() only.
- Dumpstate(const std::string& version = VERSION_CURRENT, bool dry_run = false,
- const std::string& build_type = "user");
-
- // Whether this is a dry run.
- bool dry_run_;
-
- // Build type (such as 'user' or 'eng').
- std::string build_type_;
+ Dumpstate(const std::string& version = VERSION_CURRENT);
DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
@@ -386,9 +351,6 @@
int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
int (*dump_from_fd)(const char* title, const char* path, int fd));
-/* switch to non-root user and group */
-bool drop_root_user();
-
/* sends a broadcast using Activity Manager */
void send_broadcast(const std::string& action, const std::vector<std::string>& args);
@@ -455,9 +417,6 @@
/** 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();
-
#ifdef __cplusplus
}
#endif
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 9613576..2e35112 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "dumpstate"
#include <cutils/log.h>
+#include "DumpstateInternal.h"
#include "DumpstateService.h"
#include "android/os/BnDumpstate.h"
#include "dumpstate.h"
@@ -24,6 +25,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <fcntl.h>
#include <libgen.h>
#include <signal.h>
#include <sys/types.h>
@@ -38,6 +40,7 @@
using namespace android;
using ::testing::EndsWith;
+using ::testing::HasSubstr;
using ::testing::IsNull;
using ::testing::IsEmpty;
using ::testing::NotNull;
@@ -66,8 +69,34 @@
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/";
@@ -91,20 +120,29 @@
return to.c_str();
}
- private:
- // Need a function that returns void to use assertions -
+ // 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;
- ASSERT_TRUE(android::base::ReadFileToString(from, &content)) << "could not read from "
- << from;
- ASSERT_TRUE(android::base::WriteStringToFile(content, to)) << "could not write to " << to;
+ 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());
@@ -133,26 +171,6 @@
return status;
}
- void SetDryRun(bool dry_run) {
- ALOGD("Setting dry_run_ to %s\n", dry_run ? "true" : "false");
- ds.dry_run_ = dry_run;
- }
-
- void SetBuildType(const std::string& build_type) {
- ALOGD("Setting build_type_ to '%s'\n", build_type.c_str());
- ds.build_type_ = build_type;
- }
-
- bool IsUserBuild() {
- return "user" == android::base::GetProperty("ro.build.type", "(unknown)");
- }
-
- void DropRoot() {
- drop_root_user();
- uid_t uid = getuid();
- ASSERT_EQ(2000, (int)uid);
- }
-
void SetProgress(long progress, long initial_max, long threshold = 0) {
ds.update_progress_ = true;
ds.update_progress_threshold_ = threshold;
@@ -401,6 +419,12 @@
}
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) {
@@ -417,7 +441,13 @@
}
TEST_F(DumpstateTest, RunCommandAsRootUserBuild) {
- if (!IsUserBuild()) {
+ 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");
}
@@ -432,6 +462,27 @@
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,
@@ -483,10 +534,10 @@
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) ------\n\t(skipped on dry run)\n------"));
+ 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"));
- EXPECT_THAT(err, IsEmpty());
}
TEST_F(DumpstateTest, DumpFileUpdateProgress) {
@@ -547,8 +598,7 @@
std::string expected_content =
android::base::StringPrintf("%d %d\n", expected_runs, expected_average);
std::string actual_content;
- ASSERT_TRUE(android::base::ReadFileToString(path, &actual_content))
- << "could not read stats from " << path;
+ ReadFileToString(path, &actual_content);
ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path;
}
};
@@ -658,6 +708,12 @@
// 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);
@@ -745,11 +801,243 @@
AssertStats(path, 3, 16);
}
-// TODO: RunCommandAsRootNonUserBuild must be the last test because it drops root, which could cause
-// other tests to fail if they're relyin on the process running as root.
-// For now this is fine, but eventually it might need to be moved to its own test case / process.
-TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
- if (IsUserBuild()) {
+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;
+ }
+
+ 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;
}
@@ -762,3 +1050,90 @@
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"));
+}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index c322d9f..5ee00c8 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -14,63 +14,42 @@
* limitations under the License.
*/
+#define LOG_TAG "dumpstate"
+
+#include "dumpstate.h"
+
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
-#include <limits.h>
#include <math.h>
#include <poll.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/capability.h>
#include <sys/inotify.h>
#include <sys/klog.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
-#include <string>
-#include <vector>
-
-#define LOG_TAG "dumpstate"
#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/log.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
-
-#include "dumpstate.h"
-
-#define SU_PATH "/system/xbin/su"
-
-static const int64_t NANOS_PER_SEC = 1000000000;
+#include "DumpstateInternal.h"
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);
}
-static bool IsDryRun() {
- return Dumpstate::GetInstance().IsDryRun();
-}
-static void UpdateProgress(int32_t delta) {
- ds.UpdateProgress(delta);
+bool Dumpstate::IsUserBuild() {
+ return PropertiesHelper::IsUserBuild();
}
/* list of native processes to include in the native dumps */
@@ -89,130 +68,41 @@
NULL,
};
-/* Most simple commands have 10 as timeout, so 5 is a good estimate */
-static const int WEIGHT_FILE = 5;
-
// Reasonable value for max stats.
static const int STATS_MAX_N_RUNS = 1000;
static const long STATS_MAX_AVERAGE = 100000;
-CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
-CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
-CommandOptions CommandOptions::AS_ROOT_10 = CommandOptions::WithTimeout(10).AsRoot().Build();
-CommandOptions CommandOptions::AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
-CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
-}
-
-CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
- values.always_ = true;
- return *this;
-}
-
-CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
- values.root_mode_ = SU_ROOT;
- return *this;
-}
-
-CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
- values.root_mode_ = DROP_ROOT;
- return *this;
-}
-
-CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
- values.stdout_mode_ = REDIRECT_TO_STDERR;
- return *this;
-}
-
-CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
- const std::string& message) {
- values.logging_message_ = message;
- return *this;
-}
-
-CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
- return CommandOptions(values);
-}
-
-CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
- : timeout_(timeout),
- always_(false),
- root_mode_(DONT_DROP_ROOT),
- stdout_mode_(NORMAL_STDOUT),
- logging_message_("") {
-}
-
-CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
-}
-
-int64_t CommandOptions::Timeout() const {
- return values.timeout_;
-}
-
-bool CommandOptions::Always() const {
- return values.always_;
-}
-
-RootMode CommandOptions::RootMode() const {
- return values.root_mode_;
-}
-
-StdoutMode CommandOptions::StdoutMode() const {
- return values.stdout_mode_;
-}
-
-std::string CommandOptions::LoggingMessage() const {
- return values.logging_message_;
-}
-
-CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
- return CommandOptions::CommandOptionsBuilder(timeout);
-}
-
-Dumpstate::Dumpstate(const std::string& version, bool dry_run, const std::string& build_type)
- : pid_(getpid()),
- version_(version),
- now_(time(nullptr)),
- dry_run_(dry_run),
- build_type_(build_type) {
+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),
- android::base::GetBoolProperty("dumpstate.dry_run", false),
- android::base::GetProperty("ro.build.type", "(unknown)"));
+ static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT));
return singleton_;
}
-DurationReporter::DurationReporter(const std::string& title) : DurationReporter(title, stdout) {
-}
-
-DurationReporter::DurationReporter(const std::string& title, FILE* out) : title_(title), out_(out) {
+DurationReporter::DurationReporter(const std::string& title, bool log_only)
+ : title_(title), log_only_(log_only) {
if (!title_.empty()) {
- started_ = DurationReporter::Nanotime();
+ started_ = Nanotime();
}
}
DurationReporter::~DurationReporter() {
if (!title_.empty()) {
- uint64_t elapsed = DurationReporter::Nanotime() - started_;
- // Use "Yoda grammar" to make it easier to grep|sort sections.
- if (out_ != nullptr) {
- fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
- (float)elapsed / NANOS_PER_SEC, title_.c_str());
- } else {
+ uint64_t elapsed = Nanotime() - started_;
+ if (log_only_) {
MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
+ } else {
+ // 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) {
@@ -322,14 +212,6 @@
dprintf(fd, "%saverage_max: %d\n", pr, average_max_);
}
-bool Dumpstate::IsDryRun() const {
- return dry_run_;
-}
-
-bool Dumpstate::IsUserBuild() const {
- return "user" == build_type_;
-}
-
bool Dumpstate::IsZipping() const {
return zip_writer_ != nullptr;
}
@@ -344,7 +226,10 @@
}
void for_each_userid(void (*func)(int), const char *header) {
- if (IsDryRun()) 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;
@@ -427,7 +312,10 @@
}
void for_each_pid(for_each_pid_func func, const char *header) {
- if (IsDryRun()) 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);
}
@@ -481,13 +369,17 @@
}
void for_each_tid(for_each_tid_func func, const char *header) {
- if (IsDryRun()) 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) {
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
char path[255];
char buffer[255];
@@ -554,7 +446,7 @@
}
void show_showtime(int pid, const char *name) {
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
char path[255];
char buffer[1023];
@@ -612,7 +504,7 @@
if (iotime) {
snprdec(buffer, sizeof(buffer), 79, permille);
}
- puts(buffer); // adds a trailing newline
+ puts(buffer); // adds a trailing newline
return;
}
@@ -622,7 +514,7 @@
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
/* Get size of kernel buffer */
int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
@@ -653,103 +545,17 @@
snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name);
snprintf(arg, sizeof(arg), "%d", pid);
- RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT_10);
-}
-
-// TODO: when converted to a Dumpstate function, it should be const
-static int _dump_file_from_fd_to_fd(const std::string& title, const char* path, int fd, int out_fd) {
- 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");
- }
-
- 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) {
- dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
- newline = true;
- break;
- } else if (ret == 0) {
- elapsed = DurationReporter::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;
- }
- }
- }
- UpdateProgress(WEIGHT_FILE);
- close(fd);
-
- if (!newline) dprintf(out_fd, "\n");
- if (!title.empty()) dprintf(out_fd, "\n");
- return 0;
-}
-
-// Internal function used by both DumpFile and DumpFileToFd - the former wants to print title
-// information, while the later doesn't.
-static int DumpFileToFd(const std::string& title, int out_fd, 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()) {
- printf("*** Error dumping %s: %s\n", path.c_str(), strerror(err));
- } else {
- printf("*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err));
- }
- return -1;
- }
- return _dump_file_from_fd_to_fd(title, path.c_str(), fd, out_fd);
-}
-
-int DumpFileToFd(int out_fd, const std::string& path) {
- return DumpFileToFd("", out_fd, path);
+ RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT);
}
int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
DurationReporter duration_reporter(title);
- if (IsDryRun()) {
- if (!title.empty()) {
- printf("------ %s (%s) ------\n", title.c_str(), path.c_str());
- printf("\t(skipped on dry run)\n");
- }
- UpdateProgress(WEIGHT_FILE);
- return 0;
- }
- return DumpFileToFd(title, STDOUT_FILENO, path);
+
+ int status = DumpFileToFd(STDOUT_FILENO, title, path);
+
+ UpdateProgress(WEIGHT_FILE);
+
+ return status;
}
int read_file_as_long(const char *path, long int *output) {
@@ -791,7 +597,7 @@
if (!title.empty()) {
printf("------ %s (%s) ------\n", title.c_str(), dir);
}
- if (IsDryRun()) return 0;
+ if (PropertiesHelper::IsDryRun()) return 0;
if (dir[strlen(dir) - 1] == '/') {
++slash;
@@ -848,7 +654,7 @@
* stuck.
*/
int dump_file_from_fd(const char *title, const char *path, int fd) {
- if (IsDryRun()) return 0;
+ if (PropertiesHelper::IsDryRun()) return 0;
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
@@ -860,229 +666,20 @@
close(fd);
return -1;
}
- return _dump_file_from_fd_to_fd(title, path, fd, STDOUT_FILENO);
-}
-
-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;
+ return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun());
}
int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options) {
- if (full_command.empty()) {
- MYLOGE("No arguments on command '%s'\n", title.c_str());
- return -1;
- }
-
- // TODO: SU_ROOT logic must be moved to RunCommandToFd
-
- int size = full_command.size() + 1; // null terminated
- int starting_index = 0;
- if (options.RootMode() == SU_ROOT) {
- starting_index = 2; // "su" "root"
- size += starting_index;
- }
-
- std::vector<const char*> args;
- args.resize(size);
-
- std::string command_string;
- if (options.RootMode() == SU_ROOT) {
- args[0] = SU_PATH;
- command_string += SU_PATH;
- args[1] = "root";
- command_string += " root ";
- }
- int i = starting_index;
- for (auto arg = full_command.begin(); arg != full_command.end(); ++arg) {
- args[i++] = arg->c_str();
- command_string += arg->c_str();
- if (arg != full_command.end() - 1) {
- command_string += " ";
- }
- }
- args[i] = nullptr;
- const char* command = command_string.c_str();
-
- if (options.RootMode() == SU_ROOT && ds.IsUserBuild()) {
- printf("Skipping '%s' on user build.\n", command);
- return 0;
- }
-
- if (!title.empty()) {
- printf("------ %s (%s) ------\n", title.c_str(), command);
- }
-
- fflush(stdout);
DurationReporter duration_reporter(title);
- const std::string& logging_message = options.LoggingMessage();
- if (!logging_message.empty()) {
- MYLOGI(logging_message.c_str(), command_string.c_str());
- }
-
- if (IsDryRun() && !options.Always()) {
- if (!title.empty()) {
- printf("\t(skipped on dry run)\n");
- }
- UpdateProgress(options.Timeout());
- return 0;
- }
-
- int status = RunCommandToFd(STDOUT_FILENO, args, options, command);
+ int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
/* TODO: for now we're simplifying the progress calculation by using the
* timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
* where its weight should be much higher proportionally to its timeout.
* Ideally, it should use a options.EstimatedDuration() instead...*/
- int weight = options.Timeout();
-
- if (weight > 0) {
- UpdateProgress(weight);
- }
-
- return status;
-}
-
-int RunCommandToFd(int fd, const std::vector<const char*>& full_command,
- const CommandOptions& options, const std::string& description) {
- if (full_command.empty()) {
- MYLOGE("No arguments on RunCommandToFd'\n");
- return -1;
- }
- const char* path = full_command[0];
- const char* command = description.empty() ? path : description.c_str();
-
- bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
-
- 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 (options.RootMode() == DROP_ROOT && !drop_root_user()) {
- if (!silent)
- printf("*** failed to drop root before running %s: %s\n", command, strerror(errno));
- MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
- return -1;
- }
-
- if (STDOUT_FILENO != fd) {
- TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
- close(fd);
- }
- if (silent) {
- // Redirect stderr to fd
- dup2(STDERR_FILENO, 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**)full_command.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 = DurationReporter::Nanotime() - start;
- if (!ret) {
- if (errno == ETIMEDOUT) {
- if (!silent)
- printf("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
- (float)elapsed / NANOS_PER_SEC, pid);
- MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
- (float)elapsed / NANOS_PER_SEC, pid);
- } else {
- if (!silent)
- printf("*** command '%s': Error after %.4fs (killing pid %d)\n", command,
- (float)elapsed / NANOS_PER_SEC, pid);
- MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
- (float)elapsed / NANOS_PER_SEC, pid);
- }
- kill(pid, SIGTERM);
- if (!waitpid_with_timeout(pid, 5, nullptr)) {
- kill(pid, SIGKILL);
- if (!waitpid_with_timeout(pid, 5, nullptr)) {
- if (!silent)
- printf("could not kill command '%s' (pid %d) even with SIGKILL.\n", command,
- pid);
- MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
- }
- }
- return -1;
- }
-
- if (WIFSIGNALED(status)) {
- if (!silent)
- printf("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
- MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
- } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
- status = WEXITSTATUS(status);
- if (!silent) printf("*** command '%s' failed: exit code %d\n", command, status);
- MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
- }
+ UpdateProgress(options.Timeout());
return status;
}
@@ -1095,55 +692,6 @@
RunCommand(title, dumpsys, options);
}
-bool drop_root_user() {
- 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_WAKELOCK,
- 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) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective =
- (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
- capdata[0].inheritable = 0;
- capdata[1].inheritable = 0;
-
- if (capset(&capheader, &capdata[0]) < 0) {
- MYLOGE("capset failed: %s\n", strerror(errno));
- return false;
- }
-
- return true;
-}
-
void send_broadcast(const std::string& action, const std::vector<std::string>& args) {
std::vector<std::string> am = {"/system/bin/am", "broadcast", "--user", "0", "-a", action};
@@ -1178,7 +726,7 @@
const char* title = "SYSTEM PROPERTIES";
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
size_t i;
num_props = 0;
property_list(print_prop, NULL);
@@ -1365,7 +913,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;
@@ -1387,7 +935,7 @@
MYLOGE("lseek: %s\n", strerror(errno));
} else {
dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n", pid,
- (float)(DurationReporter::Nanotime() - start) / NANOS_PER_SEC);
+ (float)(Nanotime() - start) / NANOS_PER_SEC);
}
} else if (should_dump_native_traces(data)) {
/* dump native process if appropriate */
@@ -1395,7 +943,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) {
@@ -1407,7 +955,7 @@
timeout_failures = 0;
}
dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n", pid,
- (float)(DurationReporter::Nanotime() - start) / NANOS_PER_SEC);
+ (float)(Nanotime() - start) / NANOS_PER_SEC);
}
}
}
@@ -1437,7 +985,7 @@
void dump_route_tables() {
DurationReporter duration_reporter("DUMP ROUTE TABLES");
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
ds.DumpFile("RT_TABLES", RT_TABLES_PATH);
FILE* fp = fopen(RT_TABLES_PATH, "re");
@@ -1559,19 +1107,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;
@@ -1585,8 +1130,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;
}
@@ -1596,11 +1140,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;
@@ -1629,20 +1172,17 @@
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");
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index ddf3aa8..5c04f6c 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -365,6 +365,7 @@
if (!result) {
fprintf(stderr, "Shader source:\n");
printShaderSource(lines);
+ delete[] src;
return false;
}
delete[] src;
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index e3048c7..93174bf 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -6,16 +6,20 @@
"-Werror",
],
srcs: [
- "commands.cpp",
+ "InstalldNativeService.cpp",
+ "dexopt.cpp",
"globals.cpp",
"utils.cpp",
+ "binder/android/os/IInstalld.aidl",
],
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"liblog",
"liblogwrap",
"libselinux",
+ "libutils",
],
clang: true,
@@ -30,6 +34,9 @@
defaults: ["installd_defaults"],
export_include_dirs: ["."],
+ aidl: {
+ export_aidl_headers: true,
+ },
}
//
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 54f6b5f..f567a10 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -23,13 +23,15 @@
LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
-LOCAL_SRC_FILES := otapreopt.cpp commands.cpp globals.cpp utils.cpp
+LOCAL_SRC_FILES := otapreopt.cpp InstalldNativeService.cpp globals.cpp utils.cpp dexopt.cpp binder/android/os/IInstalld.aidl
LOCAL_SHARED_LIBRARIES := \
libbase \
+ libbinder \
libcutils \
liblog \
liblogwrap \
libselinux \
+ libutils \
LOCAL_STATIC_LIBRARIES := libdiskusage
LOCAL_CLANG := true
diff --git a/cmds/installd/commands.cpp b/cmds/installd/InstalldNativeService.cpp
similarity index 79%
rename from cmds/installd/commands.cpp
rename to cmds/installd/InstalldNativeService.cpp
index 5ffc0c2..c8df61a 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -14,7 +14,7 @@
** limitations under the License.
*/
-#include "commands.h"
+#include "InstalldNativeService.h"
#include <errno.h>
#include <inttypes.h>
@@ -42,10 +42,11 @@
#include <selinux/android.h>
#include <system/thread_defs.h>
-#include <globals.h>
-#include <installd_deps.h>
-#include <otapreopt_utils.h>
-#include <utils.h>
+#include "dexopt.h"
+#include "globals.h"
+#include "installd_deps.h"
+#include "otapreopt_utils.h"
+#include "utils.h"
#ifndef LOG_TAG
#define LOG_TAG "installd"
@@ -78,12 +79,75 @@
static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
/* dexopt needed flags matching those in dalvik.system.DexFile */
-static constexpr int DEXOPT_DEX2OAT_NEEDED = 1;
-static constexpr int DEXOPT_PATCHOAT_NEEDED = 2;
-static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3;
+static constexpr int DEX2OAT_FROM_SCRATCH = 1;
+static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2;
+static constexpr int DEX2OAT_FOR_FILTER = 3;
+static constexpr int DEX2OAT_FOR_RELOCATION = 4;
+static constexpr int PATCHOAT_FOR_RELOCATION = 5;
typedef int fd_t;
+namespace {
+
+constexpr const char* kDump = "android.permission.DUMP";
+
+binder::Status checkPermission(const char* permission) {
+ pid_t pid;
+ uid_t uid;
+
+ if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid),
+ reinterpret_cast<int32_t*>(&uid))) {
+ return binder::Status::ok();
+ } else {
+ auto err = StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission);
+ return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY, String8(err.c_str()));
+ }
+}
+
+binder::Status checkUid(uid_t expectedUid) {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (uid == expectedUid || uid == AID_ROOT) {
+ return binder::Status::ok();
+ } else {
+ auto err = StringPrintf("UID %d is not expected UID %d", uid, expectedUid);
+ return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY, String8(err.c_str()));
+ }
+}
+
+#define ENFORCE_UID(uid) { \
+ binder::Status status = checkUid((uid)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+}
+
+} // namespace
+
+status_t InstalldNativeService::start() {
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ status_t ret = BinderService<InstalldNativeService>::publish();
+ if (ret != android::OK) {
+ return ret;
+ }
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ return android::OK;
+}
+
+status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) {
+ const binder::Status dump_permission = checkPermission(kDump);
+ if (!dump_permission.isOk()) {
+ const String8 msg(dump_permission.toString8());
+ write(fd, msg.string(), msg.size());
+ return PERMISSION_DENIED;
+ }
+
+ std::string msg = "installd is happy\n";
+ write(fd, msg.c_str(), strlen(msg.c_str()));
+ return NO_ERROR;
+}
+
static bool property_get_bool(const char* property_name, bool default_value = false) {
char tmp_property_value[kPropertyValueMax];
bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
@@ -104,7 +168,7 @@
* if the label of that top-level file actually changed. This can save us
* significant time by avoiding no-op traversals of large filesystem trees.
*/
-static int restorecon_app_data_lazy(const std::string& path, const char* seinfo, uid_t uid) {
+static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid) {
int res = 0;
char* before = nullptr;
char* after = nullptr;
@@ -116,7 +180,7 @@
PLOG(ERROR) << "Failed before getfilecon for " << path;
goto fail;
}
- if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, 0) < 0) {
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
PLOG(ERROR) << "Failed top-level restorecon for " << path;
goto fail;
}
@@ -130,7 +194,7 @@
if (strcmp(before, after)) {
LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path
<< "; running recursive restorecon";
- if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid,
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
PLOG(ERROR) << "Failed recursive restorecon for " << path;
goto fail;
@@ -146,9 +210,9 @@
return res;
}
-static int restorecon_app_data_lazy(const std::string& parent, const char* name, const char* seinfo,
- uid_t uid) {
- return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seinfo, uid);
+static int restorecon_app_data_lazy(const std::string& parent, const char* name,
+ const std::string& seInfo, uid_t uid) {
+ return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid);
}
static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
@@ -164,56 +228,62 @@
return prepare_app_dir(StringPrintf("%s/%s", parent.c_str(), name), target_mode, uid);
}
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- appid_t appid, const char* seinfo, int target_sdk_version) {
- uid_t uid = multiuser_get_uid(userid, appid);
- mode_t target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
+binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo, int32_t targetSdkVersion) {
+ ENFORCE_UID(AID_SYSTEM);
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+
+ uid_t uid = multiuser_get_uid(userId, appId);
+ mode_t target_mode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
if (prepare_app_dir(path, target_mode, uid) ||
prepare_app_dir(path, "cache", 0771, uid) ||
prepare_app_dir(path, "code_cache", 0771, uid)) {
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
// Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seinfo, uid) ||
- restorecon_app_data_lazy(path, "cache", seinfo, uid) ||
- restorecon_app_data_lazy(path, "code_cache", seinfo, uid)) {
- return -1;
+ if (restorecon_app_data_lazy(path, seInfo, uid) ||
+ restorecon_app_data_lazy(path, "cache", seInfo, uid) ||
+ restorecon_app_data_lazy(path, "code_cache", seInfo, uid)) {
+ return binder::Status::fromServiceSpecificError(-1);
}
// Remember inode numbers of cache directories so that we can clear
// contents while CE storage is locked
if (write_path_inode(path, "cache", kXattrInodeCache) ||
write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
}
if (flags & FLAG_STORAGE_DE) {
- auto path = create_data_user_de_package_path(uuid, userid, pkgname);
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
if (prepare_app_dir(path, target_mode, uid)) {
// TODO: include result once 25796509 is fixed
- return 0;
+ return binder::Status::ok();
}
// Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seinfo, uid)) {
- return -1;
+ if (restorecon_app_data_lazy(path, seInfo, uid)) {
+ return binder::Status::fromServiceSpecificError(-1);
}
if (property_get_bool("dalvik.vm.usejitprofiles")) {
- const std::string profile_path = create_data_user_profile_package_path(userid, pkgname);
+ const std::string profile_path = create_data_user_profile_package_path(userId, pkgname);
// read-write-execute only for the app user.
if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
PLOG(ERROR) << "Failed to prepare " << profile_path;
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
std::string profile_file = create_primary_profile(profile_path);
// read-write only for the app user.
if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
PLOG(ERROR) << "Failed to prepare " << profile_path;
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
// dex2oat/profman runs under the shared app gid and it needs to read/write reference
@@ -222,28 +292,33 @@
if (fs_prepare_dir_strict(
ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
}
}
- return 0;
+ return binder::Status::ok();
}
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
+binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+
// This method only exists to upgrade system apps that have requested
// forceDeviceEncrypted, so their default storage always lives in a
// consistent location. This only works on non-FBE devices, since we
// never want to risk exposing data on a device with real CE/DE storage.
- auto ce_path = create_data_user_ce_package_path(uuid, userid, pkgname);
- auto de_path = create_data_user_de_package_path(uuid, userid, pkgname);
+ auto ce_path = create_data_user_ce_package_path(uuid_, userId, pkgname);
+ auto de_path = create_data_user_de_package_path(uuid_, userId, pkgname);
// If neither directory is marked as default, assume CE is default
if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1
&& getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) {
if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) {
PLOG(ERROR) << "Failed to mark default storage " << ce_path;
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
}
@@ -256,15 +331,15 @@
<< " is not active; migrating from " << source;
if (delete_dir_contents_and_dir(target) != 0) {
PLOG(ERROR) << "Failed to delete";
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
if (rename(source.c_str(), target.c_str()) != 0) {
PLOG(ERROR) << "Failed to rename";
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
}
- return 0;
+ return binder::Status::ok();
}
static bool clear_profile(const std::string& profile) {
@@ -332,18 +407,24 @@
return success;
}
-int clear_app_profiles(const char* pkgname) {
+binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* pkgname = packageName.c_str();
bool success = true;
success &= clear_reference_profile(pkgname);
success &= clear_current_profiles(pkgname);
- return success ? 0 : -1;
+ return success ? binder::Status::ok() : binder::Status::fromServiceSpecificError(-1);
}
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode) {
+binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+
int res = 0;
if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
if (flags & FLAG_CLEAR_CACHE_ONLY) {
path = read_path_inode(path, "cache", kXattrInodeCache);
} else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
@@ -364,18 +445,18 @@
only_cache = true;
}
- auto path = create_data_user_de_package_path(uuid, userid, pkgname) + suffix;
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix;
if (access(path.c_str(), F_OK) == 0) {
// TODO: include result once 25796509 is fixed
delete_dir_contents(path);
}
if (!only_cache) {
- if (!clear_current_profile(pkgname, userid)) {
+ if (!clear_current_profile(pkgname, userId)) {
res |= -1;
}
}
}
- return res;
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
static int destroy_app_reference_profile(const char *pkgname) {
@@ -390,37 +471,51 @@
/*ignore_if_missing*/ true);
}
-int destroy_app_profiles(const char *pkgname) {
+binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* pkgname = packageName.c_str();
int result = 0;
std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
for (auto user : users) {
result |= destroy_app_current_profiles(pkgname, user);
}
result |= destroy_app_reference_profile(pkgname);
- return result;
+ return result ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode) {
+binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+
int res = 0;
if (flags & FLAG_STORAGE_CE) {
res |= delete_dir_contents_and_dir(
- create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode));
+ create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode));
}
if (flags & FLAG_STORAGE_DE) {
res |= delete_dir_contents_and_dir(
- create_data_user_de_package_path(uuid, userid, pkgname));
- destroy_app_current_profiles(pkgname, userid);
+ create_data_user_de_package_path(uuid_, userId, pkgname));
+ destroy_app_current_profiles(pkgname, userId);
// TODO(calin): If the package is still installed by other users it's probably
// beneficial to keep the reference profile around.
// Verify if it's ok to do that.
destroy_app_reference_profile(pkgname);
}
- return res;
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
-int move_complete_app(const char *from_uuid, const char *to_uuid, const char *package_name,
- const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version) {
+binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
+ const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+ const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+ int32_t targetSdkVersion) {
+
+ const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr;
+ const char* to_uuid = toUuid ? toUuid->c_str() : nullptr;
+ const char* package_name = packageName.c_str();
+ const char* data_app_name = dataAppName.c_str();
+
std::vector<userid_t> users = get_known_users(from_uuid);
// Copy app
@@ -465,8 +560,8 @@
continue;
}
- if (create_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
- appid, seinfo, target_sdk_version) != 0) {
+ if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
+ seInfo, targetSdkVersion).isOk()) {
LOG(ERROR) << "Failed to create package target on " << to_uuid;
goto fail;
}
@@ -509,8 +604,8 @@
}
}
- if (restorecon_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
- appid, seinfo) != 0) {
+ if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+ appId, seInfo).isOk()) {
LOG(ERROR) << "Failed to restorecon";
goto fail;
}
@@ -519,7 +614,7 @@
// We let the framework scan the new location and persist that before
// deleting the data in the old location; this ordering ensures that
// we can recover from things like battery pulls.
- return 0;
+ return binder::Status::ok();
fail:
// Nuke everything we might have already copied
@@ -543,33 +638,39 @@
}
}
}
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
-int create_user_data(const char *uuid, userid_t userid, int user_serial ATTRIBUTE_UNUSED,
- int flags) {
- if (flags & FLAG_STORAGE_DE) {
- if (uuid == nullptr) {
- return ensure_config_user_dirs(userid);
- }
- }
- return 0;
-}
-
-int destroy_user_data(const char *uuid, userid_t userid, int flags) {
+binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
int res = 0;
if (flags & FLAG_STORAGE_DE) {
- res |= delete_dir_contents_and_dir(create_data_user_de_path(uuid, userid), true);
- if (uuid == nullptr) {
- res |= delete_dir_contents_and_dir(create_data_misc_legacy_path(userid), true);
- res |= delete_dir_contents_and_dir(create_data_user_profiles_path(userid), true);
+ if (uuid_ == nullptr) {
+ res = ensure_config_user_dirs(userId);
+ }
+ }
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
+}
+
+binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid,
+ int32_t userId, int32_t flags) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ int res = 0;
+ if (flags & FLAG_STORAGE_DE) {
+ res |= delete_dir_contents_and_dir(create_data_user_de_path(uuid_, userId), true);
+ if (uuid_ == nullptr) {
+ res |= delete_dir_contents_and_dir(create_data_misc_legacy_path(userId), true);
+ res |= delete_dir_contents_and_dir(create_data_user_profiles_path(userId), true);
}
}
if (flags & FLAG_STORAGE_CE) {
- res |= delete_dir_contents_and_dir(create_data_user_ce_path(uuid, userid), true);
- res |= delete_dir_contents_and_dir(create_data_media_path(uuid, userid), true);
+ res |= delete_dir_contents_and_dir(create_data_user_ce_path(uuid_, userId), true);
+ res |= delete_dir_contents_and_dir(create_data_media_path(uuid_, userId), true);
}
- return res;
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
/* Try to ensure free_size bytes of storage are available.
@@ -579,53 +680,70 @@
* also require that apps constantly modify file metadata even
* when just reading from the cache, which is pretty awful.
*/
-int free_cache(const char *uuid, int64_t free_size) {
+binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
+ int64_t freeStorageSize) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
cache_t* cache;
int64_t avail;
- auto data_path = create_data_path(uuid);
+ auto data_path = create_data_path(uuid_);
avail = data_disk_free(data_path);
- if (avail < 0) return -1;
+ if (avail < 0) {
+ return binder::Status::fromServiceSpecificError(-1);
+ }
- ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
- if (avail >= free_size) return 0;
+ ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", freeStorageSize, avail);
+ if (avail >= freeStorageSize) {
+ return binder::Status::ok();
+ }
cache = start_cache_collection();
- auto users = get_known_users(uuid);
+ auto users = get_known_users(uuid_);
for (auto user : users) {
- add_cache_files(cache, create_data_user_ce_path(uuid, user));
- add_cache_files(cache, create_data_user_de_path(uuid, user));
+ add_cache_files(cache, create_data_user_ce_path(uuid_, user));
+ add_cache_files(cache, create_data_user_de_path(uuid_, user));
add_cache_files(cache,
- StringPrintf("%s/Android/data", create_data_media_path(uuid, user).c_str()));
+ StringPrintf("%s/Android/data", create_data_media_path(uuid_, user).c_str()));
}
- clear_cache_files(data_path, cache, free_size);
+ clear_cache_files(data_path, cache, freeStorageSize);
finish_cache_collection(cache);
- return data_disk_free(data_path) >= free_size ? 0 : -1;
+ if (data_disk_free(data_path) >= freeStorageSize) {
+ return binder::Status::ok();
+ } else {
+ return binder::Status::fromServiceSpecificError(-1);
+ }
}
-int rm_dex(const char *path, const char *instruction_set)
-{
+binder::Status InstalldNativeService::rmdex(const std::string& codePath,
+ const std::string& instructionSet) {
+ ENFORCE_UID(AID_SYSTEM);
char dex_path[PKG_PATH_MAX];
+ const char* path = codePath.c_str();
+ const char* instruction_set = instructionSet.c_str();
+
if (validate_apk_path(path) && validate_system_app_path(path)) {
ALOGE("invalid apk path '%s' (bad prefix)\n", path);
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
- if (!create_cache_path(dex_path, path, instruction_set)) return -1;
+ if (!create_cache_path(dex_path, path, instruction_set)) {
+ return binder::Status::fromServiceSpecificError(-1);
+ }
ALOGV("unlink %s\n", dex_path);
if (unlink(dex_path) < 0) {
if (errno != ENOENT) {
ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));
}
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
} else {
- return 0;
+ return binder::Status::ok();
}
}
@@ -679,39 +797,58 @@
closedir(d);
}
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
- const char *code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
- int64_t* asecsize) {
+binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode,
+ const std::string& codePath, std::vector<int64_t>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+ const char* code_path = codePath.c_str();
+
DIR *d;
int dfd;
+ int64_t codesize = 0;
+ int64_t datasize = 0;
+ int64_t cachesize = 0;
+ int64_t asecsize = 0;
d = opendir(code_path);
if (d != nullptr) {
dfd = dirfd(d);
- *codesize += calculate_dir_size(dfd);
+ codesize += calculate_dir_size(dfd);
closedir(d);
}
if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
- add_app_data_size(path, codesize, datasize, cachesize);
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+ add_app_data_size(path, &codesize, &datasize, &cachesize);
}
if (flags & FLAG_STORAGE_DE) {
- auto path = create_data_user_de_package_path(uuid, userid, pkgname);
- add_app_data_size(path, codesize, datasize, cachesize);
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
+ add_app_data_size(path, &codesize, &datasize, &cachesize);
}
- *asecsize = 0;
-
- return 0;
+ std::vector<int64_t> res;
+ res.push_back(codesize);
+ res.push_back(datasize);
+ res.push_back(cachesize);
+ res.push_back(asecsize);
+ *_aidl_return = res;
+ return binder::Status::ok();
}
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode) {
+binder::Status InstalldNativeService::getAppDataInode(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+
+ int res = 0;
if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
- return get_path_inode(path, inode);
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
+ res = get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return));
}
- return -1;
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
static int split_count(const char *str)
@@ -1383,7 +1520,13 @@
// Dumps the contents of a profile file, using pkgname's dex files for pretty
// printing the result.
-bool dump_profile(uid_t uid, const char* pkgname, const char* code_path_string) {
+binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName,
+ const std::string& codePaths, bool* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+
+ const char* pkgname = packageName.c_str();
+ const char* code_path_string = codePaths.c_str();
+
std::vector<fd_t> profile_fds;
fd_t reference_profile_fd = -1;
std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname);
@@ -1397,13 +1540,15 @@
if (!has_reference_profile && !has_profiles) {
ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
- return false;
+ *_aidl_return = false;
+ return binder::Status::ok();
}
fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
- return false;
+ *_aidl_return = false;
+ return binder::Status::ok();
}
std::vector<std::string> code_full_paths = base::Split(code_path_string, ";");
std::vector<std::string> dex_locations;
@@ -1413,7 +1558,8 @@
fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
if (apk_fd == -1) {
ALOGE("installd cannot open '%s'\n", full_path);
- return false;
+ *_aidl_return = false;
+ return binder::Status::ok();
}
dex_locations.push_back(get_location_from_path(full_path));
apk_fds.push_back(apk_fd);
@@ -1437,9 +1583,11 @@
if (!WIFEXITED(return_code)) {
LOG(WARNING) << "profman failed for package " << pkgname << ": "
<< return_code;
- return false;
+ *_aidl_return = false;
+ return binder::Status::ok();
}
- return true;
+ *_aidl_return = true;
+ return binder::Status::ok();
}
static std::string replace_file_extension(const std::string& oat_path, const std::string& new_ext) {
@@ -1546,30 +1694,12 @@
}
// TODO: Consider returning error codes.
-bool merge_profiles(uid_t uid, const char *pkgname) {
- return analyse_profiles(uid, pkgname);
-}
-
-static const char* parse_null(const char* arg) {
- if (strcmp(arg, "!") == 0) {
- return nullptr;
- } else {
- return arg;
- }
-}
-
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) {
- return dexopt(params[0], // apk_path
- atoi(params[1]), // uid
- params[2], // pkgname
- params[3], // instruction_set
- atoi(params[4]), // dexopt_needed
- params[5], // oat_dir
- atoi(params[6]), // dexopt_flags
- params[7], // compiler_filter
- parse_null(params[8]), // volume_uuid
- parse_null(params[9])); // shared_libraries
- static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
+binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName,
+ bool* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* pkgname = packageName.c_str();
+ *_aidl_return = analyse_profiles(uid, pkgname);
+ return binder::Status::ok();
}
// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
@@ -1650,10 +1780,10 @@
bool do_cleanup_;
};
+// TODO: eventually move dexopt() implementation into dexopt.cpp
int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set,
- int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
- const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries)
-{
+ int dexopt_needed, const char* oat_dir, int dexopt_flags,const char* compiler_filter,
+ const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries) {
bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
@@ -1687,20 +1817,25 @@
const char *input_file;
char in_odex_path[PKG_PATH_MAX];
- switch (dexopt_needed) {
- case DEXOPT_DEX2OAT_NEEDED:
+ int dexopt_action = abs(dexopt_needed);
+ bool is_odex_location = dexopt_needed < 0;
+ switch (dexopt_action) {
+ case DEX2OAT_FROM_SCRATCH:
+ case DEX2OAT_FOR_BOOT_IMAGE:
+ case DEX2OAT_FOR_FILTER:
+ case DEX2OAT_FOR_RELOCATION:
input_file = apk_path;
break;
- case DEXOPT_PATCHOAT_NEEDED:
- if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
- return -1;
+ case PATCHOAT_FOR_RELOCATION:
+ if (is_odex_location) {
+ if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
+ return -1;
+ }
+ input_file = in_odex_path;
+ } else {
+ input_file = out_oat_path;
}
- input_file = in_odex_path;
- break;
-
- case DEXOPT_SELF_PATCHOAT_NEEDED:
- input_file = out_oat_path;
break;
default:
@@ -1737,8 +1872,7 @@
// unlink the old one.
base::unique_fd in_vdex_fd;
std::string in_vdex_path_str;
- if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
- || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
+ if (dexopt_action == PATCHOAT_FOR_RELOCATION) {
// `input_file` is the OAT file to be relocated. The VDEX has to be there as well.
in_vdex_path_str = create_vdex_filename(input_file);
if (in_vdex_path_str.empty()) {
@@ -1751,27 +1885,25 @@
in_vdex_path_str.c_str(), strerror(errno));
return -1;
}
- } else {
- // Open the possibly existing vdex in the `out_oat_path`. If none exist, we pass -1
- // to dex2oat for input-vdex-fd.
- in_vdex_path_str = create_vdex_filename(out_oat_path);
+ } else if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+ // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
+ const char* path = nullptr;
+ if (is_odex_location) {
+ if (calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
+ path = in_odex_path;
+ } else {
+ ALOGE("installd cannot compute input vdex location for '%s'\n", apk_path);
+ return -1;
+ }
+ } else {
+ path = out_oat_path;
+ }
+ in_vdex_path_str = create_vdex_filename(path);
if (in_vdex_path_str.empty()) {
- ALOGE("installd cannot compute input vdex location for '%s'\n", out_oat_path);
+ ALOGE("installd cannot compute input vdex location for '%s'\n", path);
return -1;
}
in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
- // If there is no vdex file in out_oat_path, check if we have a vdex
- // file next to the odex file. For other failures, we will just pass a -1 fd.
- if (in_vdex_fd.get() < 0 && (errno == ENOENT) && IsOutputDalvikCache(oat_dir)) {
- if (calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
- in_vdex_path_str = create_vdex_filename(std::string(in_odex_path));
- if (in_vdex_path_str.empty()) {
- ALOGE("installd cannot compute input vdex location for '%s'\n", in_odex_path);
- return -1;
- }
- in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
- }
- }
}
// Infer the name of the output VDEX and create it.
@@ -1815,7 +1947,7 @@
// Avoid generating an app image for extract only since it will not contain any classes.
Dex2oatFileWrapper<std::function<void ()>> image_fd;
const std::string image_path = create_image_filename(out_oat_path);
- if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED && !image_path.empty()) {
+ if (dexopt_action != PATCHOAT_FOR_RELOCATION && !image_path.empty()) {
char app_image_format[kPropertyValueMax];
bool have_app_image_format =
get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
@@ -1865,8 +1997,7 @@
_exit(67);
}
- if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
- || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
+ if (dexopt_action == PATCHOAT_FOR_RELOCATION) {
run_patchoat(input_fd.get(),
in_vdex_fd.get(),
out_oat_fd.get(),
@@ -1877,7 +2008,7 @@
out_vdex_path_str.c_str(),
pkgname,
instruction_set);
- } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
+ } else {
// Pass dex2oat the relative path to the input file.
const char *input_file_name = get_location_from_path(input_file);
run_dex2oat(input_fd.get(),
@@ -1895,9 +2026,6 @@
boot_complete,
reference_profile_fd.get(),
shared_libraries);
- } else {
- ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
- _exit(73);
}
_exit(68); /* only get here on exec failure */
} else {
@@ -1924,23 +2052,43 @@
return 0;
}
-int mark_boot_complete(const char* instruction_set)
-{
- char boot_marker_path[PKG_PATH_MAX];
- sprintf(boot_marker_path,
+binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
+ const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
+ const std::unique_ptr<std::string>& sharedLibraries) {
+ ENFORCE_UID(AID_SYSTEM);
+
+ const char* apk_path = apkPath.c_str();
+ const char* pkgname = packageName ? packageName->c_str() : "*";
+ const char* instruction_set = instructionSet.c_str();
+ const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
+ const char* compiler_filter = compilerFilter.c_str();
+ const char* volume_uuid = uuid ? uuid->c_str() : nullptr;
+ const char* shared_libraries = sharedLibraries ? sharedLibraries->c_str() : nullptr;
+
+ int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
+ oat_dir, dexFlags, compiler_filter, volume_uuid, shared_libraries);
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
+}
+
+binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* instruction_set = instructionSet.c_str();
+
+ char boot_marker_path[PKG_PATH_MAX];
+ sprintf(boot_marker_path,
"%s/%s/%s/.booting",
android_data_dir.path,
DALVIK_CACHE,
instruction_set);
- ALOGV("mark_boot_complete : %s", boot_marker_path);
- if (unlink(boot_marker_path) != 0) {
- ALOGE("Unable to unlink boot marker at %s, error=%s", boot_marker_path,
- strerror(errno));
- return -1;
- }
-
- return 0;
+ ALOGV("mark_boot_complete : %s", boot_marker_path);
+ if (unlink(boot_marker_path) != 0) {
+ ALOGE("Unable to unlink boot marker at %s, error=%s", boot_marker_path, strerror(errno));
+ return binder::Status::fromServiceSpecificError(-1);
+ }
+ return binder::Status::ok();
}
void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
@@ -1964,22 +2112,29 @@
}
}
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
-{
+binder::Status InstalldNativeService::linkNativeLibraryDirectory(
+ const std::unique_ptr<std::string>& uuid, const std::string& packageName,
+ const std::string& nativeLibPath32, int32_t userId) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgname = packageName.c_str();
+ const char* asecLibDir = nativeLibPath32.c_str();
struct stat s, libStat;
int rc = 0;
- std::string _pkgdir(create_data_user_ce_package_path(uuid, userId, pkgname));
+ std::string _pkgdir(create_data_user_ce_package_path(uuid_, userId, pkgname));
std::string _libsymlink(_pkgdir + PKG_LIB_POSTFIX);
const char* pkgdir = _pkgdir.c_str();
const char* libsymlink = _libsymlink.c_str();
- if (stat(pkgdir, &s) < 0) return -1;
+ if (stat(pkgdir, &s) < 0) {
+ return binder::Status::fromServiceSpecificError(-1);
+ }
if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno));
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
if (chmod(pkgdir, 0700) < 0) {
@@ -2024,10 +2179,10 @@
if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno));
- return -errno;
+ rc = -errno;
}
- return rc;
+ return rc ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
@@ -2077,8 +2232,11 @@
return 0;
}
-int idmap(const char *target_apk, const char *overlay_apk, uid_t uid)
-{
+binder::Status InstalldNativeService::idmap(const std::string& targetApkPath,
+ const std::string& overlayApkPath, int32_t uid) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* target_apk = targetApkPath.c_str();
+ const char* overlay_apk = overlayApkPath.c_str();
ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
int idmap_fd = -1;
@@ -2133,78 +2291,93 @@
}
close(idmap_fd);
- return 0;
+ return binder::Status::ok();
fail:
if (idmap_fd >= 0) {
close(idmap_fd);
unlink(idmap_path);
}
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
- appid_t appid, const char* seinfo) {
+binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo) {
+ ENFORCE_UID(AID_SYSTEM);
int res = 0;
// SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ const char* pkgName = packageName.c_str();
+ const char* seinfo = seInfo.c_str();
if (!pkgName || !seinfo) {
ALOGE("Package name or seinfo tag is null when trying to restorecon.");
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
- uid_t uid = multiuser_get_uid(userid, appid);
+ uid_t uid = multiuser_get_uid(userId, appId);
if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgName);
+ auto path = create_data_user_ce_package_path(uuid_, userId, pkgName);
if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
PLOG(ERROR) << "restorecon failed for " << path;
res = -1;
}
}
if (flags & FLAG_STORAGE_DE) {
- auto path = create_data_user_de_package_path(uuid, userid, pkgName);
+ auto path = create_data_user_de_package_path(uuid_, userId, pkgName);
if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
PLOG(ERROR) << "restorecon failed for " << path;
// TODO: include result once 25796509 is fixed
}
}
-
- return res;
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
-int create_oat_dir(const char* oat_dir, const char* instruction_set)
-{
+binder::Status InstalldNativeService::createOatDir(const std::string& oatDir,
+ const std::string& instructionSet) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* oat_dir = oatDir.c_str();
+ const char* instruction_set = instructionSet.c_str();
char oat_instr_dir[PKG_PATH_MAX];
if (validate_apk_path(oat_dir)) {
ALOGE("invalid apk path '%s' (bad prefix)\n", oat_dir);
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
if (selinux_android_restorecon(oat_dir, 0)) {
ALOGE("cannot restorecon dir '%s': %s\n", oat_dir, strerror(errno));
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
- return 0;
+ return binder::Status::ok();
}
-int rm_package_dir(const char* apk_path)
-{
+binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* apk_path = packageDir.c_str();
if (validate_apk_path(apk_path)) {
ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path);
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
- return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */);
+ int res = delete_dir_contents(apk_path, 1 /* also_delete_dir */,
+ NULL /* exclusion_predicate */);
+ return res ? binder::Status::fromServiceSpecificError(-1) : binder::Status::ok();
}
-int link_file(const char* relative_path, const char* from_base, const char* to_base) {
+binder::Status InstalldNativeService::linkFile(const std::string& relativePath,
+ const std::string& fromBase, const std::string& toBase) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* relative_path = relativePath.c_str();
+ const char* from_base = fromBase.c_str();
+ const char* to_base = toBase.c_str();
char from_path[PKG_PATH_MAX];
char to_path[PKG_PATH_MAX];
snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path);
@@ -2212,21 +2385,21 @@
if (validate_apk_path_subdirs(from_path)) {
ALOGE("invalid app data sub-path '%s' (bad prefix)\n", from_path);
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
if (validate_apk_path_subdirs(to_path)) {
ALOGE("invalid app data sub-path '%s' (bad prefix)\n", to_path);
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
const int ret = link(from_path, to_path);
if (ret < 0) {
ALOGE("link(%s, %s) failed : %s", from_path, to_path, strerror(errno));
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
- return 0;
+ return binder::Status::ok();
}
// Helper for move_ab, so that we can have common failure-case cleanup.
@@ -2287,40 +2460,41 @@
return true;
}
-int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
- if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) {
- LOG(ERROR) << "Cannot move_ab with null input";
- return -1;
- }
+binder::Status InstalldNativeService::moveAb(const std::string& apkPath,
+ const std::string& instructionSet, const std::string& outputPath) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* apk_path = apkPath.c_str();
+ const char* instruction_set = instructionSet.c_str();
+ const char* oat_dir = outputPath.c_str();
// Get the current slot suffix. No suffix, no A/B.
std::string slot_suffix;
{
char buf[kPropertyValueMax];
if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
slot_suffix = buf;
if (!ValidateTargetSlotSuffix(slot_suffix)) {
LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
}
// Validate other inputs.
if (validate_apk_path(apk_path) != 0) {
LOG(ERROR) << "invalid apk_path " << apk_path;
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
if (validate_apk_path(oat_dir) != 0) {
LOG(ERROR) << "invalid oat_dir " << oat_dir;
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
char a_path[PKG_PATH_MAX];
if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
- return -1;
+ return binder::Status::fromServiceSpecificError(-1);
}
const std::string a_vdex_path = create_vdex_filename(a_path);
const std::string a_image_path = create_image_filename(a_path);
@@ -2360,14 +2534,20 @@
success = false;
}
- return success ? 0 : -1;
+ return success ? binder::Status::ok() : binder::Status::fromServiceSpecificError(-1);
}
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir) {
+binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
+ const std::string& instructionSet, const std::string& outputPath) {
+ ENFORCE_UID(AID_SYSTEM);
+ const char* apk_path = apkPath.c_str();
+ const char* instruction_set = instructionSet.c_str();
+ const char* oat_dir = outputPath.c_str();
+
// Delete the oat/odex file.
char out_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
- return false;
+ return binder::Status::fromServiceSpecificError(-1);
}
// In case of a permission failure report the issue. Otherwise just print a warning.
@@ -2390,7 +2570,8 @@
bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
// Report success.
- return return_value_oat && return_value_art;
+ bool success = return_value_oat && return_value_art;
+ return success ? binder::Status::ok() : binder::Status::fromServiceSpecificError(-1);
}
} // namespace installd
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
new file mode 100644
index 0000000..749a218
--- /dev/null
+++ b/cmds/installd/InstalldNativeService.h
@@ -0,0 +1,102 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef COMMANDS_H_
+#define COMMANDS_H_
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <binder/BinderService.h>
+#include <cutils/multiuser.h>
+
+#include "android/os/BnInstalld.h"
+#include "installd_constants.h"
+
+namespace android {
+namespace installd {
+
+class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
+public:
+ static status_t start();
+ static char const* getServiceName() { return "installd"; }
+ virtual status_t dump(int fd, const Vector<String16> &args) override;
+
+ binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ int32_t userSerial, int32_t flags);
+ binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ int32_t flags);
+
+ binder::Status createAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo, int32_t targetSdkVersion);
+ binder::Status restoreconAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+ const std::string& seInfo);
+ binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags);
+ binder::Status clearAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+ binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+ binder::Status getAppDataInode(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t* _aidl_return);
+ binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode,
+ const std::string& codePath, std::vector<int64_t>* _aidl_return);
+
+ binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
+ const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+ const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+ int32_t targetSdkVersion);
+
+ binder::Status dexopt(const std::string& apkPath, int32_t uid,
+ const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
+ const std::unique_ptr<std::string>& sharedLibraries);
+
+ binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
+
+ binder::Status mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return);
+ binder::Status dumpProfiles(int32_t uid, const std::string& packageName,
+ const std::string& codePaths, bool* _aidl_return);
+ binder::Status clearAppProfiles(const std::string& packageName);
+ binder::Status destroyAppProfiles(const std::string& packageName);
+
+ binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
+ int32_t uid);
+ binder::Status rmPackageDir(const std::string& packageDir);
+ binder::Status markBootComplete(const std::string& instructionSet);
+ binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t freeStorageSize);
+ binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
+ const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
+ binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
+ binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
+ const std::string& toBase);
+ binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
+ const std::string& outputPath);
+ binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
+ const std::string& outputPath);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // COMMANDS_H_
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
new file mode 100644
index 0000000..bcfaca8
--- /dev/null
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IInstalld {
+ void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
+ void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
+
+ void createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+ int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
+ void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags, int appId, @utf8InCpp String seInfo);
+ void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags);
+ void clearAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags, long ceDataInode);
+ void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags, long ceDataInode);
+ long getAppDataInode(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags);
+ long[] getAppSize(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, int flags, long ceDataInode, @utf8InCpp String codePath);
+
+ void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
+ @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
+ @utf8InCpp String seInfo, int targetSdkVersion);
+
+ void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+ @utf8InCpp String instructionSet, int dexoptNeeded,
+ @nullable @utf8InCpp String outputPath, int dexFlags,
+ @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
+ @nullable @utf8InCpp String sharedLibraries);
+
+ void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
+
+ boolean mergeProfiles(int uid, @utf8InCpp String packageName);
+ boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String codePaths);
+ void clearAppProfiles(@utf8InCpp String packageName);
+ void destroyAppProfiles(@utf8InCpp String packageName);
+
+ void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
+ void rmPackageDir(@utf8InCpp String packageDir);
+ void markBootComplete(@utf8InCpp String instructionSet);
+ void freeCache(@nullable @utf8InCpp String uuid, long freeStorageSize);
+ void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
+ @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
+ void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
+ void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase,
+ @utf8InCpp String toBase);
+ void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+ @utf8InCpp String outputPath);
+ void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+ @utf8InCpp String outputPath);
+}
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
deleted file mode 100644
index ba27517..0000000
--- a/cmds/installd/commands.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef COMMANDS_H_
-#define COMMANDS_H_
-
-#include <inttypes.h>
-#include <unistd.h>
-
-#include <cutils/multiuser.h>
-
-#include <installd_constants.h>
-
-namespace android {
-namespace installd {
-
-static constexpr size_t DEXOPT_PARAM_COUNT = 10U;
-
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- appid_t appid, const char* seinfo, int target_sdk_version);
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
- appid_t appid, const char* seinfo);
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags);
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode);
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
- ino_t ce_data_inode);
-
-int move_complete_app(const char* from_uuid, const char *to_uuid, const char *package_name,
- const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version);
-
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
- const char* code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
- int64_t *asecsize);
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode);
-
-int create_user_data(const char *uuid, userid_t userid, int user_serial, int flags);
-int destroy_user_data(const char *uuid, userid_t userid, int flags);
-
-int rm_dex(const char *path, const char *instruction_set);
-int free_cache(const char *uuid, int64_t free_size);
-
-bool merge_profiles(uid_t uid, const char *pkgname);
-
-bool dump_profile(uid_t uid, const char *pkgname, const char *dex_files);
-
-int dexopt(const char *apk_path,
- uid_t uid,
- const char *pkgName,
- const char *instruction_set,
- int dexopt_needed,
- const char* oat_dir,
- int dexopt_flags,
- const char* compiler_filter,
- const char* volume_uuid,
- const char* shared_libraries);
-static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
-
-// Helper for the above, converting arguments.
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
-
-int mark_boot_complete(const char *instruction_set);
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
-int idmap(const char *target_path, const char *overlay_path, uid_t uid);
-int create_oat_dir(const char* oat_dir, const char *instruction_set);
-int rm_package_dir(const char* apk_path);
-int clear_app_profiles(const char* pkgname);
-int destroy_app_profiles(const char* pkgname);
-int link_file(const char *relative_path, const char *from_base, const char *to_base);
-
-// Move a B version over to the A location. Only works for oat_dir != nullptr.
-int move_ab(const char *apk_path, const char *instruction_set, const char* oat_dir);
-
-// Delete odex files generated by dexopt.
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir);
-
-} // namespace installd
-} // namespace android
-
-#endif // COMMANDS_H_
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
new file mode 100644
index 0000000..71836c3
--- /dev/null
+++ b/cmds/installd/dexopt.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "dexopt.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+static const char* parse_null(const char* arg) {
+ if (strcmp(arg, "!") == 0) {
+ return nullptr;
+ } else {
+ return arg;
+ }
+}
+
+int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) {
+ return dexopt(params[0], // apk_path
+ atoi(params[1]), // uid
+ params[2], // pkgname
+ params[3], // instruction_set
+ atoi(params[4]), // dexopt_needed
+ params[5], // oat_dir
+ atoi(params[6]), // dexopt_flags
+ params[7], // compiler_filter
+ parse_null(params[8]), // volume_uuid
+ parse_null(params[9])); // shared_libraries
+ static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
new file mode 100644
index 0000000..1bfef02
--- /dev/null
+++ b/cmds/installd/dexopt.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEXOPT_H_
+#define DEXOPT_H_
+
+#include <sys/types.h>
+
+namespace android {
+namespace installd {
+
+int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
+ int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+ const char* volume_uuid, const char* shared_libraries);
+
+static constexpr size_t DEXOPT_PARAM_COUNT = 10U;
+static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
+
+// Helper for the above, converting arguments.
+int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
+
+} // namespace installd
+} // namespace android
+
+#endif // DEXOPT_H_
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h
index c90beec..8242eec 100644
--- a/cmds/installd/globals.h
+++ b/cmds/installd/globals.h
@@ -20,6 +20,8 @@
#include <inttypes.h>
+#include "InstalldNativeService.h"
+
namespace android {
namespace installd {
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index c81a339..6c49aa3 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -20,30 +20,23 @@
#include <sys/capability.h>
#include <sys/fsuid.h>
#include <sys/prctl.h>
-#include <sys/socket.h>
#include <sys/stat.h>
#include <android-base/logging.h>
#include <cutils/fs.h>
#include <cutils/log.h> // TODO: Move everything to base::logging.
#include <cutils/properties.h>
-#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
-#include <commands.h>
-#include <globals.h>
-#include <installd_constants.h>
-#include <installd_deps.h> // Need to fill in requirements of commands.
-#include <utils.h>
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "installd_constants.h"
+#include "installd_deps.h" // Need to fill in requirements of commands.
+#include "utils.h"
#ifndef LOG_TAG
#define LOG_TAG "installd"
#endif
-#define SOCKET_PATH "installd"
-
-#define BUFFER_MAX 1024 /* input buffer for commands */
-#define TOKEN_MAX 16 /* max number of arguments in buffer */
-#define REPLY_MAX 256 /* largest reply allowed */
namespace android {
namespace installd {
@@ -174,395 +167,6 @@
return true;
}
-
-static char* parse_null(char* arg) {
- if (strcmp(arg, "!") == 0) {
- return nullptr;
- } else {
- return arg;
- }
-}
-
-static int do_ping(char **arg ATTRIBUTE_UNUSED, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-
-static int do_create_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags,
- appid_t appid, const char* seinfo, int target_sdk_version */
- return create_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]),
- atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_restorecon_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char* uuid, const char* pkgName, userid_t userid, int flags,
- appid_t appid, const char* seinfo */
- return restorecon_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atoi(arg[4]), arg[5]);
-}
-
-static int do_migrate_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags */
- return migrate_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_clear_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
- return clear_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-static int do_destroy_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
- return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-// We use otapreopt_chroot to get into the chroot.
-static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
-
-static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT],
- char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // Time to fork and run otapreopt.
-
- // Check that the tool exists.
- struct stat s;
- if (stat(kOtaPreopt, &s) != 0) {
- LOG(ERROR) << "Otapreopt chroot tool not found.";
- return -1;
- }
-
- pid_t pid = fork();
- if (pid == 0) {
- const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
- argv[0] = kOtaPreopt;
-
- for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
- argv[i + 1] = args[i];
- }
-
- argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
-
- execv(argv[0], (char * const *)argv);
- PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
- exit(99);
- } else {
- int res = wait_child(pid);
- if (res == 0) {
- ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
- } else {
- ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
- }
- return res;
- }
-}
-
-static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
- char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- return dexopt(args);
-}
-
-using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT],
- char reply[REPLY_MAX]);
-
-static int do_dexopt(char **arg, char reply[REPLY_MAX])
-{
- const char* args[DEXOPT_PARAM_COUNT];
- for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
- CHECK(arg[i] != nullptr);
- args[i] = arg[i];
- }
-
- int dexopt_flags = atoi(arg[6]);
- DexoptFn dexopt_fn;
- if ((dexopt_flags & DEXOPT_OTA) != 0) {
- dexopt_fn = do_ota_dexopt;
- } else {
- dexopt_fn = do_regular_dexopt;
- }
- return dexopt_fn(args, reply);
-}
-
-static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
-{
- uid_t uid = static_cast<uid_t>(atoi(arg[0]));
- const char* pkgname = arg[1];
- if (merge_profiles(uid, pkgname)) {
- strncpy(reply, "true", REPLY_MAX);
- } else {
- strncpy(reply, "false", REPLY_MAX);
- }
- return 0;
-}
-
-static int do_dump_profiles(char **arg, char reply[REPLY_MAX])
-{
- uid_t uid = static_cast<uid_t>(atoi(arg[0]));
- const char* pkgname = arg[1];
- const char* dex_files = arg[2];
- if (dump_profile(uid, pkgname, dex_files)) {
- strncpy(reply, "true", REPLY_MAX);
- } else {
- strncpy(reply, "false", REPLY_MAX);
- }
- return 0;
-}
-
-static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return mark_boot_complete(arg[0] /* instruction set */);
-}
-
-static int do_rm_dex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return rm_dex(arg[0], arg[1]); /* pkgname, instruction_set */
-}
-
-static int do_free_cache(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) /* TODO int:free_size */
-{
- return free_cache(parse_null(arg[0]), (int64_t)atoll(arg[1])); /* uuid, free_size */
-}
-
-static int do_get_app_size(char **arg, char reply[REPLY_MAX]) {
- int64_t codesize = 0;
- int64_t datasize = 0;
- int64_t cachesize = 0;
- int64_t asecsize = 0;
- int res = 0;
-
- /* const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
- const char* code_path */
- res = get_app_size(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]),
- arg[5], &codesize, &datasize, &cachesize, &asecsize);
-
- /*
- * Each int64_t can take up 22 characters printed out. Make sure it
- * doesn't go over REPLY_MAX in the future.
- */
- snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64,
- codesize, datasize, cachesize, asecsize);
- return res;
-}
-
-static int do_get_app_data_inode(char **arg, char reply[REPLY_MAX]) {
- ino_t inode = 0;
- int res = 0;
-
- /* const char *uuid, const char *pkgname, int userid, int flags */
- res = get_app_data_inode(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), &inode);
-
- snprintf(reply, REPLY_MAX, "%" PRId64, (int64_t) inode);
- return res;
-}
-
-static int do_move_complete_app(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- /* const char* from_uuid, const char *to_uuid, const char *package_name,
- const char *data_app_name, appid_t appid, const char* seinfo,
- int target_sdk_version */
- return move_complete_app(parse_null(arg[0]), parse_null(arg[1]), arg[2], arg[3],
- atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_create_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* const char *uuid, userid_t userid, int user_serial, int flags */
- return create_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_destroy_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* const char *uuid, userid_t userid, int flags */
- return destroy_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]));
-}
-
-static int do_linklib(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return linklib(parse_null(arg[0]), arg[1], arg[2], atoi(arg[3]));
-}
-
-static int do_idmap(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- return idmap(arg[0], arg[1], atoi(arg[2]));
-}
-
-static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* oat_dir, instruction_set */
- return create_oat_dir(arg[0], arg[1]);
-}
-
-static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* oat_dir */
- return rm_package_dir(arg[0]);
-}
-
-static int do_clear_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* package_name */
- return clear_app_profiles(arg[0]);
-}
-
-static int do_destroy_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* package_name */
- return destroy_app_profiles(arg[0]);
-}
-
-static int do_link_file(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
- /* relative_path, from_base, to_base */
- return link_file(arg[0], arg[1], arg[2]);
-}
-
-static int do_move_ab(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // apk_path, instruction_set, oat_dir
- return move_ab(arg[0], arg[1], arg[2]);
-}
-
-static int do_delete_odex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // apk_path, instruction_set, oat_dir
- return delete_odex(arg[0], arg[1], arg[2]) ? 0 : -1;
-}
-
-struct cmdinfo {
- const char *name;
- unsigned numargs;
- int (*func)(char **arg, char reply[REPLY_MAX]);
-};
-
-struct cmdinfo cmds[] = {
- { "ping", 0, do_ping },
-
- { "create_app_data", 7, do_create_app_data },
- { "restorecon_app_data", 6, do_restorecon_app_data },
- { "migrate_app_data", 4, do_migrate_app_data },
- { "clear_app_data", 5, do_clear_app_data },
- { "destroy_app_data", 5, do_destroy_app_data },
- { "move_complete_app", 7, do_move_complete_app },
- { "get_app_size", 6, do_get_app_size },
- { "get_app_data_inode", 4, do_get_app_data_inode },
-
- { "create_user_data", 4, do_create_user_data },
- { "destroy_user_data", 3, do_destroy_user_data },
-
- { "dexopt", 10, do_dexopt },
- { "markbootcomplete", 1, do_mark_boot_complete },
- { "rmdex", 2, do_rm_dex },
- { "freecache", 2, do_free_cache },
- { "linklib", 4, do_linklib },
- { "idmap", 3, do_idmap },
- { "createoatdir", 2, do_create_oat_dir },
- { "rmpackagedir", 1, do_rm_package_dir },
- { "clear_app_profiles", 1, do_clear_app_profiles },
- { "destroy_app_profiles", 1, do_destroy_app_profiles },
- { "linkfile", 3, do_link_file },
- { "move_ab", 3, do_move_ab },
- { "merge_profiles", 2, do_merge_profiles },
- { "dump_profiles", 3, do_dump_profiles },
- { "delete_odex", 3, do_delete_odex },
-};
-
-static int readx(int s, void *_buf, int count)
-{
- char *buf = (char *) _buf;
- int n = 0, r;
- if (count < 0) return -1;
- while (n < count) {
- r = read(s, buf + n, count - n);
- if (r < 0) {
- if (errno == EINTR) continue;
- ALOGE("read error: %s\n", strerror(errno));
- return -1;
- }
- if (r == 0) {
- ALOGE("eof\n");
- return -1; /* EOF */
- }
- n += r;
- }
- return 0;
-}
-
-static int writex(int s, const void *_buf, int count)
-{
- const char *buf = (const char *) _buf;
- int n = 0, r;
- if (count < 0) return -1;
- while (n < count) {
- r = write(s, buf + n, count - n);
- if (r < 0) {
- if (errno == EINTR) continue;
- ALOGE("write error: %s\n", strerror(errno));
- return -1;
- }
- n += r;
- }
- return 0;
-}
-
-
-/* Tokenize the command buffer, locate a matching command,
- * ensure that the required number of arguments are provided,
- * call the function(), return the result.
- */
-static int execute(int s, char cmd[BUFFER_MAX])
-{
- char reply[REPLY_MAX];
- char *arg[TOKEN_MAX+1];
- unsigned i;
- unsigned n = 0;
- unsigned short count;
- int ret = -1;
-
- // ALOGI("execute('%s')\n", cmd);
-
- /* default reply is "" */
- reply[0] = 0;
-
- /* n is number of args (not counting arg[0]) */
- arg[0] = cmd;
- while (*cmd) {
- if (isspace(*cmd)) {
- *cmd++ = 0;
- n++;
- arg[n] = cmd;
- if (n == TOKEN_MAX) {
- ALOGE("too many arguments\n");
- goto done;
- }
- }
- if (*cmd) {
- cmd++;
- }
- }
-
- for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
- if (!strcmp(cmds[i].name,arg[0])) {
- if (n != cmds[i].numargs) {
- ALOGE("%s requires %d arguments (%d given)\n",
- cmds[i].name, cmds[i].numargs, n);
- } else {
- ret = cmds[i].func(arg + 1, reply);
- }
- goto done;
- }
- }
- ALOGE("unsupported command '%s'\n", arg[0]);
-
-done:
- if (reply[0]) {
- n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
- } else {
- n = snprintf(cmd, BUFFER_MAX, "%d", ret);
- }
- if (n > BUFFER_MAX) n = BUFFER_MAX;
- count = n;
-
- // ALOGI("reply: '%s'\n", cmd);
- if (writex(s, &count, sizeof(count))) return -1;
- if (writex(s, cmd, count)) return -1;
- return 0;
-}
-
static bool initialize_globals() {
const char* data_path = getenv("ANDROID_DATA");
if (data_path == nullptr) {
@@ -702,16 +306,13 @@
}
static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
- char buf[BUFFER_MAX];
- struct sockaddr addr;
- socklen_t alen;
- int lsocket, s;
+ int ret;
int selinux_enabled = (is_selinux_enabled() > 0);
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(argv);
- ALOGI("installd firing up\n");
+ LOG(INFO) << "installd firing up";
union selinux_callback cb;
cb.func_log = log_callback;
@@ -732,50 +333,14 @@
exit(1);
}
- lsocket = android_get_control_socket(SOCKET_PATH);
- if (lsocket < 0) {
- ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
+ if ((ret = InstalldNativeService::start()) != android::OK) {
+ ALOGE("Unable to start InstalldNativeService: %d", ret);
exit(1);
}
- if (listen(lsocket, 5)) {
- ALOGE("Listen on socket failed: %s\n", strerror(errno));
- exit(1);
- }
- fcntl(lsocket, F_SETFD, FD_CLOEXEC);
- for (;;) {
- alen = sizeof(addr);
- s = accept(lsocket, &addr, &alen);
- if (s < 0) {
- ALOGE("Accept failed: %s\n", strerror(errno));
- continue;
- }
- fcntl(s, F_SETFD, FD_CLOEXEC);
+ IPCThreadState::self()->joinThreadPool();
- ALOGI("new connection\n");
- for (;;) {
- unsigned short count;
- if (readx(s, &count, sizeof(count))) {
- ALOGE("failed to read size\n");
- break;
- }
- if ((count < 1) || (count >= BUFFER_MAX)) {
- ALOGE("invalid size %d\n", count);
- break;
- }
- if (readx(s, buf, count)) {
- ALOGE("failed to read command\n");
- break;
- }
- buf[count] = 0;
- if (selinux_enabled && selinux_status_updated() > 0) {
- selinux_android_seapp_context_reload();
- }
- if (execute(s, buf)) break;
- }
- ALOGI("closing connection\n");
- close(s);
- }
+ LOG(INFO) << "installd shutting down";
return 0;
}
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 5e4c925..d5d5236 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,3 +1,2 @@
service installd /system/bin/installd
class main
- socket installd stream 600 system system
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index b0bcce9..401e581 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -42,7 +42,6 @@
constexpr int DEXOPT_DEBUGGABLE = 1 << 3;
constexpr int DEXOPT_BOOTCOMPLETE = 1 << 4;
constexpr int DEXOPT_PROFILE_GUIDED = 1 << 5;
-constexpr int DEXOPT_OTA = 1 << 6;
/* all known values for dexopt flags */
constexpr int DEXOPT_MASK =
@@ -50,8 +49,7 @@
| DEXOPT_SAFEMODE
| DEXOPT_DEBUGGABLE
| DEXOPT_BOOTCOMPLETE
- | DEXOPT_PROFILE_GUIDED
- | DEXOPT_OTA;
+ | DEXOPT_PROFILE_GUIDED;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 7e02b6b..d53018f 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -36,13 +36,14 @@
#include <cutils/properties.h>
#include <private/android_filesystem_config.h>
-#include <commands.h>
-#include <file_parsing.h>
-#include <globals.h>
-#include <installd_deps.h> // Need to fill in requirements of commands.
-#include <otapreopt_utils.h>
-#include <system_properties.h>
-#include <utils.h>
+#include "InstalldNativeService.h"
+#include "dexopt.h"
+#include "file_parsing.h"
+#include "globals.h"
+#include "installd_deps.h" // Need to fill in requirements of commands.
+#include "otapreopt_utils.h"
+#include "system_properties.h"
+#include "utils.h"
#ifndef LOG_TAG
#define LOG_TAG "otapreopt"
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 5ea89e6..cec8f68 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -25,8 +25,9 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
-#include <commands.h>
-#include <otapreopt_utils.h>
+#include "installd_constants.h"
+#include "otapreopt_utils.h"
+#include "dexopt.h"
#ifndef LOG_TAG
#define LOG_TAG "otapreopt"
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 447c8bd..a32df22 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -5,8 +5,8 @@
srcs: ["installd_utils_test.cpp"],
shared_libs: [
"libbase",
- "libutils",
"liblog",
+ "libutils",
"libcutils",
],
static_libs: [
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 9b2de88..947cc0d 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -19,9 +19,9 @@
#include <gtest/gtest.h>
-#include <commands.h>
-#include <globals.h>
-#include <utils.h>
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
#undef LOG_TAG
#define LOG_TAG "utils_test"
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 8123e9b..ead73fe 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -73,7 +73,6 @@
std::string create_data_path(const char* volume_uuid);
std::string create_data_app_path(const char* volume_uuid);
-
std::string create_data_app_package_path(const char* volume_uuid, const char* package_name);
std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid);
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 68e3ceb..ea41558 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -60,7 +60,6 @@
return 1;
}
-static int selinux_enabled;
static char *service_manager_context;
static struct selabel_handle* sehandle;
@@ -89,10 +88,6 @@
static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm)
{
- if (selinux_enabled <= 0) {
- return true;
- }
-
return check_mac_perms(spid, uid, service_manager_context, perm, NULL);
}
@@ -101,10 +96,6 @@
bool allowed;
char *tctx = NULL;
- if (selinux_enabled <= 0) {
- return true;
- }
-
if (!sehandle) {
ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n");
abort();
@@ -384,20 +375,17 @@
return -1;
}
- selinux_enabled = is_selinux_enabled();
sehandle = selinux_android_service_context_handle();
selinux_status_open(true);
- if (selinux_enabled > 0) {
- if (sehandle == NULL) {
- ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
- abort();
- }
+ if (sehandle == NULL) {
+ ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
+ abort();
+ }
- if (getcon(&service_manager_context) != 0) {
- ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
- abort();
- }
+ if (getcon(&service_manager_context) != 0) {
+ ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
+ abort();
}
union selinux_callback cb;
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/gui/BufferQueue.h b/include/gui/BufferQueue.h
index f24f135..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.
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 65dea0d..5541468 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -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,
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..0e95ec3 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -17,29 +17,256 @@
#ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
#define ANDROID_GUI_FRAMETIMESTAMPS_H
-#include <utils/Timers.h>
+#include <ui/Fence.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) {}
- uint64_t frameNumber;
- nsecs_t postedTime;
- nsecs_t acquireTime;
- nsecs_t refreshStartTime;
- nsecs_t glCompositionDoneTime;
- nsecs_t displayRetireTime;
- nsecs_t releaseTime;
+struct FrameEvents;
+class FrameEventHistoryDelta;
+class String8;
+
+
+enum class FrameEvent {
+ POSTED,
+ REQUESTED_PRESENT,
+ LATCH,
+ ACQUIRE,
+ FIRST_REFRESH_START,
+ LAST_REFRESH_START,
+ GL_COMPOSITION_DONE,
+ DISPLAY_PRESENT,
+ DISPLAY_RETIRE,
+ 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;
+
+ 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{0};
+ nsecs_t requestedPresentTime{0};
+ nsecs_t latchTime{0};
+ nsecs_t firstRefreshStartTime{0};
+ nsecs_t lastRefreshStartTime{0};
+
+ nsecs_t acquireTime{0};
+ nsecs_t gpuCompositionDoneTime{0};
+ nsecs_t displayPresentTime{0};
+ nsecs_t displayRetireTime{0};
+ nsecs_t releaseTime{0};
+
+ sp<Fence> acquireFence{Fence::NO_FENCE};
+ sp<Fence> gpuCompositionDoneFence{Fence::NO_FENCE};
+ sp<Fence> displayPresentFence{Fence::NO_FENCE};
+ sp<Fence> displayRetireFence{Fence::NO_FENCE};
+ sp<Fence> releaseFence{Fence::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;
+
+ void updateAcquireFence(uint64_t frameNumber, sp<Fence> acquire);
+ void applyDelta(const FrameEventHistoryDelta& delta);
+
+private:
+ size_t mAcquireOffset{0};
+};
+
+
+// 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};
+ sp<Fence> acquireFence{Fence::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,
+ sp<Fence> gpuCompositionDone, sp<Fence> displayPresent);
+ void addRetire(uint64_t frameNumber, sp<Fence> displayRetire);
+ void addRelease(uint64_t frameNumber, sp<Fence> release);
+
+ void getAndResetDelta(FrameEventHistoryDelta* delta);
+
+private:
+ std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty;
+ size_t mQueueOffset{0};
+ size_t mCompositionOffset{0};
+ size_t mRetireOffset{0};
+ size_t mReleaseOffset{0};
+};
+
+
+// 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);
+
+ // 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};
+
+ sp<Fence> mGpuCompositionDoneFence{Fence::NO_FENCE};
+ sp<Fence> mDisplayPresentFence{Fence::NO_FENCE};
+ sp<Fence> mDisplayRetireFence{Fence::NO_FENCE};
+ sp<Fence> mReleaseFence{Fence::NO_FENCE};
+
+ // 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:
+ // 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..6ff1303 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -250,7 +250,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) {
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 0ab7590..93dd4ac 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -82,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/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 982cc9d..492db35 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;
@@ -349,41 +359,23 @@
uint32_t stickyTransform{0};
sp<Fence> fence;
Region surfaceDamage;
+ bool getFrameTimestamps{false};
};
- // QueueBufferOutput must be a POD structure
- struct 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:
+ struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
+ // 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,
@@ -580,13 +572,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 a3ee798..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.
@@ -193,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,
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 1d97d8b..a10dad1 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -134,10 +134,17 @@
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,
+ status_t getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
nsecs_t* outReleaseTime);
status_t getUniqueId(uint64_t* outId) const;
@@ -191,6 +198,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:
@@ -238,6 +246,8 @@
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
private:
+ void querySupportedTimestampsLocked() const;
+
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
@@ -380,6 +390,15 @@
Condition mQueueBufferCondition;
uint64_t mNextFrameNumber;
+
+ // 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;
+ ProducerFrameEventHistory mFrameEventHistory;
};
namespace view {
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index b18fd54..4556c8f 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/include/media/openmax/OMX_AsString.h
@@ -534,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";
@@ -548,6 +550,8 @@
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_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index 63fbff8..d0ae867 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -76,14 +76,14 @@
OMX_IndexConfigVideoVp8ReferenceFrame, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMETYPE */
OMX_IndexConfigVideoVp8ReferenceFrameType, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE */
OMX_IndexParamVideoAndroidVp8Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE */
- OMX_IndexParamVideoVp9, /**< reference: OMX_VIDEO_PARAM_VP9TYPE */
- OMX_IndexParamVideoAndroidVp9Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP9ENCODERTYPE */
OMX_IndexParamVideoHevc, /**< reference: OMX_VIDEO_PARAM_HEVCTYPE */
OMX_IndexParamSliceSegments, /**< reference: OMX_VIDEO_SLICESEGMENTSTYPE */
OMX_IndexConfigAndroidIntraRefresh, /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */
OMX_IndexParamAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */
OMX_IndexConfigAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */
OMX_IndexParamMaxFrameDurationForBitrateControl,/**< reference: OMX_PARAM_U32TYPE */
+ OMX_IndexParamVideoVp9, /**< reference: OMX_VIDEO_PARAM_VP9TYPE */
+ OMX_IndexParamVideoAndroidVp9Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP9ENCODERTYPE */
OMX_IndexExtVideoEndUnused,
/* Image & Video common configurations */
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index 1df15f8..99b39d8 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -42,6 +42,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.
@@ -94,8 +99,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
diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h
new file mode 100644
index 0000000..27cc720
--- /dev/null
+++ b/include/ui/FenceTime.h
@@ -0,0 +1,161 @@
+/*
+ * 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>
+
+namespace android {
+
+// 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 {
+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;
+
+ // 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:
+ enum class State {
+ VALID,
+ INVALID,
+ };
+
+ 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;
+};
+
+}; // namespace android
+
+#endif // ANDROID_FENCE_TIME_H
diff --git a/include/ui/GrallocAllocator.h b/include/ui/GrallocAllocator.h
index e3c4248..5645bed 100644
--- a/include/ui/GrallocAllocator.h
+++ b/include/ui/GrallocAllocator.h
@@ -32,6 +32,7 @@
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.
@@ -40,12 +41,12 @@
Allocator();
// this will be removed and Allocator will be always valid
- bool valid() const { return (mService != nullptr); }
+ bool valid() const { return (mAllocator != nullptr); }
std::string dumpDebugInfo() const;
Error createBufferDescriptor(
- const IAllocator::BufferDescriptorInfo& descriptorInfo,
+ const IAllocatorClient::BufferDescriptorInfo& descriptorInfo,
BufferDescriptor& descriptor) const;
void destroyBufferDescriptor(BufferDescriptor descriptor) const;
@@ -56,7 +57,8 @@
native_handle_t*& bufferHandle) const;
private:
- sp<IAllocator> mService;
+ sp<IAllocator> mAllocator;
+ sp<IAllocatorClient> mClient;
};
} // namespace Gralloc2
diff --git a/include/ui/mat3.h b/include/ui/mat3.h
index cd24a44..4f5dba9 100644
--- a/include/ui/mat3.h
+++ b/include/ui/mat3.h
@@ -292,7 +292,7 @@
m_value[2] = col_type(0, 0, v.z);
}
-// construct from 16 scalars. Note that the arrangement
+// construct from 9 scalars. Note that the arrangement
// of values in the constructor is the transpose of the matrix
// notation.
template<typename T>
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 2062b3b..3aeff2e 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -135,10 +135,12 @@
{
unsigned n;
for (n = 0; n < 5; n++){
+ if (n > 0) {
+ ALOGI("Waiting for service %s...", String8(name).string());
+ sleep(1);
+ }
sp<IBinder> svc = checkService(name);
if (svc != NULL) return svc;
- ALOGI("Waiting for service %s...\n", String8(name).string());
- sleep(1);
}
return NULL;
}
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 9ba85a6..bf758ce 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -71,6 +71,7 @@
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DisplayEventReceiver.cpp",
+ "FrameTimestamps.cpp",
"GLConsumer.cpp",
"GraphicBufferAlloc.cpp",
"GuiConfig.cpp",
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 47f5eba..f76a282 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,13 +61,13 @@
}
}
-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,
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 81d4f31..75198d7 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -348,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);
@@ -560,6 +561,8 @@
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+ addAndGetFrameTimestamps(nullptr, outTimestamps);
+
return returnFlags;
}
@@ -740,19 +743,21 @@
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;
}
@@ -771,6 +776,7 @@
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
+ uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -809,8 +815,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);
@@ -828,11 +835,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;
@@ -842,12 +852,12 @@
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.mIsDroppable = mCore->mAsyncMode ||
mCore->mDequeueBufferCannotBlock ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
@@ -908,10 +918,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()));
@@ -958,10 +969,21 @@
// small trade-off in favor of latency rather than throughput.
mLastQueueBufferFence->waitForever("Throttling EGL Production");
}
- mLastQueueBufferFence = fence;
+ mLastQueueBufferFence = acquireFence;
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
+ // Update and get FrameEventHistory.
+ nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ NewFrameEventsEntry newFrameEventsEntry = {
+ currentFrameNumber,
+ postedTime,
+ requestedPresentTimestamp,
+ acquireFence
+ };
+ addAndGetFrameTimestamps(&newFrameEventsEntry,
+ getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
return NO_ERROR;
}
@@ -1126,10 +1148,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
@@ -1449,20 +1474,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/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..30ff65f
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,648 @@
+/*
+* 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 <inttypes.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+#include <numeric>
+
+namespace android {
+
+static inline bool isValidTimestamp(nsecs_t time) {
+ return time > 0 && time < INT64_MAX;
+}
+
+// ============================================================================
+// FrameEvents
+// ============================================================================
+
+bool FrameEvents::hasPostedInfo() const {
+ return isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+ return isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+ return isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+ return 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::hasAcquireInfo() const {
+ return isValidTimestamp(acquireTime) || 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;
+}
+
+static void checkFenceForCompletion(sp<Fence>* fence, nsecs_t* dstTime) {
+ if ((*fence)->isValid()) {
+ nsecs_t time = (*fence)->getSignalTime();
+ if (isValidTimestamp(time)) {
+ *dstTime = time;
+ *fence = Fence::NO_FENCE;
+ }
+ }
+}
+
+void FrameEvents::checkFencesForCompletion() {
+ checkFenceForCompletion(&acquireFence, &acquireTime);
+ checkFenceForCompletion(&gpuCompositionDoneFence, &gpuCompositionDoneTime);
+ checkFenceForCompletion(&displayPresentFence, &displayPresentTime);
+ checkFenceForCompletion(&displayRetireFence, &displayRetireTime);
+ checkFenceForCompletion(&releaseFence, &releaseTime);
+}
+
+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 (isValidTimestamp(latchTime)) {
+ outString.appendFormat("%" PRId64 "\n", latchTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (First)\t");
+ if (isValidTimestamp(firstRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (Last)\t");
+ if (isValidTimestamp(lastRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Acquire \t");
+ if (isValidTimestamp(acquireTime)) {
+ outString.appendFormat("%" PRId64 "\n", acquireTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- GPU Composite Done\t");
+ if (isValidTimestamp(gpuCompositionDoneTime)) {
+ outString.appendFormat("%" PRId64 "\n", gpuCompositionDoneTime);
+ } else if (!addPostCompositeCalled || gpuCompositionDoneFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Display Present\t");
+ if (isValidTimestamp(displayPresentTime)) {
+ outString.appendFormat("%" PRId64 "\n", displayPresentTime);
+ } else if (!addPostCompositeCalled || displayPresentFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Display Retire\t");
+ if (isValidTimestamp(displayRetireTime)) {
+ outString.appendFormat("%" PRId64 "\n", displayRetireTime);
+ } else if (!addRetireCalled || displayRetireFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Release \t");
+ if (isValidTimestamp(releaseTime)) {
+ outString.appendFormat("%" PRId64 "\n", releaseTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+}
+
+
+// ============================================================================
+// 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, sp<Fence> acquire) {
+ FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+ if (frame == nullptr) {
+ ALOGE("ProducerFrameEventHistory::updateAcquireFence: "
+ "Did not find frame.");
+ return;
+ }
+
+ if (acquire->isValid()) {
+ frame->acquireFence = acquire;
+ } else {
+ // If there isn't an acquire fence, assume that buffer was
+ // ready for the consumer when posted.
+ frame->acquireTime = frame->postedTime;
+ }
+}
+
+static void applyFenceDelta(sp<Fence>* dst, const sp<Fence>& src) {
+ if (src->isValid()) {
+ if ((*dst)->isValid()) {
+ ALOGE("applyFenceDelta: Unexpected fence.");
+ }
+ *dst = src;
+ }
+}
+
+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;
+
+ if (frame.frameNumber == d.mFrameNumber) {
+ // Existing frame. Merge.
+ // Consumer never sends timestamps of fences, only the fences
+ // themselves, so we never need to update the fence timestamps here.
+ applyFenceDelta(
+ &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
+ applyFenceDelta(&frame.displayPresentFence, d.mDisplayPresentFence);
+ applyFenceDelta(&frame.displayRetireFence, d.mDisplayRetireFence);
+ applyFenceDelta(&frame.releaseFence, d.mReleaseFence);
+ } else {
+ // New frame. Overwrite.
+ frame.frameNumber = d.mFrameNumber;
+
+ frame.gpuCompositionDoneFence = d.mGpuCompositionDoneFence;
+ frame.displayPresentFence = d.mDisplayPresentFence;
+ frame.displayRetireFence = d.mDisplayRetireFence;
+ frame.releaseFence = d.mReleaseFence;
+
+ // Set aquire fence and time at this point.
+ frame.acquireTime = 0;
+ frame.acquireFence = Fence::NO_FENCE;
+
+ // Reset fence-related timestamps
+ frame.gpuCompositionDoneTime = 0;
+ frame.displayPresentTime = 0;
+ frame.displayRetireTime = 0;
+ frame.releaseTime = 0;
+
+ // The consumer only sends valid frames.
+ frame.valid = true;
+ }
+ }
+}
+
+
+// ============================================================================
+// 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("ConsumerFrameEventHistory::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("ConsumerFrameEventHistory::addPreComposition: "
+ "Did not find frame.");
+ return;
+ }
+ frame->lastRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+ if (!isValidTimestamp(frame->firstRefreshStartTime)) {
+ frame->firstRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+ }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+ sp<Fence> gpuCompositionDone, sp<Fence> displayPresent) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::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, sp<Fence> displayRetire) {
+ FrameEvents* frame = getFrame(frameNumber, &mRetireOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addRetire: Did not find frame.");
+ return;
+ }
+ frame->addRetireCalled = true;
+ frame->displayRetireFence = displayRetire;
+ mFramesDirty[mRetireOffset].setDirty<FrameEvent::DISPLAY_RETIRE>();
+}
+
+void ConsumerFrameEventHistory::addRelease(
+ uint64_t frameNumber, sp<Fence> release) {
+ FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addRelease: Did not find frame.");
+ return;
+ }
+ frame->addReleaseCalled = true;
+ frame->releaseFence = release;
+ mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+ FrameEventHistoryDelta* delta) {
+ delta->mDeltas.reserve(mFramesDirty.size());
+ for (size_t i = 0; i < mFramesDirty.size(); i++) {
+ if (mFramesDirty[i].anyDirty()) {
+ delta->mDeltas.push_back(
+ FrameEventsDelta(i, mFrames[i], mFramesDirty[i]));
+ mFramesDirty[i].reset();
+ }
+ }
+}
+
+
+// ============================================================================
+// 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) {
+ mGpuCompositionDoneFence =
+ dirtyFields.isDirty<FrameEvent::GL_COMPOSITION_DONE>() ?
+ frameTimestamps.gpuCompositionDoneFence : Fence::NO_FENCE;
+ mDisplayPresentFence = dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>() ?
+ frameTimestamps.displayPresentFence : Fence::NO_FENCE;
+ mDisplayRetireFence = dirtyFields.isDirty<FrameEvent::DISPLAY_RETIRE>() ?
+ frameTimestamps.displayRetireFence : Fence::NO_FENCE;
+ mReleaseFence = dirtyFields.isDirty<FrameEvent::RELEASE>() ?
+ frameTimestamps.releaseFence : Fence::NO_FENCE;
+}
+
+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);
+ 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 sp<Fence>* 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 sp<Fence>* 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);
+
+ // 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);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ *fence = new Fence;
+ status_t status = (*fence)->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+// ============================================================================
+// FrameEventHistoryDelta
+// ============================================================================
+
+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/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/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 846c205..e37b65b 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -118,24 +118,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 +222,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 +283,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 +440,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 +524,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 +579,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 +617,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 +722,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 +756,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 +783,7 @@
if (size < getFlattenedSize()) {
return NO_MEMORY;
}
+
FlattenableUtils::write(buffer, size, timestamp);
FlattenableUtils::write(buffer, size, isAutoTimestamp);
FlattenableUtils::write(buffer, size, dataSpace);
@@ -793,6 +791,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 +803,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 +814,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 +824,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 6c1662c..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;
@@ -520,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());
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 351d184..6a02a77 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)
+ mNextFrameNumber(1),
+ mQueriedSupportedTimestamps(false),
+ mFrameTimestampsSupportsPresent(false),
+ mFrameTimestampsSupportsRetire(false),
+ mEnableFrameTimestamps(false)
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -135,37 +139,80 @@
outTransformMatrix);
}
-bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+void Surface::enableFrameTimestamps(bool enable) {
+ Mutex::Autolock lock(mMutex);
+ mEnableFrameTimestamps = enable;
+}
+
+status_t Surface::getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
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);
+
+ // Update our cache of events if the requested events are not available.
+ if (events == nullptr ||
+ (outRequestedPresentTime && !events->hasRequestedPresentInfo()) ||
+ (outAcquireTime && !events->hasAcquireInfo()) ||
+ (outRefreshStartTime && !events->hasFirstRefreshStartInfo()) ||
+ (outGlCompositionDoneTime && !events->hasGpuCompositionDoneInfo()) ||
+ (outDisplayPresentTime && !events->hasDisplayPresentInfo()) ||
+ (outDisplayRetireTime && !events->hasDisplayRetireInfo()) ||
+ (outReleaseTime && !events->hasReleaseInfo())) {
+ FrameEventHistoryDelta delta;
+ mGraphicBufferProducer->getFrameTimestamps(&delta);
+ mFrameEventHistory.applyDelta(delta);
+ events = mFrameEventHistory.getFrame(frameNumber);
+ }
+
+ // A record for the requested frame does not exist.
+ if (events == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
+ events->checkFencesForCompletion();
+
+ if (outRequestedPresentTime) {
+ *outRequestedPresentTime = events->requestedPresentTime;
+ }
+ if (outAcquireTime) {
+ *outAcquireTime = events->acquireTime;
+ }
+ if (outRefreshStartTime) {
+ *outRefreshStartTime = events->firstRefreshStartTime;
+ }
+ if (outGlCompositionDoneTime) {
+ *outGlCompositionDoneTime = events->gpuCompositionDoneTime;
+ }
+ if (outDisplayPresentTime) {
+ *outDisplayPresentTime = events->displayPresentTime;
+ }
+ if (outDisplayRetireTime) {
+ *outDisplayRetireTime = events->displayRetireTime;
+ }
+ if (outReleaseTime) {
+ *outReleaseTime = events->releaseTime;
+ }
+
+ return NO_ERROR;
}
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,6 +318,7 @@
uint32_t reqHeight;
PixelFormat reqFormat;
uint32_t reqUsage;
+ bool enableFrameTimestamps;
{
Mutex::Autolock lock(mMutex);
@@ -281,6 +329,8 @@
reqFormat = mReqFormat;
reqUsage = mReqUsage;
+ enableFrameTimestamps = mEnableFrameTimestamps;
+
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -295,8 +345,13 @@
int buf = -1;
sp<Fence> fence;
nsecs_t now = systemTime();
+
+ FrameEventHistoryDelta frameTimestamps;
+ FrameEventHistoryDelta* frameTimestampsOrNull =
+ enableFrameTimestamps ? &frameTimestamps : nullptr;
+
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
- reqWidth, reqHeight, reqFormat, reqUsage);
+ reqWidth, reqHeight, reqFormat, reqUsage, frameTimestampsOrNull);
mLastDequeueDuration = systemTime() - now;
if (result < 0) {
@@ -317,6 +372,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 +494,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 +566,24 @@
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, fence);
+ }
+
+ 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 +599,32 @@
return err;
}
+void Surface::querySupportedTimestampsLocked() const {
+ // mMutex must be locked when calling this method.
+
+ if (mQueriedSupportedTimestamps) {
+ return;
+ }
+ mQueriedSupportedTimestamps = true;
+
+ std::vector<FrameEvent> supportedFrameTimestamps;
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ status_t err = composer->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");
@@ -595,6 +687,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 +772,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 +898,25 @@
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* outGlCompositionDoneTime = va_arg(args, int64_t*);
+ nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
nsecs_t* outDisplayRetireTime = 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, outRefreshStartTime,
+ outGlCompositionDoneTime, outDisplayPresentTime,
+ outDisplayRetireTime, outReleaseTime);
}
int Surface::connect(int api) {
@@ -819,17 +931,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;
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/ui/Android.bp b/libs/ui/Android.bp
index 0adfdea..3328a92 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -43,6 +43,7 @@
srcs: [
"ColorSpace.cpp",
"Fence.cpp",
+ "FenceTime.cpp",
"FrameStats.cpp",
"Gralloc1.cpp",
"Gralloc1On0Adapter.cpp",
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 7cf8233..a1dda3a 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -109,17 +109,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..c0245eb
--- /dev/null
+++ b/libs/ui/FenceTime.cpp
@@ -0,0 +1,282 @@
+/*
+* 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 <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();
+
+ // 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);
+}
+
+// ============================================================================
+// 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;
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/ui/GrallocAllocator.cpp b/libs/ui/GrallocAllocator.cpp
index 2eb1988..021122a 100644
--- a/libs/ui/GrallocAllocator.cpp
+++ b/libs/ui/GrallocAllocator.cpp
@@ -28,14 +28,25 @@
Allocator::Allocator()
{
- mService = IAllocator::getService("gralloc");
+ 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;
- mService->dumpDebugInfo([&](const auto& tmpInfo) {
+ mAllocator->dumpDebugInfo([&](const auto& tmpInfo) {
info = tmpInfo.c_str();
});
@@ -43,11 +54,11 @@
}
Error Allocator::createBufferDescriptor(
- const IAllocator::BufferDescriptorInfo& descriptorInfo,
+ const IAllocatorClient::BufferDescriptorInfo& descriptorInfo,
BufferDescriptor& descriptor) const
{
Error error = kDefaultError;
- mService->createDescriptor(descriptorInfo,
+ mClient->createDescriptor(descriptorInfo,
[&](const auto& tmpError, const auto& tmpDescriptor) {
error = tmpError;
if (error != Error::NONE) {
@@ -62,7 +73,7 @@
void Allocator::destroyBufferDescriptor(BufferDescriptor descriptor) const
{
- mService->destroyDescriptor(descriptor);
+ mClient->destroyDescriptor(descriptor);
}
Error Allocator::allocate(BufferDescriptor descriptor, Buffer& buffer) const
@@ -71,7 +82,7 @@
descriptors.setToExternal(&descriptor, 1);
Error error = kDefaultError;
- auto status = mService->allocate(descriptors,
+ auto status = mClient->allocate(descriptors,
[&](const auto& tmpError, const auto& tmpBuffers) {
error = tmpError;
if (tmpError != Error::NONE) {
@@ -86,14 +97,14 @@
void Allocator::free(Buffer buffer) const
{
- mService->free(buffer);
+ mClient->free(buffer);
}
Error Allocator::exportHandle(BufferDescriptor descriptor, Buffer buffer,
native_handle_t*& bufferHandle) const
{
Error error = kDefaultError;
- auto status = mService->exportHandle(descriptor, buffer,
+ auto status = mClient->exportHandle(descriptor, buffer,
[&](const auto& tmpError, const auto& tmpBufferHandle) {
error = tmpError;
if (tmpError != Error::NONE) {
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index e333bc1..d258586 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -106,7 +106,7 @@
PixelFormat format, uint32_t layerCount, uint32_t usage)
: mAllocator(allocator), mBufferValid(false)
{
- Gralloc2::IAllocator::BufferDescriptorInfo info = {};
+ Gralloc2::IAllocatorClient::BufferDescriptorInfo info = {};
info.width = width;
info.height = height;
info.format = static_cast<Gralloc2::PixelFormat>(format);
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 1c6b4c2..73e5e07 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -632,12 +632,13 @@
#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_DISPLAY_PRESENT_TIME_ANDROID 0x3432
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3433
+#define EGL_READS_DONE_TIME_ANDROID 0x3434
#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/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 0bfefd0..8d9fa5a 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1204,6 +1204,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 :
@@ -1212,8 +1215,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
@@ -1839,12 +1847,6 @@
if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID) {
usage |= GRALLOC_USAGE_HW_TEXTURE;
}
- // The buffer must be used for either a texture or a
- // renderbuffer.
- if ((value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) &&
- (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID)) {
- return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
- }
break;
default:
return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
@@ -2021,34 +2023,32 @@
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* GLCompositionDoneTime = nullptr;
+ nsecs_t* displayPresentTime = nullptr;
nsecs_t* displayRetireTime = 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];
@@ -2059,6 +2059,9 @@
case EGL_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;
@@ -2066,21 +2069,29 @@
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, refreshStartTime,
+ GLCompositionDoneTime, displayPresentTime, displayRetireTime,
+ 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,
@@ -2090,25 +2101,41 @@
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_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 9b2bbbc..4c5551c 100644
--- a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
+++ b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
@@ -129,12 +129,10 @@
desirable) do not route the entire composition to the external sink.
EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID: The buffer will be
- used to create a renderbuffer. This flag must not be set if
- EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID is set.
+ used to create a color-renderable texture.
EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID: The buffer will be used to
- create a texture. This flag must not be set if
- EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID is set.
+ create a filterable texture.
Errors
@@ -154,11 +152,6 @@
with the value of EGL_FORMAT, the error EGL_BAD_PARAMETER is
Generated.
- * If both the EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID and
- EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID are set in the value of
- EGL_NATIVE_BUFFER_USAGE_ANDROID, the error EGL_BAD_PARAMETER is
- Generated."
-
Issues
1. Should this extension define what combinations of formats and usage flags
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index 30337ad..b5b6eb5 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,13 @@
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_DISPLAY_PRESENT_TIME_ANDROID 0x3432
+ EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3433
+ EGL_READS_DONE_TIME_ANDROID 0x3434
Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
"Surface Attributes", page 43:
@@ -98,9 +99,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,8 +113,10 @@
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
@@ -122,12 +125,14 @@
compositor's rendering work for this frame finished. This will be zero
if composition was handled by the display and the compositor didn't do
any rendering.
+ - EGL_DISPLAY_PRESENT_TIME_ANDROID - The time at which this frame
+ started to scan out on the physical display.
- EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
replaced by the next frame on-screen.
- 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 +148,7 @@
#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.
diff --git a/opengl/specs/README b/opengl/specs/README
index f0c024e..1ee99fb 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -20,10 +20,11 @@
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)
+0x3432 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3433 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3434 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3435 - 0x343F (unused)
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/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/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp
index 81f32cd..452c8c6 100644
--- a/services/sensorservice/BatteryService.cpp
+++ b/services/sensorservice/BatteryService.cpp
@@ -30,12 +30,7 @@
namespace android {
// ---------------------------------------------------------------------------
-BatteryService::BatteryService() {
- const sp<IServiceManager> sm(defaultServiceManager());
- if (sm != NULL) {
- const String16 name("batterystats");
- mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
- }
+BatteryService::BatteryService() : mBatteryStatService(nullptr) {
}
bool BatteryService::addSensor(uid_t uid, int handle) {
@@ -61,7 +56,7 @@
void BatteryService::enableSensorImpl(uid_t uid, int handle) {
- if (mBatteryStatService != 0) {
+ if (checkService()) {
if (addSensor(uid, handle)) {
int64_t identity = IPCThreadState::self()->clearCallingIdentity();
mBatteryStatService->noteStartSensor(uid, handle);
@@ -70,7 +65,7 @@
}
}
void BatteryService::disableSensorImpl(uid_t uid, int handle) {
- if (mBatteryStatService != 0) {
+ if (checkService()) {
if (removeSensor(uid, handle)) {
int64_t identity = IPCThreadState::self()->clearCallingIdentity();
mBatteryStatService->noteStopSensor(uid, handle);
@@ -80,7 +75,7 @@
}
void BatteryService::cleanupImpl(uid_t uid) {
- if (mBatteryStatService != 0) {
+ if (checkService()) {
Mutex::Autolock _l(mActivationsLock);
int64_t identity = IPCThreadState::self()->clearCallingIdentity();
for (size_t i=0 ; i<mActivations.size() ; i++) {
@@ -95,6 +90,17 @@
}
}
+bool BatteryService::checkService() {
+ if (mBatteryStatService == nullptr) {
+ const sp<IServiceManager> sm(defaultServiceManager());
+ if (sm != NULL) {
+ const String16 name("batterystats");
+ mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
+ }
+ }
+ return mBatteryStatService != nullptr;
+}
+
ANDROID_SINGLETON_STATIC_INSTANCE(BatteryService)
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/BatteryService.h b/services/sensorservice/BatteryService.h
index 08ba857..43a750c 100644
--- a/services/sensorservice/BatteryService.h
+++ b/services/sensorservice/BatteryService.h
@@ -49,6 +49,7 @@
SortedVector<Info> mActivations;
bool addSensor(uid_t uid, int handle);
bool removeSensor(uid_t uid, int handle);
+ bool checkService();
public:
static void enableSensor(uid_t uid, int handle) {
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 4369fd2..20fe0fd 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -10,7 +10,6 @@
DispSync.cpp \
EventControlThread.cpp \
EventThread.cpp \
- FenceTracker.cpp \
FrameTracker.cpp \
GpuService.cpp \
Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 18c7945..61c231d 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -205,7 +205,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 31af8a1..dd909aa 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -924,21 +924,21 @@
return Error::None;
}
-Error Display::present(sp<Fence>* outRetireFence)
+Error Display::present(sp<Fence>* outPresentFence)
{
- int32_t retireFenceFd = 0;
+ int32_t presentFenceFd = 0;
#ifdef BYPASS_IHWC
int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
- &retireFenceFd);
+ &presentFenceFd);
#else
- auto intError = mDevice.mComposer->presentDisplay(mId, retireFenceFd);
+ 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;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 1c709b2..33fb8cb 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -313,7 +313,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(
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 40c6715..1727bd6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -808,7 +808,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) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index daa988c..aa96004 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -138,8 +138,7 @@
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));
@@ -151,7 +150,6 @@
}
private:
- mutable std::mutex mMutex;
std::queue<sp<Fence>> mFences;
};
@@ -310,14 +308,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});
@@ -335,7 +325,6 @@
mTypeChanges;
std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
mLayerRequests;
- std::unordered_set<HWC2::DisplayRequest> mDisplayRequests;
};
std::shared_ptr<const Config>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 82a900c..c82b0c4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -309,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>>
@@ -408,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;
}
@@ -424,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);
@@ -432,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));
}
}
@@ -593,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::retireFenceRepresentsStartOfScanout() const {
+ return mAdapter ? false : true;
}
sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId,
@@ -615,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)) {
@@ -624,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;
@@ -873,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..e63bdd4 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 retire 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 retireFenceRepresentsStartOfScanout() 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/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 2190466..0511df2 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
@@ -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());
@@ -618,10 +616,8 @@
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);
+ mQueueBufferOutput = qbo;
+ mQueueBufferOutput.transformHint = 0;
}
void VirtualDisplaySurface::resetPerFrameState() {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 70f717f..b435bf5 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);
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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5f96f66..9ae9752 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -80,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),
@@ -904,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;
}
@@ -976,7 +978,7 @@
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());
@@ -986,8 +988,8 @@
}
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,
@@ -1729,50 +1731,87 @@
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(sp<Fence> glDoneFence) {
+ // mFrameLatencyNeeded is true when a new frame was latched for the
+ // composition.
- 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);
- }
+ if (!mFrameLatencyNeeded)
+ return false;
- const HWComposer& hwc = mFlinger->getHwComposer();
+ 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;
+ sp<Fence> retireFence = Fence::NO_FENCE;
+ sp<Fence> presentFence = Fence::NO_FENCE;
+ sp<Fence> presentOrRetireFence = Fence::NO_FENCE;
+ if (hwc.retireFenceRepresentsStartOfScanout()) {
+ presentFence = hwc.getPresentFence(HWC_DISPLAY_PRIMARY);
+ presentOrRetireFence = presentFence;
+ } else {
+ retireFence = hwc.getPresentFence(HWC_DISPLAY_PRIMARY);
+ presentOrRetireFence = retireFence;
}
- return frameLatencyNeeded;
+ bool wasGpuComposited = mHwcLayers.count(HWC_DISPLAY_PRIMARY) ?
+ mHwcLayers.at(HWC_DISPLAY_PRIMARY).compositionType ==
+ HWC2::Composition::Client : true;
+#else
+ sp<Fence> retireFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+ sp<Fence> presentFence = Fence::NO_FENCE;
+ sp<Fence> presentOrRetireFence = retireFence;
+ bool wasGpuComposited = mIsGlesComposition;
+#endif
+
+ // Update mFrameEventHistory.
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addPostComposition(mCurrentFrameNumber,
+ wasGpuComposited ? glDoneFence : Fence::NO_FENCE,
+ presentFence);
+ mFrameEventHistory.addRetire(mPreviousFrameNumber,
+ retireFence);
+ }
+
+ // Update mFrameTracker.
+ nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
+ mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+ 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);
+ }
+
+ if (presentOrRetireFence->isValid()) {
+ mFrameTracker.setActualPresentFence(presentOrRetireFence);
+ } 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;
+ return true;
}
#ifdef USE_HWC2
void Layer::releasePendingBuffer() {
mSurfaceFlingerConsumer->releasePendingBuffer();
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addRelease(mPreviousFrameNumber,
+ mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
}
#endif
@@ -1813,7 +1852,7 @@
return !matchingFramesFound || allTransactionsApplied;
}
-Region Layer::latchBuffer(bool& recomputeVisibleRegions)
+Region Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime)
{
ATRACE_CALL();
@@ -1937,6 +1976,19 @@
return outDirtyRegion;
}
+ mBufferLatched = true;
+ mPreviousFrameNumber = mCurrentFrameNumber;
+ mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+#ifndef USE_HWC2
+ mFrameEventHistory.addRelease(mPreviousFrameNumber,
+ mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+#endif
+ }
+
mRefreshPending = true;
mFrameLatencyNeeded = true;
if (oldActiveBuffer == NULL) {
@@ -1972,8 +2024,6 @@
recomputeVisibleRegions = true;
}
- mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
// Remove any sync points corresponding to the buffer which was just
// latched
{
@@ -2158,22 +2208,24 @@
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) {
+ mFrameEventHistory.addQueue(*newTimestamps);
+ }
+
+ if (outDelta) {
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ }
}
std::vector<OccupancyTracker::Segment> Layer::getOccupancyHistory(
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 64b049c..a051292 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -270,13 +270,13 @@
* 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(sp<Fence> glCompositionDoneFence);
#ifdef USE_HWC2
// If a buffer was replaced this frame, release the former buffer
@@ -323,7 +323,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;}
@@ -383,7 +383,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;
@@ -402,20 +402,15 @@
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;
@@ -464,7 +459,7 @@
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,
bool useIdentityTransform) const;
@@ -583,8 +578,15 @@
// 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;
+
// main thread
sp<GraphicBuffer> mActiveBuffer;
sp<NativeHandle> mSidebandStream;
@@ -594,7 +596,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
@@ -644,7 +648,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/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index ffaee7a..359ca4e 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -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,6 +147,10 @@
outTransformMatrix);
}
+void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ mProducer->getFrameTimestamps(outDelta);
+}
+
status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
return mProducer->getUniqueId(outId);
}
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 66f6cf0..17adaa7 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 37fd70d..dde8d11 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -566,6 +566,20 @@
return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
}
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ getHwComposer().retireFenceRepresentsStartOfScanout() ?
+ FrameEvent::DISPLAY_PRESENT : FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::RELEASE,
+ };
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
Vector<DisplayInfo>* configs) {
if ((configs == NULL) || (display.get() == NULL)) {
@@ -683,6 +697,10 @@
}
int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
+ if (display == NULL) {
+ ALOGE("%s : display is NULL", __func__);
+ return BAD_VALUE;
+ }
sp<DisplayDevice> device(getDisplayDevice(display));
if (device != NULL) {
return device->getActiveConfig();
@@ -1099,7 +1117,7 @@
}
bool SurfaceFlinger::handleMessageTransaction() {
- uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+ uint32_t transactionFlags = peekTransactionFlags();
if (transactionFlags) {
handleTransaction(transactionFlags);
return true;
@@ -1117,14 +1135,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) {
@@ -1133,10 +1151,6 @@
mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
}
- // Release any buffers which were replaced this frame
- for (auto& layer : mLayersWithQueuedFrames) {
- layer->releasePendingBuffer();
- }
mLayersWithQueuedFrames.clear();
}
@@ -1184,7 +1198,7 @@
}
}
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
{
ATRACE_CALL();
ALOGV("preComposition");
@@ -1193,7 +1207,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;
}
}
@@ -1202,22 +1216,33 @@
}
}
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
{
ATRACE_CALL();
ALOGV("postComposition");
+ // Release any buffers which were replaced this frame
+ for (auto& layer : mLayersWithQueuedFrames) {
+ layer->releasePendingBuffer();
+ }
+
+ bool hadClientComposition = mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY);
+
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+ sp<Fence> glCompositionDoneFence = hadClientComposition
+ ? hw->getClientTargetAcquireFence()
+ : Fence::NO_FENCE;
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(glCompositionDoneFence);
if (frameLatched) {
recordBufferingStats(layers[i]->getName().string(),
layers[i]->getOccupancyHistory(false));
}
}
- sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+ sp<Fence> presentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
if (mPrimaryDispSync.addPresentFence(presentFence)) {
@@ -1227,16 +1252,12 @@
}
}
- const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
if (kIgnorePresentFences) {
if (hw->isDisplayOn()) {
enableHardwareVsync();
}
}
- mFenceTracker.addFrame(refreshStartTime, presentFence,
- hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1459,7 +1480,7 @@
}
const auto hwcId = displayDevice->getHwcDisplayId();
if (hwcId >= 0) {
- mHwc->commit(hwcId);
+ mHwc->presentAndGetReleaseFences(hwcId);
}
displayDevice->onSwapBuffersCompleted();
displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
@@ -1993,7 +2014,7 @@
{
ALOGV("handlePageFlip");
- Region dirtyRegion;
+ nsecs_t latchTime = systemTime();
bool visibleRegions = false;
const LayerVector& layers(mDrawingState.layersSortedByZ);
@@ -2022,7 +2043,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);
@@ -2047,14 +2068,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;
@@ -2065,35 +2087,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(
@@ -2201,7 +2223,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;
}
@@ -2237,8 +2259,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);
}
@@ -2282,7 +2304,7 @@
return status_t(index);
}
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
return android_atomic_release_load(&mTransactionFlags);
}
@@ -2819,9 +2841,9 @@
}
if ((index < numArgs) &&
- (args[index] == String16("--fences"))) {
+ (args[index] == String16("--frame-events"))) {
index++;
- mFenceTracker.dump(&result);
+ dumpFrameEventsLocked(result);
dumpAll = false;
}
}
@@ -2962,6 +2984,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> "
@@ -3881,11 +3913,6 @@
}
}
-bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
- uint64_t frameNumber, FrameTimestamps* outTimestamps) {
- return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
-}
-
// ---------------------------------------------------------------------------
SurfaceFlinger::LayerVector::LayerVector() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3af1754..4ec1a72 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -53,7 +53,6 @@
#include "Barrier.h"
#include "DisplayDevice.h"
#include "DispSync.h"
-#include "FenceTracker.h"
#include "FrameTracker.h"
#include "MessageQueue.h"
#include "SurfaceInterceptor.h"
@@ -200,6 +199,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,
@@ -282,7 +283,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);
@@ -399,20 +400,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
@@ -444,14 +445,13 @@
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);
-
/* ------------------------------------------------------------------------
* Attributes
*/
@@ -517,7 +517,6 @@
nsecs_t mLastTransactionTime;
bool mBootFinished;
bool mForceFullDamage;
- FenceTracker mFenceTracker;
#ifdef USE_HWC2
bool mPropagateBackpressure = true;
#endif
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 8e5c565..55edc15 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -599,6 +599,19 @@
return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
}
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::RELEASE,
+ };
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
Vector<DisplayInfo>* configs) {
if ((configs == NULL) || (display.get() == NULL)) {
@@ -1037,7 +1050,7 @@
}
bool SurfaceFlinger::handleMessageTransaction() {
- uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+ uint32_t transactionFlags = peekTransactionFlags();
if (transactionFlags) {
handleTransaction(transactionFlags);
return true;
@@ -1055,12 +1068,12 @@
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- preComposition();
+ preComposition(refreshStartTime);
rebuildLayerStacks();
setUpHWComposer();
doDebugFlashRegions();
doComposition();
- postComposition(refreshStartTime);
+ postComposition();
}
void SurfaceFlinger::doDebugFlashRegions()
@@ -1103,13 +1116,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;
}
}
@@ -1118,19 +1131,28 @@
}
}
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
{
+ const HWComposer& hwc = getHwComposer();
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+
+ bool hadGlesComposition =
+ getHwComposer().hasGlesComposition(hw->getHwcDisplayId());
+ sp<Fence> glCompositionDoneFence = hadGlesComposition
+ ? hw->getClientTargetAcquireFence()
+ : Fence::NO_FENCE;
+
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(
+ glCompositionDoneFence);
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()) {
@@ -1141,16 +1163,12 @@
}
}
- const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
if (kIgnorePresentFences) {
if (hw->isDisplayOn()) {
enableHardwareVsync();
}
}
- mFenceTracker.addFrame(refreshStartTime, presentFence,
- hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1918,6 +1936,7 @@
bool SurfaceFlinger::handlePageFlip()
{
+ nsecs_t latchTime = systemTime();
Region dirtyRegion;
bool visibleRegions = false;
@@ -1949,7 +1968,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);
@@ -2124,7 +2143,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;
}
@@ -2204,7 +2223,7 @@
return status_t(index);
}
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
return android_atomic_release_load(&mTransactionFlags);
}
@@ -2741,9 +2760,9 @@
}
if ((index < numArgs) &&
- (args[index] == String16("--fences"))) {
+ (args[index] == String16("--frame-events"))) {
index++;
- mFenceTracker.dump(&result);
+ dumpFrameEventsLocked(result);
dumpAll = false;
}
}
@@ -2866,6 +2885,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);
@@ -3740,11 +3769,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) {
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index d97cf5e..ba3cf79 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -20,6 +20,13 @@
license: "include/vulkan/NOTICE",
}
+cc_library_static {
+ name: "vulkan_headers",
+ export_include_dirs: ["include"],
+}
+
subdirs = [
+ "nulldrv",
"libvulkan",
+ "tools",
]
diff --git a/vulkan/Android.mk b/vulkan/Android.mk
deleted file mode 100644
index d125673..0000000
--- a/vulkan/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-named-subdir-makefiles, libvulkan nulldrv tools)
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 5e3f4dd..5a0a93b 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -18,3 +18,62 @@
symbol_file: "libvulkan.map.txt",
first_version: "24",
}
+
+cc_library_shared {
+ name: "libvulkan",
+ clang: true,
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+
+ cflags: [
+ "-DLOG_TAG=\"vulkan\"",
+ "-DVK_USE_PLATFORM_ANDROID_KHR",
+ "-DVK_NO_PROTOTYPES",
+ "-fvisibility=hidden",
+ "-fstrict-aliasing",
+ "-Weverything",
+ "-Werror",
+ "-Wno-padded",
+ "-Wno-switch-enum",
+ "-Wno-undef",
+
+ //"-DLOG_NDEBUG=0",
+ ],
+
+ cppflags: [
+ "-std=c++14",
+ "-Wno-c99-extensions",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-exit-time-destructors",
+ "-Wno-global-constructors",
+ "-Wno-zero-length-array",
+ ],
+
+ srcs: [
+ "api.cpp",
+ "api_gen.cpp",
+ "debug_report.cpp",
+ "driver.cpp",
+ "driver_gen.cpp",
+ "layers_extensions.cpp",
+ "stubhal.cpp",
+ "swapchain.cpp",
+ "vulkan_loader_data.cpp",
+ ],
+
+ export_static_lib_headers: ["vulkan_headers"],
+ static_libs: [
+ "vulkan_headers",
+ ],
+ shared_libs: [
+ "libziparchive",
+ "libhardware",
+ "libsync",
+ "libbase",
+ "liblog",
+ "libutils",
+ "libcutils",
+ "libz",
+ ],
+}
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk
deleted file mode 100644
index f1155ca..0000000
--- a/vulkan/libvulkan/Android.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-LOCAL_CFLAGS := -DLOG_TAG=\"vulkan\" \
- -DVK_USE_PLATFORM_ANDROID_KHR \
- -DVK_NO_PROTOTYPES \
- -std=c99 -fvisibility=hidden -fstrict-aliasing \
- -Weverything -Werror \
- -Wno-padded \
- -Wno-switch-enum \
- -Wno-undef
-
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-LOCAL_CPPFLAGS := -std=c++14 \
- -Wno-c99-extensions \
- -Wno-c++98-compat-pedantic \
- -Wno-exit-time-destructors \
- -Wno-global-constructors \
- -Wno-zero-length-array
-
-LOCAL_C_INCLUDES := \
- frameworks/native/vulkan/include \
- system/core/libsync/include
-
-LOCAL_SRC_FILES := \
- api.cpp \
- api_gen.cpp \
- debug_report.cpp \
- driver.cpp \
- driver_gen.cpp \
- layers_extensions.cpp \
- stubhal.cpp \
- swapchain.cpp \
- vulkan_loader_data.cpp
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_STATIC_LIBRARIES := libziparchive
-LOCAL_SHARED_LIBRARIES := libhardware libsync libbase liblog libutils libcutils libz
-
-LOCAL_MODULE := libvulkan
-include $(BUILD_SHARED_LIBRARY)
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
new file mode 100644
index 0000000..ea3b781
--- /dev/null
+++ b/vulkan/nulldrv/Android.bp
@@ -0,0 +1,47 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ // Real drivers would set this to vulkan.$(TARGET_BOARD_PLATFORM)
+ name: "vulkan.default",
+ proprietary: true,
+ relative_install_path: "hw",
+
+ clang: true,
+ cflags: [
+ "-fvisibility=hidden",
+ "-fstrict-aliasing",
+ "-DLOG_TAG=\"vknulldrv\"",
+ "-Weverything",
+ "-Werror",
+ "-Wno-padded",
+ "-Wno-undef",
+ "-Wno-zero-length-array",
+
+ "-DLOG_NDEBUG=0",
+ ],
+ cppflags: [
+ "-std=c++1y",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-c99-extensions",
+ ],
+
+ srcs: [
+ "null_driver.cpp",
+ "null_driver_gen.cpp",
+ ],
+
+ static_libs: ["vulkan_headers"],
+ shared_libs: ["liblog"],
+}
diff --git a/vulkan/nulldrv/Android.mk b/vulkan/nulldrv/Android.mk
deleted file mode 100644
index 77d4746..0000000
--- a/vulkan/nulldrv/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CFLAGS := -std=c99 -fvisibility=hidden -fstrict-aliasing \
- -DLOG_TAG=\"vknulldrv\" \
- -Weverything -Werror \
- -Wno-padded \
- -Wno-undef \
- -Wno-zero-length-array
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-LOCAL_CPPFLAGS := -std=c++1y \
- -Wno-c++98-compat-pedantic \
- -Wno-c99-extensions
-
-LOCAL_C_INCLUDES := \
- frameworks/native/vulkan/include
-
-LOCAL_SRC_FILES := \
- null_driver.cpp \
- null_driver_gen.cpp
-
-LOCAL_SHARED_LIBRARIES := liblog
-
-# Real drivers would set this to vulkan.$(TARGET_BOARD_PLATFORM)
-LOCAL_MODULE := vulkan.default
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/vulkan/tools/Android.bp b/vulkan/tools/Android.bp
new file mode 100644
index 0000000..d81d9ec
--- /dev/null
+++ b/vulkan/tools/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "vkinfo",
+
+ clang: true,
+ cflags: [
+ "-fvisibility=hidden",
+ "-fstrict-aliasing",
+
+ "-DLOG_TAG=\"vkinfo\"",
+
+ "-Weverything",
+ "-Werror",
+ "-Wno-padded",
+ "-Wno-undef",
+ "-Wno-switch-enum",
+ ],
+ cppflags: [
+ "-std=c++1y",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-c99-extensions",
+ "-Wno-old-style-cast",
+ ],
+
+ srcs: ["vkinfo.cpp"],
+
+ shared_libs: [
+ "libvulkan",
+ "liblog",
+ ],
+}
diff --git a/vulkan/tools/Android.mk b/vulkan/tools/Android.mk
deleted file mode 100644
index 337e683..0000000
--- a/vulkan/tools/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CFLAGS := -std=c99 -fvisibility=hidden -fstrict-aliasing
-LOCAL_CFLAGS += -DLOG_TAG=\"vkinfo\"
-LOCAL_CFLAGS += -Weverything -Werror -Wno-padded -Wno-undef -Wno-switch-enum
-LOCAL_CPPFLAGS := -std=c++1y \
- -Wno-c++98-compat-pedantic \
- -Wno-c99-extensions \
- -Wno-old-style-cast
-
-LOCAL_C_INCLUDES := \
- frameworks/native/vulkan/include
-
-LOCAL_SRC_FILES := vkinfo.cpp
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_SHARED_LIBRARIES := libvulkan liblog
-
-LOCAL_MODULE := vkinfo
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)