Merge "Populate recovery DTBO offset correctly"
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 71bf154..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"],
             },
@@ -130,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",
@@ -139,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/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 01b8948..8ed92a6 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -445,6 +445,10 @@
     *hw_reset* )                            var="hard,hw_reset" ;;
     *usb* )                                 var="cold,charger" ;;
     *rtc* )                                 var="cold,rtc" ;;
+    *2sec_reboot* )                         var="cold,rtc,2sec" ;;
+    *wdt_by_pass_pwk* )                     var="warm" ;;
+    wdt )                                   var="reboot" ;;
+    *tool_by_pass_pwk* )                    var="reboot,tool" ;;
     *bootloader* )                          var="bootloader" ;;
     * )                                     var="reboot" ;;
   esac
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1b13b21..d5fb047 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -201,7 +201,7 @@
     {"cold", 56},
     {"hard", 57},
     {"warm", 58},
-    // {"recovery", 59},  // Duplicate of enum 3 above. Immediate reuse possible.
+    {"reboot,kernel_power_off_charging__reboot_system", 59},  // Can not happen
     {"thermal-shutdown", 60},
     {"shutdown,thermal", 61},
     {"shutdown,battery", 62},
@@ -228,7 +228,7 @@
     {"2sec_reboot", 83},
     {"reboot,by_key", 84},
     {"reboot,longkey", 85},
-    {"reboot,2sec", 86},
+    {"reboot,2sec", 86},  // Deprecate in two years, replaced with cold,rtc,2sec
     {"shutdown,thermal,battery", 87},
     {"reboot,its_just_so_hard", 88},  // produced by boot_reason_test
     {"reboot,Its Just So Hard", 89},  // produced by boot_reason_test
@@ -306,6 +306,10 @@
     {"kernel_panic,sysrq,livelock,alarm", 161},   // llkd
     {"kernel_panic,sysrq,livelock,driver", 162},  // llkd
     {"kernel_panic,sysrq,livelock,zombie", 163},  // llkd
+    {"kernel_panic,modem", 164},
+    {"kernel_panic,adsp", 165},
+    {"kernel_panic,dsps", 166},
+    {"kernel_panic,wcnss", 167},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -702,6 +706,10 @@
         {"Corrupt kernel stack", "stack"},
         {"low stack detected", "stack"},
         {"corrupted stack end", "stack"},
+        {"subsys-restart: Resetting the SoC - modem crashed.", "modem"},
+        {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"},
+        {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"},
+        {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"},
     };
 
     ret = "kernel_panic";
@@ -782,7 +790,10 @@
         {"hard,hw_reset", "hw_reset"},
         {"cold,charger", "usb"},
         {"cold,rtc", "rtc"},
-        {"reboot,2sec", "2sec_reboot"},
+        {"cold,rtc,2sec", "2sec_reboot"},
+        {"!warm", "wdt_by_pass_pwk"},  // change flavour of blunt
+        {"!reboot", "^wdt$"},          // change flavour of blunt
+        {"reboot,tool", "tool_by_pass_pwk"},
         {"bootloader", ""},
     };
 
@@ -842,6 +853,10 @@
             ret = "reboot," + subReason;  // legitimize unknown reasons
           }
         }
+        // Some bootloaders shutdown results record in last kernel message.
+        if (!strcmp(ret.c_str(), "reboot,kernel_power_off_charging__reboot_system")) {
+          ret = "shutdown";
+        }
       }
 
       // Check for kernel panics, allowed to override reboot command.
@@ -1114,6 +1129,13 @@
   return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
 }
 
+void SetSystemBootReason() {
+  const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+  const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
+  // Record the scrubbed system_boot_reason to the property
+  SetProperty(system_reboot_reason_property, system_boot_reason);
+}
+
 // Records several metrics related to the time it takes to boot the device,
 // including disambiguating boot time on encrypted or non-encrypted devices.
 void RecordBootComplete() {
@@ -1193,12 +1215,10 @@
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
 
   // Log the scrubbed system_boot_reason.
-  const std::string system_reason(BootReasonStrToReason(reason));
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
   int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
   boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
 
-  // Record the scrubbed system_boot_reason to the property
-  SetProperty(system_reboot_reason_property, system_reason);
   if (reason == "") {
     SetProperty(bootloader_reboot_reason_property, system_reason);
   }
@@ -1264,20 +1284,22 @@
 
   int option_index = 0;
   static const char value_str[] = "value";
+  static const char system_boot_reason_str[] = "set_system_boot_reason";
   static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
       // clang-format off
-      { "help",            no_argument,       NULL,   'h' },
-      { "log",             no_argument,       NULL,   'l' },
-      { "print",           no_argument,       NULL,   'p' },
-      { "record",          required_argument, NULL,   'r' },
-      { value_str,         required_argument, NULL,   0 },
-      { boot_complete_str, no_argument,       NULL,   0 },
-      { boot_reason_str,   no_argument,       NULL,   0 },
-      { factory_reset_str, no_argument,       NULL,   0 },
-      { NULL,              0,                 NULL,   0 }
+      { "help",                 no_argument,       NULL,   'h' },
+      { "log",                  no_argument,       NULL,   'l' },
+      { "print",                no_argument,       NULL,   'p' },
+      { "record",               required_argument, NULL,   'r' },
+      { value_str,              required_argument, NULL,   0 },
+      { system_boot_reason_str, no_argument,       NULL,   0 },
+      { boot_complete_str,      no_argument,       NULL,   0 },
+      { boot_reason_str,        no_argument,       NULL,   0 },
+      { factory_reset_str,      no_argument,       NULL,   0 },
+      { NULL,                   0,                 NULL,   0 }
       // clang-format on
   };
 
@@ -1293,6 +1315,8 @@
           // |optarg| is an external variable set by getopt representing
           // the option argument.
           value = optarg;
+        } else if (option_name == system_boot_reason_str) {
+          SetSystemBootReason();
         } else if (option_name == boot_complete_str) {
           RecordBootComplete();
         } else if (option_name == boot_reason_str) {
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f06a38f..1300a27 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -68,8 +68,9 @@
 
 # Record boot complete metrics.
 on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+    # Converts bootloader boot reason to system boot reason
     # Record boot_complete and related stats (decryption, etc).
     # Record the boot reason.
     # Record time since factory reset.
     # Log all boot events.
-    exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
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/init/Android.bp b/init/Android.bp
index a3083c1..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",
@@ -235,7 +236,8 @@
         "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",
@@ -248,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 5652c5e..82648d9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -553,22 +553,30 @@
     }
 }
 
-void HandleKeychord(int id) {
+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") {
-        Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
-        if (svc) {
-            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
+    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 " << id
-                           << ": " << result.error();
+                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
+                           << android::base::Join(keycodes, ' ') << ": " << result.error();
             }
-        } else {
-            LOG(ERROR) << "Service for keychord " << id << " not found";
         }
-    } else {
-        LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+    }
+    if (!found) {
+        LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
     }
 }
 
@@ -753,7 +761,7 @@
     am.QueueBuiltinAction(
         [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
             for (const auto& svc : ServiceList::GetInstance()) {
-                svc->set_keychord_id(keychords.GetId(svc->keycodes()));
+                keychords.Register(svc->keycodes());
             }
             keychords.Start(&epoll, HandleKeychord);
             return Success();
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 9aa8b2a..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -37,7 +37,7 @@
 namespace android {
 namespace init {
 
-Keychords::Keychords() : epoll_(nullptr), count_(0), inotify_fd_(-1) {}
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
 
 Keychords::~Keychords() noexcept {
     if (inotify_fd_ >= 0) {
@@ -108,23 +108,22 @@
     }
 }
 
-Keychords::Entry::Entry(const std::vector<int>& keycodes, int id)
-    : keycodes(keycodes), id(id), notified(false) {}
+Keychords::Entry::Entry() : notified(false) {}
 
 void Keychords::LambdaCheck() {
-    for (auto& e : entries_) {
+    for (auto& [keycodes, entry] : entries_) {
         auto found = true;
-        for (auto& code : e.keycodes) {
+        for (auto& code : keycodes) {
             if (!current_.GetBit(code)) {
-                e.notified = false;
+                entry.notified = false;
                 found = false;
                 break;
             }
         }
         if (!found) continue;
-        if (e.notified) continue;
-        e.notified = true;
-        handler_(e.id);
+        if (entry.notified) continue;
+        entry.notified = true;
+        handler_(keycodes);
     }
 }
 
@@ -158,8 +157,8 @@
 #endif
 
     Keychords::Mask mask;
-    for (auto& e : entries_) {
-        for (auto& code : e.keycodes) {
+    for (auto& [keycodes, entry] : entries_) {
+        for (auto& code : keycodes) {
             mask.resize(code);
             mask.SetBit(code);
         }
@@ -271,17 +270,15 @@
     }
 }
 
-int Keychords::GetId(const std::vector<int>& keycodes) {
-    if (keycodes.empty()) return 0;
-    ++count_;
-    entries_.emplace_back(Entry(keycodes, count_));
-    return count_;
+void Keychords::Register(const std::vector<int>& keycodes) {
+    if (keycodes.empty()) return;
+    entries_.try_emplace(keycodes, Entry());
 }
 
-void Keychords::Start(Epoll* epoll, std::function<void(int)> handler) {
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
     epoll_ = epoll;
     handler_ = handler;
-    if (count_) GeteventOpenDevice();
+    if (entries_.size()) GeteventOpenDevice();
 }
 
 }  // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index 74a9195..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -36,8 +36,8 @@
     Keychords& operator=(Keychords&&) = delete;
     ~Keychords() noexcept;
 
-    int GetId(const std::vector<int>& keycodes);
-    void Start(Epoll* epoll, std::function<void(int)> handler);
+    void Register(const std::vector<int>& keycodes);
+    void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
 
   private:
     // Bit management
@@ -65,10 +65,8 @@
     };
 
     struct Entry {
-        Entry(const std::vector<int>& keycodes, int id);
+        Entry();
 
-        const std::vector<int> keycodes;
-        const int id;
         bool notified;
     };
 
@@ -84,12 +82,11 @@
     void GeteventCloseDevice(const std::string& device);
 
     Epoll* epoll_;
-    std::function<void(int)> handler_;
+    std::function<void(const std::vector<int>&)> handler_;
 
     std::map<std::string, int> registration_;
 
-    int count_;
-    std::vector<Entry> entries_;
+    std::map<const std::vector<int>, Entry> entries_;
 
     Mask current_;
 
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/service.cpp b/init/service.cpp
index 5778a93..565cae7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -33,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>
@@ -47,8 +48,6 @@
 #if defined(__ANDROID__)
 #include <sys/system_properties.h>
 
-#include <android-base/properties.h>
-
 #include "init.h"
 #include "property_service.h"
 #else
@@ -229,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),
@@ -549,7 +547,7 @@
             for (auto& key : keycodes_) {
                 if (key == code) return Error() << "duplicate keycode: " << args[i];
             }
-            keycodes_.emplace_back(code);
+            keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
         } else {
             return Error() << "invalid keycode: " << args[i];
         }
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/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/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 444a5ac..443c3ea 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -27,6 +27,7 @@
 // Test the APIs in this standalone include file
 #include <log/log_read.h>
 // Do not use anything in log/log_time.h despite side effects of the above.
+#include <private/android_logger.h>
 
 TEST(liblog, __android_log_write__android_logger_list_read) {
 #ifdef __ANDROID__
@@ -105,7 +106,10 @@
       // framework (b/68266385).
       EXPECT_LE(  // boolean 1 or 0 depending on expected content or empty
           !!((strcmp("crash", name) != 0) &&
-             ((strcmp("kernel", name) != 0) || __android_log_is_debuggable()) &&
+             ((strcmp("kernel", name) != 0) ||
+              __android_logger_property_get_bool(
+                  "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
+                                        BOOL_DEFAULT_FLAG_SVELTE)) &&
              (strcmp("stats", name) != 0)),
           android_logger_get_log_readable_size(logger));
     } else {
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 58295fa..1cebb5d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -39,18 +39,14 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#ifdef __ANDROID__
 #include <android-base/properties.h>
-#endif
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
-#ifdef __ANDROID__
 using android::base::GetBoolProperty;
-#endif
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -68,15 +64,10 @@
 static const std::string& GetCgroupRootPath() {
     static std::string cgroup_root_path;
     std::call_once(init_path_flag, [&]() {
-#ifdef __ANDROID__
         // 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);
-#else
-        // host does not support Android properties
-        bool per_app_memcg = false;
-#endif
         if (per_app_memcg) {
             // Check if mem cgroup is mounted, only then check for
             // write-access to avoid SELinux denials
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 3fae5e6..c95563d 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -40,13 +40,6 @@
 }
 
 cc_test {
-    name: "sync_test",
-    defaults: ["libsync_defaults"],
-    gtest: false,
-    srcs: ["sync_test.c"],
-}
-
-cc_test {
     name: "sync-unit-tests",
     shared_libs: ["libsync"],
     srcs: ["tests/sync_test.cpp"],
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
index 68f74a0..32bb878 100644
--- a/libsync/include/android/sync.h
+++ b/libsync/include/android/sync.h
@@ -41,28 +41,8 @@
 
 __BEGIN_DECLS
 
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
 /* timeout in msecs */
 int sync_wait(int fd, int timeout);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
-                                  struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
 
 __END_DECLS
 
diff --git a/libsync/sync.c b/libsync/sync.c
index 6b187fa..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -30,6 +30,29 @@
 
 #include <android/sync.h>
 
+/* Prototypes for deprecated functions that used to be declared in the legacy
+ * android/sync.h. They've been moved here to make sure new code does not use
+ * them, but the functions are still defined to avoid breaking existing
+ * binaries. Eventually they can be removed altogether.
+ */
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
 /* Legacy Sync API */
 
 struct sync_legacy_merge_data {
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
deleted file mode 100644
index f1ffdcf..0000000
--- a/libsync/sync_test.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- *  sync_test.c
- *
- *   Copyright 2012 Google, Inc
- *
- *  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 <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android/sync.h>
-#include "sw_sync.h"
-
-pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-struct sync_thread_data {
-    int thread_no;
-    int fd[2];
-};
-
-void *sync_thread(void *data)
-{
-    struct sync_thread_data *sync_data = data;
-    struct sync_fence_info_data *info;
-    int err;
-    int i;
-
-    for (i = 0; i < 2; i++) {
-        err = sync_wait(sync_data->fd[i], 10000);
-
-        pthread_mutex_lock(&printf_mutex);
-        if (err < 0) {
-            printf("thread %d wait %d failed: %s\n", sync_data->thread_no,
-                   i, strerror(errno));
-        } else {
-            printf("thread %d wait %d done\n", sync_data->thread_no, i);
-        }
-        info = sync_fence_info(sync_data->fd[i]);
-        if (info) {
-            struct sync_pt_info *pt_info = NULL;
-            printf("  fence %s %d\n", info->name, info->status);
-
-            while ((pt_info = sync_pt_info(info, pt_info))) {
-                int ts_sec = pt_info->timestamp_ns / 1000000000LL;
-                int ts_usec = (pt_info->timestamp_ns % 1000000000LL) / 1000LL;
-                printf("    pt %s %s %d %d.%06d", pt_info->obj_name,
-                       pt_info->driver_name, pt_info->status,
-                       ts_sec, ts_usec);
-                if (!strcmp(pt_info->driver_name, "sw_sync"))
-                    printf(" val=%d\n", *(uint32_t *)pt_info->driver_data);
-                else
-                    printf("\n");
-            }
-            sync_fence_info_free(info);
-        }
-        pthread_mutex_unlock(&printf_mutex);
-    }
-
-    return NULL;
-}
-
-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
-{
-    struct sync_thread_data sync_data[4];
-    pthread_t threads[4];
-    int sync_timeline_fd;
-    int i, j;
-    char str[256];
-
-    sync_timeline_fd = sw_sync_timeline_create();
-    if (sync_timeline_fd < 0) {
-        perror("can't create sw_sync_timeline:");
-        return 1;
-    }
-
-    for (i = 0; i < 3; i++) {
-        sync_data[i].thread_no = i;
-
-        for (j = 0; j < 2; j++) {
-            unsigned val = i + j * 3 + 1;
-            snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
-            int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
-            if (fd < 0) {
-                printf("can't create sync pt %d: %s", val, strerror(errno));
-                return 1;
-            }
-            sync_data[i].fd[j] = fd;
-            printf("sync_data[%d].fd[%d] = %d;\n", i, j, fd);
-
-        }
-    }
-
-    sync_data[3].thread_no = 3;
-    for (j = 0; j < 2; j++) {
-        snprintf(str, sizeof(str), "merged_fence%d", j);
-        sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
-        if (sync_data[3].fd[j] < 0) {
-            printf("can't merge sync pts %d and %d: %s\n",
-                   sync_data[0].fd[j], sync_data[1].fd[j], strerror(errno));
-            return 1;
-        }
-    }
-
-    for (i = 0; i < 4; i++)
-        pthread_create(&threads[i], NULL, sync_thread, &sync_data[i]);
-
-
-    for (i = 0; i < 3; i++) {
-        int err;
-        printf("press enter to inc to %d\n", i+1);
-        fgets(str, sizeof(str), stdin);
-        err = sw_sync_timeline_inc(sync_timeline_fd, 1);
-        if (err < 0) {
-            perror("can't increment sync obj:");
-            return 1;
-        }
-    }
-
-    printf("press enter to close sync_timeline\n");
-    fgets(str, sizeof(str), stdin);
-
-    close(sync_timeline_fd);
-
-    printf("press enter to end test\n");
-    fgets(str, sizeof(str), stdin);
-
-    for (i = 0; i < 3; i++) {
-        void *val;
-        pthread_join(threads[i], &val);
-    }
-
-    return 0;
-}
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 0fb86d6..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -15,6 +15,35 @@
 #include <random>
 #include <unordered_map>
 
+/* These deprecated declarations were in the legacy android/sync.h. They've been removed to
+ * encourage code to move to the modern equivalents. But they are still implemented in libsync.so
+ * to avoid breaking existing binaries; as long as that's true we should keep testing them here.
+ * That means making local copies of the declarations.
+ */
+extern "C" {
+
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+}  // extern "C"
+
 // TODO: better stress tests?
 // Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
 // Handle wraparound in timelines like nvidia.
diff --git a/libsysutils/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
index 1004f06..c657526 100644
--- a/libsysutils/include/sysutils/SocketClient.h
+++ b/libsysutils/include/sysutils/SocketClient.h
@@ -1,8 +1,6 @@
 #ifndef _SOCKET_CLIENT_H
 #define _SOCKET_CLIENT_H
 
-#include "List.h"
-
 #include <pthread.h>
 #include <cutils/atomic.h>
 #include <sys/types.h>
@@ -35,7 +33,7 @@
     SocketClient(int sock, bool owned, bool useCmdNum);
     virtual ~SocketClient();
 
-    int getSocket() { return mSocket; }
+    int getSocket() const { return mSocket; }
     pid_t getPid() const { return mPid; }
     uid_t getUid() const { return mUid; }
     gid_t getGid() const { return mGid; }
@@ -84,5 +82,4 @@
     int sendDataLockedv(struct iovec *iov, int iovcnt);
 };
 
-typedef android::sysutils::List<SocketClient *> SocketClientCollection;
 #endif
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
index bc93b86..56f2478 100644
--- a/libsysutils/include/sysutils/SocketListener.h
+++ b/libsysutils/include/sysutils/SocketListener.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 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.
@@ -18,6 +18,8 @@
 
 #include <pthread.h>
 
+#include <unordered_map>
+
 #include <sysutils/SocketClient.h>
 #include "SocketClientCommand.h"
 
@@ -25,7 +27,7 @@
     bool                    mListen;
     const char              *mSocketName;
     int                     mSock;
-    SocketClientCollection  *mClients;
+    std::unordered_map<int, SocketClient*> mClients;
     pthread_mutex_t         mClientsLock;
     int                     mCtrlPipe[2];
     pthread_t               mThread;
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 3f8f3db..128a27a 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 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.
@@ -19,13 +19,15 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/select.h>
+#include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <vector>
+
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <sysutils/SocketListener.h>
@@ -52,7 +54,6 @@
     mSock = socketFd;
     mUseCmdNum = useCmdNum;
     pthread_mutex_init(&mClientsLock, NULL);
-    mClients = new SocketClientCollection();
 }
 
 SocketListener::~SocketListener() {
@@ -63,12 +64,9 @@
         close(mCtrlPipe[0]);
         close(mCtrlPipe[1]);
     }
-    SocketClientCollection::iterator it;
-    for (it = mClients->begin(); it != mClients->end();) {
-        (*it)->decRef();
-        it = mClients->erase(it);
+    for (auto pair : mClients) {
+        pair.second->decRef();
     }
-    delete mClients;
 }
 
 int SocketListener::startListener() {
@@ -95,7 +93,7 @@
         SLOGE("Unable to listen on socket (%s)", strerror(errno));
         return -1;
     } else if (!mListen)
-        mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
+        mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
 
     if (pipe(mCtrlPipe)) {
         SLOGE("pipe failed (%s)", strerror(errno));
@@ -135,11 +133,10 @@
         mSock = -1;
     }
 
-    SocketClientCollection::iterator it;
-    for (it = mClients->begin(); it != mClients->end();) {
-        delete (*it);
-        it = mClients->erase(it);
+    for (auto pair : mClients) {
+        delete pair.second;
     }
+    mClients.clear();
     return 0;
 }
 
@@ -152,47 +149,30 @@
 }
 
 void SocketListener::runListener() {
-
-    SocketClientCollection pendingList;
-
-    while(1) {
-        SocketClientCollection::iterator it;
-        fd_set read_fds;
-        int rc = 0;
-        int max = -1;
-
-        FD_ZERO(&read_fds);
-
-        if (mListen) {
-            max = mSock;
-            FD_SET(mSock, &read_fds);
-        }
-
-        FD_SET(mCtrlPipe[0], &read_fds);
-        if (mCtrlPipe[0] > max)
-            max = mCtrlPipe[0];
+    while (true) {
+        std::vector<pollfd> fds;
 
         pthread_mutex_lock(&mClientsLock);
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
+        fds.reserve(2 + mClients.size());
+        fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN});
+        if (mListen) fds.push_back({.fd = mSock, .events = POLLIN});
+        for (auto pair : mClients) {
             // NB: calling out to an other object with mClientsLock held (safe)
-            int fd = (*it)->getSocket();
-            FD_SET(fd, &read_fds);
-            if (fd > max) {
-                max = fd;
-            }
+            const int fd = pair.second->getSocket();
+            if (fd != pair.first) SLOGE("fd mismatch: %d != %d", fd, pair.first);
+            fds.push_back({.fd = fd, .events = POLLIN});
         }
         pthread_mutex_unlock(&mClientsLock);
-        SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
-        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
-            if (errno == EINTR)
-                continue;
-            SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
+
+        SLOGV("mListen=%d, mSocketName=%s", mListen, mSocketName);
+        int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1));
+        if (rc < 0) {
+            SLOGE("poll failed (%s) mListen=%d", strerror(errno), mListen);
             sleep(1);
             continue;
-        } else if (!rc)
-            continue;
+        }
 
-        if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
+        if (fds[0].revents & (POLLIN | POLLERR)) {
             char c = CtrlPipe_Shutdown;
             TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
             if (c == CtrlPipe_Shutdown) {
@@ -200,7 +180,7 @@
             }
             continue;
         }
-        if (mListen && FD_ISSET(mSock, &read_fds)) {
+        if (mListen && (fds[1].revents & (POLLIN | POLLERR))) {
             int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
             if (c < 0) {
                 SLOGE("accept failed (%s)", strerror(errno));
@@ -208,32 +188,33 @@
                 continue;
             }
             pthread_mutex_lock(&mClientsLock);
-            mClients->push_back(new SocketClient(c, true, mUseCmdNum));
+            mClients[c] = new SocketClient(c, true, mUseCmdNum);
             pthread_mutex_unlock(&mClientsLock);
         }
 
-        /* Add all active clients to the pending list first */
-        pendingList.clear();
+        // Add all active clients to the pending list first, so we can release
+        // the lock before invoking the callbacks.
+        std::vector<SocketClient*> pending;
         pthread_mutex_lock(&mClientsLock);
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
-            SocketClient* c = *it;
-            // NB: calling out to an other object with mClientsLock held (safe)
-            int fd = c->getSocket();
-            if (FD_ISSET(fd, &read_fds)) {
-                pendingList.push_back(c);
+        const int size = fds.size();
+        for (int i = mListen ? 2 : 1; i < size; ++i) {
+            const struct pollfd& p = fds[i];
+            if (p.events & (POLLIN | POLLERR)) {
+                auto it = mClients.find(p.fd);
+                if (it == mClients.end()) {
+                    SLOGE("fd vanished: %d", p.fd);
+                    continue;
+                }
+                SocketClient* c = it->second;
+                pending.push_back(c);
                 c->incRef();
             }
         }
         pthread_mutex_unlock(&mClientsLock);
 
-        /* Process the pending list, since it is owned by the thread,
-         * there is no need to lock it */
-        while (!pendingList.empty()) {
-            /* Pop the first item from the list */
-            it = pendingList.begin();
-            SocketClient* c = *it;
-            pendingList.erase(it);
-            /* Process it, if false is returned, remove from list */
+        for (SocketClient* c : pending) {
+            // Process it, if false is returned, remove from the map
+            SLOGV("processing fd %d", c->getSocket());
             if (!onDataAvailable(c)) {
                 release(c, false);
             }
@@ -246,17 +227,10 @@
     bool ret = false;
     /* if our sockets are connection-based, remove and destroy it */
     if (mListen && c) {
-        /* Remove the client from our array */
+        /* Remove the client from our map */
         SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
         pthread_mutex_lock(&mClientsLock);
-        SocketClientCollection::iterator it;
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
-            if (*it == c) {
-                mClients->erase(it);
-                ret = true;
-                break;
-            }
-        }
+        ret = (mClients.erase(c->getSocket()) != 0);
         pthread_mutex_unlock(&mClientsLock);
         if (ret) {
             ret = c->decRef();
@@ -270,25 +244,19 @@
 }
 
 void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
-    SocketClientCollection safeList;
-
-    /* Add all active clients to the safe list first */
-    safeList.clear();
+    // Add all clients to a separate list first, so we don't have to hold
+    // the lock while processing it.
+    std::vector<SocketClient*> clients;
     pthread_mutex_lock(&mClientsLock);
-    SocketClientCollection::iterator i;
-
-    for (i = mClients->begin(); i != mClients->end(); ++i) {
-        SocketClient* c = *i;
+    clients.reserve(mClients.size());
+    for (auto pair : mClients) {
+        SocketClient* c = pair.second;
         c->incRef();
-        safeList.push_back(c);
+        clients.push_back(c);
     }
     pthread_mutex_unlock(&mClientsLock);
 
-    while (!safeList.empty()) {
-        /* Pop the first item from the list */
-        i = safeList.begin();
-        SocketClient* c = *i;
-        safeList.erase(i);
+    for (SocketClient* c : clients) {
         // broadcasts are unsolicited and should not include a cmd number
         if (c->sendMsg(code, msg, addErrno, false)) {
             SLOGW("Error sending broadcast (%s)", strerror(errno));
@@ -298,25 +266,19 @@
 }
 
 void SocketListener::runOnEachSocket(SocketClientCommand *command) {
-    SocketClientCollection safeList;
-
-    /* Add all active clients to the safe list first */
-    safeList.clear();
+    // Add all clients to a separate list first, so we don't have to hold
+    // the lock while processing it.
+    std::vector<SocketClient*> clients;
     pthread_mutex_lock(&mClientsLock);
-    SocketClientCollection::iterator i;
-
-    for (i = mClients->begin(); i != mClients->end(); ++i) {
-        SocketClient* c = *i;
+    clients.reserve(mClients.size());
+    for (auto pair : mClients) {
+        SocketClient* c = pair.second;
         c->incRef();
-        safeList.push_back(c);
+        clients.push_back(c);
     }
     pthread_mutex_unlock(&mClientsLock);
 
-    while (!safeList.empty()) {
-        /* Pop the first item from the list */
-        i = safeList.begin();
-        SocketClient* c = *i;
-        safeList.erase(i);
+    for (SocketClient* c : clients) {
         command->runSocketCommand(c);
         c->decRef();
     }
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/