Merge "Deprecate eglCreatePixmapSurface"
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 7edc7de..2b23a3d 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -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 \
@@ -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)
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..30b5d9a 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;
}
@@ -686,12 +687,13 @@
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");
+ fflush(stdout);
}
// List of file extensions that can cause a zip file attachment to be rejected by some email
@@ -778,7 +780,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 +917,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 +932,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 +945,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 +1048,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 +1059,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 +1106,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());
@@ -1198,9 +1200,12 @@
}
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");
+ fflush(stdout);
native_handle_close(handle);
native_handle_delete(handle);
@@ -1495,7 +1500,7 @@
}
}
- if (ds.IsDryRun()) {
+ if (PropertiesHelper::IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
@@ -1657,7 +1662,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 +1676,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..01a60da 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,42 @@
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());
+ fflush(stdout);
}
}
}
-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 +213,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 +227,7 @@
}
void for_each_userid(void (*func)(int), const char *header) {
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
DIR *d;
struct dirent *de;
@@ -427,7 +310,7 @@
}
void for_each_pid(for_each_pid_func func, const char *header) {
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
__for_each_pid(for_each_pid_helper, header, (void *) func);
}
@@ -481,13 +364,13 @@
}
void for_each_tid(for_each_tid_func func, const char *header) {
- if (IsDryRun()) return;
+ 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 +437,7 @@
}
void show_showtime(int pid, const char *name) {
- if (IsDryRun()) return;
+ if (PropertiesHelper::IsDryRun()) return;
char path[255];
char buffer[1023];
@@ -622,7 +505,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 +536,19 @@
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);
+
+ fflush(stdout);
+
+ return status;
}
int read_file_as_long(const char *path, long int *output) {
@@ -791,7 +590,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 +647,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 +659,22 @@
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();
+ UpdateProgress(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);
- }
+ fflush(stdout);
return status;
}
@@ -1095,55 +687,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 +721,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 +908,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 +930,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 +938,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 +950,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 +980,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");
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/commands.cpp b/cmds/installd/commands.cpp
index 5ffc0c2..9f19597 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -78,9 +78,11 @@
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;
@@ -1687,20 +1689,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 +1744,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 +1757,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 +1819,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 +1869,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 +1880,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 +1898,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 {
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 4dc7467..17352cc 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -25,7 +25,7 @@
struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
FrameTimestamps() :
frameNumber(0),
- postedTime(0),
+ requestedPresentTime(0),
acquireTime(0),
refreshStartTime(0),
glCompositionDoneTime(0),
@@ -33,7 +33,7 @@
releaseTime(0) {}
uint64_t frameNumber;
- nsecs_t postedTime;
+ nsecs_t requestedPresentTime;
nsecs_t acquireTime;
nsecs_t refreshStartTime;
nsecs_t glCompositionDoneTime;
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 982cc9d..20cffe0 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -295,6 +295,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
@@ -313,11 +314,12 @@
dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
transform(_transform), stickyTransform(_sticky), fence(_fence),
surfaceDamage() { }
+
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) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outDataSpace = dataSpace;
@@ -351,8 +353,7 @@
Region surfaceDamage;
};
- // QueueBufferOutput must be a POD structure
- struct QueueBufferOutput {
+ struct QueueBufferOutput : public Flattenable<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
@@ -369,6 +370,7 @@
*outNumPendingBuffers = numPendingBuffers;
*outNextFrameNumber = nextFrameNumber;
}
+
inline void inflate(uint32_t inWidth, uint32_t inHeight,
uint32_t inTransformHint, uint32_t inNumPendingBuffers,
uint64_t inNextFrameNumber) {
@@ -378,7 +380,13 @@
numPendingBuffers = inNumPendingBuffers;
nextFrameNumber = inNextFrameNumber;
}
- private:
+
+ // Flattenable protocol
+ 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};
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 1d97d8b..470992c 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -135,10 +135,10 @@
sp<Fence>* outFence, float outTransformMatrix[16]);
// See IGraphicBufferProducer::getFrameTimestamps
- bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
- nsecs_t* outReleaseTime);
+ bool getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outDisplayRetireTime, nsecs_t* outReleaseTime);
status_t getUniqueId(uint64_t* outId) const;
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/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/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/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 846c205..89f852c 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -120,24 +120,24 @@
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
uint32_t height, PixelFormat format, uint32_t usage) {
Parcel data, reply;
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeUint32(width);
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
data.writeUint32(usage);
+
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);
- if (result != NO_ERROR) {
- fence->clear();
- return result;
- }
+ *fence = new Fence();
+ result = reply.read(**fence);
+ if (result != NO_ERROR) {
+ fence->clear();
+ return result;
}
result = reply.readInt32();
return result;
@@ -211,14 +211,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 +272,7 @@
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+ reply.read(*output);
result = reply.readInt32();
return result;
}
@@ -522,15 +529,14 @@
uint32_t height = data.readUint32();
PixelFormat format = static_cast<PixelFormat>(data.readInt32());
uint32_t usage = data.readUint32();
+
int buf = 0;
- sp<Fence> fence;
+ sp<Fence> fence = Fence::NO_FENCE;
int result = dequeueBuffer(&buf, &fence, width, height, format,
usage);
+
reply->writeInt32(buf);
- reply->writeInt32(fence != NULL);
- if (fence != NULL) {
- reply->write(*fence);
- }
+ reply->write(*fence);
reply->writeInt32(result);
return NO_ERROR;
}
@@ -575,11 +581,11 @@
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;
}
@@ -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;
}
@@ -832,4 +836,50 @@
return surfaceDamage.unflatten(buffer, size);
}
+// ----------------------------------------------------------------------------
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ size_t size = sizeof(width)
+ + sizeof(height)
+ + sizeof(transformHint)
+ + sizeof(numPendingBuffers)
+ + sizeof(nextFrameNumber);
+ return size;
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return 0;
+}
+
+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 NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size,
+ int const*& /*fds*/, size_t& /*count*/)
+{
+ if (size < getFlattenedSize()) {
+ 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 NO_ERROR;
+}
+
}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 351d184..ac93c53 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -135,18 +135,18 @@
outTransformMatrix);
}
-bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
- nsecs_t* outReleaseTime) {
+bool Surface::getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+ nsecs_t* outDisplayRetireTime, nsecs_t* outReleaseTime) {
ATRACE_CALL();
FrameTimestamps timestamps;
bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
×tamps);
if (found) {
- if (outPostedTime) {
- *outPostedTime = timestamps.postedTime;
+ if (outRequestedPresentTime) {
+ *outRequestedPresentTime = timestamps.requestedPresentTime;
}
if (outAcquireTime) {
*outAcquireTime = timestamps.acquireTime;
@@ -795,14 +795,14 @@
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* outDisplayRetireTime = va_arg(args, int64_t*);
nsecs_t* outReleaseTime = va_arg(args, int64_t*);
bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
- outPostedTime, outAcquireTime, outRefreshStartTime,
+ outRequestedPresentTime, outAcquireTime, outRefreshStartTime,
outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
return ret ? NO_ERROR : BAD_VALUE;
}
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..c2fa43f 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -632,7 +632,7 @@
#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
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 0bfefd0..1acdeb08 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -2038,7 +2038,7 @@
return EGL_FALSE;
}
- nsecs_t* postedTime = nullptr;
+ nsecs_t* requestedPresentTime = nullptr;
nsecs_t* acquireTime = nullptr;
nsecs_t* refreshStartTime = nullptr;
nsecs_t* GLCompositionDoneTime = nullptr;
@@ -2047,8 +2047,8 @@
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];
@@ -2072,8 +2072,8 @@
}
status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
- postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime,
- displayRetireTime, releaseTime);
+ requestedPresentTime, acquireTime, refreshStartTime,
+ GLCompositionDoneTime, displayRetireTime, releaseTime);
if (ret != NO_ERROR) {
setError(EGL_BAD_ACCESS, EGL_FALSE);
@@ -2102,7 +2102,7 @@
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:
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index 30337ad..0882cef 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -67,7 +67,7 @@
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
@@ -98,9 +98,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 +112,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
@@ -143,3 +145,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.
+
diff --git a/opengl/specs/README b/opengl/specs/README
index f0c024e..8414d05 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -20,7 +20,7 @@
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)
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/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/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 2190466..69902e2 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -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;
}
@@ -618,10 +613,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/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
index 0e18a93..276890d 100644
--- a/services/surfaceflinger/FenceTracker.cpp
+++ b/services/surfaceflinger/FenceTracker.cpp
@@ -60,8 +60,8 @@
outString->appendFormat("---- Frame # %" PRIu64 " (%s)\n",
layer.frameNumber,
layer.isGlesComposition ? "GLES" : "HWC");
- outString->appendFormat("---- Posted\t%" PRId64 "\n",
- layer.postedTime);
+ outString->appendFormat("---- Req.Present.\t%" PRId64 "\n",
+ layer.requestedPresentTime);
if (layer.acquireTime) {
outString->appendFormat("---- Acquire\t%" PRId64 "\n",
layer.acquireTime);
@@ -134,25 +134,26 @@
String8 name;
uint64_t frameNumber;
bool glesComposition;
- nsecs_t postedTime;
+ nsecs_t requestedPresentTime;
sp<Fence> acquireFence;
sp<Fence> prevReleaseFence;
int32_t layerId = layers[i]->getSequence();
layers[i]->getFenceData(&name, &frameNumber, &glesComposition,
- &postedTime, &acquireFence, &prevReleaseFence);
+ &requestedPresentTime, &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));
+ requestedPresentTime, 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));
+ requestedPresentTime, 0, 0, acquireFence, Fence::NO_FENCE));
auto prevLayer = prevFrame.layers.find(layerId);
if (prevLayer != prevFrame.layers.end()) {
prevLayer->second.releaseFence = prevReleaseFence;
@@ -162,7 +163,7 @@
frame.layers.emplace(std::piecewise_construct,
std::forward_as_tuple(layerId),
std::forward_as_tuple(name, frameNumber, glesComposition,
- postedTime, 0, 0, acquireFence,
+ requestedPresentTime, 0, 0, acquireFence,
glesComposition ? Fence::NO_FENCE : prevReleaseFence));
if (glesComposition) {
wasGlesCompositionDone = true;
@@ -171,7 +172,7 @@
frame.layers.emplace(std::piecewise_construct,
std::forward_as_tuple(layerId),
std::forward_as_tuple(name, frameNumber, glesComposition,
- postedTime, 0, 0, acquireFence, prevReleaseFence));
+ requestedPresentTime, 0, 0, acquireFence, prevReleaseFence));
}
frame.frameId = mFrameCounter;
@@ -207,7 +208,7 @@
const FrameRecord& frameRecord = mFrames[i];
const LayerRecord& layerRecord = mFrames[i].layers[layerId];
outTimestamps->frameNumber = frameNumber;
- outTimestamps->postedTime = layerRecord.postedTime;
+ outTimestamps->requestedPresentTime = layerRecord.requestedPresentTime;
outTimestamps->acquireTime = layerRecord.acquireTime;
outTimestamps->refreshStartTime = frameRecord.refreshStartTime;
outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime;
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
index 4cb14a5..385c970 100644
--- a/services/surfaceflinger/FenceTracker.h
+++ b/services/surfaceflinger/FenceTracker.h
@@ -50,23 +50,25 @@
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
+ // time the producer requested this frame be presented
+ nsecs_t requestedPresentTime;
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,
+ bool isGlesComposition, nsecs_t requestedPresentTime,
nsecs_t acquireTime, nsecs_t releaseTime,
sp<Fence> acquireFence, sp<Fence> releaseFence) :
name(name), frameNumber(frameNumber),
- isGlesComposition(isGlesComposition), postedTime(postedTime),
+ isGlesComposition(isGlesComposition),
+ requestedPresentTime(requestedPresentTime),
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),
+ isGlesComposition(false), requestedPresentTime(0),
+ acquireTime(0), releaseTime(0), acquireFence(Fence::NO_FENCE),
releaseFence(Fence::NO_FENCE) {};
};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5f96f66..20c0261 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2159,7 +2159,7 @@
}
void Layer::getFenceData(String8* outName, uint64_t* outFrameNumber,
- bool* outIsGlesComposition, nsecs_t* outPostedTime,
+ bool* outIsGlesComposition, nsecs_t* outRequestedPresentTime,
sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const {
*outName = mName;
*outFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
@@ -2171,7 +2171,7 @@
#else
*outIsGlesComposition = mIsGlesComposition;
#endif
- *outPostedTime = mSurfaceFlingerConsumer->getTimestamp();
+ *outRequestedPresentTime = mSurfaceFlingerConsumer->getTimestamp();
*outAcquireFence = mSurfaceFlingerConsumer->getCurrentFence();
*outPrevReleaseFence = mSurfaceFlingerConsumer->getPrevReleaseFence();
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 64b049c..5aba69b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -407,7 +407,7 @@
void getFrameStats(FrameStats* outStats) const;
void getFenceData(String8* outName, uint64_t* outFrameNumber,
- bool* outIsGlesComposition, nsecs_t* outPostedTime,
+ bool* outIsGlesComposition, nsecs_t* outRequestedPresentTime,
sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const;
std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 37fd70d..328594b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -683,6 +683,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();