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)