Merge "memtrack HAL uses "default" service name"
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 831150b..c145933 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -36,7 +36,7 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <log/logger.h>
+#include <log/log.h>
#include <procinfo/process.h>
#include <selinux/selinux.h>
@@ -57,8 +57,8 @@
}
// Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_attach_thread(pid_t pid, pid_t tid, std::string* error) {
- if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
+static bool ptrace_seize_thread(pid_t pid, pid_t tid, std::string* error) {
+ if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
*error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
return false;
}
@@ -71,6 +71,12 @@
*error = StringPrintf("thread %d is not in process %d", tid, pid);
return false;
}
+
+ // Put the task into ptrace-stop state.
+ if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to interrupt thread " << tid;
+ }
+
return true;
}
@@ -160,11 +166,15 @@
return true;
}
+static void signal_handler(int) {
+ // We can't log easily, because the heap might be corrupt.
+ // Just die and let the surrounding log context explain things.
+ _exit(1);
+}
+
static void abort_handler(pid_t target, const bool& tombstoned_connected,
unique_fd& tombstoned_socket, unique_fd& output_fd,
const char* abort_msg) {
- LOG(ERROR) << abort_msg;
-
// If we abort before we get an output fd, contact tombstoned to let any
// potential listeners know that we failed.
if (!tombstoned_connected) {
@@ -177,7 +187,6 @@
dprintf(output_fd.get(), "crash_dump failed to dump process %d: %s\n", target, abort_msg);
- // Don't dump ourselves.
_exit(1);
}
@@ -203,6 +212,11 @@
abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
});
+ // Don't try to dump ourselves.
+ struct sigaction action = {};
+ action.sa_handler = signal_handler;
+ debuggerd_register_handlers(&action);
+
if (argc != 2) {
return 1;
}
@@ -243,10 +257,13 @@
exit(0);
}
+ // Die if we take too long.
+ alarm(20);
+
check_process(target_proc_fd, target);
std::string attach_error;
- if (!ptrace_attach_thread(target, main_tid, &attach_error)) {
+ if (!ptrace_seize_thread(target, main_tid, &attach_error)) {
LOG(FATAL) << attach_error;
}
@@ -267,13 +284,14 @@
} else {
unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
+ output_fd = std::move(devnull);
}
LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
- // At this point, the thread that made the request has been PTRACE_ATTACHed
- // and has the signal that triggered things queued. Send PTRACE_CONT, and
- // then wait for the signal.
+ // At this point, the thread that made the request has been attached and is
+ // in ptrace-stopped state. After resumption, the triggering signal that has
+ // been queued will be delivered.
if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
exit(1);
@@ -302,17 +320,16 @@
// Now that we have the signal that kicked things off, attach all of the
// sibling threads, and then proceed.
bool fatal_signal = signo != DEBUGGER_SIGNAL;
- int resume_signal = fatal_signal ? signo : 0;
std::set<pid_t> siblings;
std::set<pid_t> attached_siblings;
- if (resume_signal == 0) {
+ if (fatal_signal) {
if (!android::procinfo::GetProcessTids(target, &siblings)) {
PLOG(FATAL) << "failed to get process siblings";
}
siblings.erase(main_tid);
for (pid_t sibling_tid : siblings) {
- if (!ptrace_attach_thread(target, sibling_tid, &attach_error)) {
+ if (!ptrace_seize_thread(target, sibling_tid, &attach_error)) {
LOG(WARNING) << attach_error;
} else {
attached_siblings.insert(sibling_tid);
@@ -338,40 +355,39 @@
attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
}
+ // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
+ // group-stop state, which is true as long as no stopping signals are sent.
+
bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
- if (wait_for_gdb) {
+ if (!fatal_signal || siginfo.si_code == SI_USER) {
// Don't wait_for_gdb when the process didn't actually crash.
- if (!fatal_signal) {
- wait_for_gdb = false;
- } else {
- // Use ALOGI to line up with output from engrave_tombstone.
- ALOGI(
- "***********************************************************\n"
- "* Process %d has been suspended while crashing.\n"
- "* To attach gdbserver and start gdb, run this on the host:\n"
- "*\n"
- "* gdbclient.py -p %d\n"
- "*\n"
- "***********************************************************",
- target, main_tid);
- }
+ wait_for_gdb = false;
}
- for (pid_t tid : attached_siblings) {
- // Don't send the signal to sibling threads.
- if (ptrace(PTRACE_DETACH, tid, 0, wait_for_gdb ? SIGSTOP : 0) != 0) {
- PLOG(ERROR) << "ptrace detach from " << tid << " failed";
+ // If the process crashed or we need to send it SIGSTOP for wait_for_gdb,
+ // get it in a state where it can receive signals, and then send the relevant
+ // signal.
+ if (wait_for_gdb || fatal_signal) {
+ if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) {
+ PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid;
}
- }
- if (ptrace(PTRACE_DETACH, main_tid, 0, wait_for_gdb ? SIGSTOP : resume_signal)) {
- PLOG(ERROR) << "ptrace detach from main thread " << main_tid << " failed";
+ if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) {
+ PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid;
+ }
}
if (wait_for_gdb) {
- if (tgkill(target, main_tid, resume_signal) != 0) {
- PLOG(ERROR) << "failed to resend signal to process " << target;
- }
+ // Use ALOGI to line up with output from engrave_tombstone.
+ ALOGI(
+ "***********************************************************\n"
+ "* Process %d has been suspended while crashing.\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
+ "*\n"
+ "* gdbclient.py -p %d\n"
+ "*\n"
+ "***********************************************************",
+ target, main_tid);
}
if (fatal_signal) {
@@ -380,7 +396,7 @@
// Close stdout before we notify tombstoned of completion.
close(STDOUT_FILENO);
- if (!tombstoned_notify_completion(tombstoned_socket.get())) {
+ if (tombstoned_connected && !tombstoned_notify_completion(tombstoned_socket.get())) {
LOG(ERROR) << "failed to notify tombstoned of completion";
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f51b5f2..e7503e9 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -17,6 +17,7 @@
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <chrono>
@@ -90,6 +91,7 @@
// Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
void FinishIntercept(int* result);
+ void StartProcess(std::function<void()> function);
void StartCrasher(const std::string& crash_type);
void FinishCrasher();
void AssertDeath(int signo);
@@ -166,9 +168,8 @@
}
}
-void CrasherTest::StartCrasher(const std::string& crash_type) {
- std::string type = "wait-" + crash_type;
-
+void CrasherTest::StartProcess(std::function<void()> function) {
+ unique_fd read_pipe;
unique_fd crasher_read_pipe;
if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
FAIL() << "failed to create pipe: " << strerror(errno);
@@ -182,9 +183,17 @@
dup2(crasher_read_pipe.get(), STDIN_FILENO);
dup2(devnull.get(), STDOUT_FILENO);
dup2(devnull.get(), STDERR_FILENO);
+ function();
+ _exit(0);
+ }
+}
+
+void CrasherTest::StartCrasher(const std::string& crash_type) {
+ std::string type = "wait-" + crash_type;
+ StartProcess([type]() {
execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
err(1, "exec failed");
- }
+ });
}
void CrasherTest::FinishCrasher() {
@@ -340,24 +349,15 @@
AssertDeath(SIGABRT);
}
+// wait_for_gdb shouldn't trigger on manually sent signals.
TEST_F(CrasherTest, wait_for_gdb_signal) {
if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
FAIL() << "failed to enable wait_for_gdb";
}
StartCrasher("abort");
- ASSERT_EQ(0, kill(crasher_pid, SIGABRT)) << strerror(errno);
-
- std::this_thread::sleep_for(500ms);
-
- int status;
- ASSERT_EQ(crasher_pid, (TIMEOUT(1, waitpid(crasher_pid, &status, WUNTRACED))));
- ASSERT_TRUE(WIFSTOPPED(status));
- ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
-
- ASSERT_EQ(0, kill(crasher_pid, SIGCONT)) << strerror(errno);
-
- AssertDeath(SIGABRT);
+ ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
+ AssertDeath(SIGSEGV);
}
TEST_F(CrasherTest, backtrace) {
@@ -388,3 +388,20 @@
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
}
+
+TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
+ StartProcess([]() {
+ prctl(PR_SET_DUMPABLE, 0);
+ volatile char* null = static_cast<char*>(nullptr);
+ *null = '\0';
+ });
+ AssertDeath(SIGSEGV);
+}
+
+TEST_F(CrasherTest, PR_SET_DUMPABLE_0_raise) {
+ StartProcess([]() {
+ prctl(PR_SET_DUMPABLE, 0);
+ raise(SIGUSR1);
+ });
+ AssertDeath(SIGUSR1);
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 6033a6b..c009869 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -31,6 +31,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/futex.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
@@ -46,6 +47,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include "private/bionic_futex.h"
#include "private/libc_logging.h"
// see man(2) prctl, specifically the section about PR_GET_NAME
@@ -61,6 +63,9 @@
static debuggerd_callbacks_t g_callbacks;
+// Mutex to ensure only one crashing thread dumps itself.
+static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
+
// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
#define fatal(...) \
do { \
@@ -151,8 +156,7 @@
}
struct debugger_thread_info {
- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- bool crash_dump_started = false;
+ bool crash_dump_started;
pid_t crashing_tid;
pid_t pseudothread_tid;
int signal_number;
@@ -228,16 +232,43 @@
}
}
- pthread_mutex_unlock(&thread_info->mutex);
+ syscall(__NR_exit, 0);
return 0;
}
+static void resend_signal(siginfo_t* info, bool crash_dump_started) {
+ // Signals can either be fatal or nonfatal.
+ // For fatal signals, crash_dump will send us the signal we crashed with
+ // before resuming us, so that processes using waitpid on us will see that we
+ // exited with the correct exit status (e.g. so that sh will report
+ // "Segmentation fault" instead of "Killed"). For this to work, we need
+ // to deregister our signal handler for that signal before continuing.
+ if (info->si_signo != DEBUGGER_SIGNAL) {
+ signal(info->si_signo, SIG_DFL);
+ }
+
+ // We need to return from our signal handler so that crash_dump can see the
+ // signal via ptrace and dump the thread that crashed. However, returning
+ // does not guarantee that the signal will be thrown again, even for SIGSEGV
+ // and friends, since the signal could have been sent manually. We blocked
+ // all signals when registering the handler, so resending the signal (using
+ // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
+ // when our signal handler returns.
+ if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
+ int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), info->si_signo, info);
+ if (rc != 0) {
+ fatal("failed to resend signal during crash: %s", strerror(errno));
+ }
+ }
+
+ if (info->si_signo == DEBUGGER_SIGNAL) {
+ pthread_mutex_unlock(&crash_mutex);
+ }
+}
+
// Handler that does crash dumping by forking and doing the processing in the child.
// Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump.
static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
- // Mutex to prevent multiple crashing threads from trying to talk
- // to debuggerd at the same time.
- static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
int ret = pthread_mutex_lock(&crash_mutex);
if (ret != 0) {
__libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
@@ -250,59 +281,10 @@
info = nullptr;
}
- log_signal_summary(signal_number, info);
- if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) {
- // process has disabled core dumps and PTRACE_ATTACH, and does not want to be dumped.
- // Honor that intention by not connecting to debuggerd and asking it to dump our internal state.
- __libc_format_log(ANDROID_LOG_INFO, "libc",
- "Suppressing debuggerd output because prctl(PR_GET_DUMPABLE)==0");
-
- pthread_mutex_unlock(&crash_mutex);
- return;
- }
-
- void* abort_message = nullptr;
- if (g_callbacks.get_abort_message) {
- abort_message = g_callbacks.get_abort_message();
- }
-
- debugger_thread_info thread_info = {
- .crashing_tid = gettid(),
- .signal_number = signal_number,
- .info = info
- };
- pthread_mutex_lock(&thread_info.mutex);
-
- // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
- pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
- CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID,
- &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
- if (child_pid == -1) {
- fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
- }
-
- // Wait for the child to finish and unlock the mutex.
- // This relies on bionic behavior that isn't guaranteed by the standard.
- pthread_mutex_lock(&thread_info.mutex);
-
- // Signals can either be fatal or nonfatal.
- // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
- // crashed with, so that processes using waitpid on us will see that we
- // exited with the correct exit status (e.g. so that sh will report
- // "Segmentation fault" instead of "Killed"). For this to work, we need
- // to deregister our signal handler for that signal before continuing.
- if (signal_number != DEBUGGER_SIGNAL) {
- signal(signal_number, SIG_DFL);
- }
-
- // We need to return from the signal handler so that debuggerd can dump the
- // thread that crashed, but returning here does not guarantee that the signal
- // will be thrown again, even for SIGSEGV and friends, since the signal could
- // have been sent manually. Resend the signal with rt_tgsigqueueinfo(2) to
- // preserve the SA_SIGINFO contents.
- struct siginfo si;
+ struct siginfo si = {};
if (!info) {
memset(&si, 0, sizeof(si));
+ si.si_signo = signal_number;
si.si_code = SI_USER;
si.si_pid = getpid();
si.si_uid = getuid();
@@ -314,23 +296,66 @@
// check to allow all si_code values in calls coming from inside the house.
}
+ log_signal_summary(signal_number, info);
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) {
+ // The process has disabled core dumps and PTRACE_ATTACH, and does not want to be dumped.
+ __libc_format_log(ANDROID_LOG_INFO, "libc",
+ "Suppressing debuggerd output because prctl(PR_GET_DUMPABLE)==0");
+ resend_signal(info, false);
+ return;
+ }
+
+ if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+ // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context.
+ __libc_format_log(ANDROID_LOG_INFO, "libc",
+ "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1");
+ resend_signal(info, false);
+ return;
+ }
+
+ void* abort_message = nullptr;
+ if (g_callbacks.get_abort_message) {
+ abort_message = g_callbacks.get_abort_message();
+ }
// Populate si_value with the abort message address, if found.
if (abort_message) {
info->si_value.sival_ptr = abort_message;
}
- // Only resend the signal if we know that either crash_dump has ptraced us or
- // the signal was fatal.
- if (thread_info.crash_dump_started || signal_number != DEBUGGER_SIGNAL) {
- int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info);
- if (rc != 0) {
- fatal("failed to resend signal during crash: %s", strerror(errno));
- }
+ debugger_thread_info thread_info = {
+ .crash_dump_started = false,
+ .pseudothread_tid = -1,
+ .crashing_tid = gettid(),
+ .signal_number = signal_number,
+ .info = info
+ };
+
+ // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+ pid_t child_pid =
+ clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
+ CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
+ &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
+ if (child_pid == -1) {
+ fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
}
- if (signal_number == DEBUGGER_SIGNAL) {
- pthread_mutex_unlock(&crash_mutex);
+ // Wait for the child to start...
+ __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+
+ // and then wait for it to finish.
+ __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+
+ // Signals can either be fatal or nonfatal.
+ // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
+ // crashed with, so that processes using waitpid on us will see that we
+ // exited with the correct exit status (e.g. so that sh will report
+ // "Segmentation fault" instead of "Killed"). For this to work, we need
+ // to deregister our signal handler for that signal before continuing.
+ if (signal_number != DEBUGGER_SIGNAL) {
+ signal(signal_number, SIG_DFL);
}
+
+ resend_signal(info, thread_info.crash_dump_started);
}
void debuggerd_init(debuggerd_callbacks_t* callbacks) {
@@ -363,15 +388,5 @@
// Use the alternate signal stack if available so we can catch stack overflows.
action.sa_flags |= SA_ONSTACK;
-
- sigaction(SIGABRT, &action, nullptr);
- sigaction(SIGBUS, &action, nullptr);
- sigaction(SIGFPE, &action, nullptr);
- sigaction(SIGILL, &action, nullptr);
- sigaction(SIGSEGV, &action, nullptr);
-#if defined(SIGSTKFLT)
- sigaction(SIGSTKFLT, &action, nullptr);
-#endif
- sigaction(SIGTRAP, &action, nullptr);
- sigaction(DEBUGGER_SIGNAL, &action, nullptr);
+ debuggerd_register_handlers(&action);
}
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 302f4c2..7196e0a 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -39,4 +39,18 @@
// to the log.
#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
+static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
+ sigaction(SIGABRT, action, nullptr);
+ sigaction(SIGBUS, action, nullptr);
+ sigaction(SIGFPE, action, nullptr);
+ sigaction(SIGILL, action, nullptr);
+ sigaction(SIGSEGV, action, nullptr);
+#if defined(SIGSTKFLT)
+ sigaction(SIGSTKFLT, action, nullptr);
+#endif
+ sigaction(SIGSYS, action, nullptr);
+ sigaction(SIGTRAP, action, nullptr);
+ sigaction(DEBUGGER_SIGNAL, action, nullptr);
+}
+
__END_DECLS
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 63e3dbd..8705ece 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -85,7 +85,13 @@
std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
struct stat st;
if (stat(path.c_str(), &st) != 0) {
- PLOG(ERROR) << "failed to stat " << path;
+ if (errno == ENOENT) {
+ oldest_tombstone = i;
+ break;
+ } else {
+ PLOG(ERROR) << "failed to stat " << path;
+ continue;
+ }
}
if (st.st_mtime < oldest_time) {
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index eaae9c4..b8345ca 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -2,6 +2,9 @@
user tombstoned
group system
+ # Don't start tombstoned until after the real /data is mounted.
+ class late_start
+
socket tombstoned_crash seqpacket 0666 system system
socket tombstoned_intercept seqpacket 0666 system system
writepid /dev/cpuset/system-background/tasks
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3f8bc8f..f970e68 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -358,7 +358,7 @@
" devices [-l] List all connected devices [with\n"
" device paths].\n"
" continue Continue with autoboot.\n"
- " reboot [bootloader] Reboot device [into bootloader].\n"
+ " reboot [bootloader|emergency] Reboot device [into bootloader or emergency mode].\n"
" reboot-bootloader Reboot device into bootloader.\n"
" help Show this help message.\n"
"\n"
@@ -1397,6 +1397,7 @@
bool wants_wipe = false;
bool wants_reboot = false;
bool wants_reboot_bootloader = false;
+ bool wants_reboot_emergency = false;
bool skip_reboot = false;
bool wants_set_active = false;
bool skip_secondary = false;
@@ -1648,6 +1649,11 @@
wants_reboot_bootloader = true;
skip(1);
}
+ if (!strcmp(*argv, "emergency")) {
+ wants_reboot = false;
+ wants_reboot_emergency = true;
+ skip(1);
+ }
}
require(0);
} else if(!strcmp(*argv, "reboot-bootloader")) {
@@ -1807,6 +1813,9 @@
} else if (wants_reboot_bootloader) {
fb_queue_command("reboot-bootloader", "rebooting into bootloader");
fb_queue_wait_for_disconnect();
+ } else if (wants_reboot_emergency) {
+ fb_queue_command("reboot-emergency", "rebooting into emergency download (EDL) mode");
+ fb_queue_wait_for_disconnect();
}
return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 8d5b51b..e321c17 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -11,7 +11,8 @@
libcrypto \
libext4_utils \
libsquashfs_utils \
- libselinux
+ libselinux \
+ libavb
include $(CLEAR_VARS)
LOCAL_CLANG := true
@@ -22,7 +23,9 @@
fs_mgr_format.c \
fs_mgr_fstab.c \
fs_mgr_slotselect.c \
- fs_mgr_verity.cpp
+ fs_mgr_verity.cpp \
+ fs_mgr_avb.cpp \
+ fs_mgr_avb_ops.cpp
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
system/vold \
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index e699b71..43fb9ea 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -46,6 +46,7 @@
#include <private/android_logger.h>
#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_avb.h"
#include "fs_mgr_priv_verity.h"
#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
@@ -670,11 +671,17 @@
int mret = -1;
int mount_errno = 0;
int attempted_idx = -1;
+ int avb_ret = FS_MGR_SETUP_AVB_FAIL;
if (!fstab) {
return -1;
}
+ if (fs_mgr_is_avb_used() &&
+ (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
+ return -1;
+ }
+
for (i = 0; i < fstab->num_entries; i++) {
/* Don't mount entries that are managed by vold or not for the mount mode*/
if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
@@ -713,7 +720,22 @@
wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
}
- if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+ if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
+ /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
+ * should set up the device without using dm-verity.
+ * The actual mounting still take place in the following
+ * mount_with_alternatives().
+ */
+ if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
+ INFO("AVB HASHTREE disabled\n");
+ } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
+ FS_MGR_SETUP_AVB_SUCCESS) {
+ ERROR("Failed to set up AVB on partition: %s, skipping!\n",
+ fstab->recs[i].mount_point);
+ /* Skips mounting the device. */
+ continue;
+ }
+ } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
INFO("Verity disabled");
@@ -722,6 +744,7 @@
continue;
}
}
+
int last_idx_inspected;
int top_idx = i;
@@ -825,6 +848,10 @@
}
}
+ if (fs_mgr_is_avb_used()) {
+ fs_mgr_unload_vbmeta_images();
+ }
+
if (error_count) {
return -1;
} else {
@@ -845,11 +872,17 @@
int mount_errors = 0;
int first_mount_errno = 0;
char *m;
+ int avb_ret = FS_MGR_SETUP_AVB_FAIL;
if (!fstab) {
return ret;
}
+ if (fs_mgr_is_avb_used() &&
+ (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
+ return ret;
+ }
+
for (i = 0; i < fstab->num_entries; i++) {
if (!fs_match(fstab->recs[i].mount_point, n_name)) {
continue;
@@ -882,7 +915,22 @@
do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i]);
}
- if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+ if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
+ /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
+ * should set up the device without using dm-verity.
+ * The actual mounting still take place in the following
+ * mount_with_alternatives().
+ */
+ if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
+ INFO("AVB HASHTREE disabled\n");
+ } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
+ FS_MGR_SETUP_AVB_SUCCESS) {
+ ERROR("Failed to set up AVB on partition: %s, skipping!\n",
+ fstab->recs[i].mount_point);
+ /* Skips mounting the device. */
+ continue;
+ }
+ } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
INFO("Verity disabled");
@@ -921,6 +969,9 @@
}
out:
+ if (fs_mgr_is_avb_used()) {
+ fs_mgr_unload_vbmeta_images();
+ }
return ret;
}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
new file mode 100644
index 0000000..51632cf
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/properties.h>
+#include <libavb/libavb.h>
+#include <openssl/sha.h>
+#include <sys/ioctl.h>
+#include <utils/Compat.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_avb_ops.h"
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_avb.h"
+#include "fs_mgr_priv_dm_ioctl.h"
+#include "fs_mgr_priv_sha.h"
+
+/* The format of dm-verity construction parameters:
+ * <version> <dev> <hash_dev> <data_block_size> <hash_block_size>
+ * <num_data_blocks> <hash_start_block> <algorithm> <digest> <salt>
+ */
+#define VERITY_TABLE_FORMAT \
+ "%u %s %s %u %u " \
+ "%" PRIu64 " %" PRIu64 " %s %s %s "
+
+#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt) \
+ hashtree_desc.dm_verity_version, blk_device, blk_device, \
+ hashtree_desc.data_block_size, hashtree_desc.hash_block_size, \
+ hashtree_desc.image_size / \
+ hashtree_desc.data_block_size, /* num_data_blocks. */ \
+ hashtree_desc.tree_offset / \
+ hashtree_desc.hash_block_size, /* hash_start_block. */ \
+ (char *)hashtree_desc.hash_algorithm, digest, salt
+
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+/* The default format of dm-verity optional parameters:
+ * <#opt_params> ignore_zero_blocks restart_on_corruption
+ */
+#define VERITY_TABLE_OPT_DEFAULT_FORMAT "2 %s %s"
+#define VERITY_TABLE_OPT_DEFAULT_PARAMS \
+ VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
+
+/* The FEC (forward error correction) format of dm-verity optional parameters:
+ * <#opt_params> use_fec_from_device <fec_dev>
+ * fec_roots <num> fec_blocks <num> fec_start <offset>
+ * ignore_zero_blocks restart_on_corruption
+ */
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+ "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 \
+ " fec_start %" PRIu64 " %s %s"
+
+/* Note that fec_blocks is the size that FEC covers, *not* the
+ * size of the FEC data. Since we use FEC for everything up until
+ * the FEC data, it's the same as the offset (fec_start).
+ */
+#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device) \
+ blk_device, hashtree_desc.fec_num_roots, \
+ hashtree_desc.fec_offset / \
+ hashtree_desc.data_block_size, /* fec_blocks */ \
+ hashtree_desc.fec_offset / \
+ hashtree_desc.data_block_size, /* fec_start */ \
+ VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
+
+AvbSlotVerifyData *fs_mgr_avb_verify_data = nullptr;
+AvbOps *fs_mgr_avb_ops = nullptr;
+
+enum HashAlgorithm {
+ kInvalid = 0,
+ kSHA256 = 1,
+ kSHA512 = 2,
+};
+
+struct androidboot_vbmeta {
+ HashAlgorithm hash_alg;
+ uint8_t digest[SHA512_DIGEST_LENGTH];
+ size_t vbmeta_size;
+ bool allow_verification_error;
+};
+
+androidboot_vbmeta fs_mgr_vbmeta_prop;
+
+static inline bool nibble_value(const char &c, uint8_t *value)
+{
+ FS_MGR_CHECK(value != nullptr);
+
+ switch (c) {
+ case '0' ... '9':
+ *value = c - '0';
+ break;
+ case 'a' ... 'f':
+ *value = c - 'a' + 10;
+ break;
+ case 'A' ... 'F':
+ *value = c - 'A' + 10;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool hex_to_bytes(uint8_t *bytes,
+ size_t bytes_len,
+ const std::string &hex)
+{
+ FS_MGR_CHECK(bytes != nullptr);
+
+ if (hex.size() % 2 != 0) {
+ return false;
+ }
+ if (hex.size() / 2 > bytes_len) {
+ return false;
+ }
+ for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
+ uint8_t high;
+ if (!nibble_value(hex[i], &high)) {
+ return false;
+ }
+ uint8_t low;
+ if (!nibble_value(hex[i + 1], &low)) {
+ return false;
+ }
+ bytes[j] = (high << 4) | low;
+ }
+ return true;
+}
+
+static std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len)
+{
+ FS_MGR_CHECK(bytes != nullptr);
+
+ static const char *hex_digits = "0123456789abcdef";
+ std::string hex;
+
+ for (size_t i = 0; i < bytes_len; i++) {
+ hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
+ hex.push_back(hex_digits[bytes[i] & 0x0F]);
+ }
+ return hex;
+}
+
+static bool load_vbmeta_prop(androidboot_vbmeta *vbmeta_prop)
+{
+ FS_MGR_CHECK(vbmeta_prop != nullptr);
+
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+
+ std::string hash_alg;
+ std::string digest;
+
+ for (const auto &entry :
+ android::base::Split(android::base::Trim(cmdline), " ")) {
+ std::vector<std::string> pieces = android::base::Split(entry, "=");
+ const std::string &key = pieces[0];
+ const std::string &value = pieces[1];
+
+ if (key == "androidboot.vbmeta.device_state") {
+ vbmeta_prop->allow_verification_error = (value == "unlocked");
+ } else if (key == "androidboot.vbmeta.hash_alg") {
+ hash_alg = value;
+ } else if (key == "androidboot.vbmeta.size") {
+ if (!android::base::ParseUint(value.c_str(),
+ &vbmeta_prop->vbmeta_size)) {
+ return false;
+ }
+ } else if (key == "androidboot.vbmeta.digest") {
+ digest = value;
+ }
+ }
+
+ // Reads hash algorithm.
+ size_t expected_digest_size = 0;
+ if (hash_alg == "sha256") {
+ expected_digest_size = SHA256_DIGEST_LENGTH * 2;
+ vbmeta_prop->hash_alg = kSHA256;
+ } else if (hash_alg == "sha512") {
+ expected_digest_size = SHA512_DIGEST_LENGTH * 2;
+ vbmeta_prop->hash_alg = kSHA512;
+ } else {
+ ERROR("Unknown hash algorithm: %s\n", hash_alg.c_str());
+ return false;
+ }
+
+ // Reads digest.
+ if (digest.size() != expected_digest_size) {
+ ERROR("Unexpected digest size: %zu (expected %zu)\n", digest.size(),
+ expected_digest_size);
+ return false;
+ }
+
+ if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest),
+ digest)) {
+ ERROR("Hash digest contains non-hexidecimal character: %s\n",
+ digest.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Hasher>
+static std::pair<size_t, bool> verify_vbmeta_digest(
+ const AvbSlotVerifyData &verify_data, const androidboot_vbmeta &vbmeta_prop)
+{
+ size_t total_size = 0;
+ Hasher hasher;
+ for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
+ hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
+ verify_data.vbmeta_images[n].vbmeta_size);
+ total_size += verify_data.vbmeta_images[n].vbmeta_size;
+ }
+
+ bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest,
+ Hasher::DIGEST_SIZE) == 0);
+
+ return std::make_pair(total_size, matched);
+}
+
+static bool verify_vbmeta_images(const AvbSlotVerifyData &verify_data,
+ const androidboot_vbmeta &vbmeta_prop)
+{
+ if (verify_data.num_vbmeta_images == 0) {
+ ERROR("No vbmeta images\n");
+ return false;
+ }
+
+ size_t total_size = 0;
+ bool digest_matched = false;
+
+ if (vbmeta_prop.hash_alg == kSHA256) {
+ std::tie(total_size, digest_matched) =
+ verify_vbmeta_digest<SHA256Hasher>(verify_data, vbmeta_prop);
+ } else if (vbmeta_prop.hash_alg == kSHA512) {
+ std::tie(total_size, digest_matched) =
+ verify_vbmeta_digest<SHA512Hasher>(verify_data, vbmeta_prop);
+ }
+
+ if (total_size != vbmeta_prop.vbmeta_size) {
+ ERROR("total vbmeta size mismatch: %zu (expected: %zu)\n", total_size,
+ vbmeta_prop.vbmeta_size);
+ return false;
+ }
+
+ if (!digest_matched) {
+ ERROR("vbmeta digest mismatch\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool hashtree_load_verity_table(
+ struct dm_ioctl *io,
+ const std::string &dm_device_name,
+ int fd,
+ const std::string &blk_device,
+ const AvbHashtreeDescriptor &hashtree_desc,
+ const std::string &salt,
+ const std::string &root_digest)
+{
+ fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
+
+ // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
+ char *buffer = (char *)io;
+
+ // Builds the dm_target_spec arguments.
+ struct dm_target_spec *dm_target =
+ (struct dm_target_spec *)&buffer[sizeof(struct dm_ioctl)];
+ io->target_count = 1;
+ dm_target->status = 0;
+ dm_target->sector_start = 0;
+ dm_target->length = hashtree_desc.image_size / 512;
+ strcpy(dm_target->target_type, "verity");
+
+ // Builds the verity params.
+ char *verity_params =
+ buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+ size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
+
+ int res = 0;
+ if (hashtree_desc.fec_size > 0) {
+ res = snprintf(
+ verity_params, bufsize,
+ VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT,
+ VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(),
+ root_digest.c_str(), salt.c_str()),
+ VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str()));
+ } else {
+ res = snprintf(verity_params, bufsize,
+ VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT,
+ VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(),
+ root_digest.c_str(), salt.c_str()),
+ VERITY_TABLE_OPT_DEFAULT_PARAMS);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ ERROR("Error building verity table; insufficient buffer size?\n");
+ return false;
+ }
+
+ INFO("loading verity table: '%s'", verity_params);
+
+ // Sets ext target boundary.
+ verity_params += strlen(verity_params) + 1;
+ verity_params = (char *)(((unsigned long)verity_params + 7) & ~7);
+ dm_target->next = verity_params - buffer;
+
+ // Sends the ioctl to load the verity table.
+ if (ioctl(fd, DM_TABLE_LOAD, io)) {
+ ERROR("Error loading verity table (%s)\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static bool hashtree_dm_verity_setup(struct fstab_rec *fstab_entry,
+ const AvbHashtreeDescriptor &hashtree_desc,
+ const std::string &salt,
+ const std::string &root_digest)
+{
+ // Gets the device mapper fd.
+ android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
+ if (fd < 0) {
+ ERROR("Error opening device mapper (%s)\n", strerror(errno));
+ return false;
+ }
+
+ // Creates the device.
+ alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
+ struct dm_ioctl *io = (struct dm_ioctl *)buffer;
+ const std::string mount_point(basename(fstab_entry->mount_point));
+ if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+ ERROR("Couldn't create verity device!\n");
+ return false;
+ }
+
+ // Gets the name of the device file.
+ std::string verity_blk_name;
+ if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
+ ERROR("Couldn't get verity device number!\n");
+ return false;
+ }
+
+ // Loads the verity mapping table.
+ if (!hashtree_load_verity_table(io, mount_point, fd,
+ std::string(fstab_entry->blk_device),
+ hashtree_desc, salt, root_digest)) {
+ ERROR("Couldn't load verity table!\n");
+ return false;
+ }
+
+ // Activates the device.
+ if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+ return false;
+ }
+
+ // Marks the underlying block device as read-only.
+ fs_mgr_set_blk_ro(fstab_entry->blk_device);
+
+ // TODO(bowgotsai): support verified all partition at boot.
+ // Updates fstab_rec->blk_device to verity device name.
+ free(fstab_entry->blk_device);
+ fstab_entry->blk_device = strdup(verity_blk_name.c_str());
+
+ // Makes sure we've set everything up properly.
+ if (fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool get_hashtree_descriptor(const std::string &partition_name,
+ const AvbSlotVerifyData &verify_data,
+ AvbHashtreeDescriptor *out_hashtree_desc,
+ std::string *out_salt,
+ std::string *out_digest)
+{
+ bool found = false;
+ const uint8_t *desc_partition_name;
+
+ for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) {
+ // Get descriptors from vbmeta_images[i].
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor *[], decltype(&avb_free)>
+ descriptors(
+ avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
+ verify_data.vbmeta_images[i].vbmeta_size,
+ &num_descriptors),
+ avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ continue;
+ }
+
+ // Ensures that hashtree descriptor is either in /vbmeta or in
+ // the same partition for verity setup.
+ std::string vbmeta_partition_name(
+ verify_data.vbmeta_images[i].partition_name);
+ if (vbmeta_partition_name != "vbmeta" &&
+ vbmeta_partition_name != partition_name) {
+ WARNING("Skip vbmeta image at %s for partition: %s\n",
+ verify_data.vbmeta_images[i].partition_name,
+ partition_name.c_str());
+ continue;
+ }
+
+ for (size_t j = 0; j < num_descriptors && !found; j++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) {
+ WARNING("Descriptor is invalid.\n");
+ continue;
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
+ desc_partition_name = (const uint8_t *)descriptors[j] +
+ sizeof(AvbHashtreeDescriptor);
+ if (!avb_hashtree_descriptor_validate_and_byteswap(
+ (AvbHashtreeDescriptor *)descriptors[j],
+ out_hashtree_desc)) {
+ continue;
+ }
+ if (out_hashtree_desc->partition_name_len !=
+ partition_name.length()) {
+ continue;
+ }
+ // Notes that desc_partition_name is not NUL-terminated.
+ std::string hashtree_partition_name(
+ (const char *)desc_partition_name,
+ out_hashtree_desc->partition_name_len);
+ if (hashtree_partition_name == partition_name) {
+ found = true;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ ERROR("Partition descriptor not found: %s\n", partition_name.c_str());
+ return false;
+ }
+
+ const uint8_t *desc_salt =
+ desc_partition_name + out_hashtree_desc->partition_name_len;
+ *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len);
+
+ const uint8_t *desc_digest = desc_salt + out_hashtree_desc->salt_len;
+ *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len);
+
+ return true;
+}
+
+static inline bool polling_vbmeta_blk_device(struct fstab *fstab)
+{
+ // It needs the block device symlink: fstab_rec->blk_device to read
+ // /vbmeta partition. However, the symlink created by ueventd might
+ // not be ready at this point. Use test_access() to poll it before
+ // trying to read the partition.
+ struct fstab_rec *fstab_entry =
+ fs_mgr_get_entry_for_mount_point(fstab, "/vbmeta");
+
+ // Makes sure /vbmeta block device is ready to access.
+ if (fs_mgr_test_access(fstab_entry->blk_device) < 0) {
+ return false;
+ }
+ return true;
+}
+
+static bool init_is_avb_used()
+{
+ // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
+ // size, digest} in kernel cmdline. They will then be imported by init
+ // process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
+ //
+ // Checks hash_alg as an indicator for whether AVB is used.
+ // We don't have to parse and check all of them here. The check will
+ // be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will
+ // be returned when there is an error.
+
+ std::string hash_alg =
+ android::base::GetProperty("ro.boot.vbmeta.hash_alg", "");
+
+ if (hash_alg == "sha256" || hash_alg == "sha512") {
+ return true;
+ }
+
+ return false;
+}
+
+bool fs_mgr_is_avb_used()
+{
+ static bool result = init_is_avb_used();
+ return result;
+}
+
+int fs_mgr_load_vbmeta_images(struct fstab *fstab)
+{
+ FS_MGR_CHECK(fstab != nullptr);
+
+ if (!polling_vbmeta_blk_device(fstab)) {
+ ERROR("Failed to find block device of /vbmeta\n");
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ // Gets the expected hash value of vbmeta images from
+ // kernel cmdline.
+ if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ fs_mgr_avb_ops = fs_mgr_dummy_avb_ops_new(fstab);
+ if (fs_mgr_avb_ops == nullptr) {
+ ERROR("Failed to allocate dummy avb_ops\n");
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ // Invokes avb_slot_verify() to load and verify all vbmeta images.
+ // Sets requested_partitions to nullptr as it's to copy the contents
+ // of HASH partitions into fs_mgr_avb_verify_data, which is not required as
+ // fs_mgr only deals with HASHTREE partitions.
+ const char *requested_partitions[] = {nullptr};
+ const char *ab_suffix =
+ android::base::GetProperty("ro.boot.slot_suffix", "").c_str();
+ AvbSlotVerifyResult verify_result = avb_slot_verify(
+ fs_mgr_avb_ops, requested_partitions, ab_suffix,
+ fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
+
+ // Only allow two verify results:
+ // - AVB_SLOT_VERIFY_RESULT_OK.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
+ if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) {
+ if (!fs_mgr_vbmeta_prop.allow_verification_error) {
+ ERROR("ERROR_VERIFICATION isn't allowed\n");
+ goto fail;
+ }
+ } else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
+ ERROR("avb_slot_verify failed, result: %d\n", verify_result);
+ goto fail;
+ }
+
+ // Verifies vbmeta images against the digest passed from bootloader.
+ if (!verify_vbmeta_images(*fs_mgr_avb_verify_data, fs_mgr_vbmeta_prop)) {
+ ERROR("verify_vbmeta_images failed\n");
+ goto fail;
+ } else {
+ // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ AvbVBMetaImageHeader vbmeta_header;
+ avb_vbmeta_image_header_to_host_byte_order(
+ (AvbVBMetaImageHeader *)fs_mgr_avb_verify_data->vbmeta_images[0]
+ .vbmeta_data,
+ &vbmeta_header);
+
+ bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+ AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (hashtree_disabled) {
+ return FS_MGR_SETUP_AVB_HASHTREE_DISABLED;
+ }
+ }
+
+ if (verify_result == AVB_SLOT_VERIFY_RESULT_OK) {
+ return FS_MGR_SETUP_AVB_SUCCESS;
+ }
+
+fail:
+ fs_mgr_unload_vbmeta_images();
+ return FS_MGR_SETUP_AVB_FAIL;
+}
+
+void fs_mgr_unload_vbmeta_images()
+{
+ if (fs_mgr_avb_verify_data != nullptr) {
+ avb_slot_verify_data_free(fs_mgr_avb_verify_data);
+ }
+
+ if (fs_mgr_avb_ops != nullptr) {
+ fs_mgr_dummy_avb_ops_free(fs_mgr_avb_ops);
+ }
+}
+
+int fs_mgr_setup_avb(struct fstab_rec *fstab_entry)
+{
+ if (!fstab_entry || !fs_mgr_avb_verify_data ||
+ fs_mgr_avb_verify_data->num_vbmeta_images < 1) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ std::string partition_name(basename(fstab_entry->mount_point));
+ if (!avb_validate_utf8((const uint8_t *)partition_name.c_str(),
+ partition_name.length())) {
+ ERROR("Partition name: %s is not valid UTF-8.\n",
+ partition_name.c_str());
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ AvbHashtreeDescriptor hashtree_descriptor;
+ std::string salt;
+ std::string root_digest;
+ if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data,
+ &hashtree_descriptor, &salt, &root_digest)) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ // Converts HASHTREE descriptor to verity_table_params.
+ if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt,
+ root_digest)) {
+ return FS_MGR_SETUP_AVB_FAIL;
+ }
+
+ return FS_MGR_SETUP_AVB_SUCCESS;
+}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
new file mode 100644
index 0000000..f3030eb
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <libavb/libavb.h>
+#include <utils/Compat.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_avb_ops.h"
+#include "fs_mgr_priv.h"
+
+static struct fstab *fs_mgr_fstab = nullptr;
+
+static AvbIOResult read_from_partition(AvbOps *ops ATTRIBUTE_UNUSED,
+ const char *partition,
+ int64_t offset,
+ size_t num_bytes,
+ void *buffer,
+ size_t *out_num_read)
+{
+ // The input |partition| name is with ab_suffix, e.g. system_a.
+ // Slot suffix (e.g. _a) will be appended to the device file path
+ // for partitions having 'slotselect' optin in fstab file, but it
+ // won't be appended to the mount point.
+ //
+ // In AVB, we can assume that there's an entry for the /misc mount
+ // point and use that to get the device file for the misc partition.
+ // From there we'll assume that a by-name scheme is used
+ // so we can just replace the trailing "misc" by the given
+ // |partition|, e.g.
+ //
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
+
+ struct fstab_rec *fstab_entry =
+ fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
+
+ if (fstab_entry == nullptr) {
+ ERROR("Partition (%s) not found in fstab\n", partition);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ std::string partition_name(partition);
+ std::string path(fstab_entry->blk_device);
+ // Replaces the last field of device file if it's not misc.
+ if (!android::base::StartsWith(partition_name, "misc")) {
+ size_t end_slash = path.find_last_of("/");
+ std::string by_name_prefix(path.substr(0, end_slash + 1));
+ path = by_name_prefix + partition_name;
+ }
+
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+
+ if (fd < 0) {
+ ERROR("Failed to open %s (%s)\n", path.c_str(), strerror(errno));
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ // If offset is negative, interprets its absolute value as the
+ // number of bytes from the end of the partition.
+ if (offset < 0) {
+ off64_t total_size = lseek64(fd, 0, SEEK_END);
+ if (total_size == -1) {
+ ERROR("Failed to lseek64 to end of the partition\n");
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ offset = total_size + offset;
+ // Repositions the offset to the beginning.
+ if (lseek64(fd, 0, SEEK_SET) == -1) {
+ ERROR("Failed to lseek64 to the beginning of the partition\n");
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ }
+
+ // On Linux, we never get partial reads from block devices (except
+ // for EOF).
+ ssize_t num_read =
+ TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
+
+ if (num_read < 0 || (size_t)num_read != num_bytes) {
+ ERROR("Failed to read %zu bytes from %s offset %" PRId64 " (%s)\n",
+ num_bytes, path.c_str(), offset, strerror(errno));
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ if (out_num_read != nullptr) {
+ *out_num_read = num_read;
+ }
+
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_read_rollback_index(AvbOps *ops ATTRIBUTE_UNUSED,
+ size_t rollback_index_location
+ ATTRIBUTE_UNUSED,
+ uint64_t *out_rollback_index)
+{
+ // rollback_index has been checked in bootloader phase.
+ // In user-space, returns the smallest value 0 to pass the check.
+ *out_rollback_index = 0;
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_validate_vbmeta_public_key(
+ AvbOps *ops ATTRIBUTE_UNUSED,
+ const uint8_t *public_key_data ATTRIBUTE_UNUSED,
+ size_t public_key_length ATTRIBUTE_UNUSED,
+ const uint8_t *public_key_metadata ATTRIBUTE_UNUSED,
+ size_t public_key_metadata_length ATTRIBUTE_UNUSED,
+ bool *out_is_trusted)
+{
+ // vbmeta public key has been checked in bootloader phase.
+ // In user-space, returns true to pass the check.
+ //
+ // Addtionally, user-space should check
+ // androidboot.vbmeta.{hash_alg, size, digest} against the digest
+ // of all vbmeta images after invoking avb_slot_verify().
+
+ *out_is_trusted = true;
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_read_is_device_unlocked(AvbOps *ops ATTRIBUTE_UNUSED,
+ bool *out_is_unlocked)
+{
+ // The function is for bootloader to update the value into
+ // androidboot.vbmeta.device_state in kernel cmdline.
+ // In user-space, returns true as we don't need to update it anymore.
+ *out_is_unlocked = true;
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_get_unique_guid_for_partition(
+ AvbOps *ops ATTRIBUTE_UNUSED,
+ const char *partition ATTRIBUTE_UNUSED,
+ char *guid_buf,
+ size_t guid_buf_size)
+{
+ // The function is for bootloader to set the correct UUID
+ // for a given partition in kernel cmdline.
+ // In user-space, returns a faking one as we don't need to update
+ // it anymore.
+ snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
+ return AVB_IO_RESULT_OK;
+}
+
+AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab)
+{
+ AvbOps *ops;
+
+ // Assigns the fstab to the static variable for later use.
+ fs_mgr_fstab = fstab;
+
+ ops = (AvbOps *)calloc(1, sizeof(AvbOps));
+ if (ops == nullptr) {
+ ERROR("Error allocating memory for AvbOps.\n");
+ return nullptr;
+ }
+
+ // We only need these operations since that's all what is being used
+ // by the avb_slot_verify(); Most of them are dummy operations because
+ // they're only required in bootloader but not required in user-space.
+ ops->read_from_partition = read_from_partition;
+ ops->read_rollback_index = dummy_read_rollback_index;
+ ops->validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
+ ops->read_is_device_unlocked = dummy_read_is_device_unlocked;
+ ops->get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+
+ return ops;
+}
+
+void fs_mgr_dummy_avb_ops_free(AvbOps *ops)
+{
+ free(ops);
+}
diff --git a/fs_mgr/fs_mgr_avb_ops.h b/fs_mgr/fs_mgr_avb_ops.h
new file mode 100644
index 0000000..9f99be8
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb_ops.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CORE_FS_MGR_AVB_OPS_H
+#define __CORE_FS_MGR_AVB_OPS_H
+
+#include <libavb/libavb.h>
+
+#include "fs_mgr.h"
+
+__BEGIN_DECLS
+
+/* Allocates a "dummy" AvbOps instance solely for use in user-space.
+ * Returns nullptr on OOM.
+ *
+ * It mainly provides read_from_partitions() for user-space to get
+ * AvbSlotVerifyData.vbmeta_images[] and the caller MUST check their
+ * integrity against the androidboot.vbmeta.{hash_alg, size, digest}
+ * values from /proc/cmdline, e.g. verify_vbmeta_images()
+ * in fs_mgr_avb.cpp.
+ *
+ * Other I/O operations are only required in boot loader so we set
+ * them as dummy operations here.
+ * - Will allow any public key for signing.
+ * - returns 0 for any rollback index location.
+ * - returns device is unlocked regardless of the actual state.
+ * - returns a dummy guid for any partition.
+ *
+ * Frees with fs_mgr_dummy_avb_ops_free().
+ */
+AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab);
+
+/* Frees an AvbOps instance previously allocated with fs_mgr_avb_ops_new(). */
+void fs_mgr_dummy_avb_ops_free(AvbOps *ops);
+
+__END_DECLS
+
+#endif /* __CORE_FS_MGR_AVB_OPS_H */
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
index 75ce621..939657e 100644
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -68,7 +68,7 @@
int fd,
std::string *out_dev_name)
{
- CHECK(out_dev_name != nullptr);
+ FS_MGR_CHECK(out_dev_name != nullptr);
fs_mgr_verity_ioctl_init(io, name, 0);
if (ioctl(fd, DM_DEV_STATUS, io)) {
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 41fb746..b9d5617 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -35,6 +35,8 @@
unsigned int zram_size;
uint64_t reserved_size;
unsigned int file_encryption_mode;
+ unsigned int erase_blk_size;
+ unsigned int logical_blk_size;
};
struct flag_list {
@@ -77,6 +79,7 @@
{ "max_comp_streams=", MF_MAX_COMP_STREAMS },
{ "verifyatboot", MF_VERIFYATBOOT },
{ "verify", MF_VERIFY },
+ { "avb", MF_AVB },
{ "noemulatedsd", MF_NOEMULATEDSD },
{ "notrim", MF_NOTRIM },
{ "formattable", MF_FORMATTABLE },
@@ -85,6 +88,8 @@
{ "latemount", MF_LATEMOUNT },
{ "reservedsize=", MF_RESERVEDSIZE },
{ "quota", MF_QUOTA },
+ { "eraseblk=", MF_ERASEBLKSIZE },
+ { "logicalblk=", MF_LOGICALBLKSIZE },
{ "defaults", 0 },
{ 0, 0 },
};
@@ -239,6 +244,22 @@
* reserved size of the partition. Get it and return it.
*/
flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_ERASEBLKSIZE) && flag_vals) {
+ /* The erase block size flag is followed by an = and the flash
+ * erase block size. Get it, check that it is a power of 2 and
+ * at least 4096, and return it.
+ */
+ unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+ if (val >= 4096 && (val & (val - 1)) == 0)
+ flag_vals->erase_blk_size = val;
+ } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
+ /* The logical block size flag is followed by an = and the flash
+ * logical block size. Get it, check that it is a power of 2 and
+ * at least 4096, and return it.
+ */
+ unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+ if (val >= 4096 && (val & (val - 1)) == 0)
+ flag_vals->logical_blk_size = val;
}
break;
}
@@ -385,6 +406,8 @@
fstab->recs[cnt].zram_size = flag_vals.zram_size;
fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
+ fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
+ fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
cnt++;
}
/* If an A/B partition, modify block device to be the real block device */
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7f917d9..0a27c7a 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -20,6 +20,17 @@
#include <cutils/klog.h>
#include <fs_mgr.h>
+#ifdef __cplusplus
+#include <android-base/logging.h>
+/* The CHECK() in logging.h will use program invocation name as the tag.
+ * Thus, the log will have prefix "init: " when libfs_mgr is statically
+ * linked in the init process. This might be opaque when debugging.
+ * Appends "in libfs_mgr" at the end of the abort message to explicitly
+ * indicate the check happens in fs_mgr.
+ */
+#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
+#endif
+
__BEGIN_DECLS
#define INFO(x...) KLOG_INFO("fs_mgr", x)
@@ -89,6 +100,9 @@
#define MF_MAX_COMP_STREAMS 0x100000
#define MF_RESERVEDSIZE 0x200000
#define MF_QUOTA 0x400000
+#define MF_ERASEBLKSIZE 0x800000
+#define MF_LOGICALBLKSIZE 0X1000000
+#define MF_AVB 0X2000000
#define DM_BUF_SIZE 4096
diff --git a/fs_mgr/fs_mgr_priv_avb.h b/fs_mgr/fs_mgr_priv_avb.h
new file mode 100644
index 0000000..6d0171c
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_avb.h
@@ -0,0 +1,56 @@
+/*
+ * 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 __CORE_FS_MGR_PRIV_AVB_H
+#define __CORE_FS_MGR_PRIV_AVB_H
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#include "fs_mgr.h"
+
+__BEGIN_DECLS
+
+#define FS_MGR_SETUP_AVB_HASHTREE_DISABLED (-2)
+#define FS_MGR_SETUP_AVB_FAIL (-1)
+#define FS_MGR_SETUP_AVB_SUCCESS 0
+
+bool fs_mgr_is_avb_used();
+
+/* Gets AVB metadata through external/avb/libavb for all partitions:
+ * AvbSlotVerifyData.vbmeta_images[] and checks their integrity
+ * against the androidboot.vbmeta.{hash_alg, size, digest} values
+ * from /proc/cmdline.
+ *
+ * Return values:
+ * - FS_MGR_SETUP_AVB_SUCCESS: the metadata cab be trusted.
+ * - FS_MGR_SETUP_AVB_FAIL: any error when reading and verifying the
+ * metadata, e.g. I/O error, digest value mismatch, size mismatch.
+ * - FS_MGR_SETUP_AVB_HASHTREE_DISABLED: to support the existing
+ * 'adb disable-verity' feature in Android. It's very helpful for
+ * developers to make the filesystem writable to allow replacing
+ * binaries on the device.
+ */
+int fs_mgr_load_vbmeta_images(struct fstab *fstab);
+
+void fs_mgr_unload_vbmeta_images();
+
+int fs_mgr_setup_avb(struct fstab_rec *fstab_entry);
+
+__END_DECLS
+
+#endif /* __CORE_FS_MGR_PRIV_AVB_H */
diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/fs_mgr_priv_sha.h
new file mode 100644
index 0000000..1abc273
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_sha.h
@@ -0,0 +1,74 @@
+/*
+ * 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 __CORE_FS_MGR_PRIV_SHA_H
+#define __CORE_FS_MGR_PRIV_SHA_H
+
+#include <openssl/sha.h>
+
+class SHA256Hasher
+{
+ private:
+ SHA256_CTX sha256_ctx;
+ uint8_t hash[SHA256_DIGEST_LENGTH];
+
+ public:
+ enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
+
+ SHA256Hasher()
+ {
+ SHA256_Init(&sha256_ctx);
+ }
+
+ void update(const void *data, size_t data_size)
+ {
+ SHA256_Update(&sha256_ctx, data, data_size);
+ }
+
+ const uint8_t *finalize()
+ {
+ SHA256_Final(hash, &sha256_ctx);
+ return hash;
+ }
+};
+
+class SHA512Hasher
+{
+ private:
+ SHA512_CTX sha512_ctx;
+ uint8_t hash[SHA512_DIGEST_LENGTH];
+
+ public:
+ enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
+
+ SHA512Hasher()
+ {
+ SHA512_Init(&sha512_ctx);
+ }
+
+ void update(const uint8_t *data, size_t data_size)
+ {
+ SHA512_Update(&sha512_ctx, data, data_size);
+ }
+
+ const uint8_t *finalize()
+ {
+ SHA512_Final(hash, &sha512_ctx);
+ return hash;
+ }
+};
+
+#endif /* __CORE_FS_MGR_PRIV_SHA_H */
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index ef7fdd3..d959798 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -77,6 +77,8 @@
unsigned int zram_size;
uint64_t reserved_size;
unsigned int file_encryption_mode;
+ unsigned int erase_blk_size;
+ unsigned int logical_blk_size;
};
// Callback function for verity status
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index ec3de34..91774c6 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -30,6 +30,8 @@
#include <time.h>
#include <unistd.h>
+#include <functional>
+
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -42,6 +44,7 @@
#include <cutils/misc.h>
#include <cutils/uevent.h>
#include <cutils/properties.h>
+#include <minui/minui.h>
#ifdef CHARGER_ENABLE_SUSPEND
#include <suspend/autosuspend.h>
@@ -49,7 +52,6 @@
#include "animation.h"
#include "AnimationParser.h"
-#include "minui/minui.h"
#include <healthd/healthd.h>
@@ -568,9 +570,8 @@
}
}
-static int set_key_callback(int code, int value, void *data)
+static int set_key_callback(struct charger *charger, int code, int value)
{
- struct charger *charger = (struct charger *)data;
int64_t now = curr_time_ms();
int down = !!value;
@@ -605,7 +606,7 @@
{
if (ev->type != EV_KEY)
return;
- set_key_callback(ev->code, ev->value, charger);
+ set_key_callback(charger, ev->code, ev->value);
}
static void set_next_key_check(struct charger *charger,
@@ -762,9 +763,8 @@
return (int)timeout;
}
-static int input_callback(int fd, unsigned int epevents, void *data)
+static int input_callback(struct charger *charger, int fd, unsigned int epevents)
{
- struct charger *charger = (struct charger *)data;
struct input_event ev;
int ret;
@@ -841,7 +841,8 @@
LOGW("--------------- STARTING CHARGER MODE ---------------\n");
- ret = ev_init(input_callback, charger);
+ ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1,
+ std::placeholders::_2));
if (!ret) {
epollfd = ev_get_epollfd();
healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
@@ -880,7 +881,8 @@
anim->frames[i].surface = scale_frames[i];
}
}
- ev_sync_key_state(set_key_callback, charger);
+ ev_sync_key_state(std::bind(&set_key_callback, charger, std::placeholders::_1,
+ std::placeholders::_2));
charger->next_screen_transition = -1;
charger->next_key_check = -1;
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
index e7cd300..5ae6216 100644
--- a/include/cutils/klog.h
+++ b/include/cutils/klog.h
@@ -23,7 +23,6 @@
__BEGIN_DECLS
-int klog_get_level(void);
void klog_set_level(int level);
void klog_write(int level, const char *fmt, ...)
diff --git a/include/system/window.h b/include/system/window.h
index 236de8d..56c3da6 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -355,6 +355,7 @@
NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 23,
NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 24,
+ NATIVE_WINDOW_GET_REFRESH_CYCLE_PERIOD = 25,
};
/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -1039,6 +1040,14 @@
outDisplayRetireTime, outDequeueReadyTime, outReleaseTime);
}
+static inline int native_window_get_refresh_cycle_period(
+ struct ANativeWindow* window,
+ int64_t* outMinRefreshDuration, int64_t* outMaxRefreshDuration)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_REFRESH_CYCLE_PERIOD,
+ outMinRefreshDuration, outMaxRefreshDuration);
+}
+
__END_DECLS
diff --git a/init/Android.mk b/init/Android.mk
index 35e6f4f..759be52 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -107,6 +107,7 @@
libz \
libprocessgroup \
libnl \
+ libavb
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
diff --git a/init/init.cpp b/init/init.cpp
index ddc707f..1ce3c35 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -835,7 +835,12 @@
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
- parser.ParseConfig("/init.rc");
+ std::string bootscript = property_get("ro.boot.init_rc");
+ if (bootscript.empty()) {
+ parser.ParseConfig("/init.rc");
+ } else {
+ parser.ParseConfig(bootscript);
+ }
ActionManager& am = ActionManager::GetInstance();
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 72fcb5b..d323425 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -27,6 +27,7 @@
#include <sys/poll.h>
#include <memory>
+#include <vector>
#include <cutils/misc.h>
#include <cutils/sockets.h>
@@ -48,6 +49,7 @@
#include <fs_mgr.h>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include "bootimg.h"
#include "property_service.h"
@@ -70,30 +72,30 @@
}
}
-static int check_mac_perms(const char *name, char *sctx, struct ucred *cr)
-{
- char *tctx = NULL;
- int result = 0;
+static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
+
+ if (!sctx) {
+ return false;
+ }
+
+ if (!sehandle_prop) {
+ return false;
+ }
+
+ char* tctx = nullptr;
+ if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) {
+ return false;
+ }
+
property_audit_data audit_data;
- if (!sctx)
- goto err;
-
- if (!sehandle_prop)
- goto err;
-
- if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
- goto err;
-
- audit_data.name = name;
+ audit_data.name = name.c_str();
audit_data.cr = cr;
- if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0)
- result = 1;
+ bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0);
freecon(tctx);
- err:
- return result;
+ return has_access;
}
static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
@@ -142,11 +144,9 @@
}
}
-bool is_legal_property_name(const std::string &name)
-{
+bool is_legal_property_name(const std::string& name) {
size_t namelen = name.size();
- if (namelen >= PROP_NAME_MAX) return false;
if (namelen < 1) return false;
if (name[0] == '.') return false;
if (name[namelen - 1] == '.') return false;
@@ -169,60 +169,219 @@
return true;
}
-int property_set(const char* name, const char* value) {
- size_t valuelen = strlen(value);
+uint32_t property_set(const std::string& name, const std::string& value) {
+ size_t valuelen = value.size();
if (!is_legal_property_name(name)) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
- return -1;
+ return PROP_ERROR_INVALID_NAME;
}
+
if (valuelen >= PROP_VALUE_MAX) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "value too long";
- return -1;
+ return PROP_ERROR_INVALID_VALUE;
}
- if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
- if (restorecon(value, SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ if (name == "selinux.restorecon_recursive" && valuelen > 0) {
+ if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
LOG(ERROR) << "Failed to restorecon_recursive " << value;
}
}
- prop_info* pi = (prop_info*) __system_property_find(name);
+ prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
- if (!strncmp(name, "ro.", 3)) {
+ if (android::base::StartsWith(name, "ro.")) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "property already set";
- return -1;
+ return PROP_ERROR_READ_ONLY_PROPERTY;
}
- __system_property_update(pi, value, valuelen);
+ __system_property_update(pi, value.c_str(), valuelen);
} else {
- int rc = __system_property_add(name, strlen(name), value, valuelen);
+ int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "__system_property_add failed";
- return rc;
+ return PROP_ERROR_SET_FAILED;
}
}
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
- if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) {
- write_persistent_property(name, value);
+ if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
+ write_persistent_property(name.c_str(), value.c_str());
}
- property_changed(name, value);
- return 0;
+ property_changed(name.c_str(), value.c_str());
+ return PROP_SUCCESS;
}
-static void handle_property_set_fd()
-{
- prop_msg msg;
- int r;
- char * source_ctx = NULL;
+class SocketConnection {
+ public:
+ SocketConnection(int socket, const struct ucred& cred)
+ : socket_(socket), cred_(cred) {}
- int s = accept(property_set_fd, nullptr, nullptr);
+ ~SocketConnection() {
+ close(socket_);
+ }
+
+ bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
+ return RecvFully(value, sizeof(*value), timeout_ms);
+ }
+
+ bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {
+ return RecvFully(chars, size, timeout_ms);
+ }
+
+ bool RecvString(std::string* value, uint32_t* timeout_ms) {
+ uint32_t len = 0;
+ if (!RecvUint32(&len, timeout_ms)) {
+ return false;
+ }
+
+ if (len == 0) {
+ *value = "";
+ return true;
+ }
+
+ std::vector<char> chars(len);
+ if (!RecvChars(&chars[0], len, timeout_ms)) {
+ return false;
+ }
+
+ *value = std::string(&chars[0], len);
+ return true;
+ }
+
+ bool SendUint32(uint32_t value) {
+ int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
+ return result == sizeof(value);
+ }
+
+ int socket() {
+ return socket_;
+ }
+
+ const struct ucred& cred() {
+ return cred_;
+ }
+
+ private:
+ bool PollIn(uint32_t* timeout_ms) {
+ struct pollfd ufds[1];
+ ufds[0].fd = socket_;
+ ufds[0].events = POLLIN;
+ ufds[0].revents = 0;
+ while (*timeout_ms > 0) {
+ Timer timer;
+ int nr = poll(ufds, 1, *timeout_ms);
+ uint64_t millis = timer.duration_ns()/1000000;
+ *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
+
+ if (nr > 0) {
+ return true;
+ }
+
+ if (nr == 0) {
+ // Timeout
+ break;
+ }
+
+ if (nr < 0 && errno != EINTR) {
+ PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid << " to send property message";
+ return false;
+ } else { // errno == EINTR
+ // Timer rounds milliseconds down in case of EINTR we want it to be rounded up
+ // to avoid slowing init down by causing EINTR with under millisecond timeout.
+ if (*timeout_ms > 0) {
+ --(*timeout_ms);
+ }
+ }
+ }
+
+ LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid << " to send property message.";
+ return false;
+ }
+
+ bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {
+ size_t bytes_left = size;
+ char* data = static_cast<char*>(data_ptr);
+ while (*timeout_ms > 0 && bytes_left > 0) {
+ if (!PollIn(timeout_ms)) {
+ return false;
+ }
+
+ int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
+ if (result <= 0) {
+ return false;
+ }
+
+ bytes_left -= result;
+ data += result;
+ }
+
+ return bytes_left == 0;
+ }
+
+ int socket_;
+ struct ucred cred_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
+};
+
+static void handle_property_set(SocketConnection& socket,
+ const std::string& name,
+ const std::string& value,
+ bool legacy_protocol) {
+ const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
+ if (!is_legal_property_name(name)) {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
+ socket.SendUint32(PROP_ERROR_INVALID_NAME);
+ return;
+ }
+
+ struct ucred cr = socket.cred();
+ char* source_ctx = nullptr;
+ getpeercon(socket.socket(), &source_ctx);
+
+ if (android::base::StartsWith(name, "ctl.")) {
+ if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
+ handle_control_message(name.c_str() + 4, value.c_str());
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_SUCCESS);
+ }
+ } else {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
+ << " service ctl [" << value << "]"
+ << " uid:" << cr.uid
+ << " gid:" << cr.gid
+ << " pid:" << cr.pid;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
+ }
+ }
+ } else {
+ if (check_mac_perms(name, source_ctx, &cr)) {
+ uint32_t result = property_set(name, value);
+ if (!legacy_protocol) {
+ socket.SendUint32(result);
+ }
+ } else {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+ }
+ }
+ }
+
+ freecon(source_ctx);
+}
+
+static void handle_property_set_fd() {
+ static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
+
+ int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}
@@ -236,72 +395,50 @@
return;
}
- static constexpr int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
- struct pollfd ufds[1];
- ufds[0].fd = s;
- ufds[0].events = POLLIN;
- ufds[0].revents = 0;
- int nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
- if (nr == 0) {
- LOG(ERROR) << "sys_prop: timeout waiting for uid " << cr.uid << " to send property message.";
- close(s);
- return;
- } else if (nr < 0) {
- PLOG(ERROR) << "sys_prop: error waiting for uid " << cr.uid << " to send property message";
- close(s);
+ SocketConnection socket(s, cr);
+ uint32_t timeout_ms = kDefaultSocketTimeout;
+
+ uint32_t cmd = 0;
+
+ if (!socket.RecvUint32(&cmd, &timeout_ms)) {
+ PLOG(ERROR) << "sys_prop: error while reading command from the socket";
+ socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
- r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
- if(r != sizeof(prop_msg)) {
- PLOG(ERROR) << "sys_prop: mis-match msg size received: " << r << " expected: " << sizeof(prop_msg);
- close(s);
- return;
- }
+ switch(cmd) {
+ case PROP_MSG_SETPROP: {
+ char prop_name[PROP_NAME_MAX];
+ char prop_value[PROP_VALUE_MAX];
- switch(msg.cmd) {
- case PROP_MSG_SETPROP:
- msg.name[PROP_NAME_MAX-1] = 0;
- msg.value[PROP_VALUE_MAX-1] = 0;
-
- if (!is_legal_property_name(msg.name)) {
- LOG(ERROR) << "sys_prop: illegal property name \"" << msg.name << "\"";
- close(s);
- return;
+ if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
+ !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
+ PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
+ return;
}
- getpeercon(s, &source_ctx);
+ prop_name[PROP_NAME_MAX-1] = 0;
+ prop_value[PROP_VALUE_MAX-1] = 0;
- if(memcmp(msg.name,"ctl.",4) == 0) {
- // Keep the old close-socket-early behavior when handling
- // ctl.* properties.
- close(s);
- if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
- handle_control_message((char*) msg.name + 4, (char*) msg.value);
- } else {
- LOG(ERROR) << "sys_prop: Unable to " << (msg.name + 4)
- << " service ctl [" << msg.value << "]"
- << " uid:" << cr.uid
- << " gid:" << cr.gid
- << " pid:" << cr.pid;
- }
- } else {
- if (check_mac_perms(msg.name, source_ctx, &cr)) {
- property_set((char*) msg.name, (char*) msg.value);
- } else {
- LOG(ERROR) << "sys_prop: permission denied uid:" << cr.uid << " name:" << msg.name;
- }
-
- // Note: bionic's property client code assumes that the
- // property server will not close the socket until *AFTER*
- // the property is written to memory.
- close(s);
- }
- freecon(source_ctx);
+ handle_property_set(socket, prop_value, prop_value, true);
break;
+ }
+ case PROP_MSG_SETPROP2: {
+ std::string name;
+ std::string value;
+ if (!socket.RecvString(&name, &timeout_ms) ||
+ !socket.RecvString(&value, &timeout_ms)) {
+ PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
+ socket.SendUint32(PROP_ERROR_READ_DATA);
+ return;
+ }
+
+ handle_property_set(socket, name, value, false);
+ break;
+ }
default:
- close(s);
+ socket.SendUint32(PROP_ERROR_INVALID_CMD);
break;
}
}
@@ -510,6 +647,8 @@
}
void start_property_service() {
+ property_set("ro.property_service.version", "2");
+
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
diff --git a/init/property_service.h b/init/property_service.h
index e3a2acb..5d59473 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -27,14 +27,14 @@
const char* name;
};
-extern void property_init(void);
-extern void property_load_boot_defaults(void);
-extern void load_persist_props(void);
-extern void load_system_props(void);
-extern void start_property_service(void);
+void property_init(void);
+void property_load_boot_defaults(void);
+void load_persist_props(void);
+void load_system_props(void);
+void start_property_service(void);
std::string property_get(const char* name);
-extern int property_set(const char *name, const char *value);
-extern bool is_legal_property_name(const std::string &name);
+uint32_t property_set(const std::string& name, const std::string& value);
+bool is_legal_property_name(const std::string& name);
#endif /* _INIT_PROPERTY_H */
diff --git a/init/seccomp.cpp b/init/seccomp.cpp
index d9f2f79..d632302 100644
--- a/init/seccomp.cpp
+++ b/init/seccomp.cpp
@@ -170,6 +170,12 @@
// Needed for trusty
AllowSyscall(f, __NR_syncfs);
+ // Needed for strace
+ AllowSyscall(f, __NR_tkill); // __NR_tkill
+
+ // Needed for kernel to restart syscalls
+ AllowSyscall(f, __NR_restart_syscall);
+
// arm64-only filter - autogenerated from bionic syscall usage
for (size_t i = 0; i < arm64_filter_size; ++i)
f.push_back(arm64_filter[i]);
@@ -201,6 +207,12 @@
// Syscalls needed to run GFXBenchmark
AllowSyscall(f, 190); // __NR_vfork
+ // Needed for strace
+ AllowSyscall(f, 238); // __NR_tkill
+
+ // Needed for kernel to restart syscalls
+ AllowSyscall(f, 0); // __NR_restart_syscall
+
// arm32-on-arm64 only filter - autogenerated from bionic syscall usage
for (size_t i = 0; i < arm_filter_size; ++i)
f.push_back(arm_filter[i]);
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
index 15adf6b..d301276 100644
--- a/libcutils/klog.cpp
+++ b/libcutils/klog.cpp
@@ -29,10 +29,6 @@
static int klog_level = KLOG_INFO_LEVEL;
-int klog_get_level(void) {
- return klog_level;
-}
-
void klog_set_level(int level) {
klog_level = level;
}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index bbe7d79..40cdcee 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -57,7 +57,7 @@
srcs: liblog_target_sources,
// AddressSanitizer runtime library depends on liblog.
sanitize: {
- never: true,
+ address: false,
},
},
android_arm: {
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 8a390f6..337861e 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -23,6 +23,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <memory>
#include <string>
@@ -54,6 +55,11 @@
TEST(logcat, buckets) {
FILE *fp;
+#undef LOG_TAG
+#define LOG_TAG "inject"
+ RLOGE("logcat.buckets");
+ sleep(1);
+
ASSERT_TRUE(NULL != (fp = popen(
"logcat -b radio -b events -b system -b main -d 2>/dev/null",
"r")));
@@ -165,13 +171,39 @@
if (!ep) {
continue;
}
- ep -= 7;
+ static const size_t tag_field_width = 7;
+ ep -= tag_field_width;
*ep = '\0';
return cp;
}
return NULL;
}
+// If there is not enough background noise in the logs, then spam the logs to
+// permit tail checking so that the tests can progress.
+static size_t inject(ssize_t count) {
+ if (count <= 0) return 0;
+
+ static const size_t retry = 3;
+ size_t errors = retry;
+ size_t num = 0;
+ for(;;) {
+ log_time ts(CLOCK_MONOTONIC);
+ if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
+ if (++num >= (size_t)count) {
+ sleep(1); // let data settle end-to-end
+ return num;
+ }
+ errors = retry;
+ usleep(50);
+ } else if (--errors <= 0) {
+ return num;
+ }
+ }
+ // NOTREACH
+ return num;
+}
+
TEST(logcat, tz) {
if (android_log_clockid() == CLOCK_MONOTONIC) {
@@ -201,7 +233,7 @@
pclose(fp);
- } while ((count < 3) && --tries && (sleep(1), true));
+ } while ((count < 3) && --tries && inject(3 - count));
ASSERT_EQ(3, count);
}
@@ -236,8 +268,7 @@
char buffer[BIG_BUFFER];
snprintf(buffer, sizeof(buffer),
- "logcat -v long -b radio -b events -b system -b main -t %d 2>/dev/null",
- num);
+ "logcat -v long -b all -t %d 2>/dev/null", num);
FILE *fp;
ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
@@ -250,7 +281,7 @@
pclose(fp);
- } while ((count < num) && --tries && (sleep(1), true));
+ } while ((count < num) && --tries && inject(num - count));
ASSERT_EQ(num, count);
}
@@ -273,26 +304,34 @@
TEST(logcat, tail_time) {
FILE *fp;
-
- ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
-
+ int count;
char buffer[BIG_BUFFER];
char *last_timestamp = NULL;
char *first_timestamp = NULL;
- int count = 0;
-
char *cp;
- while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
- ++count;
- if (!first_timestamp) {
- first_timestamp = strdup(cp);
- }
- free(last_timestamp);
- last_timestamp = strdup(cp);
- }
- pclose(fp);
- EXPECT_EQ(10, count);
+ int tries = 3; // in case run too soon after system start or buffer clear
+
+ // Do not be tempted to use -v usec because that increases the
+ // chances of an occasional test failure by 1000 (see below).
+ do {
+ ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
+
+ count = 0;
+
+ while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ ++count;
+ if (!first_timestamp) {
+ first_timestamp = strdup(cp);
+ }
+ free(last_timestamp);
+ last_timestamp = strdup(cp);
+ }
+ pclose(fp);
+
+ } while ((count < 10) && --tries && inject(10 - count));
+
+ EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
EXPECT_TRUE(last_timestamp != NULL);
EXPECT_TRUE(first_timestamp != NULL);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1ae43fb..f4bca32 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -360,9 +360,6 @@
start vold
installkey /data
- # start tombstoned to record early-boot crashes.
- start tombstoned
-
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
diff --git a/storaged/Android.mk b/storaged/Android.mk
index db97040..0e8b574 100644
--- a/storaged/Android.mk
+++ b/storaged/Android.mk
@@ -2,14 +2,16 @@
LOCAL_PATH := $(call my-dir)
-LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap
+LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap libpackagelistparser
include $(CLEAR_VARS)
LOCAL_SRC_FILES := storaged.cpp \
storaged_service.cpp \
storaged_utils.cpp \
+ storaged_uid_monitor.cpp \
EventLogTags.logtags
+
LOCAL_MODULE := libstoraged
LOCAL_CFLAGS := -Werror
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
index ee92dd1..71fda25 100644
--- a/storaged/EventLogTags.logtags
+++ b/storaged/EventLogTags.logtags
@@ -37,3 +37,5 @@
2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
2733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)
+
+2734 storaged_uid_io_alert (name|3),(read|2),(write|2),(interval|2)
\ No newline at end of file
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index 521cc9e..7fa9958 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -17,13 +17,17 @@
#ifndef _STORAGED_H_
#define _STORAGED_H_
-#include <queue>
#include <semaphore.h>
#include <stdint.h>
+#include <time.h>
+
+#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
+#include "storaged_uid_monitor.h"
+
#define FRIEND_TEST(test_case_name, test_name) \
friend class test_case_name##_##test_name##_Test
@@ -165,6 +169,8 @@
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
+#define UID_IO_STATS_PATH "/proc/uid_io/stats"
+
class disk_stats_monitor {
private:
FRIEND_TEST(storaged_test, disk_stats_monitor);
@@ -260,12 +266,15 @@
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 86400 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT ( 3600 )
struct storaged_config {
int periodic_chores_interval_unit;
int periodic_chores_interval_disk_stats_publish;
int periodic_chores_interval_emmc_info_publish;
+ int periodic_chores_interval_uid_io;
bool proc_taskio_readable; // are /proc/[pid]/{io, comm, cmdline, stat} all readable
+ bool proc_uid_io_available; // whether uid_io is accessible
bool emmc_available; // whether eMMC est_csd file is readable
bool diskstats_available; // whether diskstats is accessible
};
@@ -278,6 +287,7 @@
disk_stats_monitor mDsm;
emmc_info_t mEmmcInfo;
tasks_t mTasks;
+ uid_monitor mUidm;
time_t mStarttime;
public:
storaged_t(void);
@@ -295,6 +305,9 @@
void set_emmc_interval(int emmc_info) {
mConfig.periodic_chores_interval_emmc_info_publish = emmc_info;
}
+ void set_uid_io_interval(int uid_io) {
+ mUidm.set_periodic_chores_interval(uid_io);
+ }
std::vector<struct task_info> get_tasks(void) {
// There could be a race when get_tasks() and the main thread is updating at the same time
// While update_running_tasks() is updating the critical sections at the end of the function
@@ -312,11 +325,16 @@
time_t get_starttime(void) {
return mStarttime;
}
+
+ std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+ return mUidm.get_uids();
+ }
};
// Eventlog tag
// The content must match the definition in EventLogTags.logtags
#define EVENTLOGTAG_DISKSTATS ( 2732 )
#define EVENTLOGTAG_EMMCINFO ( 2733 )
+#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )
#endif /* _STORAGED_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 64a9c81..220038c 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -31,9 +31,11 @@
public:
enum {
DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION,
+ DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION + 1,
};
// Request the service to run the test function
virtual std::vector<struct task_info> dump_tasks(const char* option) = 0;
+ virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
DECLARE_META_INTERFACE(Storaged);
};
@@ -43,6 +45,7 @@
public:
BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
virtual std::vector<struct task_info> dump_tasks(const char* option);
+ virtual std::vector<struct uid_info> dump_uids(const char* option);
};
// Server
@@ -52,6 +55,7 @@
class Storaged : public BnStoraged {
virtual std::vector<struct task_info> dump_tasks(const char* option);
+ virtual std::vector<struct uid_info> dump_uids(const char* option);
};
sp<IStoraged> get_storaged_service();
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
new file mode 100644
index 0000000..eceb7fd
--- /dev/null
+++ b/storaged/include/storaged_uid_monitor.h
@@ -0,0 +1,59 @@
+/*
+ * 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 _STORAGED_UID_MONITOR_H_
+#define _STORAGED_UID_MONITOR_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <unordered_map>
+
+enum {
+ UID_FOREGROUND = 0,
+ UID_BACKGROUND = 1,
+ UID_STATS_SIZE = 2
+};
+
+struct uid_io_stats {
+ uint64_t rchar; // characters read
+ uint64_t wchar; // characters written
+ uint64_t read_bytes; // bytes read (from storage layer)
+ uint64_t write_bytes; // bytes written (to storage layer)
+};
+
+struct uid_info {
+ uint32_t uid; // user id
+ std::string name; // package name
+ struct uid_io_stats io[UID_STATS_SIZE]; // [0]:foreground [1]:background
+
+};
+
+class uid_monitor {
+private:
+ std::unordered_map<uint32_t, struct uid_info> last_uids;
+ void set_last_uids(std::unordered_map<uint32_t, struct uid_info>&& uids, uint64_t ts);
+ int interval; // monitor interval in seconds
+ uint64_t last_report_ts; // timestamp of last report in nsec
+public:
+ uid_monitor();
+ void set_periodic_chores_interval(int t) { interval = t; }
+ int get_periodic_chores_interval() { return interval; }
+ std::unordered_map<uint32_t, struct uid_info> get_uids();
+ void report();
+};
+
+#endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 83538c2..bb14708 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -34,12 +34,15 @@
// Task I/O
bool parse_task_info(uint32_t pid, struct task_info* info);
void sort_running_tasks_info(std::vector<struct task_info> &tasks);
+// UID I/O
+void sort_running_uids_info(std::vector<struct uid_info> &uids);
// Logging
void log_console_running_tasks_info(std::vector<struct task_info> tasks);
+void log_console_running_uids_info(std::vector<struct uid_info> uids);
void log_debug_disk_perf(struct disk_perf* perf, const char* type);
void log_event_disk_stats(struct disk_stats* stats, const char* type);
void log_event_emmc_info(struct emmc_info* info_);
-#endif /* _STORAGED_UTILS_H_ */
\ No newline at end of file
+#endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 0cb0f5f..9151574 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -105,9 +105,11 @@
static void help_message(void) {
printf("usage: storaged [OPTION]\n");
printf(" -d --dump Dump task I/O usage to stdout\n");
+ printf(" -u --uid Dump uid I/O usage to stdout\n");
printf(" -s --start Start storaged (default)\n");
printf(" --emmc=INTERVAL Set publish interval of emmc lifetime information (in days)\n");
printf(" --diskstats=INTERVAL Set publish interval of diskstats (in hours)\n");
+ printf(" --uidio=INTERVAL Set publish interval of uid io (in hours)\n");
printf(" --unit=INTERVAL Set storaged's refresh interval (in seconds)\n");
fflush(stdout);
}
@@ -118,10 +120,12 @@
int main(int argc, char** argv) {
int flag_main_service = 0;
int flag_dump_task = 0;
+ int flag_dump_uid = 0;
int flag_config = 0;
int unit_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
int diskstats_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
int emmc_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
+ int uid_io_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT;
int fd_emmc = -1;
int opt;
@@ -131,12 +135,14 @@
{"start", no_argument, 0, 's'},
{"kill", no_argument, 0, 'k'},
{"dump", no_argument, 0, 'd'},
+ {"uid", no_argument, 0, 'u'},
{"help", no_argument, 0, 'h'},
{"unit", required_argument, 0, 0 },
{"diskstats", required_argument, 0, 0 },
- {"emmc", required_argument, 0, 0 }
+ {"emmc", required_argument, 0, 0 },
+ {"uidio", required_argument, 0, 0 }
};
- opt = getopt_long(argc, argv, ":skdh0", long_options, &opt_idx);
+ opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
if (opt == -1) {
break;
}
@@ -165,7 +171,15 @@
} else if (strcmp(long_options[opt_idx].name, "emmc") == 0) {
emmc_interval = atoi(optarg) * DAY_TO_SEC;
- if (diskstats_interval == 0) {
+ if (emmc_interval == 0) {
+ fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
+ long_options[opt_idx].name);
+ help_message();
+ return -1;
+ }
+ } else if (strcmp(long_options[opt_idx].name, "uidio") == 0) {
+ uid_io_interval = atoi(optarg) * HOUR_TO_SEC;
+ if (uid_io_interval == 0) {
fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
long_options[opt_idx].name);
help_message();
@@ -187,6 +201,9 @@
case 'd':
flag_dump_task = 1;
break;
+ case 'u':
+ flag_dump_uid = 1;
+ break;
case 'h':
help_message();
return 0;
@@ -230,6 +247,7 @@
storaged.set_unit_interval(unit_interval);
storaged.set_diskstats_interval(diskstats_interval);
storaged.set_emmc_interval(emmc_interval);
+ storaged.set_uid_io_interval(uid_io_interval);
}
// Start the main thread of storaged
@@ -278,5 +296,24 @@
return 0;
}
+ if (flag_dump_uid) {
+ sp<IStoraged> storaged_service = get_storaged_service();
+ if (storaged_service == NULL) {
+ fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+ return -1;
+ }
+ std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
+
+ if (res.size() == 0) {
+ fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+ return 0;
+ }
+
+ sort_running_uids_info(res);
+ log_console_running_uids_info(res);
+
+ return 0;
+ }
+
return 0;
}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index e4d3e68..0c53f44 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -174,9 +174,12 @@
}
}
+ mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
+
mConfig.periodic_chores_interval_unit = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
mConfig.periodic_chores_interval_disk_stats_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
mConfig.periodic_chores_interval_emmc_info_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
+ mUidm.set_periodic_chores_interval(DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT);
mStarttime = time(NULL);
}
@@ -202,5 +205,10 @@
mEmmcInfo.publish();
}
+ if (mConfig.proc_uid_io_available && mTimer &&
+ (mTimer % mUidm.get_periodic_chores_interval()) == 0) {
+ mUidm.report();
+ }
+
mTimer += mConfig.periodic_chores_interval_unit;
}
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index aa38ceb..2a81aef 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <stdint.h>
+
#include <vector>
#include <binder/IBinder.h>
@@ -41,6 +43,22 @@
return res;
}
+std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
+
+ remote()->transact(DUMPUIDS, data, &reply);
+
+ uint32_t res_size = reply.readInt32();
+ std::vector<struct uid_info> res(res_size);
+ for (auto&& uid : res) {
+ uid.uid = reply.readInt32();
+ uid.name = reply.readCString();
+ reply.read(&uid.io, sizeof(uid.io));
+ }
+ return res;
+}
+
IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
@@ -57,14 +75,36 @@
return NO_ERROR;
}
break;
+ case DUMPUIDS: {
+ std::vector<struct uid_info> res = dump_uids(NULL);
+ reply->writeInt32(res.size());
+ for (auto uid : res) {
+ reply->writeInt32(uid.uid);
+ reply->writeCString(uid.name.c_str());
+ reply->write(&uid.io, sizeof(uid.io));
+ }
+ return NO_ERROR;
+ }
+ break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
+
std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) {
return storaged.get_tasks();
}
+std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
+ std::vector<struct uid_info> uids_v;
+ std::unordered_map<uint32_t, struct uid_info> uids_m = storaged.get_uids();
+
+ for (const auto& it : uids_m) {
+ uids_v.push_back(it.second);
+ }
+ return uids_v;
+}
+
sp<IStoraged> get_storaged_service() {
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL) return NULL;
@@ -75,4 +115,4 @@
sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
return storaged;
-}
\ No newline at end of file
+}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
new file mode 100644
index 0000000..4105dae
--- /dev/null
+++ b/storaged/storaged_uid_monitor.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "storaged"
+
+#include <stdint.h>
+#include <time.h>
+
+#include <string>
+#include <sstream>
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <packagelistparser/packagelistparser.h>
+
+#include "storaged.h"
+#include "storaged_uid_monitor.h"
+
+static const uint64_t io_alert_threshold = 1024 * 1024 * 1024; // 1GB
+
+using namespace android;
+using namespace android::base;
+
+static bool packagelist_parse_cb(pkg_info* info, void* userdata)
+{
+ std::unordered_map<uint32_t, struct uid_info>* uids =
+ reinterpret_cast<std::unordered_map<uint32_t, struct uid_info>*>(userdata);
+
+ if (uids->find(info->uid) != uids->end()) {
+ (*uids)[info->uid].name = info->name;
+ }
+
+ packagelist_free(info);
+ return true;
+}
+
+void uid_monitor::set_last_uids(std::unordered_map<uint32_t, struct uid_info>&& uids,
+ uint64_t ts)
+{
+ last_uids = uids;
+ last_report_ts = ts;
+}
+
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uids()
+{
+ std::unordered_map<uint32_t, struct uid_info> uids;
+ std::string buffer;
+ if (!android::base::ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
+ PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
+ return uids;
+ }
+
+ std::stringstream ss(buffer);
+ struct uid_info u;
+ bool refresh_uid = false;
+
+ while (ss >> u.uid) {
+ ss >> u.io[UID_FOREGROUND].rchar >> u.io[UID_FOREGROUND].wchar
+ >> u.io[UID_FOREGROUND].read_bytes >> u.io[UID_FOREGROUND].write_bytes
+ >> u.io[UID_BACKGROUND].rchar >> u.io[UID_BACKGROUND].wchar
+ >> u.io[UID_BACKGROUND].read_bytes >> u.io[UID_BACKGROUND].write_bytes;
+
+ if (!ss.good()) {
+ ss.clear(std::ios_base::badbit);
+ break;
+ }
+
+ if (last_uids.find(u.uid) == last_uids.end()) {
+ refresh_uid = true;
+ u.name = std::to_string(u.uid);
+ } else {
+ u.name = last_uids[u.uid].name;
+ }
+ uids[u.uid] = u;
+ }
+
+ if (!ss.eof() || ss.bad()) {
+ uids.clear();
+ LOG_TO(SYSTEM, ERROR) << "read UID IO stats failed";
+ }
+
+ if (refresh_uid) {
+ packagelist_parse(packagelist_parse_cb, &uids);
+ }
+
+ return uids;
+}
+
+void uid_monitor::report()
+{
+ struct timespec ts;
+
+ // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+ // when system is running.
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ return;
+ }
+
+ uint64_t curr_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
+ uint64_t ts_delta = curr_ts - last_report_ts;
+ uint64_t adjusted_threshold = io_alert_threshold * ((double)ts_delta / interval / NS_PER_SEC);
+
+ std::unordered_map<uint32_t, struct uid_info> uids = get_uids();
+ if (uids.empty()) {
+ return;
+ }
+
+ for (const auto& it : uids) {
+ const struct uid_info& uid = it.second;
+ uint64_t bg_read_delta = uid.io[UID_BACKGROUND].read_bytes -
+ last_uids[uid.uid].io[UID_BACKGROUND].read_bytes;
+ uint64_t bg_write_delta = uid.io[UID_BACKGROUND].write_bytes -
+ last_uids[uid.uid].io[UID_BACKGROUND].write_bytes;
+
+ if (bg_read_delta + bg_write_delta >= adjusted_threshold) {
+ android_log_event_list(EVENTLOGTAG_UID_IO_ALERT)
+ << uid.name << bg_read_delta << bg_write_delta
+ << uint64_t(ts_delta / NS_PER_SEC) << LOG_ID_EVENTS;
+ }
+ }
+
+ set_last_uids(std::move(uids), curr_ts);
+}
+
+uid_monitor::uid_monitor()
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ return;
+ }
+ last_report_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
+}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index c845ac4..6e4ddb6 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -435,6 +435,54 @@
fflush(stdout);
}
+static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+ // Compare background I/O first.
+ for (int i = UID_STATS_SIZE - 1; i >= 0; i--) {
+ uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
+ uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes;
+ uint64_t l_chars = l.io[i].rchar + l.io[i].wchar;
+ uint64_t r_chars = r.io[i].rchar + r.io[i].wchar;
+
+ if (l_bytes != r_bytes) {
+ return l_bytes > r_bytes;
+ }
+ if (l_chars != r_chars) {
+ return l_chars > r_chars;
+ }
+ }
+
+ return l.name < r.name;
+}
+
+void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+ std::sort(uids.begin(), uids.end(), cmp_uid_info);
+}
+
+// Logging functions
+void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+// Sample Output:
+// Application FG Read FG Write FG Read FG Write BG Read BG Write BG Read BG Write
+// NAME/UID Characters Characters Bytes Bytes Characters Characters Bytes Bytes
+// ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
+// com.google.android.gsf.login 0 0 0 0 57195097 5137089 176386048 6512640
+// com.google.android.googlequicksearchbox 0 0 0 0 4196821 12123468 34295808 13225984
+// 1037 4572 537 0 0 131352 5145643 34263040 5144576
+// com.google.android.youtube 2182 70 0 0 63969383 482939 38731776 466944
+
+ // Title
+ printf("Per-UID I/O stats\n");
+ printf(" Application FG Read FG Write FG Read FG Write BG Read BG Write BG Read BG Write\n"
+ " NAME/UID Characters Characters Bytes Bytes Characters Characters Bytes Bytes\n"
+ " ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n");
+
+ for (const auto& uid : uids) {
+ printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n", uid.name.c_str(),
+ uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
+ uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes);
+ }
+ fflush(stdout);
+}
+
#if DEBUG
void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
// skip if the input structure are all zeros
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
index 4a0e45c..26d04b1 100644
--- a/storaged/tests/Android.mk
+++ b/storaged/tests/Android.mk
@@ -40,6 +40,6 @@
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_STATIC_LIBRARIES := libstoraged
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)