Merge "SocketListener: use poll() instead of select()"
diff --git a/adb/Android.bp b/adb/Android.bp
index 1f41e4f..553473f 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -96,6 +96,7 @@
     "adb_io.cpp",
     "adb_listeners.cpp",
     "adb_trace.cpp",
+    "adb_unique_fd.cpp",
     "adb_utils.cpp",
     "fdevent.cpp",
     "services.cpp",
@@ -276,6 +277,7 @@
 cc_library_static {
     name: "libadbd",
     defaults: ["adb_defaults"],
+    recovery_available: true,
 
     // libminadbd wants both, for some reason.
     compile_multilib: "both",
@@ -302,6 +304,7 @@
 
     // adbd must be static, as it is copied into the recovery image.
     static_executable: true,
+    recovery_available: true,
 
     srcs: [
         "daemon/main.cpp",
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
new file mode 100644
index 0000000..2079be1
--- /dev/null
+++ b/adb/adb_unique_fd.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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 "adb_unique_fd.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags) {
+    int pipefd[2];
+#if !defined(__APPLE__)
+    if (pipe2(pipefd, flags) != 0) {
+        return false;
+    }
+#else
+    // Darwin doesn't have pipe2. Implement it ourselves.
+    if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
+        errno = EINVAL;
+        return false;
+    }
+
+    if (pipe(pipefd) != 0) {
+        return false;
+    }
+
+    if (flags & O_CLOEXEC) {
+        if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
+            fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+            adb_close(pipefd[0]);
+            adb_close(pipefd[1]);
+            return false;
+        }
+    }
+
+    if (flags & O_NONBLOCK) {
+        if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
+            fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+            adb_close(pipefd[0]);
+            adb_close(pipefd[1]);
+            return false;
+        }
+    }
+#endif
+
+    read->reset(pipefd[0]);
+    write->reset(pipefd[1]);
+    return true;
+}
+#endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 7d2354d..be63262 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <errno.h>
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
@@ -28,40 +29,5 @@
 using unique_fd = android::base::unique_fd_impl<AdbCloser>;
 
 #if !defined(_WIN32)
-inline bool Pipe(unique_fd* read, unique_fd* write, int flags = 0) {
-    int pipefd[2];
-#if !defined(__APPLE__)
-    if (pipe2(pipefd, flags) != 0) {
-        return false;
-    }
-#else
-    // Darwin doesn't have pipe2. Implement it ourselves.
-    if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
-        errno = EINVAL;
-        return false;
-    }
-
-    if (pipe(pipefd) != 0) {
-        return false;
-    }
-
-    if (flags & O_CLOEXEC) {
-        if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
-            fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
-            PLOG(FATAL) << "failed to set FD_CLOEXEC on newly created pipe";
-        }
-    }
-
-    if (flags & O_NONBLOCK) {
-        if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
-            fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
-            PLOG(FATAL) << "failed to set O_NONBLOCK  on newly created pipe";
-        }
-    }
-#endif
-
-    read->reset(pipefd[0]);
-    write->reset(pipefd[1]);
-    return true;
-}
+bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
 #endif
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index eb46903..830b35d 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -29,10 +29,13 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <set>
 #include <string>
 #include <vector>
 
 #include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
 #include <ext4_utils/ext4_utils.h>
 
 #include "adb.h"
@@ -142,7 +145,7 @@
     return true;
 }
 
-static bool remount_partition(int fd, const char* dir, std::vector<std::string>& dedup) {
+static bool remount_partition(int fd, const char* dir) {
     if (!directory_exists(dir)) {
         return true;
     }
@@ -168,32 +171,83 @@
         return false;
     }
     if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
-        if (errno == EROFS && fs_has_shared_blocks(dev.c_str())) {
-            if (!can_unshare_blocks(fd, dev.c_str())) {
-                return false;
-            }
-            // We return true so remount_service() can detect that the only
-            // failure was deduplicated filesystems.
-            dedup.push_back(dev);
-            return true;
-        }
         WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
         return false;
     }
     return true;
 }
 
+static void reboot_for_remount(int fd, bool need_fsck) {
+    std::string reboot_cmd = "reboot";
+    if (need_fsck) {
+        const std::vector<std::string> options = {"--fsck_unshare_blocks"};
+        std::string err;
+        if (!write_bootloader_message(options, &err)) {
+            WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
+            return;
+        }
+
+        WriteFdExactly(fd,
+                       "The device will now reboot to recovery and attempt "
+                       "un-deduplication.\n");
+        reboot_cmd = "reboot,recovery";
+    }
+
+    sync();
+    android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
+}
+
 void remount_service(int fd, void* cookie) {
+    unique_fd close_fd(fd);
+
+    const char* cmd = reinterpret_cast<const char*>(cookie);
+    bool user_requested_reboot = cmd && !strcmp(cmd, "-R");
+
     if (getuid() != 0) {
         WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
-        adb_close(fd);
         return;
     }
 
     bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
     bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
 
-    if (system_verified || vendor_verified) {
+    std::vector<std::string> partitions = {"/odm", "/oem", "/product", "/vendor"};
+    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
+        partitions.push_back("/");
+    } else {
+        partitions.push_back("/system");
+    }
+
+    // Find partitions that are deduplicated, and can be un-deduplicated.
+    std::set<std::string> dedup;
+    for (const auto& partition : partitions) {
+        std::string dev = find_mount(partition.c_str(), partition == "/");
+        if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) {
+            continue;
+        }
+        if (can_unshare_blocks(fd, dev.c_str())) {
+            dedup.emplace(partition);
+        }
+    }
+
+    bool verity_enabled = (system_verified || vendor_verified);
+
+    // Reboot now if the user requested it (and an operation needs a reboot).
+    if (user_requested_reboot) {
+        if (!dedup.empty() || verity_enabled) {
+            if (verity_enabled) {
+                set_verity_enabled_state_service(fd, nullptr);
+            }
+            reboot_for_remount(fd, !dedup.empty());
+            return;
+        }
+        WriteFdExactly(fd, "No reboot needed, skipping -R.\n");
+    }
+
+    // If we need to disable-verity, but we also need to perform a recovery
+    // fsck for deduplicated partitions, hold off on warning about verity. We
+    // can handle both verity and the recovery fsck in the same reboot cycle.
+    if (verity_enabled && dedup.empty()) {
         // Allow remount but warn of likely bad effects
         bool both = system_verified && vendor_verified;
         WriteFdFmt(fd,
@@ -206,32 +260,40 @@
                        "Use \"adb disable-verity\" to disable verity.\n"
                        "If you do not, remount may succeed, however, you will still "
                        "not be able to write to these volumes.\n");
+        WriteFdExactly(fd,
+                       "Alternately, use \"adb remount -R\" to disable verity "
+                       "and automatically reboot.\n");
     }
 
     bool success = true;
-    std::vector<std::string> dedup;
-    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
-        success &= remount_partition(fd, "/", dedup);
-    } else {
-        success &= remount_partition(fd, "/system", dedup);
+    for (const auto& partition : partitions) {
+        // Don't try to remount partitions that need an fsck in recovery.
+        if (dedup.count(partition)) {
+            continue;
+        }
+        success &= remount_partition(fd, partition.c_str());
     }
-    success &= remount_partition(fd, "/odm", dedup);
-    success &= remount_partition(fd, "/oem", dedup);
-    success &= remount_partition(fd, "/product", dedup);
-    success &= remount_partition(fd, "/vendor", dedup);
 
-    if (!success) {
-        WriteFdExactly(fd, "remount failed\n");
-    } else if (dedup.empty()) {
-        WriteFdExactly(fd, "remount succeeded\n");
-    } else {
+    if (!dedup.empty()) {
         WriteFdExactly(fd,
-                       "The following partitions are deduplicated and could "
-                       "not be remounted:\n");
+                       "The following partitions are deduplicated and cannot "
+                       "yet be remounted:\n");
         for (const std::string& name : dedup) {
             WriteFdFmt(fd, "  %s\n", name.c_str());
         }
+
+        WriteFdExactly(fd,
+                       "To reboot and un-deduplicate the listed partitions, "
+                       "please retry with adb remount -R.\n");
+        if (system_verified || vendor_verified) {
+            WriteFdExactly(fd, "Note: verity will be automatically disabled after reboot.\n");
+        }
+        return;
     }
 
-    adb_close(fd);
+    if (!success) {
+        WriteFdExactly(fd, "remount failed\n");
+    } else {
+        WriteFdExactly(fd, "remount succeeded\n");
+    }
 }
diff --git a/adb/services.cpp b/adb/services.cpp
index 0b0c161..a757d90 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -291,7 +291,9 @@
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread("sync", file_sync_service, nullptr);
     } else if(!strncmp(name, "remount:", 8)) {
-        ret = create_service_thread("remount", remount_service, nullptr);
+        const char* options = name + strlen("remount:");
+        void* cookie = const_cast<void*>(reinterpret_cast<const void*>(options));
+        ret = create_service_thread("remount", remount_service, cookie);
     } else if(!strncmp(name, "reboot:", 7)) {
         void* arg = strdup(name + 7);
         if (arg == NULL) return -1;
diff --git a/base/Android.bp b/base/Android.bp
index 47b29c6..3d80d97 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -48,6 +48,7 @@
         "file.cpp",
         "logging.cpp",
         "parsenetaddress.cpp",
+        "properties.cpp",
         "quick_exit.cpp",
         "stringprintf.cpp",
         "strings.cpp",
@@ -58,9 +59,6 @@
     shared_libs: ["liblog"],
     target: {
         android: {
-            srcs: [
-                "properties.cpp",
-            ],
             sanitize: {
                 misc_undefined: ["integer"],
             },
@@ -95,6 +93,7 @@
     name: "libbase",
     defaults: ["libbase_defaults"],
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     vndk: {
         enabled: true,
@@ -129,6 +128,7 @@
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
+        "properties_test.cpp",
         "quick_exit_test.cpp",
         "scopeguard_test.cpp",
         "stringprintf_test.cpp",
@@ -138,7 +138,6 @@
     ],
     target: {
         android: {
-            srcs: ["properties_test.cpp"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 041586c..3a05143 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -19,10 +19,6 @@
 
 #include <sys/cdefs.h>
 
-#if !defined(__BIONIC__)
-#error Only bionic supports system properties.
-#endif
-
 #include <chrono>
 #include <limits>
 #include <string>
@@ -62,14 +58,18 @@
 // Waits for the system property `key` to have the value `expected_value`.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
+#if defined(__BIONIC__)
 bool WaitForProperty(const std::string& key, const std::string& expected_value,
                      std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
+#endif
 
 // Waits for the system property `key` to be created.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
+#if defined(__BIONIC__)
 bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
                                                          std::chrono::milliseconds::max());
+#endif
 
 } // namespace base
 } // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 6cf43f9..d5a5918 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,16 +14,18 @@
  * limitations under the License.
  */
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-
 #include "android-base/properties.h"
 
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/system_properties.h>
 #include <sys/_system_properties.h>
+#endif
 
 #include <algorithm>
 #include <chrono>
 #include <limits>
+#include <map>
 #include <string>
 
 #include <android-base/parseint.h>
@@ -31,24 +33,6 @@
 namespace android {
 namespace base {
 
-std::string GetProperty(const std::string& key, const std::string& default_value) {
-  const prop_info* pi = __system_property_find(key.c_str());
-  if (pi == nullptr) return default_value;
-
-  std::string property_value;
-  __system_property_read_callback(pi,
-                                  [](void* cookie, const char*, const char* value, unsigned) {
-                                    auto property_value = reinterpret_cast<std::string*>(cookie);
-                                    *property_value = value;
-                                  },
-                                  &property_value);
-
-  // If the property exists but is empty, also return the default value.
-  // Since we can't remove system properties, "empty" is traditionally
-  // the same as "missing" (this was true for cutils' property_get).
-  return property_value.empty() ? default_value : property_value;
-}
-
 bool GetBoolProperty(const std::string& key, bool default_value) {
   std::string value = GetProperty(key, "");
   if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
@@ -85,10 +69,43 @@
 template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
 template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
 
+#if !defined(__BIONIC__)
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+static int __system_property_set(const char* key, const char* value) {
+  g_properties[key] = value;
+  return 0;
+}
+#endif
+
+std::string GetProperty(const std::string& key, const std::string& default_value) {
+  std::string property_value;
+#if defined(__BIONIC__)
+  const prop_info* pi = __system_property_find(key.c_str());
+  if (pi == nullptr) return default_value;
+
+  __system_property_read_callback(pi,
+                                  [](void* cookie, const char*, const char* value, unsigned) {
+                                    auto property_value = reinterpret_cast<std::string*>(cookie);
+                                    *property_value = value;
+                                  },
+                                  &property_value);
+#else
+  auto it = g_properties.find(key);
+  if (it == g_properties.end()) return default_value;
+  property_value = it->second;
+#endif
+  // If the property exists but is empty, also return the default value.
+  // Since we can't remove system properties, "empty" is traditionally
+  // the same as "missing" (this was true for cutils' property_get).
+  return property_value.empty() ? default_value : property_value;
+}
+
 bool SetProperty(const std::string& key, const std::string& value) {
   return (__system_property_set(key.c_str(), value.c_str()) == 0);
 }
 
+#if defined(__BIONIC__)
+
 struct WaitForPropertyData {
   bool done;
   const std::string* expected_value;
@@ -175,5 +192,7 @@
   return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
 }
 
+#endif
+
 }  // namespace base
 }  // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index de5f3dc..e7d4880 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -23,7 +23,9 @@
 #include <string>
 #include <thread>
 
-using namespace std::chrono_literals;
+#if !defined(_WIN32)
+using namespace std::literals;
+#endif
 
 TEST(properties, smoke) {
   android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -126,6 +128,7 @@
 TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
 
 TEST(properties, WaitForProperty) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
@@ -138,9 +141,13 @@
   flag = true;
   ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_timeout) {
+#if defined(__BIONIC__)
   auto t0 = std::chrono::steady_clock::now();
   ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
                                               200ms));
@@ -149,9 +156,13 @@
   ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
   // Upper bounds on timing are inherently flaky, but let's try...
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_MaxTimeout) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -165,9 +176,13 @@
   // Test that this does not immediately return false due to overflow issues with the timeout.
   ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_NegativeTimeout) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -181,9 +196,13 @@
   // Assert that this immediately returns with a negative timeout
   ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForPropertyCreation) {
+#if defined(__BIONIC__)
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
     android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
@@ -192,9 +211,13 @@
   ASSERT_TRUE(android::base::WaitForPropertyCreation(
           "debug.libbase.WaitForPropertyCreation_test", 1s));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForPropertyCreation_timeout) {
+#if defined(__BIONIC__)
   auto t0 = std::chrono::steady_clock::now();
   ASSERT_FALSE(android::base::WaitForPropertyCreation(
           "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
@@ -203,4 +226,7 @@
   ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
   // Upper bounds on timing are inherently flaky, but let's try...
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7c28b28..0b13662 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -17,6 +17,7 @@
 cc_library_headers {
     name: "libdebuggerd_common_headers",
     export_include_dirs: ["common/include"],
+    recovery_available: true,
 }
 
 cc_library_shared {
@@ -67,6 +68,7 @@
 cc_library_static {
     name: "libdebuggerd_handler_core",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: ["handler/debuggerd_handler.cpp"],
 
     header_libs: [
@@ -88,6 +90,7 @@
 cc_library_static {
     name: "libdebuggerd_handler",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: ["handler/debuggerd_fallback_nop.cpp"],
 
     whole_static_libs: [
@@ -143,6 +146,7 @@
 cc_library_static {
     name: "libdebuggerd",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
 
     srcs: [
         "libdebuggerd/backtrace.cpp",
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 4b32b9d..f31337d 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -197,6 +197,7 @@
     fprintf(stderr, "  LOG-FATAL             call libbase LOG(FATAL)\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
+    fprintf(stderr, "  SIGILL                cause a SIGILL\n");
     fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
     fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
     fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
@@ -268,6 +269,16 @@
     } else if (!strcasecmp(arg, "SIGFPE")) {
         raise(SIGFPE);
         return EXIT_SUCCESS;
+    } else if (!strcasecmp(arg, "SIGILL")) {
+#if defined(__aarch64__)
+      __asm__ volatile(".word 0\n");
+#elif defined(__arm__)
+      __asm__ volatile(".word 0xe7f0def0\n");
+#elif defined(__i386__) || defined(__x86_64__)
+      __asm__ volatile("ud2\n");
+#else
+#error
+#endif
     } else if (!strcasecmp(arg, "SIGTRAP")) {
         raise(SIGTRAP);
         return EXIT_SUCCESS;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e11be1e..433bb46 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -102,10 +102,17 @@
   if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
 }
 
-static void dump_signal_info(log_t* log, const ThreadInfo& thread_info) {
-  char addr_desc[32]; // ", fault addr 0x1234"
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, Memory* process_memory) {
+  char addr_desc[64];  // ", fault addr 0x1234"
   if (signal_has_si_addr(thread_info.siginfo)) {
-    snprintf(addr_desc, sizeof(addr_desc), "%p", thread_info.siginfo->si_addr);
+    void* addr = thread_info.siginfo->si_addr;
+    if (thread_info.siginfo->si_signo == SIGILL) {
+      uint32_t instruction = {};
+      process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
+      snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
+    } else {
+      snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
+    }
   } else {
     snprintf(addr_desc, sizeof(addr_desc), "--------");
   }
@@ -418,7 +425,7 @@
   dump_thread_info(log, thread_info);
 
   if (thread_info.siginfo) {
-    dump_signal_info(log, thread_info);
+    dump_signal_info(log, thread_info, process_memory);
   }
 
   if (primary_thread) {
diff --git a/demangle/Android.bp b/demangle/Android.bp
index cf6abfd..fd79cf8 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -36,6 +36,7 @@
     name: "libdemangle",
     defaults: ["libdemangle_defaults"],
     vendor_available: true,
+    recovery_available: true,
 
     srcs: [
         "Demangler.cpp",
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index a7ecf37..6bee28c 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -2,6 +2,7 @@
     name: "libdiagnose_usb",
     cflags: ["-Wall", "-Wextra", "-Werror"],
     host_supported: true,
+    recovery_available: true,
     target: {
         windows: {
             enabled: true,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6493262..5aa87d9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -316,7 +316,8 @@
 static int show_help() {
     // clang-format off
     fprintf(stdout,
-/*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+//                    1         2         3         4         5         6         7         8
+//           12345678901234567890123456789012345678901234567890123456789012345678901234567890
             "usage: fastboot [OPTION...] COMMAND...\n"
             "\n"
             "flashing:\n"
@@ -324,8 +325,8 @@
             " flashall                   Flash all partitions from $ANDROID_PRODUCT_OUT.\n"
             "                            On A/B devices, flashed slot is set as active.\n"
             "                            Secondary images may be flashed to inactive slot.\n"
-            " flash PARTITION [FILENAME]\n"
-            "                            Flash given partition only.\n"
+            " flash PARTITION [FILENAME] Flash given partition, using the image from\n"
+            "                            $ANDROID_PRODUCT_OUT if no filename is given.\n"
             "\n"
             "basics:\n"
             " devices [-l]               List devices in bootloader (-l: with device paths).\n"
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 05dba15..bc3b04b 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -33,6 +33,7 @@
 cc_library_static {
     name: "libfs_mgr",
     defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
     export_include_dirs: ["include"],
     include_dirs: ["system/vold"],
     srcs: [
@@ -79,6 +80,7 @@
 cc_library_static {
     name: "libfstab",
     vendor_available: true,
+    recovery_available: true,
     defaults: ["fs_mgr_defaults"],
     srcs: [
         "fs_mgr_fstab.cpp",
diff --git a/init/Android.bp b/init/Android.bp
index 63f3fca..25877c0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -170,6 +170,7 @@
     srcs: [
         "devices_test.cpp",
         "init_test.cpp",
+        "keychords_test.cpp",
         "persistent_properties_test.cpp",
         "property_service_test.cpp",
         "property_type_test.cpp",
@@ -232,8 +233,11 @@
         "action_parser.cpp",
         "capabilities.cpp",
         "descriptors.cpp",
+        "epoll.cpp",
+        "keychords.cpp",
         "import_parser.cpp",
-        "host_init_parser.cpp",
+        "host_import_parser.cpp",
+        "host_init_verifier.cpp",
         "host_init_stubs.cpp",
         "parser.cpp",
         "rlimit_parser.cpp",
@@ -246,7 +250,10 @@
     proto: {
         type: "lite",
     },
-    generated_headers: ["generated_stub_builtin_function_map"],
+    generated_headers: [
+        "generated_stub_builtin_function_map",
+        "generated_android_ids"
+    ],
     target: {
         android: {
             enabled: false,
diff --git a/init/action.cpp b/init/action.cpp
index f782b51..11335ca 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,16 +18,11 @@
 
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #include "util.h"
 
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
 using android::base::Join;
 
 namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a2c9671..8a4b518 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,16 +16,11 @@
 
 #include "action_parser.h"
 
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #include "stable_properties.h"
 
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
 using android::base::GetBoolProperty;
 using android::base::StartsWith;
 
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
new file mode 100644
index 0000000..faf6fc1
--- /dev/null
+++ b/init/host_import_parser.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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 "host_import_parser.h"
+
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
+                                               const std::string& filename, int line) {
+    if (args.size() != 2) {
+        return Error() << "single argument needed for import\n";
+    }
+
+    auto import_path = args[1];
+
+    if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
+        StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
+        import_path = out_dir_ + "/" + import_path;
+    } else {
+        import_path = out_dir_ + "/root/" + import_path;
+    }
+
+    return ImportParser::ParseSection({"import", import_path}, filename, line);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
new file mode 100644
index 0000000..e2980b2
--- /dev/null
+++ b/init/host_import_parser.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "import_parser.h"
+#include "parser.h"
+
+namespace android {
+namespace init {
+
+class HostImportParser : public ImportParser {
+  public:
+    HostImportParser(const std::string& out_dir, Parser* parser)
+        : ImportParser(parser), out_dir_(out_dir) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+
+  private:
+    std::string out_dir_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
deleted file mode 100644
index df497ea..0000000
--- a/init/host_init_parser.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Copyright (C) 2018 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 <pwd.h>
-
-#include <android-base/logging.h>
-
-#include "action.h"
-#include "action_manager.h"
-#include "action_parser.h"
-#include "parser.h"
-#include "result.h"
-#include "service.h"
-
-// The host passwd file won't have the Android entries, so we fake success here.
-passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
-    char dummy_buf[] = "dummy";
-    static passwd dummy_passwd = {
-        .pw_name = dummy_buf,
-        .pw_dir = dummy_buf,
-        .pw_shell = dummy_buf,
-        .pw_uid = 123,
-        .pw_gid = 123,
-    };
-    return &dummy_passwd;
-}
-
-namespace android {
-namespace init {
-
-static Result<Success> do_stub(const BuiltinArguments& args) {
-    return Success();
-}
-
-#include "generated_stub_builtin_function_map.h"
-
-int main(int argc, char** argv) {
-    android::base::InitLogging(argv, &android::base::StdioLogger);
-    if (argc != 2) {
-        LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
-        return -1;
-    }
-    const BuiltinFunctionMap function_map;
-    Action::set_function_map(&function_map);
-    ActionManager& am = ActionManager::GetInstance();
-    ServiceList& sl = ServiceList::GetInstance();
-    Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
-
-    size_t num_errors = 0;
-    if (!parser.ParseConfig(argv[1], &num_errors)) {
-        LOG(ERROR) << "Failed to find script";
-        return -1;
-    }
-    if (num_errors > 0) {
-        LOG(ERROR) << "Parse failed with " << num_errors << " errors";
-        return -1;
-    }
-    LOG(INFO) << "Parse success!";
-    return 0;
-}
-
-}  // namespace init
-}  // namespace android
-
-int main(int argc, char** argv) {
-    android::init::main(argc, argv);
-}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 4451ac8..2352fc7 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -16,33 +16,25 @@
 
 #include "host_init_stubs.h"
 
+#include <android-base/properties.h>
+
 // unistd.h
 int setgroups(size_t __size, const gid_t* __list) {
     return 0;
 }
 
 namespace android {
-namespace base {
-
-std::string GetProperty(const std::string&, const std::string& default_value) {
-    return default_value;
-}
-
-bool GetBoolProperty(const std::string&, bool default_value) {
-    return default_value;
-}
-
-}  // namespace base
-}  // namespace android
-
-namespace android {
 namespace init {
 
 // init.h
 std::string default_console = "/dev/console";
 
 // property_service.h
-uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t SetProperty(const std::string& key, const std::string& value) {
+    android::base::SetProperty(key, value);
+    return 0;
+}
+uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
 uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
                            std::string*) {
     return 0;
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index bb241af..f0e1f07 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -29,16 +29,6 @@
 // unistd.h
 int setgroups(size_t __size, const gid_t* __list);
 
-// android-base/properties.h
-namespace android {
-namespace base {
-
-std::string GetProperty(const std::string& key, const std::string& default_value);
-bool GetBoolProperty(const std::string& key, bool default_value);
-
-}  // namespace base
-}  // namespace android
-
 namespace android {
 namespace init {
 
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
new file mode 100644
index 0000000..d6884af
--- /dev/null
+++ b/init/host_init_verifier.cpp
@@ -0,0 +1,162 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "host_import_parser.h"
+#include "host_init_stubs.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+#define EXCLUDE_FS_CONFIG_STRUCTURES
+#include "generated_android_ids.h"
+
+using namespace std::literals;
+
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
+
+static std::string out_dir;
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+    std::string passwd;
+    if (!ReadFileToString(out_dir + "/vendor/etc/passwd", &passwd)) {
+        return {};
+    }
+
+    std::vector<std::pair<std::string, int>> result;
+    auto passwd_lines = Split(passwd, "\n");
+    for (const auto& line : passwd_lines) {
+        auto split_line = Split(line, ":");
+        if (split_line.size() < 3) {
+            continue;
+        }
+        int uid = 0;
+        if (!ParseInt(split_line[2], &uid)) {
+            continue;
+        }
+        result.emplace_back(split_line[0], uid);
+    }
+    return result;
+}
+
+passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
+    // This isn't thread safe, but that's okay for our purposes.
+    static char static_name[32] = "";
+    static char static_dir[32] = "/";
+    static char static_shell[32] = "/system/bin/sh";
+    static passwd static_passwd = {
+        .pw_name = static_name,
+        .pw_dir = static_dir,
+        .pw_shell = static_shell,
+        .pw_uid = 0,
+        .pw_gid = 0,
+    };
+
+    for (size_t n = 0; n < android_id_count; ++n) {
+        if (!strcmp(android_ids[n].name, login)) {
+            snprintf(static_name, sizeof(static_name), "%s", android_ids[n].name);
+            static_passwd.pw_uid = android_ids[n].aid;
+            static_passwd.pw_gid = android_ids[n].aid;
+            return &static_passwd;
+        }
+    }
+
+    static const auto vendor_passwd = GetVendorPasswd();
+
+    for (const auto& [name, uid] : vendor_passwd) {
+        if (name == login) {
+            snprintf(static_name, sizeof(static_name), "%s", name.c_str());
+            static_passwd.pw_uid = uid;
+            static_passwd.pw_gid = uid;
+            return &static_passwd;
+        }
+    }
+
+    errno = ENOENT;
+    return nullptr;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+    return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StdioLogger);
+    android::base::SetMinimumLogSeverity(android::base::ERROR);
+    if (argc != 3) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
+        return -1;
+    }
+
+    out_dir = argv[1];
+
+    auto properties = Split(argv[2], ",");
+    for (const auto& property : properties) {
+        auto split_property = Split(property, "=");
+        if (split_property.size() != 2) {
+            continue;
+        }
+        property_set(split_property[0], split_property[1]);
+    }
+
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceList& sl = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+    parser.AddSectionParser("import", std::make_unique<HostImportParser>(out_dir, &parser));
+
+    if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
+        LOG(ERROR) << "Failed to find root init.rc script";
+        return -1;
+    }
+    if (parser.parse_error_count() > 0) {
+        LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
+        return -1;
+    }
+    return 0;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::init::main(argc, argv);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index e335fd1..fb3185e 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -41,14 +41,15 @@
     return Success();
 }
 
+Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+    return Error() << "Unexpected line found after import statement";
+}
+
 void ImportParser::EndFile() {
     auto current_imports = std::move(imports_);
     imports_.clear();
     for (const auto& [import, line_num] : current_imports) {
-        if (!parser_->ParseConfig(import)) {
-            PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
-                        << "'";
-        }
+        parser_->ParseConfig(import);
     }
 }
 
diff --git a/init/import_parser.h b/init/import_parser.h
index 5a2f894..7bc72e6 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -30,6 +30,7 @@
     ImportParser(Parser* parser) : parser_(parser) {}
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
     void EndFile() override;
 
   private:
diff --git a/init/init.cpp b/init/init.cpp
index fd9a90c..82648d9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -553,6 +553,33 @@
     }
 }
 
+void HandleKeychord(const std::vector<int>& keycodes) {
+    // Only handle keychords if adb is enabled.
+    std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
+    if (adb_enabled != "running") {
+        LOG(WARNING) << "Not starting service for keychord " << android::base::Join(keycodes, ' ')
+                     << " because ADB is disabled";
+        return;
+    }
+
+    auto found = false;
+    for (const auto& service : ServiceList::GetInstance()) {
+        auto svc = service.get();
+        if (svc->keycodes() == keycodes) {
+            found = true;
+            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord "
+                      << android::base::Join(keycodes, ' ');
+            if (auto result = svc->Start(); !result) {
+                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
+                           << android::base::Join(keycodes, ' ') << ": " << result.error();
+            }
+        }
+    }
+    if (!found) {
+        LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
+    }
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -730,9 +757,13 @@
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+    Keychords keychords;
     am.QueueBuiltinAction(
-        [&epoll](const BuiltinArguments& args) -> Result<Success> {
-            KeychordInit(&epoll);
+        [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
+            for (const auto& svc : ServiceList::GetInstance()) {
+                keychords.Register(svc->keycodes());
+            }
+            keychords.Start(&epoll, HandleKeychord);
             return Success();
         },
         "KeychordInit");
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 34de640..db60ce1 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -123,7 +123,7 @@
     bool enabled = false;
     import_kernel_cmdline(
         false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
-            if (key == "androidboot.lrap" && value == "1") {
+            if (key == "androidboot.logical_partitions" && value == "1") {
                 enabled = true;
             }
         });
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 418cdeb..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -33,150 +33,117 @@
 #include <vector>
 
 #include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "init.h"
-#include "service.h"
 
 namespace android {
 namespace init {
 
-namespace {
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
 
-int keychords_count;
-Epoll* epoll;
-
-struct KeychordEntry {
-    const std::vector<int> keycodes;
-    bool notified;
-    int id;
-
-    KeychordEntry(const std::vector<int>& keycodes, int id)
-        : keycodes(keycodes), notified(false), id(id) {}
-};
-
-std::vector<KeychordEntry> keychord_entries;
-
-// Bit management
-class KeychordMask {
-  private:
-    typedef unsigned int mask_t;
-    std::vector<mask_t> bits;
-    static constexpr size_t bits_per_byte = 8;
-
-  public:
-    explicit KeychordMask(size_t bit = 0) : bits((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
-
-    void SetBit(size_t bit, bool value = true) {
-        auto idx = bit / (bits_per_byte * sizeof(mask_t));
-        if (idx >= bits.size()) return;
-        if (value) {
-            bits[idx] |= mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t)));
-        } else {
-            bits[idx] &= ~(mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
-        }
+Keychords::~Keychords() noexcept {
+    if (inotify_fd_ >= 0) {
+        epoll_->UnregisterHandler(inotify_fd_);
+        ::close(inotify_fd_);
     }
+    while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
+}
 
-    bool GetBit(size_t bit) const {
-        auto idx = bit / (bits_per_byte * sizeof(mask_t));
-        return bits[idx] & (mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
-    }
+Keychords::Mask::Mask(size_t bit) : bits_((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
 
-    size_t bytesize() const { return bits.size() * sizeof(mask_t); }
-    void* data() { return bits.data(); }
-    size_t size() const { return bits.size() * sizeof(mask_t) * bits_per_byte; }
-    void resize(size_t bit) {
-        auto idx = bit / (bits_per_byte * sizeof(mask_t));
-        if (idx >= bits.size()) {
-            bits.resize(idx + 1, 0);
-        }
-    }
-
-    operator bool() const {
-        for (size_t i = 0; i < bits.size(); ++i) {
-            if (bits[i]) return true;
-        }
-        return false;
-    }
-
-    KeychordMask operator&(const KeychordMask& rval) const {
-        auto len = std::min(bits.size(), rval.bits.size());
-        KeychordMask ret;
-        ret.bits.resize(len);
-        for (size_t i = 0; i < len; ++i) {
-            ret.bits[i] = bits[i] & rval.bits[i];
-        }
-        return ret;
-    }
-
-    void operator|=(const KeychordMask& rval) {
-        size_t len = rval.bits.size();
-        bits.resize(len);
-        for (size_t i = 0; i < len; ++i) {
-            bits[i] |= rval.bits[i];
-        }
-    }
-};
-
-KeychordMask keychord_current;
-
-constexpr char kDevicePath[] = "/dev/input";
-
-std::map<std::string, int> keychord_registration;
-
-void HandleKeychord(int id) {
-    // Only handle keychords if adb is enabled.
-    std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
-    if (adb_enabled == "running") {
-        Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
-        if (svc) {
-            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
-            if (auto result = svc->Start(); !result) {
-                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
-                           << ": " << result.error();
-            }
-        } else {
-            LOG(ERROR) << "Service for keychord " << id << " not found";
-        }
+void Keychords::Mask::SetBit(size_t bit, bool value) {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    if (idx >= bits_.size()) return;
+    if (value) {
+        bits_[idx] |= mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t)));
     } else {
-        LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+        bits_[idx] &= ~(mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
     }
 }
 
-void KeychordLambdaCheck() {
-    for (auto& e : keychord_entries) {
-        bool found = true;
-        for (auto& code : e.keycodes) {
-            if (!keychord_current.GetBit(code)) {
-                e.notified = false;
+bool Keychords::Mask::GetBit(size_t bit) const {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    return bits_[idx] & (mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
+}
+
+size_t Keychords::Mask::bytesize() const {
+    return bits_.size() * sizeof(mask_t);
+}
+
+void* Keychords::Mask::data() {
+    return bits_.data();
+}
+
+size_t Keychords::Mask::size() const {
+    return bits_.size() * sizeof(mask_t) * kBitsPerByte;
+}
+
+void Keychords::Mask::resize(size_t bit) {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    if (idx >= bits_.size()) {
+        bits_.resize(idx + 1, 0);
+    }
+}
+
+Keychords::Mask::operator bool() const {
+    for (size_t i = 0; i < bits_.size(); ++i) {
+        if (bits_[i]) return true;
+    }
+    return false;
+}
+
+Keychords::Mask Keychords::Mask::operator&(const Keychords::Mask& rval) const {
+    auto len = std::min(bits_.size(), rval.bits_.size());
+    Keychords::Mask ret;
+    ret.bits_.resize(len);
+    for (size_t i = 0; i < len; ++i) {
+        ret.bits_[i] = bits_[i] & rval.bits_[i];
+    }
+    return ret;
+}
+
+void Keychords::Mask::operator|=(const Keychords::Mask& rval) {
+    auto len = rval.bits_.size();
+    bits_.resize(len);
+    for (size_t i = 0; i < len; ++i) {
+        bits_[i] |= rval.bits_[i];
+    }
+}
+
+Keychords::Entry::Entry() : notified(false) {}
+
+void Keychords::LambdaCheck() {
+    for (auto& [keycodes, entry] : entries_) {
+        auto found = true;
+        for (auto& code : keycodes) {
+            if (!current_.GetBit(code)) {
+                entry.notified = false;
                 found = false;
                 break;
             }
         }
         if (!found) continue;
-        if (e.notified) continue;
-        e.notified = true;
-        HandleKeychord(e.id);
+        if (entry.notified) continue;
+        entry.notified = true;
+        handler_(keycodes);
     }
 }
 
-void KeychordLambdaHandler(int fd) {
+void Keychords::LambdaHandler(int fd) {
     input_event event;
     auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
     if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
-    keychord_current.SetBit(event.code, event.value);
-    KeychordLambdaCheck();
+    current_.SetBit(event.code, event.value);
+    LambdaCheck();
 }
 
-bool KeychordGeteventEnable(int fd) {
-    static bool EviocsmaskSupported = true;
-
+bool Keychords::GeteventEnable(int fd) {
     // Make sure it is an event channel, should pass this ioctl call
     int version;
     if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
 
+#ifdef EVIOCSMASK
+    static auto EviocsmaskSupported = true;
     if (EviocsmaskSupported) {
-        KeychordMask mask(EV_KEY);
+        Keychords::Mask mask(EV_KEY);
         mask.SetBit(EV_KEY);
         input_mask msg = {};
         msg.type = EV_SYN;
@@ -187,21 +154,23 @@
             EviocsmaskSupported = false;
         }
     }
+#endif
 
-    KeychordMask mask;
-    for (auto& e : keychord_entries) {
-        for (auto& code : e.keycodes) {
+    Keychords::Mask mask;
+    for (auto& [keycodes, entry] : entries_) {
+        for (auto& code : keycodes) {
             mask.resize(code);
             mask.SetBit(code);
         }
     }
 
-    keychord_current.resize(mask.size());
-    KeychordMask available(mask.size());
+    current_.resize(mask.size());
+    Keychords::Mask available(mask.size());
     auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
     if (res == -1) return false;
     if (!(available & mask)) return false;
 
+#ifdef EVIOCSMASK
     if (EviocsmaskSupported) {
         input_mask msg = {};
         msg.type = EV_KEY;
@@ -209,46 +178,45 @@
         msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
         ::ioctl(fd, EVIOCSMASK, &msg);
     }
+#endif
 
-    KeychordMask set(mask.size());
+    Keychords::Mask set(mask.size());
     res = ::ioctl(fd, EVIOCGKEY(res), set.data());
     if (res > 0) {
-        keychord_current |= mask & available & set;
-        KeychordLambdaCheck();
+        current_ |= mask & available & set;
+        LambdaCheck();
     }
-    epoll->RegisterHandler(fd, [fd]() { KeychordLambdaHandler(fd); });
+    epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
     return true;
 }
 
-void GeteventOpenDevice(const std::string& device) {
-    if (keychord_registration.count(device)) return;
+void Keychords::GeteventOpenDevice(const std::string& device) {
+    if (registration_.count(device)) return;
     auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
     if (fd == -1) {
         PLOG(ERROR) << "Can not open " << device;
         return;
     }
-    if (!KeychordGeteventEnable(fd)) {
+    if (!GeteventEnable(fd)) {
         ::close(fd);
     } else {
-        keychord_registration.emplace(device, fd);
+        registration_.emplace(device, fd);
     }
 }
 
-void GeteventCloseDevice(const std::string& device) {
-    auto it = keychord_registration.find(device);
-    if (it == keychord_registration.end()) return;
+void Keychords::GeteventCloseDevice(const std::string& device) {
+    auto it = registration_.find(device);
+    if (it == registration_.end()) return;
     auto fd = (*it).second;
-    epoll->UnregisterHandler(fd);
-    keychord_registration.erase(it);
+    epoll_->UnregisterHandler(fd);
+    registration_.erase(it);
     ::close(fd);
 }
 
-int inotify_fd = -1;
+void Keychords::InotifyHandler() {
+    unsigned char buf[512];  // History shows 32-64 bytes typical
 
-void InotifyHandler() {
-    unsigned char buf[512];
-
-    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd, buf, sizeof(buf)));
+    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd_, buf, sizeof(buf)));
     if (res < 0) {
         PLOG(WARNING) << "could not get event";
         return;
@@ -274,14 +242,15 @@
     }
 }
 
-void GeteventOpenDevice() {
-    inotify_fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
-    if (inotify_fd < 0) {
+void Keychords::GeteventOpenDevice() {
+    inotify_fd_ = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+    if (inotify_fd_ < 0) {
         PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
-    } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) < 0) {
+    } else if (::inotify_add_watch(inotify_fd_, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) <
+               0) {
         PLOG(WARNING) << "Could not add watch for " << kDevicePath;
-        ::close(inotify_fd);
-        inotify_fd = -1;
+        ::close(inotify_fd_);
+        inotify_fd_ = -1;
     }
 
     std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
@@ -296,27 +265,20 @@
         }
     }
 
-    if (inotify_fd >= 0) epoll->RegisterHandler(inotify_fd, InotifyHandler);
+    if (inotify_fd_ >= 0) {
+        epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+    }
 }
 
-void AddServiceKeycodes(Service* svc) {
-    if (svc->keycodes().empty()) return;
-    for (auto& code : svc->keycodes()) {
-        if ((code < 0) || (code >= KEY_MAX)) return;
-    }
-    ++keychords_count;
-    keychord_entries.emplace_back(KeychordEntry(svc->keycodes(), keychords_count));
-    svc->set_keychord_id(keychords_count);
+void Keychords::Register(const std::vector<int>& keycodes) {
+    if (keycodes.empty()) return;
+    entries_.try_emplace(keycodes, Entry());
 }
 
-}  // namespace
-
-void KeychordInit(Epoll* init_epoll) {
-    epoll = init_epoll;
-    for (const auto& service : ServiceList::GetInstance()) {
-        AddServiceKeycodes(service.get());
-    }
-    if (keychords_count) GeteventOpenDevice();
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
+    epoll_ = epoll;
+    handler_ = handler;
+    if (entries_.size()) GeteventOpenDevice();
 }
 
 }  // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index f3aecbb..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,12 +17,81 @@
 #ifndef _INIT_KEYCHORDS_H_
 #define _INIT_KEYCHORDS_H_
 
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
 #include "epoll.h"
 
 namespace android {
 namespace init {
 
-void KeychordInit(Epoll* init_epoll);
+class Keychords {
+  public:
+    Keychords();
+    Keychords(const Keychords&) = delete;
+    Keychords(Keychords&&) = delete;
+    Keychords& operator=(const Keychords&) = delete;
+    Keychords& operator=(Keychords&&) = delete;
+    ~Keychords() noexcept;
+
+    void Register(const std::vector<int>& keycodes);
+    void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
+
+  private:
+    // Bit management
+    class Mask {
+      public:
+        explicit Mask(size_t bit = 0);
+
+        void SetBit(size_t bit, bool value = true);
+        bool GetBit(size_t bit) const;
+
+        size_t bytesize() const;
+        void* data();
+        size_t size() const;
+        void resize(size_t bit);
+
+        operator bool() const;
+        Mask operator&(const Mask& rval) const;
+        void operator|=(const Mask& rval);
+
+      private:
+        typedef unsigned int mask_t;
+        static constexpr size_t kBitsPerByte = 8;
+
+        std::vector<mask_t> bits_;
+    };
+
+    struct Entry {
+        Entry();
+
+        bool notified;
+    };
+
+    static constexpr char kDevicePath[] = "/dev/input";
+
+    void LambdaCheck();
+    void LambdaHandler(int fd);
+    void InotifyHandler();
+
+    bool GeteventEnable(int fd);
+    void GeteventOpenDevice(const std::string& device);
+    void GeteventOpenDevice();
+    void GeteventCloseDevice(const std::string& device);
+
+    Epoll* epoll_;
+    std::function<void(const std::vector<int>&)> handler_;
+
+    std::map<std::string, int> registration_;
+
+    std::map<const std::vector<int>, Entry> entries_;
+
+    Mask current_;
+
+    int inotify_fd_;
+};
 
 }  // namespace init
 }  // namespace android
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
new file mode 100644
index 0000000..c8c47a8
--- /dev/null
+++ b/init/keychords_test.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2018 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 "keychords.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "epoll.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+// This class is used to inject keys.
+class EventHandler {
+  public:
+    EventHandler();
+    EventHandler(const EventHandler&) = delete;
+    EventHandler(EventHandler&&);
+    EventHandler& operator=(const EventHandler&) = delete;
+    EventHandler& operator=(EventHandler&&);
+    ~EventHandler() noexcept;
+
+    bool init();
+
+    bool send(struct input_event& e);
+    bool send(uint16_t type, uint16_t code, uint16_t value);
+    bool send(uint16_t code, bool value);
+
+  private:
+    int fd_;
+};
+
+EventHandler::EventHandler() : fd_(-1) {}
+
+EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+    rval.fd_ = -1;
+}
+
+EventHandler& EventHandler::operator=(EventHandler&& rval) {
+    fd_ = rval.fd_;
+    rval.fd_ = -1;
+    return *this;
+}
+
+EventHandler::~EventHandler() {
+    if (fd_ == -1) return;
+    ::ioctl(fd_, UI_DEV_DESTROY);
+    ::close(fd_);
+}
+
+bool EventHandler::init() {
+    if (fd_ != -1) return true;
+    auto fd = TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC));
+    if (fd == -1) return false;
+    if (::ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
+        ::close(fd);
+        return false;
+    }
+
+    static const struct uinput_user_dev u = {
+        .name = "com.google.android.init.test",
+        .id.bustype = BUS_VIRTUAL,
+        .id.vendor = 0x1AE0,   // Google
+        .id.product = 0x494E,  // IN
+        .id.version = 1,
+    };
+    if (TEMP_FAILURE_RETRY(::write(fd, &u, sizeof(u))) != sizeof(u)) {
+        ::close(fd);
+        return false;
+    }
+
+    // all keys
+    for (uint16_t i = 0; i < KEY_MAX; ++i) {
+        if (::ioctl(fd, UI_SET_KEYBIT, i) == -1) {
+            ::close(fd);
+            return false;
+        }
+    }
+    if (::ioctl(fd, UI_DEV_CREATE) == -1) {
+        ::close(fd);
+        return false;
+    }
+    fd_ = fd;
+    return true;
+}
+
+bool EventHandler::send(struct input_event& e) {
+    gettimeofday(&e.time, nullptr);
+    return TEMP_FAILURE_RETRY(::write(fd_, &e, sizeof(e))) == sizeof(e);
+}
+
+bool EventHandler::send(uint16_t type, uint16_t code, uint16_t value) {
+    struct input_event e = {.type = type, .code = code, .value = value};
+    return send(e);
+}
+
+bool EventHandler::send(uint16_t code, bool value) {
+    return (code < KEY_MAX) && init() && send(EV_KEY, code, value) && send(EV_SYN, SYN_REPORT, 0);
+}
+
+std::string InitFds(const char* prefix, pid_t pid = getpid()) {
+    std::string ret;
+
+    std::string init_fds("/proc/");
+    init_fds += std::to_string(pid) + "/fd";
+    std::unique_ptr<DIR, decltype(&closedir)> fds(opendir(init_fds.c_str()), closedir);
+    if (!fds) return ret;
+
+    dirent* entry;
+    while ((entry = readdir(fds.get()))) {
+        if (entry->d_name[0] == '.') continue;
+        std::string devname = init_fds + '/' + entry->d_name;
+        char buf[256];
+        auto retval = readlink(devname.c_str(), buf, sizeof(buf) - 1);
+        if ((retval < 0) || (size_t(retval) >= (sizeof(buf) - 1))) continue;
+        buf[retval] = '\0';
+        if (!android::base::StartsWith(buf, prefix)) continue;
+        if (ret.size() != 0) ret += ",";
+        ret += buf;
+    }
+    return ret;
+}
+
+std::string InitInputFds() {
+    return InitFds("/dev/input/");
+}
+
+std::string InitInotifyFds() {
+    return InitFds("anon_inode:inotify");
+}
+
+// NB: caller (this series of tests, or conversely the service parser in init)
+// is responsible for validation, sorting and uniqueness of the chords, so no
+// fuzzing is advised.
+
+const std::vector<int> escape_chord = {KEY_ESC};
+const std::vector<int> triple1_chord = {KEY_BACKSPACE, KEY_VOLUMEDOWN, KEY_VOLUMEUP};
+const std::vector<int> triple2_chord = {KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_BACK};
+
+const std::vector<const std::vector<int>> empty_chords;
+const std::vector<const std::vector<int>> chords = {
+    escape_chord,
+    triple1_chord,
+    triple2_chord,
+};
+
+class TestFrame {
+  public:
+    TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev = nullptr);
+
+    void RelaxForMs(std::chrono::milliseconds wait = 1ms);
+
+    void SetChord(int key, bool value = true);
+    void SetChords(const std::vector<int>& chord, bool value = true);
+    void ClrChord(int key);
+    void ClrChords(const std::vector<int>& chord);
+
+    bool IsOnlyChord(const std::vector<int>& chord) const;
+    bool IsNoChord() const;
+    bool IsChord(const std::vector<int>& chord) const;
+    void WaitForChord(const std::vector<int>& chord);
+
+    std::string Format() const;
+
+  private:
+    static std::string Format(const std::vector<const std::vector<int>>& chords);
+
+    Epoll epoll_;
+    Keychords keychords_;
+    std::vector<const std::vector<int>> keycodes_;
+    EventHandler* ev_;
+};
+
+TestFrame::TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev)
+    : ev_(ev) {
+    if (!epoll_.Open()) return;
+    for (const auto& keycodes : chords) keychords_.Register(keycodes);
+    keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {
+        this->keycodes_.emplace_back(keycodes);
+    });
+}
+
+void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
+    epoll_.Wait(wait);
+}
+
+void TestFrame::SetChord(int key, bool value) {
+    ASSERT_TRUE(!!ev_);
+    RelaxForMs();
+    EXPECT_TRUE(ev_->send(key, value));
+}
+
+void TestFrame::SetChords(const std::vector<int>& chord, bool value) {
+    ASSERT_TRUE(!!ev_);
+    for (auto& key : chord) SetChord(key, value);
+    RelaxForMs();
+}
+
+void TestFrame::ClrChord(int key) {
+    ASSERT_TRUE(!!ev_);
+    SetChord(key, false);
+}
+
+void TestFrame::ClrChords(const std::vector<int>& chord) {
+    ASSERT_TRUE(!!ev_);
+    SetChords(chord, false);
+}
+
+bool TestFrame::IsOnlyChord(const std::vector<int>& chord) const {
+    auto ret = false;
+    for (const auto& keycode : keycodes_) {
+        if (keycode != chord) return false;
+        ret = true;
+    }
+    return ret;
+}
+
+bool TestFrame::IsNoChord() const {
+    return keycodes_.empty();
+}
+
+bool TestFrame::IsChord(const std::vector<int>& chord) const {
+    for (const auto& keycode : keycodes_) {
+        if (keycode == chord) return true;
+    }
+    return false;
+}
+
+void TestFrame::WaitForChord(const std::vector<int>& chord) {
+    for (int retry = 1000; retry && !IsChord(chord); --retry) RelaxForMs();
+}
+
+std::string TestFrame::Format(const std::vector<const std::vector<int>>& chords) {
+    std::string ret("{");
+    if (!chords.empty()) {
+        ret += android::base::Join(chords.front(), ' ');
+        for (auto it = std::next(chords.begin()); it != chords.end(); ++it) {
+            ret += ',';
+            ret += android::base::Join(*it, ' ');
+        }
+    }
+    return ret + '}';
+}
+
+std::string TestFrame::Format() const {
+    return Format(keycodes_);
+}
+
+}  // namespace
+
+TEST(keychords, not_instantiated) {
+    TestFrame test_frame(empty_chords);
+    EXPECT_TRUE(InitInotifyFds().size() == 0);
+}
+
+TEST(keychords, instantiated) {
+    // Test if a valid set of chords results in proper instantiation of the
+    // underlying mechanisms for /dev/input/ attachment.
+    TestFrame test_frame(chords);
+    EXPECT_TRUE(InitInotifyFds().size() != 0);
+}
+
+TEST(keychords, init_inotify) {
+    std::string before(InitInputFds());
+
+    TestFrame test_frame(chords);
+
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+
+    for (int retry = 1000; retry && before == InitInputFds(); --retry) test_frame.RelaxForMs();
+    std::string after(InitInputFds());
+    EXPECT_NE(before, after);
+}
+
+TEST(keychords, key) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    test_frame.SetChords(escape_chord);
+    test_frame.WaitForChord(escape_chord);
+    test_frame.ClrChords(escape_chord);
+    EXPECT_TRUE(test_frame.IsOnlyChord(escape_chord))
+        << "expected only " << android::base::Join(escape_chord, ' ') << " got "
+        << test_frame.Format();
+}
+
+TEST(keychords, keys_in_series) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    for (auto& key : triple1_chord) {
+        test_frame.SetChord(key);
+        test_frame.ClrChord(key);
+    }
+    test_frame.WaitForChord(triple1_chord);
+    EXPECT_TRUE(test_frame.IsNoChord()) << "expected nothing got " << test_frame.Format();
+}
+
+TEST(keychords, keys_in_parallel) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    test_frame.SetChords(triple2_chord);
+    test_frame.WaitForChord(triple2_chord);
+    test_frame.ClrChords(triple2_chord);
+    EXPECT_TRUE(test_frame.IsOnlyChord(triple2_chord))
+        << "expected only " << android::base::Join(triple2_chord, ' ') << " got "
+        << test_frame.Format();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index 4453aaa..ee6ee06 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
     line_callbacks_.emplace_back(prefix, callback);
 }
 
-void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
+void Parser::ParseData(const std::string& filename, const std::string& data) {
     // TODO: Use a parser with const input and remove this copy
     std::vector<char> data_copy(data.begin(), data.end());
     data_copy.push_back('\0');
@@ -57,7 +57,7 @@
         if (section_parser == nullptr) return;
 
         if (auto result = section_parser->EndSection(); !result) {
-            (*parse_errors)++;
+            parse_error_count_++;
             LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
         }
 
@@ -81,7 +81,7 @@
                         end_section();
 
                         if (auto result = callback(std::move(args)); !result) {
-                            (*parse_errors)++;
+                            parse_error_count_++;
                             LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         }
                         break;
@@ -94,16 +94,20 @@
                     if (auto result =
                             section_parser->ParseSection(std::move(args), filename, state.line);
                         !result) {
-                        (*parse_errors)++;
+                        parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                         !result) {
-                        (*parse_errors)++;
+                        parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
+                } else {
+                    parse_error_count_++;
+                    LOG(ERROR) << filename << ": " << state.line
+                               << ": Invalid section keyword found";
                 }
                 args.clear();
                 break;
@@ -114,17 +118,17 @@
     }
 }
 
-bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigFile(const std::string& path) {
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
     auto config_contents = ReadFile(path);
     if (!config_contents) {
-        LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+        LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
         return false;
     }
 
     config_contents->push_back('\n');  // TODO: fix parse_config.
-    ParseData(path, *config_contents, parse_errors);
+    ParseData(path, *config_contents);
     for (const auto& [section_name, section_parser] : section_parsers_) {
         section_parser->EndFile();
     }
@@ -133,11 +137,11 @@
     return true;
 }
 
-bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigDir(const std::string& path) {
     LOG(INFO) << "Parsing directory " << path << "...";
     std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
     if (!config_dir) {
-        PLOG(ERROR) << "Could not import directory '" << path << "'";
+        PLOG(INFO) << "Could not import directory '" << path << "'";
         return false;
     }
     dirent* current_file;
@@ -153,7 +157,7 @@
     // Sort first so we load files in a consistent order (bug 31996208)
     std::sort(files.begin(), files.end());
     for (const auto& file : files) {
-        if (!ParseConfigFile(file, parse_errors)) {
+        if (!ParseConfigFile(file)) {
             LOG(ERROR) << "could not import file '" << file << "'";
         }
     }
@@ -161,16 +165,10 @@
 }
 
 bool Parser::ParseConfig(const std::string& path) {
-    size_t parse_errors;
-    return ParseConfig(path, &parse_errors);
-}
-
-bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
-    *parse_errors = 0;
     if (is_dir(path.c_str())) {
-        return ParseConfigDir(path, parse_errors);
+        return ParseConfigDir(path);
     }
-    return ParseConfigFile(path, parse_errors);
+    return ParseConfigFile(path);
 }
 
 }  // namespace init
diff --git a/init/parser.h b/init/parser.h
index f6e237f..3501d8c 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,17 +72,19 @@
     Parser();
 
     bool ParseConfig(const std::string& path);
-    bool ParseConfig(const std::string& path, size_t* parse_errors);
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
+    size_t parse_error_count() const { return parse_error_count_; }
+
   private:
-    void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
-    bool ParseConfigFile(const std::string& path, size_t* parse_errors);
-    bool ParseConfigDir(const std::string& path, size_t* parse_errors);
+    void ParseData(const std::string& filename, const std::string& data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
     std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+    size_t parse_error_count_ = 0;
 };
 
 }  // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 741fde0..d1c427d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -95,6 +95,11 @@
 
 void CreateSerializedPropertyInfo();
 
+struct PropertyAuditData {
+    const ucred* cr;
+    const char* name;
+};
+
 void property_init() {
     mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
     CreateSerializedPropertyInfo();
@@ -111,7 +116,7 @@
         return false;
     }
 
-    property_audit_data audit_data;
+    PropertyAuditData audit_data;
 
     audit_data.name = name.c_str();
     audit_data.cr = &cr;
@@ -393,6 +398,35 @@
     DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
 };
 
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+                               const std::string& source_context, const ucred& cr) {
+    // We check the legacy method first but these properties are dontaudit, so we only log an audit
+    // if the newer method fails as well.  We only do this with the legacy ctl. properties.
+    if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+        // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+        // their value is the name of the service to apply that action to.  Permissions for these
+        // actions are based on the service, so we must create a fake name of ctl.<service> to
+        // check permissions.
+        auto control_string_legacy = "ctl." + value;
+        const char* target_context_legacy = nullptr;
+        const char* type_legacy = nullptr;
+        property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+                                            &type_legacy);
+
+        if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+            return true;
+        }
+    }
+
+    auto control_string_full = name + "$" + value;
+    const char* target_context_full = nullptr;
+    const char* type_full = nullptr;
+    property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+                                        &type_full);
+
+    return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
 // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                            const std::string& source_context, const ucred& cr, std::string* error) {
@@ -402,15 +436,9 @@
     }
 
     if (StartsWith(name, "ctl.")) {
-        // ctl. properties have their name ctl.<action> and their value is the name of the service
-        // to apply that action to.  Permissions for these actions are based on the service, so we
-        // must create a fake name of ctl.<service> to check permissions.
-        auto control_string = "ctl." + value;
-        const char* target_context = nullptr;
-        const char* type = nullptr;
-        property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
-        if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
-            *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
+        if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+            *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+                                  value.c_str());
             return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
         }
 
@@ -742,7 +770,7 @@
 }
 
 static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
-    property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+    auto* d = reinterpret_cast<PropertyAuditData*>(data);
 
     if (!d || !d->name || !d->cr) {
         LOG(ERROR) << "AuditCallback invoked with null data arguments!";
diff --git a/init/property_service.h b/init/property_service.h
index 4a354c2..cacd987 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,11 +26,6 @@
 namespace android {
 namespace init {
 
-struct property_audit_data {
-    const ucred* cr;
-    const char* name;
-};
-
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/service.cpp b/init/service.cpp
index 0e08d9b..565cae7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,6 +18,7 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/input.h>
 #include <linux/securebits.h>
 #include <sched.h>
 #include <sys/mount.h>
@@ -32,6 +33,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -46,8 +48,6 @@
 #if defined(__ANDROID__)
 #include <sys/system_properties.h>
 
-#include <android-base/properties.h>
-
 #include "init.h"
 #include "property_service.h"
 #else
@@ -228,7 +228,6 @@
       seclabel_(seclabel),
       onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                  "onrestart", {}),
-      keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
       priority_(0),
@@ -544,10 +543,13 @@
 Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
     for (std::size_t i = 1; i < args.size(); i++) {
         int code;
-        if (ParseInt(args[i], &code)) {
-            keycodes_.emplace_back(code);
+        if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+            for (auto& key : keycodes_) {
+                if (key == code) return Error() << "duplicate keycode: " << args[i];
+            }
+            keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
         } else {
-            LOG(WARNING) << "ignoring invalid keycode: " << args[i];
+            return Error() << "invalid keycode: " << args[i];
         }
     }
     return Success();
diff --git a/init/service.h b/init/service.h
index cbfd52f..ea79a07 100644
--- a/init/service.h
+++ b/init/service.h
@@ -108,8 +108,6 @@
     const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
     const std::string& seclabel() const { return seclabel_; }
     const std::vector<int>& keycodes() const { return keycodes_; }
-    int keychord_id() const { return keychord_id_; }
-    void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
     IoSchedClass ioprio_class() const { return ioprio_class_; }
     int ioprio_pri() const { return ioprio_pri_; }
     const std::set<std::string>& interfaces() const { return interfaces_; }
@@ -199,9 +197,8 @@
 
     std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name
 
-    // keycodes for triggering this service via /dev/keychord
+    // keycodes for triggering this service via /dev/input/input*
     std::vector<int> keycodes_;
-    int keychord_id_;
 
     IoSchedClass ioprio_class_;
     int ioprio_pri_;
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b43c2e9..194aa2b 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -46,7 +46,6 @@
     EXPECT_EQ(0U, service_in_old_memory->uid());
     EXPECT_EQ(0U, service_in_old_memory->gid());
     EXPECT_EQ(0U, service_in_old_memory->namespace_flags());
-    EXPECT_EQ(0, service_in_old_memory->keychord_id());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory->priority());
@@ -66,7 +65,6 @@
     EXPECT_EQ(0U, service_in_old_memory2->uid());
     EXPECT_EQ(0U, service_in_old_memory2->gid());
     EXPECT_EQ(0U, service_in_old_memory2->namespace_flags());
-    EXPECT_EQ(0, service_in_old_memory2->keychord_id());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory2->priority());
diff --git a/init/util.cpp b/init/util.cpp
index 4455b2e..5f2b87d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,6 +33,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -43,8 +44,6 @@
 #include "reboot.h"
 
 #if defined(__ANDROID__)
-#include <android-base/properties.h>
-
 #include "selinux.h"
 #else
 #include "host_init_stubs.h"
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 8a2afea..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
     name: "libasyncio",
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     srcs: [
         "AsyncIO.cpp",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 0f93dd0..b4bf35f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -58,6 +58,7 @@
 cc_library {
     name: "libbacktrace",
     vendor_available: false,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -102,8 +103,6 @@
             include_dirs: [
                 "art/runtime",
             ],
-
-            header_libs: ["jni_headers"],
         },
         android: {
             static_libs: ["libasync_safe"],
@@ -112,6 +111,10 @@
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
             exclude_shared_libs: ["libdexfile"],
         },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_shared_libs: ["libdexfile"],
+        },
     },
     whole_static_libs: ["libdemangle"],
 }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 1e3d379..f78a31f 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -46,6 +46,7 @@
 
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <cutils/atomic.h>
 #include <cutils/threads.h>
@@ -1186,49 +1187,45 @@
   ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
 }
 
-static const char* CopySharedLibrary() {
-#if defined(__LP64__)
-  const char* lib_name = "lib64";
-#else
-  const char* lib_name = "lib";
-#endif
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+  std::string system_dir;
 
 #if defined(__BIONIC__)
-  const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
-  std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
-                                                   lib_name, tmp_so_name);
+  system_dir = "/system/lib";
 #else
-  const char* tmp_so_name = "/tmp/libbacktrace_test.so";
-  if (getenv("ANDROID_HOST_OUT") == NULL) {
-    fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
-    return nullptr;
-  }
-  std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
-                                                   getenv("ANDROID_HOST_OUT"), lib_name,
-                                                   tmp_so_name);
+  const char* host_out_env = getenv("ANDROID_HOST_OUT");
+  ASSERT_TRUE(host_out_env != nullptr);
+  system_dir = std::string(host_out_env) + "/lib";
 #endif
 
-  // Copy the shared so to a tempory directory.
-  system(cp_cmd.c_str());
+#if defined(__LP64__)
+  system_dir += "64";
+#endif
 
-  return tmp_so_name;
+  *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+  std::string cp_cmd =
+      android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+
+  // Copy the shared so to a tempory directory.
+  ASSERT_EQ(0, system(cp_cmd.c_str()));
 }
 
 TEST(libbacktrace, check_unreadable_elf_local) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
 
   struct stat buf;
-  ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+  ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
   uint64_t map_size = buf.st_size;
 
-  int fd = open(tmp_so_name, O_RDONLY);
+  int fd = open(tmp_so_name.c_str(), O_RDONLY);
   ASSERT_TRUE(fd != -1);
 
   void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
   ASSERT_TRUE(map != MAP_FAILED);
   close(fd);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   std::vector<std::string> found_functions;
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
@@ -1256,32 +1253,33 @@
 }
 
 TEST(libbacktrace, check_unreadable_elf_remote) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
 
   g_ready = 0;
 
   struct stat buf;
-  ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+  ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
   uint64_t map_size = buf.st_size;
 
   pid_t pid;
   if ((pid = fork()) == 0) {
-    int fd = open(tmp_so_name, O_RDONLY);
+    int fd = open(tmp_so_name.c_str(), O_RDONLY);
     if (fd == -1) {
-      fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
-      unlink(tmp_so_name);
+      fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+      unlink(tmp_so_name.c_str());
       exit(0);
     }
 
     void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
     if (map == MAP_FAILED) {
       fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
-      unlink(tmp_so_name);
+      unlink(tmp_so_name.c_str());
       exit(0);
     }
     close(fd);
-    if (unlink(tmp_so_name) == -1) {
+    if (unlink(tmp_so_name.c_str()) == -1) {
       fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
       exit(0);
     }
@@ -1394,11 +1392,13 @@
 typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
 
 TEST(libbacktrace, unwind_through_unreadable_elf_local) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
-  void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+  void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
   ASSERT_TRUE(lib_handle != nullptr);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   test_func_t test_func;
   test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1411,11 +1411,13 @@
 }
 
 TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
-  void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+  void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
   ASSERT_TRUE(lib_handle != nullptr);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   test_func_t test_func;
   test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1444,7 +1446,8 @@
 
     size_t frame_num;
     if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
-                                 &frame_num)) {
+                                 &frame_num) &&
+        frame_num != 0) {
       VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
       done = true;
     }
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 47de12a..e47560f 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,7 @@
 cc_library {
     name: "libcrypto_utils",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 0f2b460..6169324 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -80,6 +80,7 @@
     { 00775, AID_ROOT,         AID_ROOT,         0, "data/preloads" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
+    { 00755, AID_ROOT,         AID_SHELL,        0, "product/bin" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
@@ -195,6 +196,7 @@
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "product/bin/*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
@@ -237,9 +239,10 @@
     return fd;
 }
 
-// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>" or
+// "vendor/<stuff>"
 static bool is_partition(const char* path, size_t len) {
-    static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+    static const char* partitions[] = {"odm/", "oem/", "product/", "vendor/"};
     for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
         size_t plen = strlen(partitions[i]);
         if (len <= plen) continue;
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index f3593ff..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -2,6 +2,7 @@
     name: "libkeyutils",
     cflags: ["-Werror"],
     defaults: ["linux_bionic_supported"],
+    recovery_available: true,
     export_include_dirs: ["include/"],
     local_include_dirs: ["include/"],
     srcs: ["keyutils.cpp"],
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 1bd796a..4a165a0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -83,8 +83,8 @@
         },
         android_arm: {
             // TODO: This is to work around b/24465209. Remove after root cause is fixed
+            pack_relocations: false,
             ldflags: ["-Wl,--hash-style=both"],
-            use_clang_lld: false,
         },
         windows: {
             srcs: ["uio.c"],
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index 6d9141a..9cc0c9b 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -44,7 +44,7 @@
 
 ### C++ interface ###
 
-####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+#### `bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)` ####
 Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks.  Returns true if leak detection succeeded.
 
 #### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index f2cc942..5c28a9f 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1 +1,2 @@
 dimitry@google.com
+jiyong@google.com
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6dfa697..1cebb5d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -39,12 +39,14 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
+using android::base::GetBoolProperty;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -62,12 +64,20 @@
 static const std::string& GetCgroupRootPath() {
     static std::string cgroup_root_path;
     std::call_once(init_path_flag, [&]() {
-            // Check if mem cgroup is mounted, only then check for write-access to avoid
-            // SELinux denials
+        // low-ram devices use per-app memcg by default, unlike high-end ones
+        bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+        bool per_app_memcg =
+            GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+        if (per_app_memcg) {
+            // Check if mem cgroup is mounted, only then check for
+            // write-access to avoid SELinux denials
             cgroup_root_path =
-                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
-                                                                                 : MEM_CGROUP_PATH);
-            });
+                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
+                ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
+        } else {
+            cgroup_root_path = ACCT_CGROUP_PATH;
+        }
+    });
     return cgroup_root_path;
 }
 
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index d776b3d..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,7 @@
     name: "libprocinfo",
     defaults: ["libprocinfo_defaults"],
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index b894656..c7c089f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,6 +3,7 @@
 cc_library {
     name: "libsparse",
     host_supported: true,
+    recovery_available: true,
     unique_host_soname: true,
     srcs: [
         "backed_block.c",
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a6bf730..26be64d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -38,6 +38,7 @@
 cc_library {
     name: "libunwindstack",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -62,6 +63,7 @@
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "LocalUnwinder.cpp",
         "Regs.cpp",
         "RegsArm.cpp",
         "RegsArm64.cpp",
@@ -93,6 +95,14 @@
             ],
             exclude_shared_libs: ["libdexfile"],
         },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
+            exclude_shared_libs: ["libdexfile"],
+        },
     },
 
     arch: {
@@ -125,6 +135,21 @@
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
+cc_test_library {
+    name: "libunwindstack_local",
+    defaults: ["libunwindstack_flags"],
+    srcs: ["tests/TestLocal.cpp"],
+
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+
+    shared_libs: [
+        "libunwindstack",
+    ],
+}
+
 cc_test {
     name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
@@ -151,6 +176,7 @@
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
         "tests/JitDebugTest.cpp",
+        "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..952b332
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+  pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+  // Create the maps.
+  maps_.reset(new unwindstack::LocalUpdatableMaps());
+  if (!maps_->Parse()) {
+    maps_.reset();
+    return false;
+  }
+
+  process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+  return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+  for (const std::string& skip_library : skip_libraries_) {
+    if (skip_library == map_name) {
+      return true;
+    }
+  }
+  return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+  pthread_rwlock_rdlock(&maps_rwlock_);
+  MapInfo* map_info = maps_->Find(pc);
+  pthread_rwlock_unlock(&maps_rwlock_);
+
+  if (map_info == nullptr) {
+    pthread_rwlock_wrlock(&maps_rwlock_);
+    // This is guaranteed not to invalidate any previous MapInfo objects so
+    // we don't need to worry about any MapInfo* values already in use.
+    if (maps_->Reparse()) {
+      map_info = maps_->Find(pc);
+    }
+    pthread_rwlock_unlock(&maps_rwlock_);
+  }
+
+  return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+  unwindstack::RegsGetLocal(regs.get());
+
+  size_t num_frames = 0;
+  bool adjust_pc = false;
+  while (true) {
+    uint64_t cur_pc = regs->pc();
+    uint64_t cur_sp = regs->sp();
+
+    MapInfo* map_info = GetMapInfo(cur_pc);
+    if (map_info == nullptr) {
+      break;
+    }
+
+    Elf* elf = map_info->GetElf(process_memory_, true);
+    uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+    uint64_t step_pc = rel_pc;
+    uint64_t pc_adjustment;
+    if (adjust_pc) {
+      pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+    } else {
+      pc_adjustment = 0;
+    }
+    step_pc -= pc_adjustment;
+    // Skip any locations that are within this library.
+    if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+      // Add frame information.
+      std::string func_name;
+      uint64_t func_offset;
+      if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+                                 func_name, func_offset);
+      } else {
+        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+      }
+      num_frames++;
+    }
+    if (!elf->valid()) {
+      break;
+    }
+    if (frame_info->size() == max_frames) {
+      break;
+    }
+
+    adjust_pc = true;
+    bool finished;
+    if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
+      break;
+    }
+    // pc and sp are the same, terminate the unwind.
+    if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
+      break;
+    }
+  }
+  return num_frames != 0;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index bb682ea..e676a5a 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -105,4 +105,83 @@
   return "/proc/" + std::to_string(pid_) + "/maps";
 }
 
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+  return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+  // New maps will be added at the end without deleting the old ones.
+  size_t last_map_idx = maps_.size();
+  if (!Parse()) {
+    // Delete any maps added by the Parse call.
+    for (size_t i = last_map_idx; i < maps_.size(); i++) {
+      delete maps_[i];
+    }
+    maps_.resize(last_map_idx);
+    return false;
+  }
+
+  size_t total_entries = maps_.size();
+  size_t search_map_idx = 0;
+  for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
+    MapInfo* new_map_info = maps_[new_map_idx];
+    uint64_t start = new_map_info->start;
+    uint64_t end = new_map_info->end;
+    uint64_t flags = new_map_info->flags;
+    std::string* name = &new_map_info->name;
+    for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
+      MapInfo* info = maps_[old_map_idx];
+      if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
+        // No need to check
+        search_map_idx = old_map_idx + 1;
+        delete new_map_info;
+        maps_[new_map_idx] = nullptr;
+        total_entries--;
+        break;
+      } else if (info->start > start) {
+        // Stop, there isn't going to be a match.
+        search_map_idx = old_map_idx;
+        break;
+      }
+
+      // Never delete these maps, they may be in use. The assumption is
+      // that there will only every be a handfull of these so waiting
+      // to destroy them is not too expensive.
+      saved_maps_.push_back(info);
+      maps_[old_map_idx] = nullptr;
+      total_entries--;
+    }
+    if (search_map_idx >= last_map_idx) {
+      break;
+    }
+  }
+
+  // Now move out any of the maps that never were found.
+  for (size_t i = search_map_idx; i < last_map_idx; i++) {
+    saved_maps_.push_back(maps_[i]);
+    maps_[i] = nullptr;
+    total_entries--;
+  }
+
+  // Sort all of the values such that the nullptrs wind up at the end, then
+  // resize them away.
+  std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+    if (a == nullptr) {
+      return false;
+    } else if (b == nullptr) {
+      return true;
+    }
+    return a->start < b->start;
+  });
+  maps_.resize(total_entries);
+
+  return true;
+}
+
+LocalUpdatableMaps::~LocalUpdatableMaps() {
+  for (auto map_info : saved_maps_) {
+    delete map_info;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 0000000..80bb53e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+  LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+                 uint64_t function_offset)
+      : map_info(map_info),
+        pc(pc),
+        rel_pc(rel_pc),
+        function_name(function_name),
+        function_offset(function_offset) {}
+
+  MapInfo* map_info;
+  uint64_t pc;
+  uint64_t rel_pc;
+  std::string function_name;
+  uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+  LocalUnwinder() = default;
+  LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+  ~LocalUnwinder() = default;
+
+  bool Init();
+
+  bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+  bool ShouldSkipLibrary(const std::string& map_name);
+
+  MapInfo* GetMapInfo(uint64_t pc);
+
+  ErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+  pthread_rwlock_t maps_rwlock_;
+  std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+  std::shared_ptr<Memory> process_memory_;
+  std::vector<std::string> skip_libraries_;
+  ErrorData last_error_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 74e5c47..67fbed2 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -87,6 +87,19 @@
   virtual ~LocalMaps() = default;
 };
 
+class LocalUpdatableMaps : public Maps {
+ public:
+  LocalUpdatableMaps() : Maps() {}
+  virtual ~LocalUpdatableMaps();
+
+  bool Reparse();
+
+  const std::string GetMapsFile() const override;
+
+ private:
+  std::vector<MapInfo*> saved_maps_;
+};
+
 class BufferMaps : public Maps {
  public:
   BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 0000000..56a18cd
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 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 <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+  g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+  SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+  SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+  SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+                            const std::vector<LocalFrameData>& frame_info) {
+  std::string unwind;
+  size_t i = 0;
+  for (const auto& frame : frame_info) {
+    unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+                                          frame.pc, frame.rel_pc);
+    if (frame.map_info != nullptr) {
+      if (!frame.map_info->name.empty()) {
+        unwind += " " + frame.map_info->name;
+      } else {
+        unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+                                              frame.map_info->end);
+      }
+      if (frame.map_info->offset != 0) {
+        unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+      }
+    }
+    if (!frame.function_name.empty()) {
+      unwind += " " + frame.function_name;
+      if (frame.function_offset != 0) {
+        unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+      }
+    }
+    unwind += '\n';
+  }
+
+  return std::string(
+             "Unwind completed without finding all frames\n"
+             "  Looking for function: ") +
+         function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  std::vector<LocalFrameData> frame_info;
+  g_frame_info = &frame_info;
+  g_unwinder = unwinder;
+  std::vector<const char*> expected_function_names;
+
+  if (unwind_through_signal) {
+    struct sigaction act, oldact;
+    memset(&act, 0, sizeof(act));
+    act.sa_sigaction = SignalLocalCallerHandler;
+    act.sa_flags = SA_RESTART | SA_ONSTACK;
+    ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+    raise(SIGUSR1);
+
+    ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+    expected_function_names = {"LocalOuterFunction",        "LocalMiddleFunction",
+                               "LocalInnerFunction",        "SignalLocalOuterFunction",
+                               "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+  } else {
+    ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+    expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+  }
+
+  for (auto& frame : frame_info) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
+      if (expected_function_names.empty()) {
+        break;
+      }
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    unwinder_.reset(new LocalUnwinder);
+    ASSERT_TRUE(unwinder_->Init());
+  }
+
+  std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+  LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+  LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+  // Prime the maps data.
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  std::string testlib(testing::internal::GetArgvs()[0]);
+  auto const value = testlib.find_last_of('/');
+  if (value == std::string::npos) {
+    testlib = "../";
+  } else {
+    testlib = testlib.substr(0, value + 1) + "../";
+  }
+  testlib += "libunwindstack_local.so";
+
+  void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr);
+
+  void (*unwind_function)(void*, void*) =
+      reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+  ASSERT_TRUE(unwind_function != nullptr);
+
+  std::vector<LocalFrameData> frame_info;
+  unwind_function(unwinder_.get(), &frame_info);
+
+  ASSERT_EQ(0, dlclose(handle));
+
+  std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+                                                   "TestlibLevel3", "TestlibLevel4"};
+
+  for (auto& frame : frame_info) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
+      if (expected_function_names.empty()) {
+        break;
+      }
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 0000000..fa0baff
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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 <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+  unwindstack::LocalUnwinder* unwinder =
+      reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+  std::vector<unwindstack::LocalFrameData>* frame_info =
+      reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+  unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+  TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+  TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+  TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a..83695bb 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -106,15 +106,12 @@
   Unwinder unwinder(512, maps, regs, process_memory);
   unwinder.Unwind();
 
-  std::string expected_function = expected_function_names.back();
-  expected_function_names.pop_back();
   for (auto& frame : unwinder.frames()) {
-    if (frame.function_name == expected_function) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
       if (expected_function_names.empty()) {
         break;
       }
-      expected_function = expected_function_names.back();
-      expected_function_names.pop_back();
     }
   }
 
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index d4ba4f4..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -12,6 +12,7 @@
 cc_library {
     name: "liblogwrap",
     defaults: ["logwrapper_defaults"],
+    recovery_available: true,
     srcs: ["logwrap.c"],
     shared_libs: [
         "libcutils",
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index 406e208..bce308b 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -111,7 +111,7 @@
 
 struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
     uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
-    uint64_t recovery_dtbo_offset; /* physical load addr */
+    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
     uint32_t header_size;
 } __attribute__((packed));
 
@@ -138,11 +138,14 @@
  * 1. kernel and ramdisk are required (size != 0)
  * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
  * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
+ * 4. load each element (kernel, ramdisk, second) at
  *    the specified physical address (kernel_addr, etc)
- * 5. prepare tags at tag_addr.  kernel_args[] is
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
+ *    apply the correct set of overlays on the base device tree depending on the
+ *    hardware/product revision.
+ * 6. prepare tags at tag_addr.  kernel_args[] is
  *    appended to the kernel commandline in the tags.
- * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 7. if second_size != 0: jump to second_addr
+ * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 8. if second_size != 0: jump to second_addr
  *    else: jump to kernel_addr
  */
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index 93c347b..c6bda4a 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -3,6 +3,7 @@
 cc_library_static {
     name: "libqemu_pipe",
     vendor_available: true,
+    recovery_available: true,
     sanitize: {
         misc_undefined: ["integer"],
     },
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 51e3f9e..a0b1996 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
+dir.system = /product/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index ab03755..db65c14 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,6 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
+dir.system = /product/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/