Merge "fs_mgr: removing __BEGIN_DECLS and __END_DECLS"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 8c24bbb..6b30be8 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -257,19 +257,6 @@
     send_packet(cp, t);
 }
 
-#if ADB_HOST
-
-void SendConnectOnHost(atransport* t) {
-    // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
-    // host and ep_in on device may not be the same.
-    apacket* p = get_apacket();
-    CHECK(p);
-    send_packet(p, t);
-    send_connect(t);
-}
-
-#endif
-
 // qual_overwrite is used to overwrite a qualifier string.  dst is a
 // pointer to a char pointer.  It is assumed that if *dst is non-NULL, it
 // was malloc'ed and needs to freed.  *dst will be set to a dup of src.
@@ -370,7 +357,7 @@
         if (p->msg.arg0){
             send_packet(p, t);
 #if ADB_HOST
-            SendConnectOnHost(t);
+            send_connect(t);
 #endif
         } else {
             t->SetConnectionState(kCsOffline);
diff --git a/adb/adb.h b/adb/adb.h
index 6a9897f..a4d233e 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -224,9 +224,6 @@
 void handle_offline(atransport *t);
 
 void send_connect(atransport *t);
-#if ADB_HOST
-void SendConnectOnHost(atransport* t);
-#endif
 
 void parse_banner(const std::string&, atransport* t);
 
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7025f28..8120199 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -333,6 +333,13 @@
             return;
         }
 
+        rc = libusb_set_interface_alt_setting(handle.get(), interface_num, 0);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to set interface alt setting for device '" << device_serial
+                         << "'" << libusb_error_name(rc);
+            return;
+        }
+
         for (uint8_t endpoint : {bulk_in, bulk_out}) {
             rc = libusb_clear_halt(handle.get(), endpoint);
             if (rc != 0) {
diff --git a/adb/services.cpp b/adb/services.cpp
index ca34556..aff7012 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -58,6 +58,7 @@
 #include "transport.h"
 
 struct stinfo {
+    const char* service_name;
     void (*func)(int fd, void *cookie);
     int fd;
     void *cookie;
@@ -65,7 +66,7 @@
 
 static void service_bootstrap_func(void* x) {
     stinfo* sti = reinterpret_cast<stinfo*>(x);
-    adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
+    adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
     sti->func(sti->fd, sti->cookie);
     free(sti);
 }
@@ -160,8 +161,7 @@
     return true;
 }
 
-void reboot_service(int fd, void* arg)
-{
+void reboot_service(int fd, void* arg) {
     if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
         // Don't return early. Give the reboot command time to take effect
         // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
@@ -236,8 +236,7 @@
 
 #endif  // !ADB_HOST
 
-static int create_service_thread(void (*func)(int, void *), void *cookie)
-{
+static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
     int s[2];
     if (adb_socketpair(s)) {
         printf("cannot create service socket pair\n");
@@ -258,6 +257,7 @@
     if (sti == nullptr) {
         fatal("cannot allocate stinfo");
     }
+    sti->service_name = service_name;
     sti->func = func;
     sti->cookie = cookie;
     sti->fd = s[1];
@@ -281,7 +281,7 @@
     } else if(!strncmp("dev:", name, 4)) {
         ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
     } else if(!strncmp(name, "framebuffer:", 12)) {
-        ret = create_service_thread(framebuffer_service, 0);
+        ret = create_service_thread("fb", framebuffer_service, nullptr);
     } else if (!strncmp(name, "jdwp:", 5)) {
         ret = create_jdwp_connection_fd(atoi(name+5));
     } else if(!strncmp(name, "shell", 5)) {
@@ -289,17 +289,17 @@
     } else if(!strncmp(name, "exec:", 5)) {
         ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if(!strncmp(name, "sync:", 5)) {
-        ret = create_service_thread(file_sync_service, NULL);
+        ret = create_service_thread("sync", file_sync_service, nullptr);
     } else if(!strncmp(name, "remount:", 8)) {
-        ret = create_service_thread(remount_service, NULL);
+        ret = create_service_thread("remount", remount_service, nullptr);
     } else if(!strncmp(name, "reboot:", 7)) {
         void* arg = strdup(name + 7);
         if (arg == NULL) return -1;
-        ret = create_service_thread(reboot_service, arg);
+        ret = create_service_thread("reboot", reboot_service, arg);
     } else if(!strncmp(name, "root:", 5)) {
-        ret = create_service_thread(restart_root_service, NULL);
+        ret = create_service_thread("root", restart_root_service, nullptr);
     } else if(!strncmp(name, "unroot:", 7)) {
-        ret = create_service_thread(restart_unroot_service, NULL);
+        ret = create_service_thread("unroot", restart_unroot_service, nullptr);
     } else if(!strncmp(name, "backup:", 7)) {
         ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
                                                           (name + 7)).c_str(),
@@ -312,17 +312,20 @@
         if (sscanf(name + 6, "%d", &port) != 1) {
             return -1;
         }
-        ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
+        ret = create_service_thread("tcp", restart_tcp_service, reinterpret_cast<void*>(port));
     } else if(!strncmp(name, "usb:", 4)) {
-        ret = create_service_thread(restart_usb_service, NULL);
+        ret = create_service_thread("usb", restart_usb_service, nullptr);
     } else if (!strncmp(name, "reverse:", 8)) {
         ret = reverse_service(name + 8);
     } else if(!strncmp(name, "disable-verity:", 15)) {
-        ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
+        ret = create_service_thread("verity-on", set_verity_enabled_state_service,
+                                    reinterpret_cast<void*>(0));
     } else if(!strncmp(name, "enable-verity:", 15)) {
-        ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
+        ret = create_service_thread("verity-off", set_verity_enabled_state_service,
+                                    reinterpret_cast<void*>(1));
     } else if (!strcmp(name, "reconnect")) {
-        ret = create_service_thread(reconnect_service, const_cast<atransport*>(transport));
+        ret = create_service_thread("reconnect", reconnect_service,
+                                    const_cast<atransport*>(transport));
 #endif
     }
     if (ret >= 0) {
@@ -484,14 +487,14 @@
             return nullptr;
         }
 
-        int fd = create_service_thread(wait_for_state, sinfo.get());
+        int fd = create_service_thread("wait", wait_for_state, sinfo.get());
         if (fd != -1) {
             sinfo.release();
         }
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
         char* host = strdup(name + 8);
-        int fd = create_service_thread(connect_service, host);
+        int fd = create_service_thread("connect", connect_service, host);
         if (fd == -1) {
             free(host);
         }
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index ee821f8..0c7e1f9 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -95,6 +95,7 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <private/android_logger.h>
 
@@ -212,6 +213,13 @@
     WaitForExit();
 }
 
+static std::string GetHostName() {
+    char buf[HOST_NAME_MAX];
+    if (gethostname(buf, sizeof(buf)) != -1 && strcmp(buf, "localhost") != 0) return buf;
+
+    return android::base::GetProperty("ro.product.device", "android");
+}
+
 bool Subprocess::ForkAndExec(std::string* error) {
     unique_fd child_stdinout_sfd, child_stderr_sfd;
     unique_fd parent_error_sfd, child_error_sfd;
@@ -250,11 +258,11 @@
     }
 
     if (pw != nullptr) {
-        // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
         env["HOME"] = pw->pw_dir;
+        env["HOSTNAME"] = GetHostName();
         env["LOGNAME"] = pw->pw_name;
-        env["USER"] = pw->pw_name;
         env["SHELL"] = pw->pw_shell;
+        env["USER"] = pw->pw_name;
     }
 
     if (!terminal_type_.empty()) {
@@ -435,8 +443,7 @@
 void Subprocess::ThreadHandler(void* userdata) {
     Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
 
-    adb_thread_setname(android::base::StringPrintf(
-            "shell srvc %d", subprocess->pid()));
+    adb_thread_setname(android::base::StringPrintf("shell svc %d", subprocess->pid()));
 
     D("passing data streams for PID %d", subprocess->pid());
     subprocess->PassDataStreams();
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 34cc026..089a1ec 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1101,11 +1101,4 @@
     keys_.pop_front();
     return result;
 }
-bool atransport::SetSendConnectOnError() {
-    if (has_send_connect_on_error_) {
-        return false;
-    }
-    has_send_connect_on_error_ = true;
-    return true;
-}
 #endif
diff --git a/adb/transport.h b/adb/transport.h
index 79e3075..8c101fd 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -122,7 +122,6 @@
 
 #if ADB_HOST
     std::shared_ptr<RSA> NextKey();
-    bool SetSendConnectOnError();
 #endif
 
     char token[TOKEN_SIZE] = {};
@@ -181,7 +180,6 @@
     std::atomic<ConnectionState> connection_state_;
 #if ADB_HOST
     std::deque<std::shared_ptr<RSA>> keys_;
-    bool has_send_connect_on_error_ = false;
 #endif
 
     DISALLOW_COPY_AND_ASSIGN(atransport);
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 6768d31..fdecccf 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -103,13 +103,6 @@
 
 err_msg:
     p->msg.command = 0;
-    if (t->GetConnectionState() == kCsOffline) {
-        // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
-        // an error message. In this case, resend one A_CNXN message to connect the device.
-        if (t->SetSendConnectOnError()) {
-            SendConnectOnHost(t);
-        }
-    }
     return 0;
 }
 
@@ -162,8 +155,7 @@
     return 0;
 }
 
-static void remote_close(atransport *t)
-{
+static void remote_close(atransport* t) {
     usb_close(t->usb);
     t->usb = 0;
 }
diff --git a/base/Android.bp b/base/Android.bp
index 82aee2a..0fd00ea 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -89,13 +89,12 @@
             cppflags: ["-Wexit-time-destructors"],
             enabled: true,
         },
-        linux: {
+        linux_glibc: {
             srcs: [
                 "chrono_utils.cpp",
                 "errors_unix.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
-            host_ldlibs: ["-lrt"],
         },
         windows: {
             srcs: [
@@ -136,9 +135,8 @@
                 misc_undefined: ["integer"],
             },
         },
-        linux: {
+        linux_glibc: {
             srcs: ["chrono_utils_test.cpp"],
-            host_ldlibs: ["-lrt"],
         },
         windows: {
             srcs: ["utf8_test.cpp"],
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index b6bf701..dbe5483 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -22,7 +22,7 @@
 namespace base {
 
 boot_clock::time_point boot_clock::now() {
-#ifdef __ANDROID__
+#ifdef __linux__
   timespec ts;
   clock_gettime(CLOCK_BOOTTIME, &ts);
   return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
@@ -30,7 +30,7 @@
 #else
   // Darwin does not support clock_gettime.
   return boot_clock::time_point();
-#endif  // __ANDROID__
+#endif  // __linux__
 }
 
 std::ostream& operator<<(std::ostream& os, const Timer& t) {
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index b76011a..5425fd3 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -32,6 +32,78 @@
   fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
 }
 
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+  adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: hasPstore
+
+Returns: true if device (likely) has pstore data" ]
+hasPstore() {
+  if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+    false
+  fi
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+  if inAdb && [ 1 -ne `adb shell getprop ro.debuggable` ]; then
+    false
+  fi
+}
+
+[ "USAGE: checkDebugBuild
+
+Returns: true if device is a userdebug or eng release" ]
+checkDebugBuild() {
+  if isDebuggable; then
+    echo "INFO: '${TEST}' test requires userdebug build"
+  else
+    echo "ERROR: '${TEST}' test requires userdebug build, skipping FAILURE"
+    false
+  fi >&2
+}
+
+[ "USAGE: enterPstore
+
+Prints a warning string requiring functional pstore
+
+Returns: pstore_ok variable set to true or false" ]
+enterPstore() {
+  if hasPstore; then
+    echo "INFO: '${TEST}' test requires functional and reliable pstore"
+    pstore_ok=true
+  else
+    echo "WARNING: '${TEST}' test requires functional pstore"
+    pstore_ok=false
+  fi >&2
+  ${pstore_ok}
+}
+
+[ "USAGE: exitPstore
+
+Prints an error string requiring functional pstore
+
+Returns: clears error if pstore dysfunctional" ]
+exitPstore() {
+  save_ret=${?}
+  if [ ${save_ret} != 0 ]; then
+    if hasPstore; then
+      return ${save_ret}
+    fi
+    if [ true = ${pstore_ok} ]; then
+      echo "WARNING: '${TEST}' test requires functional pstore"
+      return ${save_ret}
+    fi
+    echo "ERROR: '${TEST}' test requires functional pstore, skipping FAILURE"
+  fi >&2
+}
+
 [ "USAGE: format_duration <seconds>
 
 human readable output whole seconds, whole minutes or mm:ss" ]
@@ -77,17 +149,25 @@
   fi
   counter=0
   while true; do
-    [ 0 = ${counter} ] ||
-      adb wait-for-device </dev/null >/dev/null 2>/dev/null
-    vals=`adb shell getprop </dev/null 2>/dev/null |
-          sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
-    [ 0 = ${counter} ] ||
-      sleep 1
-    if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]; then
-      break
-    fi
-    if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]; then
-      break
+    if inFastboot; then
+      fastboot reboot
+    elif inAdb; then
+      if [ 0 != ${counter} ]; then
+        adb wait-for-device </dev/null >/dev/null 2>/dev/null
+      fi
+      if [ -n "`adb shell getprop sys.boot.reason </dev/null 2>/dev/null`" ]
+      then
+        vals=`adb shell getprop </dev/null 2>/dev/null |
+              sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+        if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
+        then
+          break
+        fi
+        if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
+        then
+          break
+        fi
+      fi
     fi
     counter=`expr ${counter} + 1`
     if [ ${counter} -gt ${timeout} ]; then
@@ -95,6 +175,7 @@
       echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
       return 1
     fi
+    sleep 1
   done
   ${exit_function}
 }
@@ -152,7 +233,7 @@
 report_bootstat_logs() {
   save_ret=${?}
   match=
-  for i in ${*}; do
+  for i in "${@}"; do
     if [ X"${i}" != X"${i#-}" ] ; then
       match="${match}
 ${i#-}"
@@ -211,7 +292,7 @@
   END=`date +%s`
   duration=`expr ${END} - ${START} 2>/dev/null`
   [ 0 = ${duration} ] ||
-    echo INFO: ${TEST} test duration `format_duration ${duration}` >&2
+    echo "INFO: '${TEST}' test duration `format_duration ${duration}`" >&2
   if [ ${save_ret} = 0 ]; then
     echo "${GREEN}[       OK ]${NORMAL} ${TEST} ${*}"
   else
@@ -329,7 +410,7 @@
 Decision to change the build itself rather than trick bootstat by
 rummaging through its data files was made." ]
 test_ota() {
-  echo "INFO: expected duration of ${TEST} test about 5 minutes or more" >&2
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 300`" >&2
   echo "      extended by build and flashing times" >&2
   if [ -z "${TARGET_PRODUCT}" -o \
        -z "${ANDROID_PRODUCT_OUT}" -o \
@@ -361,7 +442,7 @@
   popd >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,bootloader
+  EXPECT_PROPERTY persist.sys.boot.reason bootloader
   report_bootstat_logs reboot,ota bootloader
 }
 
@@ -369,8 +450,8 @@
 
 fast and fake (touch build_date on device to make it different)" ]
 test_optional_ota() {
-  echo "INFO: expected duration of ${TEST} test about 45 seconds" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell su root touch /data/misc/bootstat/build_date >&2
   adb reboot ota
   wait_for_screen
@@ -379,27 +460,28 @@
   report_bootstat_logs reboot,ota
 }
 
-[ "USAGE: [TEST=<test>] blind_reboot_test [<match>]
+[ "USAGE: [TEST=<test>] blind_reboot_test
 
 Simple tests helper
 - adb reboot <test>
 - (wait until screen is up, boot has completed)
 - adb shell getprop sys.boot.reason
-- NB: should report <test>, or overriden <match>
+- NB: should report <test>, or reboot,<test> depending on canonical rules
 
 We interleave the simple reboot tests between the hard/complex ones
 as a means of checking sanity and any persistent side effect of the
 other tests." ]
 blind_reboot_test() {
-  if [ -z "${1}" ]; then
-    set ${TEST}
-  fi
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+  case ${TEST} in
+    bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
+    *)                                           reason=reboot,${TEST} ;;
+  esac
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb reboot ${TEST}
   wait_for_screen
-  EXPECT_PROPERTY sys.boot.reason ${1}
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,${TEST}
-  report_bootstat_logs ${1}
+  EXPECT_PROPERTY sys.boot.reason ${reason}
+  EXPECT_PROPERTY persist.sys.boot.reason ${reason}
+  report_bootstat_logs ${reason}
 }
 
 [ "USAGE: test_cold
@@ -425,8 +507,8 @@
 Decision to rummage through bootstat data files was made as
 a _real_ factory_reset is too destructive to the device." ]
 test_factory_reset() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell su root rm /data/misc/bootstat/build_date >&2
   adb reboot >&2
   wait_for_screen
@@ -449,7 +531,7 @@
 
 For realz, and disruptive" ]
 test_optional_factory_reset() {
-  echo "INFO: expected duration of ${TEST} test roughly a minute" >&2
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 60`" >&2
   if ! inFastboot; then
     adb reboot-bootloader
   fi
@@ -502,8 +584,9 @@
 - NB: should report reboot,battery
 - (replace set logd.kernel true to the above, and retry test)" ]
 test_battery() {
-  echo "INFO: expected duration of ${TEST} test roughly two minutes" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 120`" >&2
+  enterPstore
   # Send it _many_ times to combat devices with flakey pstore
   for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
     echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
@@ -529,8 +612,49 @@
     )
 
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,cold
+  EXPECT_PROPERTY persist.sys.boot.reason cold
   report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
+  exitPstore
+}
+
+[ "USAGE: test_optional_battery
+
+battery shutdown test:
+- adb shell setprop sys.powerctl shutdown,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,battery" ]
+test_optional_battery() {
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+  report_bootstat_logs shutdown,battery
+}
+
+[ "USAGE: test_optional_battery_thermal
+
+battery thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal,battery" ]
+test_optional_battery_thermal() {
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,thermal,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+  report_bootstat_logs shutdown,thermal,battery
 }
 
 [ "USAGE: test_unknown
@@ -542,7 +666,7 @@
 - NB: should report reboot,unknown
 - NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
 test_unknown() {
-  blind_reboot_test reboot,unknown
+  blind_reboot_test
 }
 
 [ "USAGE: test_kernel_panic
@@ -553,13 +677,16 @@
 - adb shell getprop sys.boot.reason
 - NB: should report kernel_panic,sysrq" ]
 test_kernel_panic() {
-  echo "INFO: expected duration of ${TEST} test > 2 minutes" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 120`" >&2
+  panic_msg="kernel_panic,sysrq"
+  enterPstore || panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
   echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
-  EXPECT_PROPERTY sys.boot.reason kernel_panic,sysrq
-  EXPECT_PROPERTY persist.sys.boot.reason kernel_panic,sysrq
+  EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
   report_bootstat_logs kernel_panic,sysrq
+  exitPstore
 }
 
 [ "USAGE: test_warm
@@ -582,7 +709,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report shutdown,thermal" ]
 test_thermal_shutdown() {
-  echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
   echo "      power on request" >&2
   adb shell setprop sys.powerctl shutdown,thermal
   sleep 5
@@ -602,7 +729,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report shutdown,userrequested" ]
 test_userrequested_shutdown() {
-  echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
   echo "      power on request" >&2
   adb shell setprop sys.powerctl shutdown,userrequested
   sleep 5
@@ -621,7 +748,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report reboot,shell" ]
 test_shell_reboot() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
@@ -637,7 +764,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report reboot,adb" ]
 test_adb_reboot() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,adb
@@ -654,8 +781,8 @@
 - NB: should report reboot,its_just_so_hard
 - NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
 test_Its_Just_So_Hard_reboot() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
-  echo "INFO: ${TEST} cleanup requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell 'reboot "Its Just So Hard"'
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 17986b9..1954966 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -233,6 +233,7 @@
     "cold",
     "hard",
     "warm",
+    // super blunt
     "shutdown",    // Can not happen from ro.boot.bootreason
     "reboot",      // Default catch-all for anything unknown
     // clang-format on
@@ -543,19 +544,9 @@
       std::transform(content.begin(), content.end(), content.begin(), tounderline);
       std::transform(content.begin(), content.end(), content.begin(), toprintable);
 
-      // String is either "reboot,<reason>" or "shutdown,<reason>".
-      // We will set if default reasons, only override with detail if thermal.
-      if ((android::base::StartsWith(content, ret.c_str()) && (content[ret.length()] == ',')) ||
-          !isBluntRebootReason(content)) {
-        // Ok, we want it, let's squash it if secondReason is known.
-        size_t pos = content.find(',');
-        if (pos != std::string::npos) {
-          ++pos;
-          std::string secondReason(content.substr(pos));
-          ret = isKnownRebootReason(secondReason) ? secondReason : content;
-        } else {
-          ret = content;
-        }
+      // Anything in last is better than 'super-blunt' reboot or shutdown.
+      if ((ret == "") || (ret == "reboot") || (ret == "shutdown") || !isBluntRebootReason(content)) {
+        ret = content;
       }
     }
 
@@ -570,7 +561,7 @@
     ret = "reboot";
     if (android::base::StartsWith(reason, "reboot")) {
       reason = reason.substr(strlen("reboot"));
-      while (reason[0] == ',') {
+      while ((reason[0] == ',') || (reason[0] == '_')) {
         reason = reason.substr(1);
       }
     }
@@ -611,17 +602,11 @@
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
     LOG(INFO) << "Canonical boot reason: reboot,factory_reset";
     SetProperty(system_reboot_reason_property, "reboot,factory_reset");
-    if (GetProperty(bootloader_reboot_reason_property) == "") {
-      SetProperty(bootloader_reboot_reason_property, "reboot,factory_reset");
-    }
   } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
     LOG(INFO) << "Canonical boot reason: reboot,ota";
     SetProperty(system_reboot_reason_property, "reboot,ota");
-    if (GetProperty(bootloader_reboot_reason_property) == "") {
-      SetProperty(bootloader_reboot_reason_property, "reboot,ota");
-    }
   }
 
   return boot_complete_prefix;
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index e79dd96..934ceba 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -113,7 +113,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff ---         0     12000\n";
 #else
@@ -148,7 +148,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff r--         0     12000  /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
 #else
@@ -187,7 +187,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (2 entries):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff -w-         0     12000\n"
 "    12345678'9abcd000-12345678'9abdefff -w-         0     12000  /system/lib/libfake.so\n";
@@ -245,7 +245,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump =
-      "\nmemory map:\n"
+      "\nmemory map (5 entries):\n"
 #if defined(__LP64__)
       "    00000000'0a234000-00000000'0a234fff ---         0      1000\n"
       "    00000000'0a334000-00000000'0a334fff r--      f000      1000\n"
@@ -305,7 +305,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump =
-      "\nmemory map: (fault address prefixed with --->)\n"
+      "\nmemory map (3 entries):\n"
 #if defined(__LP64__)
       "--->Fault address falls at 00000000'00001000 before any mapped regions\n"
       "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
@@ -363,7 +363,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump =
-      "\nmemory map: (fault address prefixed with --->)\n"
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
       "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
       "--->Fault address falls at 00000000'0a533000 between mapped regions\n"
@@ -421,7 +421,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump =
-      "\nmemory map: (fault address prefixed with --->)\n"
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
       "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
       "--->00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
@@ -481,7 +481,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump =
-      "\nmemory map: (fault address prefixed with --->)\n"
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
       "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
       "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
@@ -521,7 +521,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump =
-      "\nmemory map:\n"
+      "\nmemory map (1 entry):\n"
 #if defined(__LP64__)
       "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n";
 #else
@@ -571,7 +571,7 @@
     }
 
     const char* expected_addr_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "--->Fault address falls at 00000000'00001000 before any mapped regions\n"
 "    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
@@ -580,7 +580,7 @@
 "    0a434000-0a434fff -w-         0      1000\n";
 #endif
     const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
 #else
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 418d092..d134cf2 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -415,16 +415,18 @@
   ScopedBacktraceMapIteratorLock lock(map);
   _LOG(log, logtype::MAPS,
        "\n"
-       "memory map (%zu entries):\n",
-       map->size());
+       "memory map (%zu entr%s):",
+       map->size(), map->size() == 1 ? "y" : "ies");
   if (print_fault_address_marker) {
     if (map->begin() != map->end() && addr < map->begin()->start) {
-      _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
+      _LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
            get_addr_string(addr).c_str());
       print_fault_address_marker = false;
     } else {
-      _LOG(log, logtype::MAPS, "(fault address prefixed with --->)\n");
+      _LOG(log, logtype::MAPS, " (fault address prefixed with --->)\n");
     }
+  } else {
+    _LOG(log, logtype::MAPS, "\n");
   }
 
   std::string line;
diff --git a/init/Android.bp b/init/Android.bp
index 0e580fc..e906771 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -78,6 +78,8 @@
         "security.cpp",
         "selinux.cpp",
         "service.cpp",
+        "subcontext.cpp",
+        "subcontext.proto",
         "rlimit_parser.cpp",
         "tokenizer.cpp",
         "uevent_listener.cpp",
@@ -173,6 +175,7 @@
         "result_test.cpp",
         "rlimit_parser_test.cpp",
         "service_test.cpp",
+        "subcontext_test.cpp",
         "ueventd_test.cpp",
         "util_test.cpp",
     ],
@@ -188,4 +191,22 @@
     ],
 }
 
+cc_benchmark {
+    name: "init_benchmarks",
+    defaults: ["init_defaults"],
+    srcs: [
+        "subcontext_benchmark.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+    ],
+    static_libs: [
+        "libinit",
+        "libselinux",
+        "libcrypto",
+        "libprotobuf-cpp-lite",
+    ],
+}
+
 subdirs = ["*"]
diff --git a/init/action.cpp b/init/action.cpp
index 60204a8..2617d00 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -24,34 +24,48 @@
 #include "util.h"
 
 using android::base::Join;
+using android::base::StartsWith;
 
 namespace android {
 namespace init {
 
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
-    : func_(f), args_(args), line_(line) {}
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+                                   const std::vector<std::string>& args,
+                                   const std::string& context) {
+    auto builtin_arguments = BuiltinArguments(context);
 
-Result<Success> Command::InvokeFunc() const {
-    std::vector<std::string> expanded_args;
-    expanded_args.resize(args_.size());
-    expanded_args[0] = args_[0];
-    for (std::size_t i = 1; i < args_.size(); ++i) {
-        if (!expand_props(args_[i], &expanded_args[i])) {
-            return Error() << "cannot expand '" << args_[i] << "'";
+    builtin_arguments.args.resize(args.size());
+    builtin_arguments.args[0] = args[0];
+    for (std::size_t i = 1; i < args.size(); ++i) {
+        if (!expand_props(args[i], &builtin_arguments.args[i])) {
+            return Error() << "cannot expand '" << args[i] << "'";
         }
     }
 
-    return func_(expanded_args);
+    return function(builtin_arguments);
+}
+
+Command::Command(BuiltinFunction f, bool execute_in_subcontext,
+                 const std::vector<std::string>& args, int line)
+    : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
+
+Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
+    if (execute_in_subcontext_ && subcontext) {
+        return subcontext->Execute(args_);
+    } else {
+        const std::string& context = subcontext ? subcontext->context() : kInitContext;
+        return RunBuiltinFunction(func_, args_, context);
+    }
 }
 
 std::string Command::BuildCommandString() const {
     return Join(args_, ' ');
 }
 
-Action::Action(bool oneshot, const std::string& filename, int line)
-    : oneshot_(oneshot), filename_(filename), line_(line) {}
+Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line)
+    : oneshot_(oneshot), subcontext_(subcontext), filename_(filename), line_(line) {}
 
-const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+const KeywordFunctionMap* Action::function_map_ = nullptr;
 
 Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
     if (!function_map_) {
@@ -61,12 +75,12 @@
     auto function = function_map_->FindFunction(args);
     if (!function) return Error() << function.error();
 
-    AddCommand(*function, args, line);
+    commands_.emplace_back(function->second, function->first, args, line);
     return Success();
 }
 
 void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
-    commands_.emplace_back(f, args, line);
+    commands_.emplace_back(f, false, args, line);
 }
 
 std::size_t Action::NumCommands() const {
@@ -88,7 +102,7 @@
 
 void Action::ExecuteCommand(const Command& command) const {
     android::base::Timer t;
-    auto result = command.InvokeFunc();
+    auto result = command.InvokeFunc(subcontext_);
     auto duration = t.duration();
 
     // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
@@ -261,7 +275,7 @@
 }
 
 void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
-    auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
+    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0);
     std::vector<std::string> name_vector{name};
 
     if (auto result = action->InitSingleTrigger(name); !result) {
@@ -341,7 +355,17 @@
         return Error() << "Actions must have a trigger";
     }
 
-    auto action = std::make_unique<Action>(false, filename, line);
+    Subcontext* action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+                action_subcontext = &subcontext;
+                break;
+            }
+        }
+    }
+
+    auto action = std::make_unique<Action>(false, action_subcontext, filename, line);
 
     if (auto result = action->InitTriggers(triggers); !result) {
         return Error() << "InitTriggers() failed: " << result.error();
diff --git a/init/action.h b/init/action.h
index d977f82..cdfc6a0 100644
--- a/init/action.h
+++ b/init/action.h
@@ -27,21 +27,27 @@
 #include "keyword_map.h"
 #include "parser.h"
 #include "result.h"
+#include "subcontext.h"
 
 namespace android {
 namespace init {
 
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+                                   const std::vector<std::string>& args, const std::string& context);
+
 class Command {
   public:
-    Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
+    Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+            int line);
 
-    Result<Success> InvokeFunc() const;
+    Result<Success> InvokeFunc(Subcontext* subcontext) const;
     std::string BuildCommandString() const;
 
     int line() const { return line_; }
 
   private:
     BuiltinFunction func_;
+    bool execute_in_subcontext_;
     std::vector<std::string> args_;
     int line_;
 };
@@ -52,7 +58,7 @@
 
 class Action {
   public:
-    explicit Action(bool oneshot, const std::string& filename, int line);
+    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line);
 
     Result<Success> AddCommand(const std::vector<std::string>& args, int line);
     void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
@@ -70,12 +76,11 @@
     bool oneshot() const { return oneshot_; }
     const std::string& filename() const { return filename_; }
     int line() const { return line_; }
-    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+    static void set_function_map(const KeywordFunctionMap* function_map) {
         function_map_ = function_map;
     }
 
-
-private:
+  private:
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
@@ -85,9 +90,10 @@
     std::string event_trigger_;
     std::vector<Command> commands_;
     bool oneshot_;
+    Subcontext* subcontext_;
     std::string filename_;
     int line_;
-    static const KeywordMap<BuiltinFunction>* function_map_;
+    static const KeywordFunctionMap* function_map_;
 };
 
 class ActionManager {
@@ -119,8 +125,8 @@
 
 class ActionParser : public SectionParser {
   public:
-    ActionParser(ActionManager* action_manager)
-        : action_manager_(action_manager), action_(nullptr) {}
+    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
+        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -128,6 +134,7 @@
 
   private:
     ActionManager* action_manager_;
+    std::vector<Subcontext>* subcontexts_;
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index ec84317..379b4fa 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -191,7 +191,7 @@
     return Success();
 }
 
-Result<Success> do_bootchart(const std::vector<std::string>& args) {
+Result<Success> do_bootchart(const BuiltinArguments& args) {
     if (args[1] == "start") return do_bootchart_start();
     return do_bootchart_stop();
 }
diff --git a/init/bootchart.h b/init/bootchart.h
index f614f71..05474ca 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,12 +20,13 @@
 #include <string>
 #include <vector>
 
+#include "builtin_arguments.h"
 #include "result.h"
 
 namespace android {
 namespace init {
 
-Result<Success> do_bootchart(const std::vector<std::string>& args);
+Result<Success> do_bootchart(const BuiltinArguments& args);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/builtin_arguments.h b/init/builtin_arguments.h
new file mode 100644
index 0000000..1742b78
--- /dev/null
+++ b/init/builtin_arguments.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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 _INIT_BUILTIN_ARGUMENTS_H
+#define _INIT_BUILTIN_ARGUMENTS_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+struct BuiltinArguments {
+    BuiltinArguments(const std::string& context) : context(context) {}
+    BuiltinArguments(std::vector<std::string> args, const std::string& context)
+        : args(std::move(args)), context(context) {}
+
+    const std::string& operator[](std::size_t i) const { return args[i]; }
+    auto begin() const { return args.begin(); }
+    auto end() const { return args.end(); }
+    auto size() const { return args.size(); }
+
+    std::vector<std::string> args;
+    const std::string& context;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index be24573..027b392 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -66,6 +66,7 @@
 #include "reboot.h"
 #include "rlimit_parser.h"
 #include "service.h"
+#include "subcontext.h"
 #include "util.h"
 
 using namespace std::literals::string_literals;
@@ -95,36 +96,43 @@
     }
 }
 
-static Result<Success> do_class_start(const std::vector<std::string>& args) {
+static Result<Success> do_class_start(const BuiltinArguments& args) {
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
-    ForEachServiceInClass(args[1], &Service::StartIfNotDisabled);
+    for (const auto& service : ServiceList::GetInstance()) {
+        if (service->classnames().count(args[1])) {
+            if (auto result = service->StartIfNotDisabled(); !result) {
+                LOG(ERROR) << "Could not start service '" << service->name()
+                           << "' as part of class '" << args[1] << "': " << result.error();
+            }
+        }
+    }
     return Success();
 }
 
-static Result<Success> do_class_stop(const std::vector<std::string>& args) {
+static Result<Success> do_class_stop(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Stop);
     return Success();
 }
 
-static Result<Success> do_class_reset(const std::vector<std::string>& args) {
+static Result<Success> do_class_reset(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Reset);
     return Success();
 }
 
-static Result<Success> do_class_restart(const std::vector<std::string>& args) {
+static Result<Success> do_class_restart(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Restart);
     return Success();
 }
 
-static Result<Success> do_domainname(const std::vector<std::string>& args) {
+static Result<Success> do_domainname(const BuiltinArguments& args) {
     if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
         return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
     }
     return Success();
 }
 
-static Result<Success> do_enable(const std::vector<std::string>& args) {
+static Result<Success> do_enable(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "Could not find service";
 
@@ -135,8 +143,8 @@
     return Success();
 }
 
-static Result<Success> do_exec(const std::vector<std::string>& args) {
-    auto service = Service::MakeTemporaryOneshotService(args);
+static Result<Success> do_exec(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
         return Error() << "Could not create exec service";
     }
@@ -148,8 +156,8 @@
     return Success();
 }
 
-static Result<Success> do_exec_background(const std::vector<std::string>& args) {
-    auto service = Service::MakeTemporaryOneshotService(args);
+static Result<Success> do_exec_background(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
         return Error() << "Could not create exec background service";
     }
@@ -161,7 +169,7 @@
     return Success();
 }
 
-static Result<Success> do_exec_start(const std::vector<std::string>& args) {
+static Result<Success> do_exec_start(const BuiltinArguments& args) {
     Service* service = ServiceList::GetInstance().FindService(args[1]);
     if (!service) {
         return Error() << "Service not found";
@@ -174,21 +182,21 @@
     return Success();
 }
 
-static Result<Success> do_export(const std::vector<std::string>& args) {
+static Result<Success> do_export(const BuiltinArguments& args) {
     if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
         return ErrnoError() << "setenv() failed";
     }
     return Success();
 }
 
-static Result<Success> do_hostname(const std::vector<std::string>& args) {
+static Result<Success> do_hostname(const BuiltinArguments& args) {
     if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
         return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
     }
     return Success();
 }
 
-static Result<Success> do_ifup(const std::vector<std::string>& args) {
+static Result<Success> do_ifup(const BuiltinArguments& args) {
     struct ifreq ifr;
 
     strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
@@ -209,7 +217,7 @@
     return Success();
 }
 
-static Result<Success> do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_insmod(const BuiltinArguments& args) {
     int flags = 0;
     auto it = args.begin() + 1;
 
@@ -231,7 +239,7 @@
 }
 
 // mkdir <path> [mode] [owner] [group]
-static Result<Success> do_mkdir(const std::vector<std::string>& args) {
+static Result<Success> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
     if (args.size() >= 3) {
         mode = std::strtoul(args[2].c_str(), 0, 8);
@@ -287,7 +295,7 @@
 }
 
 /* umount <path> */
-static Result<Success> do_umount(const std::vector<std::string>& args) {
+static Result<Success> do_umount(const BuiltinArguments& args) {
     if (umount(args[1].c_str()) < 0) {
         return ErrnoError() << "umount() failed";
     }
@@ -319,7 +327,7 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-static Result<Success> do_mount(const std::vector<std::string>& args) {
+static Result<Success> do_mount(const BuiltinArguments& args) {
     const char* options = nullptr;
     unsigned flags = 0;
     bool wait = false;
@@ -511,7 +519,7 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-static Result<Success> do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const BuiltinArguments& args) {
     std::size_t na = 0;
     bool import_rc = true;
     bool queue_event = true;
@@ -544,7 +552,7 @@
 
     if (import_rc) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
-        import_late(args, 2, path_arg_end);
+        import_late(args.args, 2, path_arg_end);
     }
 
     if (queue_event) {
@@ -559,7 +567,7 @@
     return Success();
 }
 
-static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const BuiltinArguments& args) {
     struct fstab *fstab;
     int ret;
 
@@ -571,13 +579,13 @@
     return Success();
 }
 
-static Result<Success> do_setprop(const std::vector<std::string>& args) {
+static Result<Success> do_setprop(const BuiltinArguments& args) {
     property_set(args[1], args[2]);
     return Success();
 }
 
-static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
-    auto rlimit = ParseRlimit(args);
+static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+    auto rlimit = ParseRlimit(args.args);
     if (!rlimit) return rlimit.error();
 
     if (setrlimit(rlimit->first, &rlimit->second) == -1) {
@@ -586,7 +594,7 @@
     return Success();
 }
 
-static Result<Success> do_start(const std::vector<std::string>& args) {
+static Result<Success> do_start(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     if (auto result = svc->Start(); !result) {
@@ -595,26 +603,26 @@
     return Success();
 }
 
-static Result<Success> do_stop(const std::vector<std::string>& args) {
+static Result<Success> do_stop(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
     return Success();
 }
 
-static Result<Success> do_restart(const std::vector<std::string>& args) {
+static Result<Success> do_restart(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
     return Success();
 }
 
-static Result<Success> do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return Success();
 }
 
-static Result<Success> do_symlink(const std::vector<std::string>& args) {
+static Result<Success> do_symlink(const BuiltinArguments& args) {
     if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
         // The symlink builtin is often used to create symlinks for older devices to be backwards
         // compatible with new paths, therefore we skip reporting this error.
@@ -626,21 +634,21 @@
     return Success();
 }
 
-static Result<Success> do_rm(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const BuiltinArguments& args) {
     if (unlink(args[1].c_str()) < 0) {
         return ErrnoError() << "unlink() failed";
     }
     return Success();
 }
 
-static Result<Success> do_rmdir(const std::vector<std::string>& args) {
+static Result<Success> do_rmdir(const BuiltinArguments& args) {
     if (rmdir(args[1].c_str()) < 0) {
         return ErrnoError() << "rmdir() failed";
     }
     return Success();
 }
 
-static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
+static Result<Success> do_sysclktz(const BuiltinArguments& args) {
     struct timezone tz = {};
     if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
         return Error() << "Unable to parse mins_west_of_gmt";
@@ -652,7 +660,7 @@
     return Success();
 }
 
-static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
     int mode = -1;
     bool loaded = fs_mgr_load_verity_state(&mode);
     if (loaded && mode != VERITY_MODE_DEFAULT) {
@@ -668,14 +676,14 @@
     property_set("partition."s + mount_point + ".verified", std::to_string(mode));
 }
 
-static Result<Success> do_verity_update_state(const std::vector<std::string>& args) {
+static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
     if (!fs_mgr_update_verity_state(verity_update_property)) {
         return Error() << "fs_mgr_update_verity_state() failed";
     }
     return Success();
 }
 
-static Result<Success> do_write(const std::vector<std::string>& args) {
+static Result<Success> do_write(const BuiltinArguments& args) {
     if (auto result = WriteFile(args[1], args[2]); !result) {
         return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
     }
@@ -706,7 +714,7 @@
     return Success();
 }
 
-static Result<Success> do_readahead(const std::vector<std::string>& args) {
+static Result<Success> do_readahead(const BuiltinArguments& args) {
     struct stat sb;
 
     if (stat(args[1].c_str(), &sb)) {
@@ -765,7 +773,7 @@
     return Success();
 }
 
-static Result<Success> do_copy(const std::vector<std::string>& args) {
+static Result<Success> do_copy(const BuiltinArguments& args) {
     auto file_contents = ReadFile(args[1]);
     if (!file_contents) {
         return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
@@ -777,7 +785,7 @@
     return Success();
 }
 
-static Result<Success> do_chown(const std::vector<std::string>& args) {
+static Result<Success> do_chown(const BuiltinArguments& args) {
     auto uid = DecodeUid(args[1]);
     if (!uid) {
         return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
@@ -814,7 +822,7 @@
     return mode;
 }
 
-static Result<Success> do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const BuiltinArguments& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
         return ErrnoError() << "fchmodat() failed";
@@ -822,7 +830,7 @@
     return Success();
 }
 
-static Result<Success> do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const BuiltinArguments& args) {
     int ret = 0;
 
     struct flag_type {const char* name; int value;};
@@ -864,13 +872,13 @@
     return Success();
 }
 
-static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
-    std::vector<std::string> non_const_args(args);
+static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+    std::vector<std::string> non_const_args(args.args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
-    return do_restorecon(non_const_args);
+    return do_restorecon({std::move(non_const_args), args.context});
 }
 
-static Result<Success> do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const BuiltinArguments& args) {
     // TODO: support names instead/as well?
     int log_level = -1;
     android::base::ParseInt(args[1], &log_level);
@@ -891,17 +899,17 @@
     return Success();
 }
 
-static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
     load_persist_props();
     return Success();
 }
 
-static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const BuiltinArguments& args) {
     load_system_props();
     return Success();
 }
 
-static Result<Success> do_wait(const std::vector<std::string>& args) {
+static Result<Success> do_wait(const BuiltinArguments& args) {
     auto timeout = kCommandRetryTimeout;
     if (args.size() == 3) {
         int timeout_int;
@@ -918,7 +926,7 @@
     return Success();
 }
 
-static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
@@ -939,7 +947,7 @@
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static Result<Success> do_installkey(const std::vector<std::string>& args) {
+static Result<Success> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return Success();
 
     auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
@@ -948,64 +956,66 @@
     }
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "enablefilecrypto"};
-    return do_exec(exec_args);
+    return do_exec({std::move(exec_args), args.context});
 }
 
-static Result<Success> do_init_user0(const std::vector<std::string>& args) {
+static Result<Success> do_init_user0(const BuiltinArguments& args) {
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "init_user0"};
-    return do_exec(exec_args);
+    return do_exec({std::move(exec_args), args.context});
 }
 
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map builtin_functions = {
-        {"bootchart",               {1,     1,    do_bootchart}},
-        {"chmod",                   {2,     2,    do_chmod}},
-        {"chown",                   {2,     3,    do_chown}},
-        {"class_reset",             {1,     1,    do_class_reset}},
-        {"class_restart",           {1,     1,    do_class_restart}},
-        {"class_start",             {1,     1,    do_class_start}},
-        {"class_stop",              {1,     1,    do_class_stop}},
-        {"copy",                    {2,     2,    do_copy}},
-        {"domainname",              {1,     1,    do_domainname}},
-        {"enable",                  {1,     1,    do_enable}},
-        {"exec",                    {1,     kMax, do_exec}},
-        {"exec_background",         {1,     kMax, do_exec_background}},
-        {"exec_start",              {1,     1,    do_exec_start}},
-        {"export",                  {2,     2,    do_export}},
-        {"hostname",                {1,     1,    do_hostname}},
-        {"ifup",                    {1,     1,    do_ifup}},
-        {"init_user0",              {0,     0,    do_init_user0}},
-        {"insmod",                  {1,     kMax, do_insmod}},
-        {"installkey",              {1,     1,    do_installkey}},
-        {"load_persist_props",      {0,     0,    do_load_persist_props}},
-        {"load_system_props",       {0,     0,    do_load_system_props}},
-        {"loglevel",                {1,     1,    do_loglevel}},
-        {"mkdir",                   {1,     4,    do_mkdir}},
-        {"mount_all",               {1,     kMax, do_mount_all}},
-        {"mount",                   {3,     kMax, do_mount}},
-        {"umount",                  {1,     1,    do_umount}},
-        {"readahead",               {1,     2,    do_readahead}},
-        {"restart",                 {1,     1,    do_restart}},
-        {"restorecon",              {1,     kMax, do_restorecon}},
-        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
-        {"rm",                      {1,     1,    do_rm}},
-        {"rmdir",                   {1,     1,    do_rmdir}},
-        {"setprop",                 {2,     2,    do_setprop}},
-        {"setrlimit",               {3,     3,    do_setrlimit}},
-        {"start",                   {1,     1,    do_start}},
-        {"stop",                    {1,     1,    do_stop}},
-        {"swapon_all",              {1,     1,    do_swapon_all}},
-        {"symlink",                 {2,     2,    do_symlink}},
-        {"sysclktz",                {1,     1,    do_sysclktz}},
-        {"trigger",                 {1,     1,    do_trigger}},
-        {"verity_load_state",       {0,     0,    do_verity_load_state}},
-        {"verity_update_state",     {0,     0,    do_verity_update_state}},
-        {"wait",                    {1,     2,    do_wait}},
-        {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
-        {"write",                   {2,     2,    do_write}},
+        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
+        {"chmod",                   {2,     2,    {true,   do_chmod}}},
+        {"chown",                   {2,     3,    {true,   do_chown}}},
+        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
+        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
+        {"class_start",             {1,     1,    {false,  do_class_start}}},
+        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
+        {"copy",                    {2,     2,    {true,   do_copy}}},
+        {"domainname",              {1,     1,    {true,   do_domainname}}},
+        {"enable",                  {1,     1,    {false,  do_enable}}},
+        {"exec",                    {1,     kMax, {false,  do_exec}}},
+        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
+        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
+        {"export",                  {2,     2,    {false,  do_export}}},
+        {"hostname",                {1,     1,    {true,   do_hostname}}},
+        {"ifup",                    {1,     1,    {true,   do_ifup}}},
+        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
+        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
+        {"installkey",              {1,     1,    {false,  do_installkey}}},
+        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
+        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
+        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
+        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
+        {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
+        {"mount",                   {3,     kMax, {false,  do_mount}}},
+        {"umount",                  {1,     1,    {false,  do_umount}}},
+        {"readahead",               {1,     2,    {true,   do_readahead}}},
+        {"restart",                 {1,     1,    {false,  do_restart}}},
+        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
+        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
+        {"rm",                      {1,     1,    {true,   do_rm}}},
+        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
+        //  TODO: setprop should be run in the subcontext, but property service needs to be split
+        //        out from init before that is possible.
+        {"setprop",                 {2,     2,    {false,  do_setprop}}},
+        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
+        {"start",                   {1,     1,    {false,  do_start}}},
+        {"stop",                    {1,     1,    {false,  do_stop}}},
+        {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
+        {"symlink",                 {2,     2,    {true,   do_symlink}}},
+        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
+        {"trigger",                 {1,     1,    {false,  do_trigger}}},
+        {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
+        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
+        {"wait",                    {1,     2,    {true,   do_wait}}},
+        {"wait_for_prop",           {2,     2,    {true,   do_wait_for_prop}}},
+        {"write",                   {2,     2,    {true,   do_write}}},
     };
     // clang-format on
     return builtin_functions;
diff --git a/init/builtins.h b/init/builtins.h
index f66ae19..814b2d5 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -22,14 +22,17 @@
 #include <string>
 #include <vector>
 
+#include "builtin_arguments.h"
 #include "keyword_map.h"
 #include "result.h"
 
 namespace android {
 namespace init {
 
-using BuiltinFunction = std::function<Result<Success>(const std::vector<std::string>&)>;
-class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
+
+using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
+class BuiltinFunctionMap : public KeywordFunctionMap {
   public:
     BuiltinFunctionMap() {}
 
diff --git a/init/init.cpp b/init/init.cpp
index ad045b1..51a98a2 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -84,6 +84,8 @@
 
 std::vector<std::string> late_import_paths;
 
+static std::vector<Subcontext>* subcontexts;
+
 void DumpState() {
     ServiceList::GetInstance().DumpState();
     ActionManager::GetInstance().DumpState();
@@ -92,8 +94,8 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     return parser;
@@ -220,7 +222,7 @@
     }
 }
 
-static Result<Success> wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
     Timer t;
 
     LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -241,12 +243,12 @@
     return Success();
 }
 
-static Result<Success> keychord_init_action(const std::vector<std::string>& args) {
+static Result<Success> keychord_init_action(const BuiltinArguments& args) {
     keychord_init();
     return Success();
 }
 
-static Result<Success> console_init_action(const std::vector<std::string>& args) {
+static Result<Success> console_init_action(const BuiltinArguments& args) {
     std::string console = GetProperty("ro.boot.console", "");
     if (!console.empty()) {
         default_console = "/dev/" + console;
@@ -333,13 +335,13 @@
     if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static Result<Success> property_enable_triggers_action(const std::vector<std::string>& args) {
+static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
     /* Enable property triggers. */
     property_triggers_enabled = 1;
     return Success();
 }
 
-static Result<Success> queue_property_triggers_action(const std::vector<std::string>& args) {
+static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
     ActionManager::GetInstance().QueueAllPropertyActions();
     return Success();
@@ -446,6 +448,12 @@
         return watchdogd_main(argc, argv);
     }
 
+    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
+        InitKernelLogging(argv);
+        const BuiltinFunctionMap function_map;
+        return SubcontextMain(argc, argv, &function_map);
+    }
+
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
     }
@@ -587,6 +595,8 @@
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
+    subcontexts = InitializeSubcontexts();
+
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sm = ServiceList::GetInstance();
 
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 27659f9..29a65ab 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -25,33 +25,12 @@
 #include "import_parser.h"
 #include "keyword_map.h"
 #include "parser.h"
+#include "test_function_map.h"
 #include "util.h"
 
 namespace android {
 namespace init {
 
-class TestFunctionMap : public KeywordMap<BuiltinFunction> {
-  public:
-    // Helper for argument-less functions
-    using BuiltinFunctionNoArgs = std::function<void(void)>;
-    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
-        Add(name, 0, 0, [function](const std::vector<std::string>&) {
-            function();
-            return Success();
-        });
-    }
-
-    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
-             const BuiltinFunction function) {
-        builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
-    }
-
-  private:
-    Map builtin_functions_ = {};
-
-    const Map& map() const override { return builtin_functions_; }
-};
-
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
 void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
@@ -61,7 +40,7 @@
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     ASSERT_TRUE(parser.ParseConfig(init_script_file));
@@ -171,14 +150,14 @@
     ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
 
     int num_executed = 0;
-    auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+    auto execute_command = [&num_executed](const BuiltinArguments& args) {
         EXPECT_EQ(2U, args.size());
         EXPECT_EQ(++num_executed, std::stoi(args[1]));
         return Success();
     };
 
     TestFunctionMap test_function_map;
-    test_function_map.Add("execute", 1, 1, execute_command);
+    test_function_map.Add("execute", 1, 1, false, execute_command);
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 18f493a..b17dbaf 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -56,6 +56,7 @@
 #include "service.h"
 #include "sigchld_handler.h"
 
+using android::base::Split;
 using android::base::StringPrintf;
 using android::base::Timer;
 
@@ -86,8 +87,8 @@
           mnt_type_(entry.mnt_type),
           mnt_opts_(entry.mnt_opts) {}
 
-    bool Umount() {
-        int r = umount2(mnt_dir_.c_str(), 0);
+    bool Umount(bool force) {
+        int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0);
         if (r == 0) {
             LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
             return true;
@@ -280,14 +281,15 @@
         bool unmount_done = true;
         if (emulated_devices.size() > 0) {
             unmount_done = std::all_of(emulated_devices.begin(), emulated_devices.end(),
-                                       [](auto& entry) { return entry.Umount(); });
+                                       [](auto& entry) { return entry.Umount(false); });
             if (unmount_done) {
                 sync();
             }
         }
-        unmount_done = std::all_of(block_devices.begin(), block_devices.end(),
-                                   [](auto& entry) { return entry.Umount(); }) &&
-                       unmount_done;
+        unmount_done =
+            std::all_of(block_devices.begin(), block_devices.end(),
+                        [&timeout](auto& entry) { return entry.Umount(timeout == 0ms); }) &&
+            unmount_done;
         if (unmount_done) {
             return UMOUNT_STAT_SUCCESS;
         }
@@ -345,7 +347,16 @@
     Timer t;
     LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
-    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str());
+    // Ensure last reboot reason is reduced to canonical
+    // alias reported in bootloader or system boot reason.
+    size_t skip = 0;
+    std::vector<std::string> reasons = Split(reason, ",");
+    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+         reasons[1] == "hard" || reasons[1] == "warm")) {
+        skip = strlen("reboot,");
+    }
+    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
     sync();
 
     bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
@@ -468,7 +479,7 @@
 
 bool HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
-    std::vector<std::string> cmd_params = android::base::Split(command, ",");
+    std::vector<std::string> cmd_params = Split(command, ",");
     std::string reboot_target = "";
     bool run_fsck = false;
     bool command_invalid = false;
@@ -521,8 +532,7 @@
     // Queue shutdown trigger first
     ActionManager::GetInstance().QueueEventTrigger("shutdown");
     // Queue built-in shutdown_done
-    auto shutdown_handler = [cmd, command, reboot_target,
-                             run_fsck](const std::vector<std::string>&) {
+    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
         DoReboot(cmd, command, reboot_target, run_fsck);
         return Success();
     };
diff --git a/init/security.cpp b/init/security.cpp
index aac8f2e..a3494a2 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -43,7 +43,7 @@
 // devices/configurations where these I/O operations are blocking for a long
 // time. We do not reboot or halt on failures, as this is a best-effort
 // attempt.
-Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
     unique_fd hwrandom_fd(
         TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (hwrandom_fd == -1) {
@@ -147,7 +147,7 @@
 // 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
 // ec9ee4acd97c drivers: char: random: add get_random_long()
 // 5ef11c35ce86 mm: ASLR: use get_random_long()
-Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
 // values are arch-dependent
 #if defined(USER_MODE_LINUX)
     // uml does not support mmap_rnd_bits
@@ -187,7 +187,7 @@
 // Set kptr_restrict to the highest available level.
 //
 // Aborts if unable to set this to an acceptable value.
-Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args) {
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
     std::string path = KPTR_RESTRICT_PATH;
 
     if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
diff --git a/init/security.h b/init/security.h
index 31e5790..6f6b944 100644
--- a/init/security.h
+++ b/init/security.h
@@ -20,14 +20,15 @@
 #include <string>
 #include <vector>
 
+#include "builtin_arguments.h"
 #include "result.h"
 
 namespace android {
 namespace init {
 
-Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
-Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args);
-Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args);
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 3f7ad13..c4b01e6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -328,7 +328,7 @@
     std::vector<const char*> compile_args {
         "/system/bin/secilc",
         plat_policy_cil_file,
-        "-M", "true", "-G", "-N",
+        "-m", "-M", "true", "-G", "-N",
         // Target the highest policy language version supported by the kernel
         "-c", version_as_string.c_str(),
         mapping_file.c_str(),
diff --git a/init/service.cpp b/init/service.cpp
index 86b910a..b339bc0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -155,13 +155,14 @@
 unsigned long Service::next_start_order_ = 1;
 bool Service::is_exec_service_running_ = false;
 
-Service::Service(const std::string& name, const std::vector<std::string>& args)
-    : Service(name, 0, 0, 0, {}, 0, 0, "", args) {}
+Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+                 const std::vector<std::string>& args)
+    : Service(name, 0, 0, 0, {}, 0, 0, "", subcontext_for_restart_commands, args) {}
 
 Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
                  const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
                  unsigned namespace_flags, const std::string& seclabel,
-                 const std::vector<std::string>& args)
+                 Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args)
     : name_(name),
       classnames_({"default"}),
       flags_(flags),
@@ -173,7 +174,7 @@
       capabilities_(capabilities),
       namespace_flags_(namespace_flags),
       seclabel_(seclabel),
-      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
+      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0),
       keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
@@ -1007,7 +1008,7 @@
     }
 
     return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
-                                     namespace_flags, seclabel, str_args);
+                                     namespace_flags, seclabel, nullptr, str_args);
 }
 
 // Shutdown services in the opposite order that they were started.
@@ -1055,8 +1056,18 @@
         return Error() << "ignored duplicate definition of service '" << name << "'";
     }
 
+    Subcontext* restart_action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+                restart_action_subcontext = &subcontext;
+                break;
+            }
+        }
+    }
+
     std::vector<std::string> str_args(args.begin() + 2, args.end());
-    service_ = std::make_unique<Service>(name, str_args);
+    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
     return Success();
 }
 
diff --git a/init/service.h b/init/service.h
index 67542ca..89dd780 100644
--- a/init/service.h
+++ b/init/service.h
@@ -33,6 +33,7 @@
 #include "descriptors.h"
 #include "keyword_map.h"
 #include "parser.h"
+#include "subcontext.h"
 
 #define SVC_DISABLED 0x001        // do not autostart with class
 #define SVC_ONESHOT 0x002         // do not restart on exit
@@ -60,12 +61,13 @@
 
 class Service {
   public:
-    Service(const std::string& name, const std::vector<std::string>& args);
+    Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+            const std::vector<std::string>& args);
 
     Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
             const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
             unsigned namespace_flags, const std::string& seclabel,
-            const std::vector<std::string>& args);
+            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
 
     static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
 
@@ -237,7 +239,8 @@
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser(ServiceList* service_list) : service_list_(service_list), service_(nullptr) {}
+    ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
+        : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -247,6 +250,7 @@
     bool IsValidName(const std::string& name) const;
 
     ServiceList* service_list_;
+    std::vector<Subcontext>* subcontexts_;
     std::unique_ptr<Service> service_;
 };
 
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 98d876f..b43c2e9 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -37,7 +37,8 @@
     }
 
     std::vector<std::string> dummy_args{"/bin/test"};
-    Service* service_in_old_memory = new (old_memory) Service("test_old_memory", dummy_args);
+    Service* service_in_old_memory =
+        new (old_memory) Service("test_old_memory", nullptr, dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory->flags());
     EXPECT_EQ(0, service_in_old_memory->pid());
@@ -56,8 +57,8 @@
         old_memory[i] = 0xFF;
     }
 
-    Service* service_in_old_memory2 = new (old_memory)
-        Service("test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", dummy_args);
+    Service* service_in_old_memory2 = new (old_memory) Service(
+        "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", nullptr, dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory2->flags());
     EXPECT_EQ(0, service_in_old_memory2->pid());
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index fa67199..072a0fb 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -60,22 +60,28 @@
     // want the pid to remain valid throughout that (and potentially future) usages.
     auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
 
-    if (PropertyChildReap(pid)) return true;
-
-    Service* service = ServiceList::GetInstance().FindService(pid, &Service::pid);
-
     std::string name;
     std::string wait_string;
-    if (service) {
-        name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
-        if (service->flags() & SVC_EXEC) {
-            auto exec_duration = boot_clock::now() - service->time_started();
-            auto exec_duration_ms =
-                std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
-            wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
-        }
+    Service* service = nullptr;
+
+    if (PropertyChildReap(pid)) {
+        name = "Async property child";
+    } else if (SubcontextChildReap(pid)) {
+        name = "Subcontext";
     } else {
-        name = StringPrintf("Untracked pid %d", pid);
+        service = ServiceList::GetInstance().FindService(pid, &Service::pid);
+
+        if (service) {
+            name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
+            if (service->flags() & SVC_EXEC) {
+                auto exec_duration = boot_clock::now() - service->time_started();
+                auto exec_duration_ms =
+                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
+                wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+            }
+        } else {
+            name = StringPrintf("Untracked pid %d", pid);
+        }
     }
 
     auto status = siginfo.si_status;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
new file mode 100644
index 0000000..927953d
--- /dev/null
+++ b/init/subcontext.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <selinux/android.h>
+
+#include "action.h"
+#include "system/core/init/subcontext.pb.h"
+#include "util.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetExecutablePath;
+using android::base::Join;
+using android::base::Socketpair;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+const std::string kInitContext = "u:r:init:s0";
+const std::string kVendorContext = "u:r:vendor_init:s0";
+
+namespace {
+
+constexpr size_t kBufferSize = 4096;
+
+Result<std::string> ReadMessage(int socket) {
+    char buffer[kBufferSize] = {};
+    auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
+    if (result <= 0) {
+        return ErrnoError();
+    }
+    return std::string(buffer, result);
+}
+
+template <typename T>
+Result<Success> SendMessage(int socket, const T& message) {
+    std::string message_string;
+    if (!message.SerializeToString(&message_string)) {
+        return Error() << "Unable to serialize message";
+    }
+
+    if (message_string.size() > kBufferSize) {
+        return Error() << "Serialized message too long to send";
+    }
+
+    if (auto result =
+            TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
+        result != static_cast<long>(message_string.size())) {
+        return ErrnoError() << "send() failed to send message contents";
+    }
+    return Success();
+}
+
+class SubcontextProcess {
+  public:
+    SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd)
+        : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
+    void MainLoop();
+
+  private:
+    void RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+                    SubcontextReply::ResultMessage* result_message) const;
+
+    const KeywordFunctionMap* function_map_;
+    const std::string context_;
+    const int init_fd_;
+};
+
+void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+                                   SubcontextReply::ResultMessage* result_message) const {
+    // Need to use ArraySplice instead of this code.
+    auto args = std::vector<std::string>();
+    for (const auto& string : execute_command.args()) {
+        args.emplace_back(string);
+    }
+
+    auto map_result = function_map_->FindFunction(args);
+    Result<Success> result;
+    if (!map_result) {
+        result = Error() << "Cannot find command: " << map_result.error();
+    } else {
+        result = RunBuiltinFunction(map_result->second, args, context_);
+    }
+
+    if (result) {
+        result_message->set_success(true);
+    } else {
+        result_message->set_success(false);
+        result_message->set_error_string(result.error_string());
+        result_message->set_error_errno(result.error_errno());
+    }
+}
+
+void SubcontextProcess::MainLoop() {
+    pollfd ufd[1];
+    ufd[0].events = POLLIN;
+    ufd[0].fd = init_fd_;
+
+    while (true) {
+        ufd[0].revents = 0;
+        int nr = TEMP_FAILURE_RETRY(poll(ufd, arraysize(ufd), -1));
+        if (nr == 0) continue;
+        if (nr < 0) {
+            PLOG(FATAL) << "poll() of subcontext socket failed, continuing";
+        }
+
+        auto init_message = ReadMessage(init_fd_);
+        if (!init_message) {
+            LOG(FATAL) << "Could not read message from init: " << init_message.error();
+        }
+
+        auto subcontext_command = SubcontextCommand();
+        if (!subcontext_command.ParseFromString(*init_message)) {
+            LOG(FATAL) << "Unable to parse message from init";
+        }
+
+        auto reply = SubcontextReply();
+        switch (subcontext_command.command_case()) {
+            case SubcontextCommand::kExecuteCommand: {
+                RunCommand(subcontext_command.execute_command(), reply.mutable_result());
+                break;
+            }
+            default:
+                LOG(FATAL) << "Unknown message type from init: "
+                           << subcontext_command.command_case();
+        }
+
+        if (auto result = SendMessage(init_fd_, reply); !result) {
+            LOG(FATAL) << "Failed to send message to init: " << result.error();
+        }
+    }
+}
+
+}  // namespace
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) {
+    if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
+
+    auto context = std::string(argv[2]);
+    auto init_fd = std::atoi(argv[3]);
+
+    auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
+    subcontext_process.MainLoop();
+    return 0;
+}
+
+void Subcontext::Fork() {
+    unique_fd subcontext_socket;
+    if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {
+        LOG(FATAL) << "Could not create socket pair to communicate to subcontext";
+        return;
+    }
+
+    auto result = fork();
+
+    if (result == -1) {
+        LOG(FATAL) << "Could not fork subcontext";
+    } else if (result == 0) {
+        socket_.reset();
+
+        // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
+        // in the subcontext process after we exec.
+        int child_fd = dup(subcontext_socket);
+        if (child_fd < 0) {
+            PLOG(FATAL) << "Could not dup child_fd";
+        }
+
+        if (setexeccon(context_.c_str()) < 0) {
+            PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+        }
+
+        auto init_path = GetExecutablePath();
+        auto child_fd_string = std::to_string(child_fd);
+        const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
+                              child_fd_string.c_str(), nullptr};
+        execv(init_path.data(), const_cast<char**>(args));
+
+        PLOG(FATAL) << "Could not execv subcontext init";
+    } else {
+        subcontext_socket.reset();
+        pid_ = result;
+        LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_;
+    }
+}
+
+void Subcontext::Restart() {
+    LOG(ERROR) << "Restarting subcontext '" << context_ << "'";
+    if (pid_) {
+        kill(pid_, SIGKILL);
+    }
+    pid_ = 0;
+    socket_.reset();
+    Fork();
+}
+
+Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+    auto subcontext_command = SubcontextCommand();
+    std::copy(
+        args.begin(), args.end(),
+        RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
+
+    if (auto result = SendMessage(socket_, subcontext_command); !result) {
+        Restart();
+        return ErrnoError() << "Failed to send message to subcontext";
+    }
+
+    auto subcontext_message = ReadMessage(socket_);
+    if (!subcontext_message) {
+        Restart();
+        return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
+    }
+
+    auto subcontext_reply = SubcontextReply();
+    if (!subcontext_reply.ParseFromString(*subcontext_message)) {
+        Restart();
+        return Error() << "Unable to parse message from subcontext";
+    }
+
+    switch (subcontext_reply.reply_case()) {
+        case SubcontextReply::kResult: {
+            auto result = subcontext_reply.result();
+            if (result.success()) {
+                return Success();
+            } else {
+                return ResultError(result.error_string(), result.error_errno());
+            }
+        }
+        default:
+            return Error() << "Unknown message type from subcontext: "
+                           << subcontext_reply.reply_case();
+    }
+}
+
+static std::vector<Subcontext> subcontexts;
+
+std::vector<Subcontext>* InitializeSubcontexts() {
+    if (GetBoolProperty("ro.init.subcontexts_enabled", false)) {
+        static const char* const paths_and_secontexts[][2] = {
+            {"/vendor", kVendorContext.c_str()},
+        };
+        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+            subcontexts.emplace_back(path_prefix, secontext);
+        }
+    }
+    return &subcontexts;
+}
+
+bool SubcontextChildReap(pid_t pid) {
+    for (auto& subcontext : subcontexts) {
+        if (subcontext.pid() == pid) {
+            subcontext.Restart();
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/subcontext.h b/init/subcontext.h
new file mode 100644
index 0000000..ac77e08
--- /dev/null
+++ b/init/subcontext.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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 _INIT_SUBCONTEXT_H
+#define _INIT_SUBCONTEXT_H
+
+#include <signal.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "builtins.h"
+
+namespace android {
+namespace init {
+
+extern const std::string kInitContext;
+extern const std::string kVendorContext;
+
+class Subcontext {
+  public:
+    Subcontext(std::string path_prefix, std::string context)
+        : path_prefix_(path_prefix), context_(std::move(context)) {
+        Fork();
+    }
+
+    Result<Success> Execute(const std::vector<std::string>& command);
+    void Restart();
+
+    const std::string& path_prefix() const { return path_prefix_; }
+    const std::string& context() const { return context_; }
+    pid_t pid() const { return pid_; }
+
+  private:
+    void Fork();
+
+    std::string path_prefix_;
+    std::string context_;
+    pid_t pid_;
+    android::base::unique_fd socket_;
+};
+
+// For testing, to kill the subcontext after the test has completed.
+class SubcontextKiller {
+  public:
+    SubcontextKiller(const Subcontext& subcontext) : subcontext_(subcontext) {}
+    ~SubcontextKiller() {
+        if (subcontext_.pid() > 0) {
+            kill(subcontext_.pid(), SIGTERM);
+            kill(subcontext_.pid(), SIGKILL);
+        }
+    }
+
+  private:
+    const Subcontext& subcontext_;
+};
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
+std::vector<Subcontext>* InitializeSubcontexts();
+bool SubcontextChildReap(pid_t pid);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/subcontext.proto b/init/subcontext.proto
new file mode 100644
index 0000000..0d89734
--- /dev/null
+++ b/init/subcontext.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message SubcontextCommand {
+    message ExecuteCommand { repeated string args = 1; }
+    oneof command { ExecuteCommand execute_command = 1; }
+}
+
+message SubcontextReply {
+    message ResultMessage {
+        optional bool success = 1;
+        optional string error_string = 2;
+        optional int32 error_errno = 3;
+    }
+
+    oneof reply { ResultMessage result = 1; }
+}
\ No newline at end of file
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
new file mode 100644
index 0000000..a62b959
--- /dev/null
+++ b/init/subcontext_benchmark.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <benchmark/benchmark.h>
+
+#include "test_function_map.h"
+
+namespace android {
+namespace init {
+
+static void BenchmarkSuccess(benchmark::State& state) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    while (state.KeepRunning()) {
+        subcontext.Execute(std::vector<std::string>{"return_success"});
+    }
+}
+
+BENCHMARK(BenchmarkSuccess);
+
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
+    test_function_map.Add("return_success", 0, 0, true,
+                          [](const BuiltinArguments& args) { return Success(); });
+
+    return test_function_map;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+        auto test_function_map = android::init::BuildTestFunctionMap();
+        return android::init::SubcontextMain(argc, argv, &test_function_map);
+    }
+
+    ::benchmark::Initialize(&argc, argv);
+    if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
+    ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
new file mode 100644
index 0000000..60b45b9
--- /dev/null
+++ b/init/subcontext_test.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "builtin_arguments.h"
+#include "test_function_map.h"
+
+using namespace std::literals;
+
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::WaitForProperty;
+
+namespace android {
+namespace init {
+
+TEST(subcontext, CheckDifferentPid) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
+    ASSERT_FALSE(result);
+
+    auto pids = Split(result.error_string(), " ");
+    ASSERT_EQ(2U, pids.size());
+    auto our_pid = std::to_string(getpid());
+    EXPECT_NE(our_pid, pids[0]);
+    EXPECT_EQ(our_pid, pids[1]);
+}
+
+TEST(subcontext, SetProp) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    SetProperty("init.test.subcontext", "fail");
+    WaitForProperty("init.test.subcontext", "fail");
+
+    auto args = std::vector<std::string>{
+        "setprop",
+        "init.test.subcontext",
+        "success",
+    };
+    auto result = subcontext.Execute(args);
+    ASSERT_TRUE(result) << result.error();
+
+    EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s));
+}
+
+TEST(subcontext, MultipleCommands) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto first_pid = subcontext.pid();
+
+    auto expected_words = std::vector<std::string>{
+        "this",
+        "is",
+        "a",
+        "test",
+    };
+
+    for (const auto& word : expected_words) {
+        auto args = std::vector<std::string>{
+            "add_word",
+            word,
+        };
+        auto result = subcontext.Execute(args);
+        ASSERT_TRUE(result) << result.error();
+    }
+
+    auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
+    ASSERT_FALSE(result);
+    EXPECT_EQ(Join(expected_words, " "), result.error_string());
+    EXPECT_EQ(first_pid, subcontext.pid());
+}
+
+TEST(subcontext, RecoverAfterAbort) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto first_pid = subcontext.pid();
+
+    auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
+    ASSERT_FALSE(result);
+
+    auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
+    ASSERT_FALSE(result2);
+    EXPECT_EQ("Sane error!", result2.error_string());
+    EXPECT_NE(subcontext.pid(), first_pid);
+}
+
+TEST(subcontext, ContextString) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
+    ASSERT_FALSE(result);
+    ASSERT_EQ(kVendorContext, result.error_string());
+}
+
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
+    // For CheckDifferentPid
+    test_function_map.Add("return_pids_as_error", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << getpid() << " " << getppid();
+                          });
+
+    // For SetProp
+    test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
+        android::base::SetProperty(args[1], args[2]);
+        return Success();
+    });
+
+    // For MultipleCommands
+    // Using a shared_ptr to extend lifetime of words to both lambdas
+    auto words = std::make_shared<std::vector<std::string>>();
+    test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
+        words->emplace_back(args[1]);
+        return Success();
+    });
+    test_function_map.Add("return_words_as_error", 0, 0, true,
+                          [words](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << Join(*words, " ");
+                          });
+
+    // For RecoverAfterAbort
+    test_function_map.Add("cause_log_fatal", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << std::string(4097, 'f');
+                          });
+    test_function_map.Add(
+        "generate_sane_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
+
+    // For ContextString
+    test_function_map.Add(
+        "return_context_as_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << args.context; });
+
+    return test_function_map;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+        auto test_function_map = android::init::BuildTestFunctionMap();
+        return android::init::SubcontextMain(argc, argv, &test_function_map);
+    }
+
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/init/test_function_map.h b/init/test_function_map.h
new file mode 100644
index 0000000..583df1a
--- /dev/null
+++ b/init/test_function_map.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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 _INIT_TEST_FUNCTION_MAP_H
+#define _INIT_TEST_FUNCTION_MAP_H
+
+#include <string>
+#include <vector>
+
+#include "builtin_arguments.h"
+#include "keyword_map.h"
+
+namespace android {
+namespace init {
+
+class TestFunctionMap : public KeywordFunctionMap {
+  public:
+    // Helper for argument-less functions
+    using BuiltinFunctionNoArgs = std::function<void(void)>;
+    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+        Add(name, 0, 0, false, [function](const BuiltinArguments&) {
+            function();
+            return Success();
+        });
+    }
+
+    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+             bool run_in_subcontext, const BuiltinFunction function) {
+        builtin_functions_[name] =
+            make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
+    }
+
+  private:
+    Map builtin_functions_ = {};
+
+    const Map& map() const override { return builtin_functions_; }
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index e889bdd..2f20684 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -83,7 +83,7 @@
         darwin: {
             enabled: true,
         },
-        linux: {
+        linux_glibc: {
             srcs: libbacktrace_sources,
 
             shared_libs: [
@@ -94,7 +94,6 @@
             ],
 
             static_libs: ["libcutils"],
-            host_ldlibs: ["-lrt"],
         },
         linux_bionic: {
             enabled: true,
@@ -136,7 +135,7 @@
     srcs: ["backtrace_testlib.cpp"],
 
     target: {
-        linux: {
+        linux_glibc: {
             shared_libs: [
                 "libunwind",
                 "libunwindstack",
@@ -229,15 +228,11 @@
         android: {
             cflags: ["-DENABLE_PSS_TESTS"],
             shared_libs: [
-                "libdl",
                 "libutils",
             ],
         },
-        linux: {
+        linux_glibc: {
             host_ldlibs: [
-                "-lpthread",
-                "-lrt",
-                "-ldl",
                 "-lncurses",
             ],
             static_libs: ["libutils"],
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index b4481cc..41153ce 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -22,6 +22,7 @@
 #include <ucontext.h>
 
 #include <memory>
+#include <set>
 #include <string>
 
 #if !defined(__ANDROID__)
@@ -37,6 +38,8 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsGetLocal.h>
 
+#include <unwindstack/Unwinder.h>
+
 #include "BacktraceLog.h"
 #include "UnwindStack.h"
 #include "UnwindStackMap.h"
@@ -63,135 +66,42 @@
   return name;
 }
 
-static bool IsUnwindLibrary(const std::string& map_name) {
-  const std::string library(basename(map_name.c_str()));
-  return library == "libunwindstack.so" || library == "libbacktrace.so";
-}
-
-static void SetFrameInfo(unwindstack::Regs* regs, unwindstack::MapInfo* map_info,
-                         uint64_t adjusted_rel_pc, backtrace_frame_data_t* frame) {
-  // This will point to the adjusted absolute pc. regs->pc() is
-  // unaltered.
-  frame->pc = map_info->start + adjusted_rel_pc;
-  frame->sp = regs->sp();
-  frame->rel_pc = adjusted_rel_pc;
-  frame->stack_size = 0;
-
-  frame->map.start = map_info->start;
-  frame->map.end = map_info->end;
-  frame->map.offset = map_info->offset;
-  frame->map.flags = map_info->flags;
-  frame->map.name = map_info->name;
-
-  unwindstack::Elf* elf = map_info->elf;
-  frame->map.load_bias = elf->GetLoadBias();
-  uint64_t func_offset = 0;
-  if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
-    frame->func_name = demangle(frame->func_name.c_str());
-  } else {
-    frame->func_name = "";
-  }
-  frame->func_offset = func_offset;
-}
-
 static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                    std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
+  static std::set<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
   UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
-  unwindstack::Maps* maps = stack_map->stack_maps();
-  bool adjust_rel_pc = false;
-  size_t num_frames = 0;
-  frames->clear();
-  bool return_address_attempted = false;
   auto process_memory = stack_map->process_memory();
-  while (num_frames < MAX_BACKTRACE_FRAMES) {
-    unwindstack::MapInfo* map_info = maps->Find(regs->pc());
-    bool stepped;
-    bool in_device_map = false;
-    if (map_info == nullptr) {
-      stepped = false;
-      if (num_ignore_frames == 0) {
-        frames->resize(num_frames + 1);
-        backtrace_frame_data_t* frame = &frames->at(num_frames);
-        frame->pc = regs->pc();
-        frame->sp = regs->sp();
-        frame->rel_pc = frame->pc;
-        num_frames++;
-      } else {
-        num_ignore_frames--;
-      }
-    } else {
-      unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
-      uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+  unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
+                                 regs, stack_map->process_memory());
+  unwinder.Unwind(&skip_names);
 
-      if (frames->size() != 0 || !IsUnwindLibrary(map_info->name)) {
-        if (num_ignore_frames == 0) {
-          uint64_t adjusted_rel_pc = rel_pc;
-          if (adjust_rel_pc) {
-            adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
-          }
+  if (num_ignore_frames >= unwinder.NumFrames()) {
+    frames->resize(0);
+    return true;
+  }
 
-          frames->resize(num_frames + 1);
-          backtrace_frame_data_t* frame = &frames->at(num_frames);
-          frame->num = num_frames;
-          SetFrameInfo(regs, map_info, adjusted_rel_pc, frame);
+  frames->resize(unwinder.NumFrames() - num_ignore_frames);
+  auto unwinder_frames = unwinder.frames();
+  size_t cur_frame = 0;
+  for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, cur_frame++) {
+    auto frame = &unwinder_frames[i];
+    backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
 
-          if (num_frames > 0) {
-            // Set the stack size for the previous frame.
-            backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
-            prev->stack_size = frame->sp - prev->sp;
-          }
-          num_frames++;
-        } else {
-          num_ignore_frames--;
-        }
-      }
+    back_frame->num = frame->num;
 
-      if (map_info->flags & PROT_DEVICE_MAP) {
-        // Do not stop here, fall through in case we are
-        // in the speculative unwind path and need to remove
-        // some of the speculative frames.
-        stepped = false;
-        in_device_map = true;
-      } else {
-        unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
-        if (sp_info->flags & PROT_DEVICE_MAP) {
-          // Do not stop here, fall through in case we are
-          // in the speculative unwind path and need to remove
-          // some of the speculative frames.
-          stepped = false;
-          in_device_map = true;
-        } else {
-          bool finished;
-          stepped = elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
-          if (stepped && finished) {
-            break;
-          }
-        }
-      }
-    }
-    adjust_rel_pc = true;
+    back_frame->rel_pc = frame->rel_pc;
+    back_frame->pc = frame->pc;
+    back_frame->sp = frame->sp;
 
-    if (!stepped) {
-      if (return_address_attempted) {
-        // Remove the speculative frame.
-        if (frames->size() > 0) {
-          frames->pop_back();
-        }
-        break;
-      } else if (in_device_map) {
-        // Do not attempt any other unwinding, pc or sp is in a device
-        // map.
-        break;
-      } else {
-        // Stepping didn't work, try this secondary method.
-        if (!regs->SetPcFromReturnAddress(process_memory.get())) {
-          break;
-        }
-        return_address_attempted = true;
-      }
-    } else {
-      return_address_attempted = false;
-    }
+    back_frame->func_name = frame->function_name;
+    back_frame->func_offset = frame->function_offset;
+
+    back_frame->map.name = frame->map_name;
+    back_frame->map.start = frame->map_start;
+    back_frame->map.end = frame->map_end;
+    back_frame->map.offset = frame->map_offset;
+    back_frame->map.load_bias = frame->map_load_bias;
+    back_frame->map.flags = frame->map_flags;
   }
 
   return true;
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index b4abb79..95f2259 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -51,7 +51,7 @@
     int ret;
     struct stat st;
 
-    int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
+    int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
     if (fd < 0) {
         return fd;
     }
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 23a5c79..b92f086 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -23,7 +23,7 @@
         darwin: {
             enabled: false,
         },
-        linux: {
+        linux_glibc: {
             cflags: [
                 "-O2",
                 "-g",
diff --git a/liblog/Android.bp b/liblog/Android.bp
index b98d18f..d5bb29e 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -91,9 +91,6 @@
         not_windows: {
             srcs: ["event_tag_map.cpp"],
         },
-        linux: {
-            host_ldlibs: ["-lrt"],
-        },
         linux_bionic: {
             enabled: true,
         },
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index b8ca475..07e8c8a 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -10,6 +10,8 @@
 #ifndef _LIBS_LOG_SAFETYNET_H
 #define _LIBS_LOG_SAFETYNET_H
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 3764faf..309f5d1 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -28,6 +28,10 @@
 #ifndef __struct_log_time_defined
 #define __struct_log_time_defined
 
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
 #ifdef __cplusplus
 
 /*
@@ -167,15 +171,15 @@
 #endif
 } __attribute__((__packed__));
 
-#else
+#else /* __cplusplus */
 
 typedef struct log_time {
   uint32_t tv_sec;
   uint32_t tv_nsec;
 } __attribute__((__packed__)) log_time;
 
-#endif
+#endif /* __cplusplus */
 
-#endif
+#endif /* __struct_log_time_defined */
 
 #endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
deleted file mode 120000
index abfe439..0000000
--- a/liblog/include_vndk/log/log_time.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/log/log_time.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 100644
index 0000000..5a09959
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2017 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 _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#ifndef NS_PER_SEC
+#define NS_PER_SEC 1000000000ULL
+#endif
+#ifndef US_PER_SEC
+#define US_PER_SEC 1000000ULL
+#endif
+#ifndef MS_PER_SEC
+#define MS_PER_SEC 1000ULL
+#endif
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+typedef struct log_time {
+  uint32_t tv_sec;
+  uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index fcabcc9..189bc4b 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -42,8 +42,8 @@
     LOGBUILDER_COUNTER = 803,
     LOGBUILDER_HISTOGRAM = 804,
 
-    ACTION_BOOT = 1092,
-    FIELD_PLATFORM_REASON = 1093,
+    ACTION_BOOT = 1098,
+    FIELD_PLATFORM_REASON = 1099,
 };
 
 enum {
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index b3c42f0..089f3b8 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -22,13 +22,6 @@
     cppflags: [
         "-fvisibility=protected",
     ],
-
-    host_ldlibs: ["-ldl"],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-    },
 }
 
 subdirs = ["tests"]
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index e31dae0..9e2e641 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -25,14 +25,6 @@
     ],
     header_libs: ["libnativebridge-dummy-headers"],
     cppflags: ["-fvisibility=protected"],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-        host: {
-            host_ldlibs: ["-ldl"],
-        },
-    },
 }
 
 cc_library_shared {
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 13f9744..4b21edc 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -11,14 +11,6 @@
         "libnativebridge",
         "libbase",
     ],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-        host: {
-            host_ldlibs: ["-ldl"],
-        },
-    },
     cflags: [
         "-Werror",
         "-Wall",
@@ -27,5 +19,4 @@
         "-fvisibility=hidden",
     ],
     export_include_dirs: ["include"],
-    local_include_dirs: ["include"],
 }
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index d076a1a..3a12292 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -23,7 +23,6 @@
         "libbase",
         "libcutils",
         "liblog",
-        "libnl",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libsysutils/include/sysutils/NetlinkEvent.h b/libsysutils/include/sysutils/NetlinkEvent.h
index b80f3ea..f9fc11b 100644
--- a/libsysutils/include/sysutils/NetlinkEvent.h
+++ b/libsysutils/include/sysutils/NetlinkEvent.h
@@ -64,6 +64,7 @@
     bool parseNfPacketMessage(struct nlmsghdr *nh);
     bool parseRtMessage(const struct nlmsghdr *nh);
     bool parseNdUserOptMessage(const struct nlmsghdr *nh);
+    struct nlattr* findNlAttr(const nlmsghdr* nl, size_t hdrlen, uint16_t attr);
 };
 
 #endif
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 79bc888..00b1ee2 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "NetlinkEvent"
 
 #include <arpa/inet.h>
+#include <limits.h>
+#include <linux/genetlink.h>
 #include <linux/if.h>
 #include <linux/if_addr.h>
 #include <linux/if_link.h>
@@ -26,12 +28,8 @@
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <net/if.h>
-#include <netinet/in.h>
 #include <netinet/icmp6.h>
-#include <netlink/attr.h>
-#include <netlink/genl/genl.h>
-#include <netlink/handlers.h>
-#include <netlink/msg.h>
+#include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -263,6 +261,18 @@
     return true;
 }
 
+static size_t nlAttrLen(const nlattr* nla) {
+    return nla->nla_len - NLA_HDRLEN;
+}
+
+static const uint8_t* nlAttrData(const nlattr* nla) {
+    return reinterpret_cast<const uint8_t*>(nla) + NLA_HDRLEN;
+}
+
+static uint32_t nlAttrU32(const nlattr* nla) {
+    return *reinterpret_cast<const uint32_t*>(nlAttrData(nla));
+}
+
 /*
  * Parse a LOCAL_NFLOG_PACKET message.
  */
@@ -271,17 +281,17 @@
     int len = 0;
     char* raw = NULL;
 
-    struct nlattr *uid_attr = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_UID);
+    struct nlattr* uid_attr = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_UID);
     if (uid_attr) {
-        uid = ntohl(nla_get_u32(uid_attr));
+        uid = ntohl(nlAttrU32(uid_attr));
     }
 
-    struct nlattr *payload = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
+    struct nlattr* payload = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
     if (payload) {
         /* First 256 bytes is plenty */
-        len = nla_len(payload);
+        len = nlAttrLen(payload);
         if (len > 256) len = 256;
-        raw = (char*) nla_data(payload);
+        raw = (char*)nlAttrData(payload);
     }
 
     char* hex = (char*) calloc(1, 5 + (len * 2));
@@ -646,3 +656,26 @@
     SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
     return NULL;
 }
+
+nlattr* NetlinkEvent::findNlAttr(const nlmsghdr* nh, size_t hdrlen, uint16_t attr) {
+    if (nh == nullptr || NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen) > SSIZE_MAX) {
+        return nullptr;
+    }
+
+    // Skip header, padding, and family header.
+    const ssize_t NLA_START = NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen);
+    ssize_t left = nh->nlmsg_len - NLA_START;
+    uint8_t* hdr = ((uint8_t*)nh) + NLA_START;
+
+    while (left >= NLA_HDRLEN) {
+        nlattr* nla = (nlattr*)hdr;
+        if (nla->nla_type == attr) {
+            return nla;
+        }
+
+        hdr += NLA_ALIGN(nla->nla_len);
+        left -= NLA_ALIGN(nla->nla_len);
+    }
+
+    return nullptr;
+}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ca05516..f40086e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -67,7 +67,7 @@
 
     target: {
         // Always disable optimizations for host to make it easier to debug.
-        linux: {
+        host: {
             cflags: ["-O0", "-g"],
         },
     },
@@ -107,6 +107,7 @@
         "tests/DwarfOpTest.cpp",
         "tests/DwarfSectionTest.cpp",
         "tests/DwarfSectionImplTest.cpp",
+        "tests/ElfFake.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
@@ -125,6 +126,7 @@
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
         "tests/UnwindTest.cpp",
+        "tests/UnwinderTest.cpp",
     ],
 
     cflags: [
@@ -143,14 +145,6 @@
         "libgmock",
     ],
 
-    target: {
-        linux: {
-            host_ldlibs: [
-                "-lrt",
-            ],
-        },
-    },
-
     data: [
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
@@ -178,14 +172,6 @@
     srcs: [
         "tools/unwind.cpp",
     ],
-
-    target: {
-        linux: {
-            host_ldlibs: [
-                "-lrt",
-            ],
-        },
-    },
 }
 
 cc_binary {
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 8c6172e..69e6512 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -33,26 +33,6 @@
 
 namespace unwindstack {
 
-template <typename AddressType>
-bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
-  switch (return_loc_.type) {
-  case LOCATION_REGISTER:
-    CHECK(return_loc_.value < total_regs_);
-    *value = regs_[return_loc_.value];
-    return true;
-  case LOCATION_SP_OFFSET:
-    AddressType return_value;
-    if (!memory->Read(sp_ + return_loc_.value, &return_value, sizeof(return_value))) {
-      return false;
-    }
-    *value = return_value;
-    return true;
-  case LOCATION_UNKNOWN:
-  default:
-    return false;
-  }
-}
-
 RegsArm::RegsArm()
     : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 1a86e16..e648927 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
+#define _GNU_SOURCE 1
 #include <elf.h>
 #include <inttypes.h>
 #include <stdint.h>
+#include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -28,35 +30,33 @@
 
 namespace unwindstack {
 
-void Unwinder::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
   FrameData* frame = &frames_.at(frame_num);
   frame->num = frame_num;
   frame->pc = regs_->pc();
   frame->sp = regs_->sp();
-  frame->rel_pc = frame->pc;
+  frame->rel_pc = rel_pc;
 
   if (map_info == nullptr) {
     return;
   }
 
-  Elf* elf = map_info->GetElf(process_memory_, true);
-  *rel_pc = elf->GetRelPc(regs_->pc(), map_info);
-  if (frame_num != 0) {
+  if (adjust_pc) {
     // Don't adjust the first frame pc.
-    frame->rel_pc = regs_->GetAdjustedPc(*rel_pc, elf);
+    frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
 
     // Adjust the original pc.
-    frame->pc -= *rel_pc - frame->rel_pc;
-  } else {
-    frame->rel_pc = *rel_pc;
+    frame->pc -= rel_pc - frame->rel_pc;
   }
 
   frame->map_name = map_info->name;
   frame->map_offset = map_info->elf_offset;
   frame->map_start = map_info->start;
   frame->map_end = map_info->end;
+  frame->map_flags = map_info->flags;
+  frame->map_load_bias = elf->GetLoadBias();
 
   if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
     frame->function_name = "";
@@ -64,25 +64,59 @@
   }
 }
 
-void Unwinder::Unwind() {
+void Unwinder::Unwind(std::set<std::string>* initial_map_names_to_skip) {
   frames_.clear();
 
   bool return_address_attempt = false;
+  bool adjust_pc = false;
   for (; frames_.size() < max_frames_;) {
     MapInfo* map_info = maps_->Find(regs_->pc());
 
     uint64_t rel_pc;
-    FillInFrame(map_info, &rel_pc);
+    Elf* elf;
+    if (map_info == nullptr) {
+      rel_pc = regs_->pc();
+    } else {
+      elf = map_info->GetElf(process_memory_, true);
+      rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+    }
+
+    if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
+        initial_map_names_to_skip->find(basename(map_info->name.c_str())) ==
+            initial_map_names_to_skip->end()) {
+      FillInFrame(map_info, elf, rel_pc, adjust_pc);
+      // Once a frame is added, stop skipping frames.
+      initial_map_names_to_skip = nullptr;
+    }
+    adjust_pc = true;
 
     bool stepped;
+    bool in_device_map = false;
     if (map_info == nullptr) {
       stepped = false;
     } else {
-      bool finished;
-      stepped = map_info->elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(),
-                                    &finished);
-      if (stepped && finished) {
-        break;
+      if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+        // Do not stop here, fall through in case we are
+        // in the speculative unwind path and need to remove
+        // some of the speculative frames.
+        stepped = false;
+        in_device_map = true;
+      } else {
+        MapInfo* sp_info = maps_->Find(regs_->sp());
+        if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+          // Do not stop here, fall through in case we are
+          // in the speculative unwind path and need to remove
+          // some of the speculative frames.
+          stepped = false;
+          in_device_map = true;
+        } else {
+          bool finished;
+          stepped =
+              elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(), &finished);
+          if (stepped && finished) {
+            break;
+          }
+        }
       }
     }
     if (!stepped) {
@@ -90,6 +124,10 @@
         // Remove the speculative frame.
         frames_.pop_back();
         break;
+      } else if (in_device_map) {
+        // Do not attempt any other unwinding, pc or sp is in a device
+        // map.
+        break;
       } else {
         // Steping didn't work, try this secondary method.
         if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 385ee18..9d3150b 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -55,8 +55,6 @@
   virtual uint64_t pc() = 0;
   virtual uint64_t sp() = 0;
 
-  virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;
-
   virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
 
   virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
@@ -86,8 +84,6 @@
       : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
   virtual ~RegsImpl() = default;
 
-  bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;
-
   uint64_t pc() override { return pc_; }
   uint64_t sp() override { return sp_; }
 
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index d66fe55..71703b4 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <memory>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -30,6 +31,9 @@
 
 namespace unwindstack {
 
+// Forward declarations.
+class Elf;
+
 struct FrameData {
   size_t num;
 
@@ -44,6 +48,8 @@
   uint64_t map_offset;
   uint64_t map_start;
   uint64_t map_end;
+  uint64_t map_load_bias;
+  int map_flags;
 };
 
 class Unwinder {
@@ -54,7 +60,7 @@
   }
   ~Unwinder() = default;
 
-  void Unwind();
+  void Unwind(std::set<std::string>* initial_map_names_to_skip = nullptr);
 
   size_t NumFrames() { return frames_.size(); }
 
@@ -64,7 +70,7 @@
   static std::string FormatFrame(const FrameData& frame, bool bits32);
 
  private:
-  void FillInFrame(MapInfo* map_info, uint64_t* rel_pc);
+  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
 
   size_t max_frames_;
   Maps* maps_;
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 69813e5..90baabe 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -25,7 +25,6 @@
 
 #include "LogFake.h"
 #include "MemoryFake.h"
-#include "RegsFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 07159b0..21114da 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -25,7 +25,6 @@
 
 #include "LogFake.h"
 #include "MemoryFake.h"
-#include "RegsFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 47a40cf..2d5007b 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -1486,7 +1486,7 @@
   }
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(32, 10);
+  RegsImplFake<TypeParam> regs(32, 10);
   for (size_t i = 0; i < 32; i++) {
     regs[i] = i + 10;
   }
@@ -1518,7 +1518,7 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(16, 10);
+  RegsImplFake<TypeParam> regs(16, 10);
   for (size_t i = 0; i < 16; i++) {
     regs[i] = i + 10;
   }
@@ -1544,7 +1544,7 @@
                                         0x92, 0x80, 0x15, 0x80, 0x02};
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(10, 10);
+  RegsImplFake<TypeParam> regs(10, 10);
   regs[5] = 0x45;
   regs[6] = 0x190;
   this->op_->set_regs(&regs);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 2939126..c701a29 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -90,7 +90,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -105,7 +105,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -121,7 +121,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -141,7 +141,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -159,7 +159,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -175,7 +175,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
   DwarfCie cie{.return_address_register = 60};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   bool finished;
@@ -185,7 +185,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   bool finished;
@@ -195,7 +195,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
@@ -224,7 +224,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -241,7 +241,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -259,7 +259,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -275,7 +275,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -290,7 +290,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   if (sizeof(TypeParam) == sizeof(uint64_t)) {
@@ -324,7 +324,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -342,7 +342,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -359,7 +359,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -378,7 +378,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -398,7 +398,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -416,7 +416,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
new file mode 100644
index 0000000..71f7f6b
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+std::deque<FunctionData> ElfInterfaceFake::functions_;
+std::deque<StepData> ElfInterfaceFake::steps_;
+
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
+  if (functions_.empty()) {
+    return false;
+  }
+  auto entry = functions_.front();
+  functions_.pop_front();
+  *name = entry.name;
+  *offset = entry.offset;
+  return true;
+}
+
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
+  if (steps_.empty()) {
+    return false;
+  }
+  auto entry = steps_.front();
+  steps_.pop_front();
+
+  if (entry.pc == 0 && entry.sp == 0 && !entry.finished) {
+    // Pretend as though there is no frame.
+    return false;
+  }
+
+  RegsFake* fake_regs = reinterpret_cast<RegsFake*>(regs);
+  fake_regs->FakeSetPc(entry.pc);
+  fake_regs->FakeSetSp(entry.sp);
+  *finished = entry.finished;
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
new file mode 100644
index 0000000..4359bca
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+struct StepData {
+  StepData(uint64_t pc, uint64_t sp, bool finished) : pc(pc), sp(sp), finished(finished) {}
+  uint64_t pc;
+  uint64_t sp;
+  bool finished;
+};
+
+struct FunctionData {
+  FunctionData(std::string name, uint64_t offset) : name(name), offset(offset) {}
+
+  std::string name;
+  uint64_t offset;
+};
+
+class ElfFake : public Elf {
+ public:
+  ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
+  virtual ~ElfFake() = default;
+
+  void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+};
+
+class ElfInterfaceFake : public ElfInterface {
+ public:
+  ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
+  virtual ~ElfInterfaceFake() = default;
+
+  bool Init() override { return false; }
+  void InitHeaders() override {}
+  bool GetSoname(std::string*) override { return false; }
+
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
+
+  bool Step(uint64_t, Regs*, Memory*, bool*) override;
+
+  void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+
+  static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
+  static void FakePushStepData(const StepData data) { steps_.push_back(data); }
+
+  static void FakeClear() {
+    functions_.clear();
+    steps_.clear();
+  }
+
+ private:
+  static std::deque<FunctionData> functions_;
+  static std::deque<StepData> steps_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index b667ec1..efcd029 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -24,20 +24,60 @@
 
 namespace unwindstack {
 
-template <typename TypeParam>
-class RegsFake : public RegsImpl<TypeParam> {
+class RegsFake : public Regs {
  public:
   RegsFake(uint16_t total_regs, uint16_t sp_reg)
-      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+      : Regs(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
   virtual ~RegsFake() = default;
 
+  uint32_t MachineType() override { return fake_type_; }
+  void* RawData() override { return nullptr; }
+  uint64_t pc() override { return fake_pc_; }
+  uint64_t sp() override { return fake_sp_; }
+  bool SetPcFromReturnAddress(Memory*) override {
+    if (!fake_return_address_valid_) {
+      return false;
+    }
+    fake_pc_ = fake_return_address_;
+    return true;
+  }
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
+
+  bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+  void SetFromRaw() override {}
+
+  void FakeSetMachineType(uint32_t type) { fake_type_ = type; }
+  void FakeSetPc(uint64_t pc) { fake_pc_ = pc; }
+  void FakeSetSp(uint64_t sp) { fake_sp_ = sp; }
+  void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
+  void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
+
+ private:
+  uint32_t fake_type_ = 0;
+  uint64_t fake_pc_ = 0;
+  uint64_t fake_sp_ = 0;
+  bool fake_return_address_valid_ = false;
+  uint64_t fake_return_address_ = 0;
+};
+
+template <typename TypeParam>
+class RegsImplFake : public RegsImpl<TypeParam> {
+ public:
+  RegsImplFake(uint16_t total_regs, uint16_t sp_reg)
+      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  virtual ~RegsImplFake() = default;
+
   uint32_t MachineType() override { return 0; }
 
   uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
   void SetFromRaw() override {}
   bool SetPcFromReturnAddress(Memory*) override { return false; }
   bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
-  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+
+  void FakeSetPc(uint64_t pc) { this->pc_ = pc; }
+  void FakeSetSp(uint64_t sp) { this->sp_ = sp; }
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3912e17..3b9f92b 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -23,68 +23,28 @@
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Regs.h>
 
+#include "ElfFake.h"
 #include "MemoryFake.h"
+#include "RegsFake.h"
 
 namespace unwindstack {
 
-class ElfFake : public Elf {
- public:
-  ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
-  virtual ~ElfFake() = default;
-
-  void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
-};
-
-class ElfInterfaceFake : public ElfInterface {
- public:
-  ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
-  virtual ~ElfInterfaceFake() = default;
-
-  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
-
-  bool Init() override { return false; }
-  void InitHeaders() override {}
-  bool GetSoname(std::string*) override { return false; }
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
-  bool Step(uint64_t, Regs*, Memory*, bool*) override { return false; }
-};
-
-template <typename TypeParam>
-class RegsTestImpl : public RegsImpl<TypeParam> {
- public:
-  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
-      : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
-      : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
-  virtual ~RegsTestImpl() = default;
-
-  uint32_t MachineType() override { return 0; }
-
-  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
-  void SetFromRaw() override {}
-  bool SetPcFromReturnAddress(Memory*) override { return false; }
-  bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
-};
-
 class RegsTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_ = new MemoryFake;
     elf_.reset(new ElfFake(memory_));
     elf_interface_ = new ElfInterfaceFake(elf_->memory());
-    elf_->set_elf_interface(elf_interface_);
+    elf_->FakeSetInterface(elf_interface_);
   }
 
-  template <typename AddressType>
-  void RegsReturnAddressRegister();
-
   ElfInterfaceFake* elf_interface_;
   MemoryFake* memory_;
   std::unique_ptr<ElfFake> elf_;
 };
 
 TEST_F(RegsTest, regs32) {
-  RegsTestImpl<uint32_t> regs32(50, 10);
+  RegsImplFake<uint32_t> regs32(50, 10);
   ASSERT_EQ(50U, regs32.total_regs());
   ASSERT_EQ(10U, regs32.sp_reg());
 
@@ -107,7 +67,7 @@
 }
 
 TEST_F(RegsTest, regs64) {
-  RegsTestImpl<uint64_t> regs64(30, 12);
+  RegsImplFake<uint64_t> regs64(30, 12);
   ASSERT_EQ(30U, regs64.total_regs());
   ASSERT_EQ(12U, regs64.sp_reg());
 
@@ -129,44 +89,6 @@
   ASSERT_EQ(10U, regs64[8]);
 }
 
-template <typename AddressType>
-void RegsTest::RegsReturnAddressRegister() {
-  RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
-
-  regs[5] = 0x12345;
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345U, value);
-}
-
-TEST_F(RegsTest, regs32_return_address_register) {
-  RegsReturnAddressRegister<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_return_address_register) {
-  RegsReturnAddressRegister<uint64_t>();
-}
-
-TEST_F(RegsTest, regs32_return_address_sp_offset) {
-  RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
-
-  regs.set_sp(0x2002);
-  memory_->SetData32(0x2000, 0x12345678);
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345678U, value);
-}
-
-TEST_F(RegsTest, regs64_return_address_sp_offset) {
-  RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
-
-  regs.set_sp(0x2008);
-  memory_->SetData64(0x2000, 0x12345678aabbccddULL);
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345678aabbccddULL, value);
-}
-
 TEST_F(RegsTest, rel_pc) {
   RegsArm64 arm64;
   ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get()));
@@ -193,7 +115,7 @@
   RegsArm arm;
 
   // Check fence posts.
-  elf_interface_->set_load_bias(0);
+  elf_interface_->FakeSetLoadBias(0);
   ASSERT_EQ(3U,  arm.GetAdjustedPc(0x5, elf_.get()));
   ASSERT_EQ(4U,  arm.GetAdjustedPc(0x4, elf_.get()));
   ASSERT_EQ(3U,  arm.GetAdjustedPc(0x3, elf_.get()));
@@ -201,7 +123,7 @@
   ASSERT_EQ(1U,  arm.GetAdjustedPc(0x1, elf_.get()));
   ASSERT_EQ(0U,  arm.GetAdjustedPc(0x0, elf_.get()));
 
-  elf_interface_->set_load_bias(0x100);
+  elf_interface_->FakeSetLoadBias(0x100);
   ASSERT_EQ(0xffU,  arm.GetAdjustedPc(0xff, elf_.get()));
   ASSERT_EQ(0x103U,  arm.GetAdjustedPc(0x105, elf_.get()));
   ASSERT_EQ(0x104U,  arm.GetAdjustedPc(0x104, elf_.get()));
@@ -211,13 +133,13 @@
   ASSERT_EQ(0x100U,  arm.GetAdjustedPc(0x100, elf_.get()));
 
   // Check thumb instructions handling.
-  elf_interface_->set_load_bias(0);
+  elf_interface_->FakeSetLoadBias(0);
   memory_->SetData32(0x2000, 0);
   ASSERT_EQ(0x2003U,  arm.GetAdjustedPc(0x2005, elf_.get()));
   memory_->SetData32(0x2000, 0xe000f000);
   ASSERT_EQ(0x2001U,  arm.GetAdjustedPc(0x2005, elf_.get()));
 
-  elf_interface_->set_load_bias(0x400);
+  elf_interface_->FakeSetLoadBias(0x400);
   memory_->SetData32(0x2100, 0);
   ASSERT_EQ(0x2503U,  arm.GetAdjustedPc(0x2505, elf_.get()));
   memory_->SetData32(0x2100, 0xf111f111);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
new file mode 100644
index 0000000..4d0366c
--- /dev/null
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2017 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 <elf.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+class MapsFake : public Maps {
+ public:
+  MapsFake() = default;
+  virtual ~MapsFake() = default;
+
+  bool Parse() { return true; }
+
+  void FakeClear() { maps_.clear(); }
+
+  void FakeAddMapInfo(const MapInfo& map_info) { maps_.push_back(map_info); }
+};
+
+class UnwinderTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    maps_.FakeClear();
+    MapInfo info;
+    info.name = "/system/fake/libc.so";
+    info.start = 0x1000;
+    info.end = 0x8000;
+    info.offset = 0;
+    info.flags = PROT_READ | PROT_WRITE;
+    ElfFake* elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    info.elf_offset = 0;
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "[stack]";
+    info.start = 0x10000;
+    info.end = 0x12000;
+    info.flags = PROT_READ | PROT_WRITE;
+    info.elf = nullptr;
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/dev/fake_device";
+    info.start = 0x13000;
+    info.end = 0x15000;
+    info.flags = PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP;
+    info.elf = nullptr;
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/system/fake/libunwind.so";
+    info.start = 0x20000;
+    info.end = 0x22000;
+    info.flags = PROT_READ | PROT_WRITE;
+    elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/fake/libanother.so";
+    info.start = 0x23000;
+    info.end = 0x24000;
+    info.flags = PROT_READ | PROT_WRITE;
+    elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+  }
+
+  void SetUp() override {
+    ElfInterfaceFake::FakeClear();
+    regs_.FakeSetMachineType(EM_ARM);
+  }
+
+  static MapsFake maps_;
+  static RegsFake regs_;
+  static std::shared_ptr<Memory> process_memory_;
+};
+
+MapsFake UnwinderTest::maps_;
+RegsFake UnwinderTest::regs_(5, 0);
+std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
+
+TEST_F(UnwinderTest, multiple_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x1100U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that no attempt to continue after the step indicates it is done.
+TEST_F(UnwinderTest, no_frames_after_finished) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x1000, 0x10000, true));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify the maximum frames to save.
+TEST_F(UnwinderTest, max_frames) {
+  for (size_t i = 0; i < 30; i++) {
+    ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame" + std::to_string(i), i));
+    ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
+  }
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+
+  Unwinder unwinder(20, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(20U, unwinder.NumFrames());
+
+  for (size_t i = 0; i < 20; i++) {
+    auto* frame = &unwinder.frames()[i];
+    EXPECT_EQ(i, frame->num);
+    EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i;
+    EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i;
+    EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i;
+    EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
+    EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
+    EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
+    EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
+    EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
+    EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i;
+  }
+}
+
+// Verify that initial map names frames are removed.
+TEST_F(UnwinderTest, verify_frames_skipped) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x20000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10040, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1002, 0x10050, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10060, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  std::set<std::string> skip_set{"libunwind.so", "libanother.so"};
+  unwinder.Unwind(&skip_set);
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10050U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x1000U, frame->rel_pc);
+  EXPECT_EQ(0x21000U, frame->pc);
+  EXPECT_EQ(0x10060U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x20000U, frame->map_start);
+  EXPECT_EQ(0x22000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x23000U, frame->pc);
+  EXPECT_EQ(0x10070U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/fake/libanother.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x23000U, frame->map_start);
+  EXPECT_EQ(0x24000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify SP in a non-existant map is okay.
+TEST_F(UnwinderTest, sp_not_in_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x53000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x53000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x1000U, frame->rel_pc);
+  EXPECT_EQ(0x21000U, frame->pc);
+  EXPECT_EQ(0x50020U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x20000U, frame->map_start);
+  EXPECT_EQ(0x22000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify PC in a device stops the unwind.
+TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x13000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify SP in a device stops the unwind.
+TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x13000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify a no map info frame gets a frame.
+TEST_F(UnwinderTest, pc_without_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0x41000);
+  regs_.FakeSetSp(0x13000);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x41000U, frame->rel_pc);
+  EXPECT_EQ(0x41000U, frame->pc);
+  EXPECT_EQ(0x13000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added.
+TEST_F(UnwinderTest, speculative_frame) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  // Fake as if code called a nullptr function.
+  regs_.FakeSetPc(0);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetReturnAddress(0x1202);
+  regs_.FakeSetReturnAddressValid(true);
+
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x23100U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/libanother.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x23000U, frame->map_start);
+  EXPECT_EQ(0x24000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that a speculative frame is added then removed because no other
+// frames are added.
+TEST_F(UnwinderTest, speculative_frame_removed) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  // Fake as if code called a nullptr function.
+  regs_.FakeSetPc(0);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetReturnAddress(0x1202);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame_static) {
+  FrameData frame;
+  frame.num = 1;
+  frame.rel_pc = 0x1000;
+  frame.pc = 0x4000;
+  frame.sp = 0x1000;
+  frame.function_name = "function";
+  frame.function_offset = 100;
+  frame.map_name = "/fake/libfake.so";
+  frame.map_offset = 0x2000;
+  frame.map_start = 0x3000;
+  frame.map_end = 0x6000;
+  frame.map_flags = PROT_READ;
+
+  EXPECT_EQ("  #01 pc 0000000000001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, true));
+
+  frame.map_offset = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, true));
+
+  frame.function_offset = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
+
+  frame.function_name = "";
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", Unwinder::FormatFrame(frame, true));
+
+  frame.map_name = "";
+  EXPECT_EQ("  #01 pc 0000000000001000  <anonymous:3000>", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  <anonymous:3000>", Unwinder::FormatFrame(frame, true));
+
+  frame.map_start = 0;
+  frame.map_end = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  <unknown>", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  <unknown>", Unwinder::FormatFrame(frame, true));
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
+
+  regs_.FakeSetPc(0x2300);
+  regs_.FakeSetSp(0x10000);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  regs_.FakeSetMachineType(EM_ARM);
+  EXPECT_EQ("  #00 pc 00001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+  regs_.FakeSetMachineType(EM_386);
+  EXPECT_EQ("  #00 pc 00001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+
+  regs_.FakeSetMachineType(EM_AARCH64);
+  EXPECT_EQ("  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+  regs_.FakeSetMachineType(EM_X86_64);
+  EXPECT_EQ("  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+
+  EXPECT_EQ("", unwinder.FormatFrame(1));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index faac2ef..f2530d7 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -26,17 +26,11 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-
 #include <unwindstack/Elf.h>
-#include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
 
 static bool Attach(pid_t pid) {
   if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
@@ -55,66 +49,6 @@
   return false;
 }
 
-static bool Detach(pid_t pid) {
-  return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-}
-
-std::string GetFrameInfo(size_t frame_num, unwindstack::Regs* regs,
-                         const std::shared_ptr<unwindstack::Memory>& process_memory,
-                         unwindstack::MapInfo* map_info, uint64_t* rel_pc) {
-  bool bits32;
-  switch (regs->MachineType()) {
-    case EM_ARM:
-    case EM_386:
-      bits32 = true;
-      break;
-
-    default:
-      bits32 = false;
-  }
-
-  if (map_info == nullptr) {
-    if (bits32) {
-      return android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame_num, regs->pc());
-    } else {
-      return android::base::StringPrintf("  #%02zu pc %016" PRIx64, frame_num, regs->pc());
-    }
-  }
-
-  unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
-  *rel_pc = elf->GetRelPc(regs->pc(), map_info);
-  uint64_t adjusted_rel_pc = *rel_pc;
-  // Don't need to adjust the first frame pc.
-  if (frame_num != 0) {
-    adjusted_rel_pc = regs->GetAdjustedPc(*rel_pc, elf);
-  }
-
-  std::string line;
-  if (bits32) {
-    line = android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
-  } else {
-    line = android::base::StringPrintf("  #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
-  }
-  if (!map_info->name.empty()) {
-    line += "  " + map_info->name;
-    if (map_info->elf_offset != 0) {
-      line += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
-    }
-  } else {
-    line += android::base::StringPrintf("  <anonymous:%" PRIx64 ">", map_info->offset);
-  }
-  uint64_t func_offset;
-  std::string func_name;
-  if (elf->GetFunctionName(adjusted_rel_pc, &func_name, &func_offset)) {
-    line += " (" + func_name;
-    if (func_offset != 0) {
-      line += android::base::StringPrintf("+%" PRId64, func_offset);
-    }
-    line += ')';
-  }
-  return line;
-}
-
 void DoUnwind(pid_t pid) {
   unwindstack::RemoteMaps remote_maps(pid);
   if (!remote_maps.Parse()) {
@@ -149,48 +83,12 @@
   printf("\n");
 
   auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
-  bool return_address_attempt = false;
-  std::vector<std::string> frames;
-  for (size_t frame_num = 0; frame_num < 64; frame_num++) {
-    unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc());
-    uint64_t rel_pc;
-    frames.push_back(GetFrameInfo(frame_num, regs, process_memory, map_info, &rel_pc));
-    bool stepped;
-    if (map_info == nullptr) {
-      stepped = false;
-    } else {
-      bool finished;
-      stepped =
-          map_info->elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
-      if (stepped && finished) {
-        break;
-      }
-    }
-    if (!stepped) {
-      if (return_address_attempt) {
-        // We tried the return address and it didn't work, remove the last
-        // two frames. If this bad frame is the only frame, only remove
-        // the last frame.
-        frames.pop_back();
-        if (frame_num != 1) {
-          frames.pop_back();
-        }
-        break;
-      } else {
-        // Steping didn't work, try this secondary method.
-        if (!regs->SetPcFromReturnAddress(process_memory.get())) {
-          break;
-        }
-        return_address_attempt = true;
-      }
-    } else {
-      return_address_attempt = false;
-    }
-  }
+  unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+  unwinder.Unwind();
 
   // Print the frames.
-  for (auto& frame : frames) {
-    printf("%s\n", frame.c_str());
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    printf("%s\n", unwinder.FormatFrame(i).c_str());
   }
 }
 
@@ -208,7 +106,7 @@
 
   DoUnwind(pid);
 
-  Detach(pid);
+  ptrace(PTRACE_DETACH, pid, 0, 0);
 
   return 0;
 }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 038fd73..30e6b49 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -121,7 +121,7 @@
             },
         },
 
-        linux: {
+        linux_glibc: {
             srcs: [
                 "Looper.cpp",
                 "ProcessCallStack.cpp",
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 24737b9..f5f881f 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -442,6 +442,11 @@
     // and all accesses to refs happen before its deletion in the final decWeak.
     // The destructor can safely access mRefs because either it's deleting
     // mRefs itself, or it's running entirely before the final mWeak decrement.
+    //
+    // Since we're doing atomic loads of `flags`, the static analyzer assumes
+    // they can change between `delete this;` and `refs->decWeak(id);`. This is
+    // not the case. The analyzer may become more okay with this patten when
+    // https://bugs.llvm.org/show_bug.cgi?id=34365 gets resolved. NOLINTNEXTLINE
     refs->decWeak(id);
 }
 
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index 0869175..6911fc5 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -44,10 +44,9 @@
                 "libcutils",
                 "libutils",
                 "libbase",
-                "libdl",
             ],
         },
-        linux: {
+        linux_glibc: {
             srcs: [
                 "Looper_test.cpp",
                 "RefBase_test.cpp",
@@ -59,7 +58,6 @@
                 "liblog",
                 "libbase",
             ],
-            host_ldlibs: ["-ldl"],
         },
     },
 
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 8ee5ea1..9e1541b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -1099,7 +1099,7 @@
     // and dac_read_search on every try to get past the message
     // de-duper.  We will also rotate the file name in the directory
     // as another measure.
-    static const char file[] = "/data/backup/cannot_access_directory_%u";
+    static const char file[] = "/data/drm/cannot_access_directory_%u";
     static const unsigned avc_requests_per_access = 2;
 
     rate /= avc_requests_per_access;
diff --git a/reboot/reboot.c b/reboot/reboot.c
index f0cf40c..fe763a8 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -21,13 +21,13 @@
 #include <cutils/android_reboot.h>
 #include <unistd.h>
 
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
     int ret;
     size_t prop_len;
     char property_val[PROPERTY_VALUE_MAX];
-    const char *cmd = "reboot";
-    char *optarg = "";
+    static const char reboot[] = "reboot";
+    const char* cmd = reboot;
+    char* optarg = "";
 
     opterr = 0;
     do {
@@ -60,19 +60,23 @@
 
     prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
     if (prop_len >= sizeof(property_val)) {
-        fprintf(stderr, "reboot command too long: %s\n", optarg);
+        fprintf(stderr, "%s command too long: %s\n", cmd, optarg);
         exit(EXIT_FAILURE);
     }
 
     ret = property_set(ANDROID_RB_PROPERTY, property_val);
-    if(ret < 0) {
-        perror("reboot");
+    if (ret < 0) {
+        perror(cmd);
         exit(EXIT_FAILURE);
     }
 
     // Don't return early. Give the reboot command time to take effect
     // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
-    while(1) { pause(); }
+    if (cmd == reboot) {
+        while (1) {
+            pause();
+        }
+    }
 
     fprintf(stderr, "Done\n");
     return 0;
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index e7b8cc2..b27cfad 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -194,6 +194,7 @@
   ScopedMinijail j(minijail_new());
   minijail_change_uid(j.get(), uid);
   minijail_change_gid(j.get(), gid);
+  minijail_keep_supplementary_gids(j.get());
   minijail_enter(j.get());
 
   if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
index e8f5c0b..9227964 100644
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -15,9 +15,9 @@
  */
 #include <algorithm>
 #include <fstream>
+#include <memory>
 
 #include <gtest/gtest.h>
-#include <nativehelper/UniquePtr.h>
 #include <openssl/engine.h>
 
 #include <hardware/keymaster0.h>
@@ -181,7 +181,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -200,7 +200,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8 - 1;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -217,7 +217,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8 + 1;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -272,7 +272,7 @@
 
     keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
     size_t message_len = 1024 * 7;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     // contents of message don't matter.
     uint8_t* signature;
     size_t siglen;
@@ -294,7 +294,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -315,7 +315,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -338,7 +338,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -360,7 +360,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -382,7 +382,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len + 1));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len + 1));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -422,7 +422,7 @@
 
     keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
     size_t message_len = 1024 * 7;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     // contents of message don't matter.
     uint8_t* signature;
     size_t siglen;
@@ -453,7 +453,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_size = 1024 /* key size */ / 8;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_size]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_size]);
     memset(message.get(), 'a', message_size);
     uint8_t* signature;
     size_t siglen;
@@ -491,9 +491,9 @@
 
 static void VerifySignature(const uint8_t* key, size_t key_len, const uint8_t* signature,
                             size_t signature_len, const uint8_t* message, size_t message_len) {
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
     ASSERT_TRUE(pkey.get() != NULL);
-    UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
     ASSERT_TRUE(ctx.get() != NULL);
     ASSERT_EQ(1, EVP_PKEY_verify_init(ctx.get()));
     if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
@@ -518,7 +518,7 @@
     // Sign a message so we can verify it with the exported pubkey.
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,