Merge "Vboot1.0: remove code to read verity state in userspace"
diff --git a/adb/Android.bp b/adb/Android.bp
index 01e00dd..87e4adc 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -59,6 +59,9 @@
                 // MinGW hides some things behind _POSIX_SOURCE.
                 "-D_POSIX_SOURCE",
 
+                // libusb uses __stdcall on a variadic function, which gets ignored.
+                "-Wno-ignored-attributes",
+
                 // Not supported yet.
                 "-Wno-thread-safety",
             ],
@@ -506,6 +509,52 @@
 }
 
 cc_binary {
+    name: "static_adbd",
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+
+    recovery_available: false,
+    static_executable: true,
+    host_supported: false,
+
+    srcs: [
+        "daemon/main.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    strip: {
+        keep_symbols: true,
+    },
+
+    static_libs: [
+        "libadbd",
+        "libadbd_services",
+        "libasyncio",
+        "libavb_user",
+        "libbase",
+        "libbootloader_message",
+        "libcap",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "libdiagnose_usb",
+        "libext4_utils",
+        "libfec",
+        "libfec_rs",
+        "libfs_mgr",
+        "liblog",
+        "liblp",
+        "libmdnssd",
+        "libminijail",
+        "libselinux",
+        "libsquashfs_utils",
+    ],
+}
+
+cc_binary {
     name: "abb",
 
     defaults: ["adbd_defaults"],
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 2dd22b3..24d4292 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1048,9 +1048,9 @@
         // New transport selection protocol:
         // This is essentially identical to the previous version, except it returns the selected
         // transport id to the caller as well.
-        if (ConsumePrefix(&service, "tport:")) {
+        if (android::base::ConsumePrefix(&service, "tport:")) {
             legacy = false;
-            if (ConsumePrefix(&service, "serial:")) {
+            if (android::base::ConsumePrefix(&service, "serial:")) {
                 serial_storage = service;
                 serial = serial_storage.c_str();
             } else if (service == "usb") {
@@ -1064,7 +1064,7 @@
             // Selection by id is unimplemented, since you obviously already know the transport id
             // you're connecting to.
         } else {
-            if (ConsumePrefix(&service, "transport-id:")) {
+            if (android::base::ConsumePrefix(&service, "transport-id:")) {
                 if (!ParseUint(&transport_id, service)) {
                     SendFail(reply_fd, "invalid transport id");
                     return HostRequestResult::Handled;
@@ -1075,7 +1075,7 @@
                 type = kTransportLocal;
             } else if (service == "transport-any") {
                 type = kTransportAny;
-            } else if (ConsumePrefix(&service, "transport:")) {
+            } else if (android::base::ConsumePrefix(&service, "transport:")) {
                 serial_storage = service;
                 serial = serial_storage.c_str();
             }
@@ -1216,7 +1216,7 @@
     }
 
     // Indicates a new emulator instance has started.
-    if (ConsumePrefix(&service, "emulator:")) {
+    if (android::base::ConsumePrefix(&service, "emulator:")) {
         unsigned int port;
         if (!ParseUint(&port, service)) {
           LOG(ERROR) << "received invalid port for emulator: " << service;
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 5800a62..8b0dcee 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -141,11 +141,3 @@
 
     return true;
 }
-
-inline bool ConsumePrefix(std::string_view* str, std::string_view prefix) {
-  if (str->starts_with(prefix)) {
-    str->remove_prefix(prefix.size());
-    return true;
-  }
-  return false;
-}
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 3eee426..ed6a9a8 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -53,6 +53,25 @@
     *new std::map<std::string, std::shared_ptr<RSA>>;
 static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
 
+static std::string get_user_info() {
+    std::string hostname;
+    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+    char buf[64];
+    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
+#endif
+    if (hostname.empty()) hostname = "unknown";
+
+    std::string username;
+    if (getenv("LOGNAME")) username = getenv("LOGNAME");
+#if !defined(_WIN32)
+    if (username.empty() && getlogin()) username = getlogin();
+#endif
+    if (username.empty()) hostname = "unknown";
+
+    return " " + username + "@" + hostname;
+}
+
 static bool calculate_public_key(std::string* out, RSA* private_key) {
     uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
     if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
@@ -70,6 +89,7 @@
     size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
                                            sizeof(binary_key_data));
     out->resize(actual_length);
+    out->append(get_user_info());
     return true;
 }
 
@@ -79,6 +99,7 @@
     mode_t old_mask;
     FILE *f = nullptr;
     int ret = 0;
+    std::string pubkey;
 
     EVP_PKEY* pkey = EVP_PKEY_new();
     BIGNUM* exponent = BN_new();
@@ -92,6 +113,11 @@
     RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
     EVP_PKEY_set1_RSA(pkey, rsa);
 
+    if (!calculate_public_key(&pubkey, rsa)) {
+        LOG(ERROR) << "failed to calculate public key";
+        goto out;
+    }
+
     old_mask = umask(077);
 
     f = fopen(file.c_str(), "w");
@@ -104,7 +130,12 @@
     umask(old_mask);
 
     if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
-        D("Failed to write key");
+        LOG(ERROR) << "Failed to write key";
+        goto out;
+    }
+
+    if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
+        PLOG(ERROR) << "failed to write public key";
         goto out;
     }
 
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index f25955d..f39aa58 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -838,26 +838,25 @@
 
 #define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
 
-/*
- * The sideload-host protocol serves the data in a file (given on the
- * command line) to the client, using a simple protocol:
- *
- * - The connect message includes the total number of bytes in the
- *   file and a block size chosen by us.
- *
- * - The other side sends the desired block number as eight decimal
- *   digits (eg "00000023" for block 23).  Blocks are numbered from
- *   zero.
- *
- * - We send back the data of the requested block.  The last block is
- *   likely to be partial; when the last block is requested we only
- *   send the part of the block that exists, it's not padded up to the
- *   block size.
- *
- * - When the other side sends "DONEDONE" instead of a block number,
- *   we hang up.
- */
-static int adb_sideload_host(const char* filename) {
+// Connects to the sideload / rescue service on the device (served by minadbd) and sends over the
+// data in an OTA package.
+//
+// It uses a simple protocol as follows.
+//
+// - The connect message includes the total number of bytes in the file and a block size chosen by
+//   us.
+//
+// - The other side sends the desired block number as eight decimal digits (e.g. "00000023" for
+//   block 23). Blocks are numbered from zero.
+//
+// - We send back the data of the requested block. The last block is likely to be partial; when the
+//   last block is requested we only send the part of the block that exists, it's not padded up to
+//   the block size.
+//
+// - When the other side sends "DONEDONE" or "FAILFAIL" instead of a block number, we have done all
+//   the data transfer.
+//
+static int adb_sideload_install(const char* filename, bool rescue_mode) {
     // TODO: use a LinePrinter instead...
     struct stat sb;
     if (stat(filename, &sb) == -1) {
@@ -870,14 +869,18 @@
         return -1;
     }
 
-    std::string service =
-            android::base::StringPrintf("sideload-host:%" PRId64 ":%d",
-                                        static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+    std::string service = android::base::StringPrintf(
+            "%s:%" PRId64 ":%d", rescue_mode ? "rescue-install" : "sideload-host",
+            static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
     unique_fd device_fd(adb_connect(service, &error));
     if (device_fd < 0) {
         fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
 
+        if (rescue_mode) {
+            return -1;
+        }
+
         // If this is a small enough package, maybe this is an older device that doesn't
         // support sideload-host. Try falling back to the older (<= K) sideload method.
         if (sb.st_size > INT_MAX) {
@@ -901,10 +904,14 @@
         }
         buf[8] = '\0';
 
-        if (strcmp("DONEDONE", buf) == 0) {
+        if (strcmp(kMinadbdServicesExitSuccess, buf) == 0 ||
+            strcmp(kMinadbdServicesExitFailure, buf) == 0) {
             printf("\rTotal xfer: %.2fx%*s\n",
                    static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
                    static_cast<int>(strlen(filename) + 10), "");
+            if (strcmp(kMinadbdServicesExitFailure, buf) == 0) {
+                return 1;
+            }
             return 0;
         }
 
@@ -954,6 +961,33 @@
     }
 }
 
+static int adb_wipe_devices() {
+    auto wipe_devices_message_size = strlen(kMinadbdServicesExitSuccess);
+    std::string error;
+    unique_fd fd(adb_connect(
+            android::base::StringPrintf("rescue-wipe:userdata:%zu", wipe_devices_message_size),
+            &error));
+    if (fd < 0) {
+        fprintf(stderr, "adb: wipe device connection failed: %s\n", error.c_str());
+        return 1;
+    }
+
+    std::string message(wipe_devices_message_size, '\0');
+    if (!ReadFdExactly(fd, message.data(), wipe_devices_message_size)) {
+        fprintf(stderr, "adb: failed to read wipe result: %s\n", strerror(errno));
+        return 1;
+    }
+
+    if (message == kMinadbdServicesExitSuccess) {
+        return 0;
+    }
+
+    if (message != kMinadbdServicesExitFailure) {
+        fprintf(stderr, "adb: got unexpected message from rescue wipe %s\n", message.c_str());
+    }
+    return 1;
+}
+
 /**
  * Run ppp in "notty" mode against a resource listed as the first parameter
  * eg:
@@ -1108,7 +1142,7 @@
     // If we were using a specific transport ID, there's nothing we can wait for.
     if (previous_id == 0) {
         adb_set_transport(previous_type, previous_serial, 0);
-        wait_for_device("wait-for-device", 3000ms);
+        wait_for_device("wait-for-device", 6000ms);
     }
 
     return true;
@@ -1628,11 +1662,28 @@
         return adb_kill_server() ? 0 : 1;
     } else if (!strcmp(argv[0], "sideload")) {
         if (argc != 2) error_exit("sideload requires an argument");
-        if (adb_sideload_host(argv[1])) {
+        if (adb_sideload_install(argv[1], false /* rescue_mode */)) {
             return 1;
         } else {
             return 0;
         }
+    } else if (!strcmp(argv[0], "rescue")) {
+        // adb rescue getprop <prop>
+        // adb rescue install <filename>
+        // adb rescue wipe userdata
+        if (argc != 3) error_exit("rescue requires two arguments");
+        if (!strcmp(argv[1], "getprop")) {
+            return adb_connect_command(android::base::StringPrintf("rescue-getprop:%s", argv[2]));
+        } else if (!strcmp(argv[1], "install")) {
+            if (adb_sideload_install(argv[2], true /* rescue_mode */) != 0) {
+                return 1;
+            }
+        } else if (!strcmp(argv[1], "wipe") && !strcmp(argv[2], "userdata")) {
+            return adb_wipe_devices();
+        } else {
+            error_exit("invalid rescue argument");
+        }
+        return 0;
     } else if (!strcmp(argv[0], "tcpip")) {
         if (argc != 2) error_exit("tcpip requires an argument");
         int port;
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index eeac41a..aa75bb1 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -17,6 +17,7 @@
 #include <sys/wait.h>
 
 #include <android-base/cmsg.h>
+#include <android-base/strings.h>
 #include <cmd.h>
 
 #include "adb.h"
@@ -87,9 +88,9 @@
 
         std::string_view name = data;
         auto protocol = SubprocessProtocol::kShell;
-        if (ConsumePrefix(&name, "abb:")) {
+        if (android::base::ConsumePrefix(&name, "abb:")) {
             protocol = SubprocessProtocol::kShell;
-        } else if (ConsumePrefix(&name, "abb_exec:")) {
+        } else if (android::base::ConsumePrefix(&name, "abb_exec:")) {
             protocol = SubprocessProtocol::kNone;
         } else {
             LOG(FATAL) << "Unknown command prefix for abb: " << data;
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index e82a51f..0e70d47 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -229,17 +229,13 @@
 static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
                              uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
                              bool do_unlink) {
+    int rc;
     syncmsg msg;
 
     __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
 
     unique_fd fd(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
 
-    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) <
-        0) {
-        D("[ Failed to fadvise: %d ]", errno);
-    }
-
     if (fd < 0 && errno == ENOENT) {
         if (!secure_mkdirs(Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
@@ -270,6 +266,12 @@
         fchmod(fd.get(), mode);
     }
 
+    rc = posix_fadvise(fd.get(), 0, 0,
+                       POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
+    if (rc != 0) {
+        D("[ Failed to fadvise: %s ]", strerror(rc));
+    }
+
     while (true) {
         if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
 
@@ -464,8 +466,9 @@
         return false;
     }
 
-    if (posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
-        D("[ Failed to fadvise: %d ]", errno);
+    int rc = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+    if (rc != 0) {
+        D("[ Failed to fadvise: %s ]", strerror(rc));
     }
 
     syncmsg msg;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index b0cc450..e6f4499 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -223,13 +223,13 @@
         return create_jdwp_service_socket();
     } else if (name == "track-jdwp") {
         return create_jdwp_tracker_service_socket();
-    } else if (ConsumePrefix(&name, "sink:")) {
+    } else if (android::base::ConsumePrefix(&name, "sink:")) {
         uint64_t byte_count = 0;
         if (!ParseUint(&byte_count, name)) {
             return nullptr;
         }
         return new SinkSocket(byte_count);
-    } else if (ConsumePrefix(&name, "source:")) {
+    } else if (android::base::ConsumePrefix(&name, "source:")) {
         uint64_t byte_count = 0;
         if (!ParseUint(&byte_count, name)) {
             return nullptr;
@@ -250,11 +250,11 @@
 #if defined(__ANDROID__)
     if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (ConsumePrefix(&name, "remount:")) {
+    } else if (android::base::ConsumePrefix(&name, "remount:")) {
         std::string arg(name);
         return create_service_thread("remount",
                                      std::bind(remount_service, std::placeholders::_1, arg));
-    } else if (ConsumePrefix(&name, "reboot:")) {
+    } else if (android::base::ConsumePrefix(&name, "reboot:")) {
         std::string arg(name);
         return create_service_thread("reboot",
                                      std::bind(reboot_service, std::placeholders::_1, arg));
@@ -262,7 +262,7 @@
         return create_service_thread("root", restart_root_service);
     } else if (name.starts_with("unroot:")) {
         return create_service_thread("unroot", restart_unroot_service);
-    } else if (ConsumePrefix(&name, "backup:")) {
+    } else if (android::base::ConsumePrefix(&name, "backup:")) {
         std::string cmd = "/system/bin/bu backup ";
         cmd += name;
         return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
@@ -275,7 +275,7 @@
     } else if (name.starts_with("enable-verity:")) {
         return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
                                                              std::placeholders::_1, true));
-    } else if (ConsumePrefix(&name, "tcpip:")) {
+    } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
         std::string str(name);
 
         int port;
@@ -289,22 +289,22 @@
     }
 #endif
 
-    if (ConsumePrefix(&name, "dev:")) {
+    if (android::base::ConsumePrefix(&name, "dev:")) {
         return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
-    } else if (ConsumePrefix(&name, "jdwp:")) {
+    } else if (android::base::ConsumePrefix(&name, "jdwp:")) {
         pid_t pid;
         if (!ParseUint(&pid, name)) {
             return unique_fd{};
         }
         return create_jdwp_connection_fd(pid);
-    } else if (ConsumePrefix(&name, "shell")) {
+    } else if (android::base::ConsumePrefix(&name, "shell")) {
         return ShellService(name, transport);
-    } else if (ConsumePrefix(&name, "exec:")) {
+    } else if (android::base::ConsumePrefix(&name, "exec:")) {
         return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
     } else if (name.starts_with("sync:")) {
         return create_service_thread("sync", file_sync_service);
-    } else if (ConsumePrefix(&name, "reverse:")) {
+    } else if (android::base::ConsumePrefix(&name, "reverse:")) {
         return reverse_service(name, transport);
     } else if (name == "reconnect") {
         return create_service_thread(
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 889229f..49dce66 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -111,8 +111,11 @@
             WriteFdFmt(fd, "%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
         }
     } else if (errno) {
-        WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
-                   mount_point, strerror(errno));
+        int expected_errno = enable ? EBUSY : ENOENT;
+        if (errno != expected_errno) {
+            WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n",
+                       enable ? "teardown" : "setup", mount_point, strerror(errno));
+        }
     }
     WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
     return true;
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 3b29ab5..0a116ab 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -58,10 +58,12 @@
 static std::optional<bool> gFfsAioSupported;
 
 // Not all USB controllers support operations larger than 16k, so don't go above that.
-static constexpr size_t kUsbReadQueueDepth = 32;
+// Also, each submitted operation does an allocation in the kernel of that size, so we want to
+// minimize our queue depth while still maintaining a deep enough queue to keep the USB stack fed.
+static constexpr size_t kUsbReadQueueDepth = 8;
 static constexpr size_t kUsbReadSize = 4 * PAGE_SIZE;
 
-static constexpr size_t kUsbWriteQueueDepth = 32;
+static constexpr size_t kUsbWriteQueueDepth = 8;
 static constexpr size_t kUsbWriteSize = 4 * PAGE_SIZE;
 
 static const char* to_string(enum usb_functionfs_event_type type) {
@@ -308,11 +310,13 @@
                         if (bound) {
                             LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
                             running = false;
+                            break;
                         }
 
                         if (enabled) {
                             LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
                             running = false;
+                            break;
                         }
 
                         bound = true;
@@ -322,11 +326,13 @@
                         if (!bound) {
                             LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
                             running = false;
+                            break;
                         }
 
                         if (enabled) {
                             LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
                             running = false;
+                            break;
                         }
 
                         enabled = true;
diff --git a/adb/services.cpp b/adb/services.cpp
index 335ffc4..6185aa6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -100,7 +100,7 @@
     ConnectionState state;
 };
 
-static void wait_for_state(int fd, state_info* sinfo) {
+static void wait_for_state(unique_fd fd, state_info* sinfo) {
     D("wait_for_state %d", sinfo->state);
 
     while (true) {
@@ -122,7 +122,7 @@
         }
 
         if (!is_ambiguous) {
-            adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+            adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
             int rc = adb_poll(&pfd, 1, 100);
             if (rc < 0) {
                 SendFail(fd, error);
@@ -140,7 +140,6 @@
         }
     }
 
-    adb_close(fd);
     D("wait_for_state is done");
 }
 
@@ -203,7 +202,7 @@
         return create_device_tracker(false);
     } else if (name == "track-devices-l") {
         return create_device_tracker(true);
-    } else if (ConsumePrefix(&name, "wait-for-")) {
+    } else if (android::base::ConsumePrefix(&name, "wait-for-")) {
         std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
@@ -213,11 +212,11 @@
         sinfo->serial = serial;
         sinfo->transport_id = transport_id;
 
-        if (ConsumePrefix(&name, "local")) {
+        if (android::base::ConsumePrefix(&name, "local")) {
             sinfo->transport_type = kTransportLocal;
-        } else if (ConsumePrefix(&name, "usb")) {
+        } else if (android::base::ConsumePrefix(&name, "usb")) {
             sinfo->transport_type = kTransportUsb;
-        } else if (ConsumePrefix(&name, "any")) {
+        } else if (android::base::ConsumePrefix(&name, "any")) {
             sinfo->transport_type = kTransportAny;
         } else {
             return nullptr;
@@ -241,11 +240,10 @@
             return nullptr;
         }
 
-        unique_fd fd = create_service_thread("wait", [sinfo](int fd) {
-            wait_for_state(fd, sinfo.get());
-        });
+        unique_fd fd = create_service_thread(
+                "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
         return create_local_socket(std::move(fd));
-    } else if (ConsumePrefix(&name, "connect:")) {
+    } else if (android::base::ConsumePrefix(&name, "connect:")) {
         std::string host(name);
         unique_fd fd = create_service_thread(
                 "connect", std::bind(connect_service, std::placeholders::_1, host));
diff --git a/adb/services.h b/adb/services.h
index 0ce25ba..6fc89d7 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -23,5 +23,10 @@
 constexpr char kShellServiceArgPty[] = "pty";
 constexpr char kShellServiceArgShellProtocol[] = "v2";
 
+// Special flags sent by minadbd. They indicate the end of sideload transfer and the result of
+// installation or wipe.
+constexpr char kMinadbdServicesExitSuccess[] = "DONEDONE";
+constexpr char kMinadbdServicesExitFailure[] = "FAILFAIL";
+
 unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func);
 #endif  // SERVICES_H_
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8a2bf9a..75993b3 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -31,6 +31,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/strings.h>
+
 #if !ADB_HOST
 #include <android-base/properties.h>
 #include <log/log_properties.h>
@@ -755,26 +757,26 @@
 
 #if ADB_HOST
     service = std::string_view(s->smart_socket_data).substr(4);
-    if (ConsumePrefix(&service, "host-serial:")) {
+    if (android::base::ConsumePrefix(&service, "host-serial:")) {
         // serial number should follow "host:" and could be a host:port string.
         if (!internal::parse_host_service(&serial, &service, service)) {
             LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
             goto fail;
         }
-    } else if (ConsumePrefix(&service, "host-transport-id:")) {
+    } else if (android::base::ConsumePrefix(&service, "host-transport-id:")) {
         if (!ParseUint(&transport_id, service, &service)) {
             LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
             return -1;
         }
-        if (!ConsumePrefix(&service, ":")) {
+        if (!android::base::ConsumePrefix(&service, ":")) {
             LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
             return -1;
         }
-    } else if (ConsumePrefix(&service, "host-usb:")) {
+    } else if (android::base::ConsumePrefix(&service, "host-usb:")) {
         type = kTransportUsb;
-    } else if (ConsumePrefix(&service, "host-local:")) {
+    } else if (android::base::ConsumePrefix(&service, "host-local:")) {
         type = kTransportLocal;
-    } else if (ConsumePrefix(&service, "host:")) {
+    } else if (android::base::ConsumePrefix(&service, "host:")) {
         type = kTransportAny;
     } else {
         service = std::string_view{};
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 8e9716f..b1c22c9 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -18,6 +18,7 @@
 
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <vector>
 
 namespace android {
@@ -68,5 +69,21 @@
 // Tests whether 'lhs' equals 'rhs', ignoring case.
 bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
 
+// Removes `prefix` from the start of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
+  if (!StartsWith(*s, prefix)) return false;
+  s->remove_prefix(prefix.size());
+  return true;
+}
+
+// Removes `suffix` from the end of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
+  if (!EndsWith(*s, suffix)) return false;
+  s->remove_suffix(suffix.size());
+  return true;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 9d74094..ca3c0b8 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -295,3 +295,19 @@
 TEST(strings, ubsan_28729303) {
   android::base::Split("/dev/null", ":");
 }
+
+TEST(strings, ConsumePrefix) {
+  std::string_view s{"foo.bar"};
+  ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
+  ASSERT_EQ("foo.bar", s);
+  ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
+  ASSERT_EQ("bar", s);
+}
+
+TEST(strings, ConsumeSuffix) {
+  std::string_view s{"foo.bar"};
+  ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
+  ASSERT_EQ("foo.bar", s);
+  ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
+  ASSERT_EQ("foo", s);
+}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 71d3ecb..8979b0c 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -25,6 +25,8 @@
 # Best guess to an average device's reboot time, refined as tests return
 DURATION_DEFAULT=45
 STOP_ON_FAILURE=false
+progname="${0##*/}"
+progpath="${0%${progname}}"
 
 # Helper functions
 
@@ -42,11 +44,40 @@
   adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
 }
 
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+  local args=
+  for i in "${@}"; do
+    [ -z "${args}" ] || args="${args} "
+    if [ X"${i}" != X"${i#\'}" ]; then
+      args="${args}${i}"
+    elif [ X"${i}" != X"${i#*\\}" ]; then
+      args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`"
+    elif [ X"${i}" != X"${i#* }" ]; then
+      args="${args}'${i}'"
+    elif [ X"${i}" != X"${i#*${TAB}}" ]; then
+      args="${args}'${i}'"
+    else
+      args="${args}${i}"
+    fi
+  done
+  adb shell "${args}"
+}
+
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+  adb_sh su root "${@}"
+}
+
 [ "USAGE: hasPstore
 
 Returns: true if device (likely) has pstore data" ]
 hasPstore() {
-  if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+  if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then
     false
   fi
 }
@@ -55,7 +86,7 @@
 
 Returns the property value" ]
 get_property() {
-  adb shell getprop ${1} 2>&1 </dev/null
+  adb_sh getprop ${1} 2>&1 </dev/null
 }
 
 [ "USAGE: isDebuggable
@@ -89,18 +120,18 @@
 Returns: true if device supports and set boot reason injection" ]
 setBootloaderBootReason() {
   inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
-  if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then
+  if [ -z "`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </dev/null`" ]; then
     echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
     return 1
   fi
   checkDebugBuild || return 1
-  if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" |
+  if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" </dev/null |
      grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
     echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
     echo "       does not set androidboot.bootreason kernel parameter." >&2
     return 1
   fi
-  adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
+  adb_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null </dev/null
   test_reason="`get_property persist.test.boot.reason`"
   if [ X"${test_reason}" != X"${1}" ]; then
     echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
@@ -299,7 +330,14 @@
   return ${save_ret}
 }
 
-[ "USAGE: report_bootstat_logs <expected> ...
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+  adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: report_bootstat_logs [-t<timestamp>] <expected> ...
 
 if not prefixed with a minus (-), <expected> will become a series of expected
 matches:
@@ -314,8 +352,11 @@
 report_bootstat_logs() {
   save_ret=${?}
   match=
+  timestamp=-d
   for i in "${@}"; do
-    if [ X"${i}" != X"${i#-}" ] ; then
+    if [ X"${i}" != X"${i#-t}" ]; then
+      timestamp="${i}"
+    elif [ X"${i}" != X"${i#-}" ]; then
       match="${match}
 ${i#-}"
     else
@@ -323,12 +364,13 @@
 bootstat: Canonical boot reason: ${i}"
     fi
   done
-  adb logcat -b all -d |
+  adb logcat -b all ${timestamp} |
   grep bootstat[^e] |
   grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
 bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
 bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
 bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason
 bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
 bootstat: Service started: /system/bin/bootstat -l
 bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
@@ -341,6 +383,8 @@
 init    : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
 init    : processing action (boot) from (/system/etc/init/bootstat.rc
 init    : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init    : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc
+init    : processing action (zygote-start) from (/system/etc/init/bootstat.rc
 init    : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
  (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
  (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
@@ -355,6 +399,8 @@
  (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
  (/system/bin/bootstat --record_time_since_factory_reset)'...
  (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat --set_system_boot_reason)'...
+ (/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE}
  (/system/bin/bootstat -l)'...
  (/system/bin/bootstat -l)' (pid " |
   grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
@@ -613,7 +659,7 @@
 test_optional_ota() {
   checkDebugBuild || return
   duration_test
-  adb shell su root touch /data/misc/bootstat/build_date >&2
+  adb_su touch /data/misc/bootstat/build_date >&2 </dev/null
   adb reboot ota
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,ota
@@ -679,7 +725,7 @@
 test_factory_reset() {
   checkDebugBuild || return
   duration_test
-  adb shell su root rm /data/misc/bootstat/build_date >&2
+  adb_su rm /data/misc/bootstat/build_date >&2 </dev/null
   adb reboot >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
@@ -715,7 +761,7 @@
   wait_for_screen
   ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
-  EXPECT_PROPERTY sys.boot.reason.last ""
+  EXPECT_PROPERTY sys.boot.reason.last "\(\|bootloader\)"
   check_boilerplate_properties
   report_bootstat_logs reboot,factory_reset bootloader \
     "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
@@ -766,12 +812,12 @@
   enterPstore
   # Send it _many_ times to combat devices with flakey pstore
   for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
-    echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+    echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
   done
   adb reboot cold >&2
   adb wait-for-device
   wait_for_screen
-  adb shell su root \
+  adb_su </dev/null \
     cat /proc/fs/pstore/console-ramoops \
         /proc/fs/pstore/console-ramoops-0 2>/dev/null |
     grep 'healthd: battery l=' |
@@ -780,7 +826,7 @@
       if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
         # retry
         for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
-          echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+          echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
         done
         adb reboot cold >&2
         adb wait-for-device
@@ -806,7 +852,7 @@
 test_optional_battery() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,battery
+  adb_sh setprop sys.powerctl shutdown,battery </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -827,7 +873,7 @@
 test_optional_battery_thermal() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,thermal,battery
+  adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -866,7 +912,7 @@
     panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
     pstore_ok=true
   fi
-  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  echo c | adb_su tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
   EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -893,8 +939,8 @@
     panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
     pstore_ok=true
   fi
-  echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg
-  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  echo "SysRq : Trigger a crash : 'test'" | adb_su tee /dev/kmsg
+  echo c | adb_su tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
   EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -924,7 +970,7 @@
     pstore_ok=true
   fi
   echo "Kernel panic - not syncing: hung_task: blocked tasks" |
-    adb shell su root tee /dev/kmsg
+    adb_su tee /dev/kmsg
   adb reboot warm
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
@@ -956,7 +1002,7 @@
 test_thermal_shutdown() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,thermal
+  adb_sh setprop sys.powerctl shutdown,thermal </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -977,7 +1023,7 @@
 test_userrequested_shutdown() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,userrequested
+  adb_sh setprop sys.powerctl shutdown,userrequested </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -996,7 +1042,7 @@
 - NB: should report reboot,shell" ]
 test_shell_reboot() {
   duration_test
-  adb shell reboot
+  adb_sh reboot </dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
   EXPECT_PROPERTY sys.boot.reason.last reboot,shell
@@ -1032,7 +1078,7 @@
 test_optional_rescueparty() {
   blind_reboot_test
   echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2
-  EXPECT_PROPERTY ro.boot.bootreason reboot,rescueparty
+  EXPECT_PROPERTY ro.boot.bootreason '\(reboot\|reboot,rescueparty\)'
 }
 
 [ "USAGE: test_Its_Just_So_Hard_reboot
@@ -1049,7 +1095,7 @@
   else
     duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
   fi
-  adb shell 'reboot "Its Just So Hard"'
+  adb_sh 'reboot "Its Just So Hard"' </dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
   EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
@@ -1146,7 +1192,121 @@
   run_bootloader
 }
 
-[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+[ "USAGE: run_kBootReasonMap [--boot_reason_enum] value expected
+
+bootloader boot reason injection tests:
+- if --boot_reason_enum run bootstat executable for result instead.
+- inject boot reason into sys.boot.reason
+- run bootstat --set_system_boot_reason
+- check for expected enum
+- " ]
+run_kBootReasonMap() {
+  if [ X"--boot_reason_enum" = X"${1}" ]; then
+    shift
+    local sys_expected="${1}"
+    shift
+    local enum_expected="${1}"
+    adb_su bootstat --boot_reason_enum="${sys_expected}" |
+      (
+        local retval=-1
+        while read -r id match; do
+          if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then
+            retval=0
+          fi
+          if [ ${enum_expected} != ${id} ]; then
+            echo "ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}" >&2
+            retval=1
+          fi
+        done
+        exit ${retval}
+      )
+    return
+  fi
+  local sys_expected="${1}"
+  shift
+  local enum_expected="${1}"
+  adb_su setprop sys.boot.reason "${sys_expected}" </dev/null
+  adb_su bootstat --record_boot_reason </dev/null
+  # Check values
+  EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+  local retval=${?}
+  local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null`
+  [ "${enum_expected}" = "${result}" ] ||
+    (
+      [ -n "${result}" ] || result="<nothing>"
+      echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2
+      false
+    ) ||
+    retval=${?}
+  return ${retval}
+}
+
+[ "USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout
+
+convert any regex expressions into a series of non-regex test strings" ]
+filter_kBootReasonMap() {
+  while read -r id match; do
+    case ${match} in
+      'reboot,[empty]')
+        echo ${id}          # matches b/c of special case
+        echo ${id} reboot,y # matches b/c of regex
+        echo 1 reboot,empty # negative test (ID for unknown is 1)
+        ;;
+      reboot)
+        echo 1 reboog       # negative test (ID for unknown is 1)
+        ;;
+      'reboot,pmic_off_fault,.*')
+        echo ${id} reboot,pmic_off_fault,hello,world
+        echo ${id} reboot,pmic_off_fault,
+        echo 1 reboot,pmic_off_fault
+        ;;
+    esac
+    echo ${id} "${match}"   # matches b/c of exact
+  done
+}
+
+[ "USAGE: test_kBootReasonMap
+
+kBootReasonMap test
+- (wait until screen is up, boot has completed)
+- read bootstat for kBootReasonMap entries and test them all" ]
+test_kBootReasonMap() {
+  checkDebugBuild || return
+  duration_test 15
+  local tempfile="`mktemp`"
+  local arg=--boot_reason_enum
+  adb_su bootstat ${arg} </dev/null 2>/dev/null |
+    filter_kBootReasonMap >${tempfile}
+  if [ ! -s "${tempfile}" ]; then
+    wait_for_screen
+    arg=
+    sed -n <${progpath}bootstat.cpp \
+      '/kBootReasonMap = {/,/^};/s/.*{"\([^"]*\)", *\([0-9][0-9]*\)},.*/\2 \1/p' |
+      sed 's/\\\\/\\/g' |
+      filter_kBootReasonMap >${tempfile}
+  fi
+  T=`adb_date`
+  retval=0
+  while read -r enum string; do
+    if [ X"${string}" != X"${string#*[[].[]]}" -o X"${string}" != X"${string#*\\.}" ]; then
+      if [ 'reboot\.empty' != "${string}" ]; then
+        echo "WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}" >&2
+        enum=1
+      fi
+    fi
+    run_kBootReasonMap ${arg} "${string}" "${enum}" </dev/null || retval=${?}
+  done <${tempfile}
+  rm ${tempfile}
+  ( exit ${retval} )
+  # See filter_kBootReasonMap() for negative tests and add them here too
+  report_bootstat_logs -t${T} \
+    '-bootstat: Service started: bootstat --boot_reason_enum=' \
+    '-bootstat: Unknown boot reason: reboot,empty' \
+    '-bootstat: Unknown boot reason: reboog' \
+    '-bootstat: Unknown boot reason: reboot,pmic_off_fault'
+}
+
+[ "USAGE: ${progname} [-s SERIAL] [tests]...
 
 Mainline executive to run the above tests" ]
 
@@ -1161,7 +1321,7 @@
 if [ X"--macros" != X"${1}" ]; then
 
   if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
-    echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+    echo "USAGE: ${progname} [-s SERIAL] [tests]..."
     echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
     exit 0
   fi
@@ -1210,7 +1370,7 @@
                Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
                bootloader_kernel_panic bootloader_oem_powerkey \
                bootloader_wdog_reset bootloader_cold bootloader_warm \
-               bootloader_hard bootloader_recovery
+               bootloader_hard bootloader_recovery kBootReasonMap
     fi
     if [ X"nothing" = X"${1}" ]; then
       shift 1
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1ce0ec4..558e6c4 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -89,7 +89,7 @@
 }
 
 void ShowHelp(const char* cmd) {
-  fprintf(stderr, "Usage: %s [options]\n", cmd);
+  fprintf(stderr, "Usage: %s [options]...\n", cmd);
   fprintf(stderr,
           "options include:\n"
           "  -h, --help              Show this help\n"
@@ -99,7 +99,8 @@
           "  --value                 Optional value to associate with the boot event\n"
           "  --record_boot_complete  Record metrics related to the time for the device boot\n"
           "  --record_boot_reason    Record the reason why the device booted\n"
-          "  --record_time_since_factory_reset Record the time since the device was reset\n");
+          "  --record_time_since_factory_reset  Record the time since the device was reset\n"
+          "  --boot_reason_enum=<reason>  Report the match to the kBootReasonMap table\n");
 }
 
 // Constructs a readable, printable string from the givencommand line
@@ -120,9 +121,10 @@
 // A mapping from boot reason string, as read from the ro.boot.bootreason
 // system property, to a unique integer ID. Viewers of log data dashboards for
 // the boot_reason metric may refer to this mapping to discern the histogram
-// values.
+// values.  Regex matching, to manage the scale, as a minimum require either
+// [, \ or * to be present in the string to switch to checking.
 const std::map<std::string, int32_t> kBootReasonMap = {
-    {"empty", kEmptyBootReason},
+    {"reboot,[empty]", kEmptyBootReason},
     {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
     {"normal", 2},
     {"recovery", 3},
@@ -299,6 +301,9 @@
     {"reboot,dm-verity_device_corrupted", 172},
     {"reboot,dm-verity_enforcing", 173},
     {"reboot,keys_clear", 174},
+    {"reboot,pmic_off_fault,.*", 175},
+    {"reboot,pmic_off_s3rst,.*", 176},
+    {"reboot,pmic_off_other,.*", 177},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -314,6 +319,16 @@
     return kEmptyBootReason;
   }
 
+  for (const auto& [match, id] : kBootReasonMap) {
+    // Regex matches as a minimum require either [, \ or * to be present.
+    if (match.find_first_of("[\\*") == match.npos) continue;
+    // enforce match from beginning to end
+    auto exact = match;
+    if (exact[0] != '^') exact = "^" + exact;
+    if (exact[exact.size() - 1] != '$') exact = exact + "$";
+    if (std::regex_search(boot_reason, std::regex(exact))) return id;
+  }
+
   LOG(INFO) << "Unknown boot reason: " << boot_reason;
   return kUnknownBootReason;
 }
@@ -1266,6 +1281,19 @@
   boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
 }
 
+// List the associated boot reason(s), if arg is nullptr then all.
+void PrintBootReasonEnum(const char* arg) {
+  int value = -1;
+  if (arg != nullptr) {
+    value = BootReasonStrToEnum(arg);
+  }
+  for (const auto& [match, id] : kBootReasonMap) {
+    if ((value < 0) || (value == id)) {
+      printf("%u\t%s\n", id, match.c_str());
+    }
+  }
+}
+
 }  // namespace
 
 int main(int argc, char** argv) {
@@ -1280,6 +1308,7 @@
   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 char boot_reason_enum_str[] = "boot_reason_enum";
   static const struct option long_options[] = {
       // clang-format off
       { "help",                 no_argument,       NULL,   'h' },
@@ -1291,6 +1320,7 @@
       { boot_complete_str,      no_argument,       NULL,   0 },
       { boot_reason_str,        no_argument,       NULL,   0 },
       { factory_reset_str,      no_argument,       NULL,   0 },
+      { boot_reason_enum_str,   optional_argument, NULL,   0 },
       { NULL,                   0,                 NULL,   0 }
       // clang-format on
   };
@@ -1315,6 +1345,8 @@
           RecordBootReason();
         } else if (option_name == factory_reset_str) {
           RecordFactoryReset();
+        } else if (option_name == boot_reason_enum_str) {
+          PrintBootReasonEnum(optarg);
         } else {
           LOG(ERROR) << "Invalid option: " << option_name;
         }
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 70583af..eb4b1b8 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -23,6 +23,9 @@
 
 struct ThreadInfo {
   std::unique_ptr<unwindstack::Regs> registers;
+
+  pid_t uid;
+
   pid_t tid;
   std::string thread_name;
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 3196ce8..88c206f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -343,6 +343,16 @@
   ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
 
+TEST_F(TombstoneTest, dump_thread_info_uid) {
+  dump_thread_info(&log_, ThreadInfo{.uid = 1,
+                                     .pid = 2,
+                                     .tid = 3,
+                                     .thread_name = "some_thread",
+                                     .process_name = "some_process"});
+  std::string expected = "pid: 2, tid: 3, name: some_thread  >>> some_process <<<\nuid: 1\n";
+  ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
+
 TEST_F(TombstoneTest, dump_timestamp) {
   setenv("TZ", "UTC", 1);
   tzset();
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d1726cd..d246722 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -151,6 +151,7 @@
 
   _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", thread_info.pid,
        thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
+  _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
 }
 
 static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
@@ -615,6 +616,7 @@
 
 void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext) {
+  pid_t uid = getuid();
   pid_t pid = getpid();
   pid_t tid = gettid();
 
@@ -636,6 +638,7 @@
   std::map<pid_t, ThreadInfo> threads;
   threads[gettid()] = ThreadInfo{
       .registers = std::move(regs),
+      .uid = uid,
       .tid = tid,
       .thread_name = thread_name,
       .pid = pid,
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 4ee9624..ffde114 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -121,9 +121,14 @@
     shared_libs: [
         "libbootloader_message",
         "libbase",
+        "libcutils",
         "libcrypto",
+        "libext4_utils",
         "libfec",
         "libfs_mgr",
+        "liblog",
+        "liblp",
+        "libselinux",
     ],
     header_libs: [
         "libcutils_headers",
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 6c50a17..78455d4 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -38,6 +38,7 @@
 
 using android::base::ParseByteCount;
 using android::base::ParseInt;
+using android::base::ReadFileToString;
 using android::base::Split;
 using android::base::StartsWith;
 
@@ -656,6 +657,8 @@
         TransformFstabForGsi(fstab);
     }
 
+    SkipMountingPartitions(fstab);
+
     return true;
 }
 
@@ -683,6 +686,36 @@
         return false;
     }
 
+    SkipMountingPartitions(fstab);
+
+    return true;
+}
+
+// For GSI to skip mounting /product and /product_services, until there are
+// well-defined interfaces between them and /system. Otherwise, the GSI flashed
+// on /system might not be able to work with /product and /product_services.
+// When they're skipped here, /system/product and /system/product_services in
+// GSI will be used.
+bool SkipMountingPartitions(Fstab* fstab) {
+    constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
+
+    std::string skip_config;
+    if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
+        return true;
+    }
+
+    for (const auto& skip_mount_point : Split(skip_config, "\n")) {
+        if (skip_mount_point.empty()) {
+            continue;
+        }
+        auto it = std::remove_if(fstab->begin(), fstab->end(),
+                                 [&skip_mount_point](const auto& entry) {
+                                     return entry.mount_point == skip_mount_point;
+                                 });
+        fstab->erase(it, fstab->end());
+        LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
+    }
+
     return true;
 }
 
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 8984752..71c4072 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -636,7 +636,7 @@
         LERROR << mnt_type << " has no mkfs cookbook";
         return false;
     }
-    command += " " + scratch_device;
+    command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
     fs_mgr_set_blk_ro(scratch_device, false);
     auto ret = system(command.c_str());
     if (ret) {
@@ -646,6 +646,25 @@
     return true;
 }
 
+static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
+    auto& dm = DeviceMapper::Instance();
+
+    // Remove <other> partitions
+    for (const auto& group : builder->ListGroups()) {
+        for (const auto& part : builder->ListPartitionsInGroup(group)) {
+            const auto& name = part->name();
+            if (!android::base::EndsWith(name, suffix)) {
+                continue;
+            }
+            if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name, 2s)) {
+                continue;
+            }
+            builder->ResizePartition(builder->FindPartition(name), 0);
+        }
+    }
+}
+
+// This is where we find and steal backing storage from the system.
 bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
                                      bool* partition_exists, bool* change) {
     *scratch_device = fs_mgr_overlayfs_scratch_device();
@@ -692,15 +711,24 @@
             // the adb remount overrides :-( .
             auto margin_size = uint64_t(3 * 256 * 1024);
             BlockDeviceInfo info;
-            if (builder->GetBlockDeviceInfo(partition_name, &info)) {
+            if (builder->GetBlockDeviceInfo(fs_mgr_get_super_partition_name(slot_number), &info)) {
                 margin_size = 3 * info.logical_block_size;
             }
             partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
                                       partition_size / 2);
             if (partition_size > partition->size()) {
                 if (!builder->ResizePartition(partition, partition_size)) {
-                    LERROR << "resize " << partition_name;
-                    return false;
+                    // Try to free up space by deallocating partitions in the other slot.
+                    TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
+
+                    partition_size =
+                            builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+                    partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+                                              partition_size / 2);
+                    if (!builder->ResizePartition(partition, partition_size)) {
+                        LERROR << "resize " << partition_name;
+                        return false;
+                    }
                 }
                 if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
                 changed = true;
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index cbe2008..00334bc 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -249,6 +249,7 @@
 
     // Check verity and optionally setup overlayfs backing.
     auto reboot_later = false;
+    auto user_please_reboot_later = false;
     auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
     for (auto it = partitions.begin(); it != partitions.end();) {
         auto& entry = *it;
@@ -262,7 +263,7 @@
                             false);
                     avb_ops_user_free(ops);
                     if (ret) {
-                        LOG(WARNING) << "Disable verity for " << mount_point;
+                        LOG(WARNING) << "Disabling verity for " << mount_point;
                         reboot_later = can_reboot;
                         if (reboot_later) {
                             // w/o overlayfs available, also check for dedupe
@@ -272,20 +273,22 @@
                             }
                             reboot(false);
                         }
+                        user_please_reboot_later = true;
                     } else if (fs_mgr_set_blk_ro(entry.blk_device, false)) {
                         fec::io fh(entry.blk_device.c_str(), O_RDWR);
                         if (fh && fh.set_verity_status(false)) {
-                            LOG(WARNING) << "Disable verity for " << mount_point;
+                            LOG(WARNING) << "Disabling verity for " << mount_point;
                             reboot_later = can_reboot;
                             if (reboot_later && !uses_overlayfs) {
                                 ++it;
                                 continue;
                             }
+                            user_please_reboot_later = true;
                         }
                     }
                 }
             }
-            LOG(ERROR) << "Skipping " << mount_point;
+            LOG(ERROR) << "Skipping " << mount_point << " for remount";
             it = partitions.erase(it);
             continue;
         }
@@ -307,6 +310,10 @@
 
     if (partitions.empty()) {
         if (reboot_later) reboot(false);
+        if (user_please_reboot_later) {
+            LOG(INFO) << "Now reboot your device for settings to take effect";
+            return 0;
+        }
         LOG(WARNING) << "No partitions to remount";
         return retval;
     }
@@ -383,6 +390,10 @@
     }
 
     if (reboot_later) reboot(false);
+    if (user_please_reboot_later) {
+        LOG(INFO) << "Now reboot your device for settings to take effect";
+        return 0;
+    }
 
     return retval;
 }
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 149b2d3..c7193ab 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -98,6 +98,7 @@
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
 bool ReadFstabFromDt(Fstab* fstab, bool log = true);
 bool ReadDefaultFstab(Fstab* fstab);
+bool SkipMountingPartitions(Fstab* fstab);
 
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 
diff --git a/init/Android.bp b/init/Android.bp
index 69ee34f..69498ac 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -61,6 +61,7 @@
     static_libs: [
         "libseccomp_policy",
         "libavb",
+        "libc++fs",
         "libcgrouprc_format",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
diff --git a/init/Android.mk b/init/Android.mk
index 39af0e6..b02c926 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -65,15 +65,22 @@
 LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
 
+# Install adb_debug.prop into debug ramdisk.
+# This allows adb root on a user build, when debug ramdisk is used.
+LOCAL_REQUIRED_MODULES := \
+   adb_debug.prop \
+
 # Set up the same mount points on the ramdisk that system-as-root contains.
 LOCAL_POST_INSTALL_CMD := mkdir -p \
     $(TARGET_RAMDISK_OUT)/apex \
+    $(TARGET_RAMDISK_OUT)/debug_ramdisk \
     $(TARGET_RAMDISK_OUT)/dev \
     $(TARGET_RAMDISK_OUT)/mnt \
     $(TARGET_RAMDISK_OUT)/proc \
     $(TARGET_RAMDISK_OUT)/sys \
 
 LOCAL_STATIC_LIBRARIES := \
+    libc++fs \
     libfs_avb \
     libfs_mgr \
     libfec \
diff --git a/init/README.md b/init/README.md
index da8b275..d7f809f 100644
--- a/init/README.md
+++ b/init/README.md
@@ -191,7 +191,7 @@
 
 `critical`
 > This is a device-critical service. If it exits more than four times in
-  four minutes, the device will reboot into bootloader.
+  four minutes or before boot completes, the device will reboot into bootloader.
 
 `disabled`
 > This service will not automatically start with its class.
diff --git a/init/debug_ramdisk.h b/init/debug_ramdisk.h
new file mode 100644
index 0000000..4e3a395
--- /dev/null
+++ b/init/debug_ramdisk.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 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
+
+namespace android {
+namespace init {
+
+constexpr const char kDebugRamdiskProp[] = "/debug_ramdisk/adb_debug.prop";
+constexpr const char kDebugRamdiskSEPolicy[] = "/debug_ramdisk/userdebug_plat_sepolicy.cil";
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c566676..8b95e38 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <filesystem>
 #include <string>
 #include <vector>
 
@@ -35,6 +36,7 @@
 #include <cutils/android_reboot.h>
 #include <private/android_filesystem_config.h>
 
+#include "debug_ramdisk.h"
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
 #include "switch_root.h"
@@ -44,6 +46,8 @@
 
 using namespace std::literals;
 
+namespace fs = std::filesystem;
+
 namespace android {
 namespace init {
 
@@ -159,6 +163,9 @@
     CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
 
+    // /debug_ramdisk is used to preserve additional files from the debug ramdisk
+    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"));
 #undef CHECKCALL
 
     // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
@@ -202,7 +209,14 @@
     // If this file is present, the second-stage init will use a userdebug sepolicy
     // and load adb_debug.prop to allow adb root, if the device is unlocked.
     if (access("/force_debuggable", F_OK) == 0) {
-        setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+        std::error_code ec;  // to invoke the overloaded copy_file() that won't throw.
+        if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
+            !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
+            LOG(ERROR) << "Failed to setup debug ramdisk";
+        } else {
+            // setenv for second-stage init to read above kDebugRamdisk* files.
+            setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+        }
     }
 
     if (!DoFirstStageMount()) {
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 7e07ad5..85fa874 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -43,7 +43,6 @@
 #include "uevent_listener.h"
 #include "util.h"
 
-using android::base::ReadFileToString;
 using android::base::Split;
 using android::base::Timer;
 using android::fs_mgr::AvbHandle;
@@ -55,6 +54,7 @@
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::ReadDefaultFstab;
 using android::fs_mgr::ReadFstabFromDt;
+using android::fs_mgr::SkipMountingPartitions;
 
 using namespace std::literals;
 
@@ -524,38 +524,10 @@
     return true;
 }
 
-// For GSI to skip mounting /product and /product_services, until there are
-// well-defined interfaces between them and /system. Otherwise, the GSI flashed
-// on /system might not be able to work with /product and /product_services.
-// When they're skipped here, /system/product and /system/product_services in
-// GSI will be used.
-bool FirstStageMount::TrySkipMountingPartitions() {
-    constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
-
-    std::string skip_config;
-    if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
-        return true;
-    }
-
-    for (const auto& skip_mount_point : Split(skip_config, "\n")) {
-        if (skip_mount_point.empty()) {
-            continue;
-        }
-        auto it = std::remove_if(fstab_.begin(), fstab_.end(),
-                                 [&skip_mount_point](const auto& entry) {
-                                     return entry.mount_point == skip_mount_point;
-                                 });
-        fstab_.erase(it, fstab_.end());
-        LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
-    }
-
-    return true;
-}
-
 bool FirstStageMount::MountPartitions() {
     if (!TrySwitchSystemAsRoot()) return false;
 
-    if (!TrySkipMountingPartitions()) return false;
+    if (!SkipMountingPartitions(&fstab_)) return false;
 
     for (auto current = fstab_.begin(); current != fstab_.end();) {
         // We've already mounted /system above.
diff --git a/init/init.cpp b/init/init.cpp
index ac0e67a..c79e459 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -621,6 +621,12 @@
     });
 }
 
+static void UmountDebugRamdisk() {
+    if (umount("/debug_ramdisk") != 0) {
+        LOG(ERROR) << "Failed to umount /debug_ramdisk";
+    }
+}
+
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -690,6 +696,7 @@
     InstallSignalFdHandler(&epoll);
 
     property_load_boot_defaults(load_debug_prop);
+    UmountDebugRamdisk();
     fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
     StartPropertyService(&epoll);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 467568c..a1e9551 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -41,7 +41,9 @@
 
 #include <map>
 #include <memory>
+#include <mutex>
 #include <queue>
+#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -56,6 +58,7 @@
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 
+#include "debug_ramdisk.h"
 #include "epoll.h"
 #include "init.h"
 #include "persistent_properties.h"
@@ -82,6 +85,8 @@
 namespace android {
 namespace init {
 
+static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+
 static bool persistent_properties_loaded = false;
 
 static int property_set_fd = -1;
@@ -99,7 +104,24 @@
     const char* name;
 };
 
+static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+    auto* d = reinterpret_cast<PropertyAuditData*>(data);
+
+    if (!d || !d->name || !d->cr) {
+        LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+        return 0;
+    }
+
+    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+             d->cr->gid);
+    return 0;
+}
+
 void property_init() {
+    selinux_callback cb;
+    cb.func_audit = PropertyAuditCallback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
     mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
     CreateSerializedPropertyInfo();
     if (__system_property_area_init()) {
@@ -186,88 +208,51 @@
     return PROP_SUCCESS;
 }
 
-typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+class AsyncRestorecon {
+  public:
+    void TriggerRestorecon(const std::string& path) {
+        auto guard = std::lock_guard{mutex_};
+        paths_.emplace(path);
 
-struct PropertyChildInfo {
-    pid_t pid;
-    PropertyAsyncFunc func;
-    std::string name;
-    std::string value;
+        if (!thread_started_) {
+            thread_started_ = true;
+            std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
+        }
+    }
+
+  private:
+    void ThreadFunction() {
+        auto lock = std::unique_lock{mutex_};
+
+        while (!paths_.empty()) {
+            auto path = paths_.front();
+            paths_.pop();
+
+            lock.unlock();
+            if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+                LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
+            }
+            android::base::SetProperty(kRestoreconProperty, path);
+            lock.lock();
+        }
+
+        thread_started_ = false;
+    }
+
+    std::mutex mutex_;
+    std::queue<std::string> paths_;
+    bool thread_started_ = false;
 };
 
-static std::queue<PropertyChildInfo> property_children;
-
-static void PropertyChildLaunch() {
-    auto& info = property_children.front();
-    pid_t pid = fork();
-    if (pid < 0) {
-        LOG(ERROR) << "Failed to fork for property_set_async";
-        while (!property_children.empty()) {
-            property_children.pop();
-        }
-        return;
-    }
-    if (pid != 0) {
-        info.pid = pid;
-    } else {
-        if (info.func(info.name, info.value) != 0) {
-            LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
-                       << "\") failed";
-        }
-        _exit(0);
-    }
-}
-
-bool PropertyChildReap(pid_t pid) {
-    if (property_children.empty()) {
-        return false;
-    }
-    auto& info = property_children.front();
-    if (info.pid != pid) {
-        return false;
-    }
-    std::string error;
-    if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
-        LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
-                   << error;
-    }
-    property_children.pop();
-    if (!property_children.empty()) {
-        PropertyChildLaunch();
-    }
-    return true;
-}
-
-static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
-                                 PropertyAsyncFunc func, std::string* error) {
-    if (value.empty()) {
-        return PropertySet(name, value, error);
-    }
-
-    PropertyChildInfo info;
-    info.func = func;
-    info.name = name;
-    info.value = value;
-    property_children.push(info);
-    if (property_children.size() == 1) {
-        PropertyChildLaunch();
-    }
-    return PROP_SUCCESS;
-}
-
-static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
-    return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
 uint32_t InitPropertySet(const std::string& name, const std::string& value) {
     if (StartsWith(name, "ctl.")) {
         LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
                       "functions directly";
         return PROP_ERROR_INVALID_NAME;
     }
-    if (name == "selinux.restorecon_recursive") {
-        LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
-                      "restorecon builtin directly";
+    if (name == kRestoreconProperty) {
+        LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
+                   << "' from init; use the restorecon builtin directly";
         return PROP_ERROR_INVALID_NAME;
     }
 
@@ -328,18 +313,20 @@
         return result == sizeof(value);
     }
 
+    bool GetSourceContext(std::string* source_context) const {
+        char* c_source_context = nullptr;
+        if (getpeercon(socket_, &c_source_context) != 0) {
+            return false;
+        }
+        *source_context = c_source_context;
+        freecon(c_source_context);
+        return true;
+    }
+
     int socket() { return socket_; }
 
     const ucred& cred() { return cred_; }
 
-    std::string source_context() const {
-        char* source_context = nullptr;
-        getpeercon(socket_, &source_context);
-        std::string result = source_context;
-        freecon(source_context);
-        return result;
-    }
-
   private:
     bool PollIn(uint32_t* timeout_ms) {
         struct pollfd ufds[1];
@@ -505,8 +492,14 @@
                   << process_log_string;
     }
 
-    if (name == "selinux.restorecon_recursive") {
-        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+    // If a process other than init is writing a non-empty value, it means that process is
+    // requesting that init performs a restorecon operation on the path specified by 'value'.
+    // We use a thread to do this restorecon operation to prevent holding up init, as it may take
+    // a long time to complete.
+    if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
+        static AsyncRestorecon async_restorecon;
+        async_restorecon.TriggerRestorecon(value);
+        return PROP_SUCCESS;
     }
 
     return PropertySet(name, value, error);
@@ -552,10 +545,15 @@
         prop_name[PROP_NAME_MAX-1] = 0;
         prop_value[PROP_VALUE_MAX-1] = 0;
 
+        std::string source_context;
+        if (!socket.GetSourceContext(&source_context)) {
+            PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
+            return;
+        }
+
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result =
-            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+        uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
         if (result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -574,9 +572,16 @@
           return;
         }
 
+        std::string source_context;
+        if (!socket.GetSourceContext(&source_context)) {
+            PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
+            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+            return;
+        }
+
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+        uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
         if (result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -660,7 +665,7 @@
             }
 
             if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
-                key == "selinux.restorecon_recursive"s) {
+                std::string{key} == kRestoreconProperty) {
                 LOG(ERROR) << "Ignoring disallowed property '" << key
                            << "' with special meaning in prop file '" << filename << "'";
                 continue;
@@ -887,9 +892,8 @@
     load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
 
     if (load_debug_prop) {
-        constexpr static const char kAdbDebugProp[] = "/system/etc/adb_debug.prop";
-        LOG(INFO) << "Loading " << kAdbDebugProp;
-        load_properties_from_file(kAdbDebugProp, nullptr, &properties);
+        LOG(INFO) << "Loading " << kDebugRamdiskProp;
+        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
     }
 
     for (const auto& [name, value] : properties) {
@@ -906,19 +910,6 @@
     update_sys_usb_config();
 }
 
-static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
-    auto* d = reinterpret_cast<PropertyAuditData*>(data);
-
-    if (!d || !d->name || !d->cr) {
-        LOG(ERROR) << "AuditCallback invoked with null data arguments!";
-        return 0;
-    }
-
-    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
-             d->cr->gid);
-    return 0;
-}
-
 bool LoadPropertyInfoFromFile(const std::string& filename,
                               std::vector<PropertyInfoEntry>* property_infos) {
     auto file_contents = std::string();
@@ -989,10 +980,6 @@
 }
 
 void StartPropertyService(Epoll* epoll) {
-    selinux_callback cb;
-    cb.func_audit = SelinuxAuditCallback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
     property_set("ro.property_service.version", "2");
 
     property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
diff --git a/init/property_service.h b/init/property_service.h
index 207c03b..7f9f844 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,8 +32,6 @@
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                            const std::string& source_context, const ucred& cr, std::string* error);
 
-extern bool PropertyChildReap(pid_t pid);
-
 void property_init();
 void property_load_boot_defaults(bool load_debug_prop);
 void load_persist_props();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 5b90969..0966b6c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -19,13 +19,14 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <linux/fs.h>
-#include <mntent.h>
 #include <linux/loop.h>
+#include <mntent.h>
+#include <semaphore.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
-#include <sys/swap.h>
 #include <sys/stat.h>
+#include <sys/swap.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -57,11 +58,14 @@
 #include "service.h"
 #include "sigchld_handler.h"
 
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
 using android::base::GetBoolProperty;
 using android::base::Split;
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::unique_fd;
+using android::base::WriteStringToFile;
 
 namespace android {
 namespace init {
@@ -207,8 +211,8 @@
     }
     FindPartitionsToUmount(nullptr, nullptr, true);
     // dump current CPU stack traces and uninterruptible tasks
-    android::base::WriteStringToFile("l", "/proc/sysrq-trigger");
-    android::base::WriteStringToFile("w", "/proc/sysrq-trigger");
+    WriteStringToFile("l", PROC_SYSRQ);
+    WriteStringToFile("w", PROC_SYSRQ);
 }
 
 static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -248,7 +252,91 @@
     }
 }
 
-static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
+static void KillAllProcesses() {
+    WriteStringToFile("i", PROC_SYSRQ);
+}
+
+// Create reboot/shutdwon monitor thread
+void RebootMonitorThread(unsigned int cmd, const std::string& rebootTarget, sem_t* reboot_semaphore,
+                         std::chrono::milliseconds shutdown_timeout, bool* reboot_monitor_run) {
+    unsigned int remaining_shutdown_time = 0;
+
+    // 30 seconds more than the timeout passed to the thread as there is a final Umount pass
+    // after the timeout is reached.
+    constexpr unsigned int shutdown_watchdog_timeout_default = 30;
+    auto shutdown_watchdog_timeout = android::base::GetUintProperty(
+            "ro.build.shutdown.watchdog.timeout", shutdown_watchdog_timeout_default);
+    remaining_shutdown_time = shutdown_watchdog_timeout + shutdown_timeout.count() / 1000;
+
+    while (*reboot_monitor_run == true) {
+        if (TEMP_FAILURE_RETRY(sem_wait(reboot_semaphore)) == -1) {
+            LOG(ERROR) << "sem_wait failed and exit RebootMonitorThread()";
+            return;
+        }
+
+        timespec shutdown_timeout_timespec;
+        if (clock_gettime(CLOCK_MONOTONIC, &shutdown_timeout_timespec) == -1) {
+            LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+            return;
+        }
+
+        // If there are some remaining shutdown time left from previous round, we use
+        // remaining time here.
+        shutdown_timeout_timespec.tv_sec += remaining_shutdown_time;
+
+        LOG(INFO) << "shutdown_timeout_timespec.tv_sec: " << shutdown_timeout_timespec.tv_sec;
+
+        int sem_return = 0;
+        while ((sem_return = sem_timedwait_monotonic_np(reboot_semaphore,
+                                                        &shutdown_timeout_timespec)) == -1 &&
+               errno == EINTR) {
+        }
+
+        if (sem_return == -1) {
+            LOG(ERROR) << "Reboot thread timed out";
+
+            if (android::base::GetBoolProperty("ro.debuggable", false) == true) {
+                LOG(INFO) << "Try to dump init process call trace:";
+                const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
+                int status;
+                android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true,
+                                        LOG_KLOG, true, nullptr, nullptr, 0);
+
+                LOG(INFO) << "Show stack for all active CPU:";
+                WriteStringToFile("l", PROC_SYSRQ);
+
+                LOG(INFO) << "Show tasks that are in disk sleep(uninterruptable sleep), which are "
+                             "like "
+                             "blocked in mutex or hardware register access:";
+                WriteStringToFile("w", PROC_SYSRQ);
+            }
+
+            // In shutdown case,notify kernel to sync and umount fs to read-only before shutdown.
+            if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
+                WriteStringToFile("s", PROC_SYSRQ);
+
+                WriteStringToFile("u", PROC_SYSRQ);
+
+                RebootSystem(cmd, rebootTarget);
+            }
+
+            LOG(ERROR) << "Trigger crash at last!";
+            WriteStringToFile("c", PROC_SYSRQ);
+        } else {
+            timespec current_time_timespec;
+
+            if (clock_gettime(CLOCK_MONOTONIC, &current_time_timespec) == -1) {
+                LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+                return;
+            }
+
+            remaining_shutdown_time =
+                    shutdown_timeout_timespec.tv_sec - current_time_timespec.tv_sec;
+
+            LOG(INFO) << "remaining_shutdown_time: " << remaining_shutdown_time;
+        }
+    }
+}
 
 /* Try umounting all emulated file systems R/W block device cfile systems.
  * This will just try umount and give it up if it fails.
@@ -259,7 +347,8 @@
  *
  * return true when umount was successful. false when timed out.
  */
-static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
+static UmountStat TryUmountAndFsck(unsigned int cmd, const std::string& rebootTarget, bool runFsck,
+                                   std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
     Timer t;
     std::vector<MountEntry> block_devices;
     std::vector<MountEntry> emulated_devices;
@@ -279,11 +368,17 @@
     }
 
     if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+        LOG(INFO) << "Pause reboot monitor thread before fsck";
+        sem_post(reboot_semaphore);
+
         // fsck part is excluded from timeout check. It only runs for user initiated shutdown
         // and should not affect reboot time.
         for (auto& entry : block_devices) {
             entry.DoFsck();
         }
+
+        LOG(INFO) << "Resume reboot monitor thread after fsck";
+        sem_post(reboot_semaphore);
     }
     return stat;
 }
@@ -311,7 +406,7 @@
     }
     LOG(INFO) << "swapoff() took " << swap_timer;;
 
-    if (!android::base::WriteStringToFile("1", ZRAM_RESET)) {
+    if (!WriteStringToFile("1", ZRAM_RESET)) {
         LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
         return;
     }
@@ -369,6 +464,23 @@
     }
     LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
 
+    sem_t reboot_semaphore;
+    if (sem_init(&reboot_semaphore, false, 0) == -1) {
+        // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
+        LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
+        RebootSystem(cmd, rebootTarget);
+    }
+
+    // Start a thread to monitor init shutdown process
+    LOG(INFO) << "Create reboot monitor thread.";
+    bool reboot_monitor_run = true;
+    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, rebootTarget, &reboot_semaphore,
+                                      shutdown_timeout, &reboot_monitor_run);
+    reboot_monitor_thread.detach();
+
+    // Start reboot monitor thread
+    sem_post(&reboot_semaphore);
+
     // keep debugging tools until non critical ones are all gone.
     const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
@@ -497,7 +609,8 @@
     // 5. drop caches and disable zram backing device, if exist
     KillZramBackingDevice();
 
-    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
+    UmountStat stat = TryUmountAndFsck(cmd, rebootTarget, runFsck, shutdown_timeout - t.duration(),
+                                       &reboot_semaphore);
     // Follow what linux shutdown is doing: one more sync with little bit delay
     {
         Timer sync_timer;
@@ -507,6 +620,11 @@
     }
     if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
     LogShutdownTime(stat, &t);
+
+    // Send signal to terminate reboot monitor thread.
+    reboot_monitor_run = false;
+    sem_post(&reboot_semaphore);
+
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
     RebootSystem(cmd, rebootTarget);
     abort();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index aa66baa..c49dc9f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -64,6 +64,7 @@
 #include <fs_avb/fs_avb.h>
 #include <selinux/android.h>
 
+#include "debug_ramdisk.h"
 #include "reboot_utils.h"
 #include "util.h"
 
@@ -271,8 +272,6 @@
 }
 
 constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-constexpr const char userdebug_plat_policy_cil_file[] =
-        "/system/etc/selinux/userdebug_plat_sepolicy.cil";
 
 bool IsSplitPolicyDevice() {
     return access(plat_policy_cil_file, R_OK) != -1;
@@ -292,7 +291,7 @@
     const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
     bool use_userdebug_policy =
             ((force_debuggable_env && "true"s == force_debuggable_env) &&
-             AvbHandle::IsDeviceUnlocked() && access(userdebug_plat_policy_cil_file, F_OK) == 0);
+             AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
     if (use_userdebug_policy) {
         LOG(WARNING) << "Using userdebug system sepolicy";
     }
@@ -332,6 +331,12 @@
     }
     std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
 
+    std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +
+                                     ".compat.cil");
+    if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {
+        plat_compat_cil_file.clear();
+    }
+
     std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
     if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
         product_policy_cil_file.clear();
@@ -367,7 +372,7 @@
     // clang-format off
     std::vector<const char*> compile_args {
         "/system/bin/secilc",
-        use_userdebug_policy ? userdebug_plat_policy_cil_file : plat_policy_cil_file,
+        use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,
         "-m", "-M", "true", "-G", "-N",
         "-c", version_as_string.c_str(),
         plat_mapping_file.c_str(),
@@ -377,6 +382,9 @@
     };
     // clang-format on
 
+    if (!plat_compat_cil_file.empty()) {
+        compile_args.push_back(plat_compat_cil_file.c_str());
+    }
     if (!product_policy_cil_file.empty()) {
         compile_args.push_back(product_policy_cil_file.c_str());
     }
diff --git a/init/service.cpp b/init/service.cpp
index f5c13b9..276c2aa 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -372,16 +372,20 @@
         return;
     }
 
-    // If we crash > 4 times in 4 minutes, reboot into bootloader or set crashing property
+    // If we crash > 4 times in 4 minutes or before boot_completed,
+    // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
     if (((flags_ & SVC_CRITICAL) || !pre_apexd_) && !(flags_ & SVC_RESTART)) {
-        if (now < time_crashed_ + 4min) {
+        bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+        if (now < time_crashed_ + 4min || !boot_completed) {
             if (++crash_count_ > 4) {
                 if (flags_ & SVC_CRITICAL) {
                     // Aborts into bootloader
-                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
                 } else {
-                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times in 4 minutes";
+                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
                     // Notifies update_verifier and apexd
                     property_set("ro.init.updatable_crashing", "1");
                 }
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 0b03324..987b2f9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -29,7 +29,6 @@
 #include <android-base/stringprintf.h>
 
 #include "init.h"
-#include "property_service.h"
 #include "service.h"
 
 using android::base::StringPrintf;
@@ -61,9 +60,7 @@
     std::string wait_string;
     Service* service = nullptr;
 
-    if (PropertyChildReap(pid)) {
-        name = "Async property child";
-    } else if (SubcontextChildReap(pid)) {
+    if (SubcontextChildReap(pid)) {
         name = "Subcontext";
     } else {
         service = ServiceList::GetInstance().FindService(pid, &Service::pid);
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
index 6ae79d4..614510c 100644
--- a/libion/ion_4.12.h
+++ b/libion/ion_4.12.h
@@ -1,125 +1,50 @@
-/*
- * Adapted from drivers/staging/android/uapi/ion.h
- *
- * Copyright (C) 2011 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
 #ifndef _UAPI_LINUX_ION_NEW_H
 #define _UAPI_LINUX_ION_NEW_H
-
 #include <linux/ioctl.h>
 #include <linux/types.h>
-
 #define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
-
-/**
- * DOC: Ion Userspace API
- *
- * create a client by opening /dev/ion
- * most operations handled via following ioctls
- *
- */
-
-/**
- * struct ion_new_allocation_data - metadata passed from userspace for allocations
- * @len:		size of the allocation
- * @heap_id_mask:	mask of heap ids to allocate from
- * @flags:		flags passed to heap
- * @handle:		pointer that will be populated with a cookie to use to
- *			refer to this allocation
- *
- * Provided by userspace as an argument to the ioctl - added _new to denote
- * this belongs to the new ION interface.
- */
 struct ion_new_allocation_data {
-    __u64 len;
-    __u32 heap_id_mask;
-    __u32 flags;
-    __u32 fd;
-    __u32 unused;
+  __u64 len;
+  __u32 heap_id_mask;
+  __u32 flags;
+  __u32 fd;
+  __u32 unused;
 };
-
 #define MAX_HEAP_NAME 32
-
-/**
- * struct ion_heap_data - data about a heap
- * @name - first 32 characters of the heap name
- * @type - heap type
- * @heap_id - heap id for the heap
- */
 struct ion_heap_data {
-    char name[MAX_HEAP_NAME];
-    __u32 type;
-    __u32 heap_id;
-    __u32 reserved0;
-    __u32 reserved1;
-    __u32 reserved2;
+  char name[MAX_HEAP_NAME];
+  __u32 type;
+  __u32 heap_id;
+  __u32 reserved0;
+  __u32 reserved1;
+  __u32 reserved2;
 };
-
-/**
- * struct ion_heap_query - collection of data about all heaps
- * @cnt - total number of heaps to be copied
- * @heaps - buffer to copy heap data
- */
 struct ion_heap_query {
-    __u32 cnt;       /* Total number of heaps to be copied */
-    __u32 reserved0; /* align to 64bits */
-    __u64 heaps;     /* buffer to be populated */
-    __u32 reserved1;
-    __u32 reserved2;
+  __u32 cnt;
+  __u32 reserved0;
+  __u64 heaps;
+  __u32 reserved1;
+  __u32 reserved2;
 };
-
 #define ION_IOC_MAGIC 'I'
-
-/**
- * DOC: ION_IOC_NEW_ALLOC - allocate memory
- *
- * Takes an ion_allocation_data struct and returns it with the handle field
- * populated with the opaque handle for the allocation.
- * TODO: This IOCTL will clash by design; however, only one of
- *  ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
- *  so this should not conflict.
- */
 #define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
-
-/**
- * DOC: ION_IOC_FREE - free memory
- *
- * Takes an ion_handle_data struct and frees the handle.
- *
- * #define ION_IOC_FREE		_IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
- *
- * Takes an ion_fd_data struct with the handle field populated with a valid
- * opaque handle.  Returns the struct with the fd field set to a file
- * descriptor open in the current address space.  This file descriptor
- * can then be passed to another process.  The corresponding opaque handle can
- * be retrieved via ION_IOC_IMPORT.
- *
- * #define ION_IOC_SHARE		_IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_HEAP_QUERY - information about available heaps
- *
- * Takes an ion_heap_query structure and populates information about
- * available Ion heaps.
- */
 #define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
-
-#endif /* _UAPI_LINUX_ION_NEW_H */
+#endif
diff --git a/libion/original-kernel-headers/linux/ion_4.12.h b/libion/original-kernel-headers/linux/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion_4.12.h
@@ -0,0 +1,125 @@
+/*
+ * Adapted from drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_new_allocation_data - metadata passed from userspace for allocations
+ * @len:		size of the allocation
+ * @heap_id_mask:	mask of heap ids to allocate from
+ * @flags:		flags passed to heap
+ * @handle:		pointer that will be populated with a cookie to use to
+ *			refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl - added _new to denote
+ * this belongs to the new ION interface.
+ */
+struct ion_new_allocation_data {
+    __u64 len;
+    __u32 heap_id_mask;
+    __u32 flags;
+    __u32 fd;
+    __u32 unused;
+};
+
+#define MAX_HEAP_NAME 32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+    char name[MAX_HEAP_NAME];
+    __u32 type;
+    __u32 heap_id;
+    __u32 reserved0;
+    __u32 reserved1;
+    __u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+    __u32 cnt;       /* Total number of heaps to be copied */
+    __u32 reserved0; /* align to 64bits */
+    __u64 heaps;     /* buffer to be populated */
+    __u32 reserved1;
+    __u32 reserved2;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_NEW_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ * TODO: This IOCTL will clash by design; however, only one of
+ *  ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
+ *  so this should not conflict.
+ */
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ *
+ * #define ION_IOC_FREE		_IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle.  Returns the struct with the fd field set to a file
+ * descriptor open in the current address space.  This file descriptor
+ * can then be passed to another process.  The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ *
+ * #define ION_IOC_SHARE		_IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index b2f0ed9..935590d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -122,9 +122,10 @@
  *
  * Most callers should use
  * [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
- * `<assert.h>` instead, or the `__assert` and `__assert2` functions provided by
- * bionic if more control is needed. They support automatically including the
- * source filename and line number more conveniently than this function.
+ * `&lt;assert.h&gt;` instead, or the `__assert` and `__assert2` functions
+ * provided by bionic if more control is needed. They support automatically
+ * including the source filename and line number more conveniently than this
+ * function.
  */
 void __android_log_assert(const char* cond, const char* tag, const char* fmt,
                           ...)
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
index 468a498..b7279d1 100644
--- a/liblog/log_portability.h
+++ b/liblog/log_portability.h
@@ -19,16 +19,6 @@
 #include <sys/cdefs.h>
 #include <unistd.h>
 
-/*
- * Declare this library function as reimplementation.
- * Prevent circular dependencies, but allow _real_ library to hijack
- */
-#if defined(_WIN32)
-#define LIBLOG_WEAK static /* Accept that it is totally private */
-#else
-#define LIBLOG_WEAK extern "C" __attribute__((weak, visibility("default")))
-#endif
-
 /* possible missing definitions in sys/cdefs.h */
 
 /* DECLS */
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index bc056cb..6b5ea4c 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <sys/param.h>
 #include <sys/types.h>
+#include <wchar.h>
 
 #include <cutils/list.h>
 #include <log/log.h>
@@ -1134,66 +1135,13 @@
 }
 
 /*
- * One utf8 character at a time
- *
- * Returns the length of the utf8 character in the buffer,
- * or -1 if illegal or truncated
- *
- * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
- * can not remove from here because of library circular dependencies.
- * Expect one-day utf8_character_length with the same signature could
- * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
- * propagate globally.
- */
-LIBLOG_WEAK ssize_t utf8_character_length(const char* src, size_t len) {
-  const char* cur = src;
-  const char first_char = *cur++;
-  static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
-  int32_t mask, to_ignore_mask;
-  size_t num_to_read;
-  uint32_t utf32;
-
-  if ((first_char & 0x80) == 0) { /* ASCII */
-    return first_char ? 1 : -1;
-  }
-
-  /*
-   * (UTF-8's character must not be like 10xxxxxx,
-   *  but 110xxxxx, 1110xxxx, ... or 1111110x)
-   */
-  if ((first_char & 0x40) == 0) {
-    return -1;
-  }
-
-  for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
-       num_to_read < 5 && (first_char & mask); num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
-    if (num_to_read > len) {
-      return -1;
-    }
-    if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
-      return -1;
-    }
-    utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
-  }
-  /* "first_char" must be (110xxxxx - 11110xxx) */
-  if (num_to_read >= 5) {
-    return -1;
-  }
-  to_ignore_mask |= mask;
-  utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
-  if (utf32 > kUnicodeMaxCodepoint) {
-    return -1;
-  }
-  return num_to_read;
-}
-
-/*
  * Convert to printable from message to p buffer, return string length. If p is
  * NULL, do not copy, but still return the expected string length.
  */
-static size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+size_t convertPrintable(char* p, const char* message, size_t messageLen) {
   char* begin = p;
   bool print = p != NULL;
+  mbstate_t mb_state = {};
 
   while (messageLen) {
     char buf[6];
@@ -1201,11 +1149,10 @@
     if ((size_t)len > messageLen) {
       len = messageLen;
     }
-    len = utf8_character_length(message, len);
+    len = mbrtowc(nullptr, message, len, &mb_state);
 
     if (len < 0) {
-      snprintf(buf, sizeof(buf), ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
-               *message & 0377);
+      snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
       len = 1;
     } else {
       buf[0] = '\0';
@@ -1225,7 +1172,7 @@
         } else if (*message == '\\') {
           strcpy(buf, "\\\\");
         } else if ((*message < ' ') || (*message & 0x80)) {
-          snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
+          snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
         }
       }
       if (!buf[0]) {
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 50755ce..d9d1a21 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -62,6 +62,7 @@
         "log_system_test.cpp",
         "log_time_test.cpp",
         "log_wrap_test.cpp",
+        "logprint_test.cpp",
     ],
     shared_libs: [
         "libcutils",
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
new file mode 100644
index 0000000..7ca02ac
--- /dev/null
+++ b/liblog/tests/logprint_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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 <gtest/gtest.h>
+
+size_t convertPrintable(char* p, const char* message, size_t messageLen);
+
+TEST(liblog, convertPrintable_ascii) {
+  auto input = "easy string, output same";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+  EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_escapes) {
+  // Note that \t is not escaped.
+  auto input = "escape\a\b\t\v\f\r\\";
+  auto expected_output = "escape\\a\\b\t\\v\\f\\r\\\\";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+  EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_validutf8) {
+  auto input = u8"¢ह€𐍈";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(input));
+  EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_invalidutf8) {
+  auto input = "\x80\xC2\x01\xE0\xA4\x06\xE0\x06\xF0\x90\x8D\x06\xF0\x90\x06\xF0\x0E";
+  auto expected_output =
+      "\\x80\\xC2\\x01\\xE0\\xA4\\x06\\xE0\\x06\\xF0\\x90\\x8D\\x06\\xF0\\x90\\x06\\xF0\\x0E";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+  EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_mixed) {
+  auto input =
+      u8"\x80\xC2¢ह€𐍈\x01\xE0\xA4\x06¢ह€𐍈\xE0\x06\a\b\xF0\x90¢ह€𐍈\x8D\x06\xF0\t\t\x90\x06\xF0\x0E";
+  auto expected_output =
+      u8"\\x80\\xC2¢ह€𐍈\\x01\\xE0\\xA4\\x06¢ह€𐍈\\xE0\\x06\\a\\b\\xF0\\x90¢ह€𐍈\\x8D\\x06\\xF0\t\t"
+      u8"\\x90\\x06\\xF0\\x0E";
+  auto output_size = convertPrintable(nullptr, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+
+  char output[output_size];
+
+  output_size = convertPrintable(output, input, strlen(input));
+  EXPECT_EQ(output_size, strlen(expected_output));
+  EXPECT_STREQ(expected_output, output);
+}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 66cb49f..4d65b50 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -17,7 +17,9 @@
     name: "libnativeloader",
     defaults: ["libnativeloader-defaults"],
     host_supported: true,
-    srcs: ["native_loader.cpp"],
+    srcs: [
+        "native_loader.cpp",
+    ],
     shared_libs: [
         "libnativehelper",
         "liblog",
@@ -26,6 +28,9 @@
     ],
     target: {
         android: {
+            srcs: [
+                "library_namespaces.cpp",
+            ],
             shared_libs: [
                 "libdl_android",
             ],
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
new file mode 100644
index 0000000..3c4911f
--- /dev/null
+++ b/libnativeloader/library_namespaces.cpp
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2019 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 "library_namespaces.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/properties.h"
+#include "android-base/strings.h"
+#include "nativehelper/ScopedUtfChars.h"
+#include "nativeloader/dlext_namespaces.h"
+
+namespace android {
+
+namespace {
+using namespace std::string_literals;
+
+constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] = "/etc/public.libraries.txt";
+constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
+constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
+    sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
+constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
+constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
+    sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
+constexpr const char kPublicNativeLibrariesVendorConfig[] = "/vendor/etc/public.libraries.txt";
+constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] = "/etc/llndk.libraries.txt";
+constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] = "/etc/vndksp.libraries.txt";
+
+const std::vector<const std::string> kRuntimePublicLibraries = {
+    "libicuuc.so",
+    "libicui18n.so",
+};
+
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+constexpr const char* kVendorNamespaceName = "sphal";
+constexpr const char* kVndkNamespaceName = "vndk";
+constexpr const char* kDefaultNamespaceName = "default";
+constexpr const char* kPlatformNamespaceName = "platform";
+constexpr const char* kRuntimeNamespaceName = "runtime";
+
+// classloader-namespace is a linker namespace that is created for the loaded
+// app. To be specific, it is created for the app classloader. When
+// System.load() is called from a Java class that is loaded from the
+// classloader, the classloader-namespace namespace associated with that
+// classloader is selected for dlopen. The namespace is configured so that its
+// search path is set to the app-local JNI directory and it is linked to the
+// platform namespace with the names of libs listed in the public.libraries.txt.
+// This way an app can only load its own JNI libraries along with the public libs.
+constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+// Same thing for vendor APKs.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
+
+// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
+// System.load() with an absolute path which is outside of the classloader library search path.
+// This list includes all directories app is allowed to access this way.
+constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
+
+#if defined(__LP64__)
+constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+constexpr const char* kVendorLibPath = "/vendor/lib64";
+constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
+#else
+constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+constexpr const char* kVendorLibPath = "/vendor/lib";
+constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
+#endif
+
+const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+  APK_ORIGIN_DEFAULT = 0,
+  APK_ORIGIN_VENDOR = 1,
+  APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
+bool is_debuggable() {
+  bool debuggable = false;
+  debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+  return debuggable;
+}
+
+std::string vndk_version_str() {
+  std::string version = android::base::GetProperty("ro.vndk.version", "");
+  if (version != "" && version != "current") {
+    return "." + version;
+  }
+  return "";
+}
+
+void insert_vndk_version_str(std::string* file_name) {
+  CHECK(file_name != nullptr);
+  size_t insert_pos = file_name->find_last_of(".");
+  if (insert_pos == std::string::npos) {
+    insert_pos = file_name->length();
+  }
+  file_name->insert(insert_pos, vndk_version_str());
+}
+
+const std::function<bool(const std::string&, std::string*)> always_true =
+    [](const std::string&, std::string*) { return true; };
+
+bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
+                const std::function<bool(const std::string& /* soname */,
+                                         std::string* /* error_msg */)>& check_soname,
+                std::string* error_msg = nullptr) {
+  // Read list of public native libraries from the config file.
+  std::string file_content;
+  if (!base::ReadFileToString(configFile, &file_content)) {
+    if (error_msg) *error_msg = strerror(errno);
+    return false;
+  }
+
+  std::vector<std::string> lines = base::Split(file_content, "\n");
+
+  for (auto& line : lines) {
+    auto trimmed_line = base::Trim(line);
+    if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+      continue;
+    }
+    size_t space_pos = trimmed_line.rfind(' ');
+    if (space_pos != std::string::npos) {
+      std::string type = trimmed_line.substr(space_pos + 1);
+      if (type != "32" && type != "64") {
+        if (error_msg) *error_msg = "Malformed line: " + line;
+        return false;
+      }
+#if defined(__LP64__)
+      // Skip 32 bit public library.
+      if (type == "32") {
+        continue;
+      }
+#else
+      // Skip 64 bit public library.
+      if (type == "64") {
+        continue;
+      }
+#endif
+      trimmed_line.resize(space_pos);
+    }
+
+    if (check_soname(trimmed_line, error_msg)) {
+      sonames->push_back(trimmed_line);
+    } else {
+      return false;
+    }
+  }
+  return true;
+}
+
+void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
+  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
+  if (dir != nullptr) {
+    // Failing to opening the dir is not an error, which can happen in
+    // webview_zygote.
+    while (struct dirent* ent = readdir(dir.get())) {
+      if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+        continue;
+      }
+      const std::string filename(ent->d_name);
+      if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
+          android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
+        const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
+        const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
+        const std::string company_name = filename.substr(start, end - start);
+        const std::string config_file_path = dirname + "/"s + filename;
+        LOG_ALWAYS_FATAL_IF(
+            company_name.empty(),
+            "Error extracting company name from public native library list file path \"%s\"",
+            config_file_path.c_str());
+
+        std::string error_msg;
+
+        LOG_ALWAYS_FATAL_IF(
+            !ReadConfig(config_file_path, sonames,
+                        [&company_name](const std::string& soname, std::string* error_msg) {
+                          if (android::base::StartsWith(soname, "lib") &&
+                              android::base::EndsWith(soname, "." + company_name + ".so")) {
+                            return true;
+                          } else {
+                            *error_msg = "Library name \"" + soname +
+                                         "\" does not end with the company name: " + company_name +
+                                         ".";
+                            return false;
+                          }
+                        },
+                        &error_msg),
+            "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
+            error_msg.c_str());
+      }
+    }
+  }
+}
+
+/**
+ * Remove the public libs in runtime namespace
+ */
+void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+  for (const std::string& lib_name : kRuntimePublicLibraries) {
+    std::string path(kRuntimeApexLibPath);
+    path.append("/").append(lib_name);
+
+    struct stat s;
+    // Do nothing if the path in /apex does not exist.
+    // Runtime APEX must be mounted since libnativeloader is in the same APEX
+    if (stat(path.c_str(), &s) != 0) {
+      continue;
+    }
+
+    auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+    if (it != sonames.end()) {
+      sonames.erase(it);
+    }
+  }
+}
+
+jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
+  jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+  jmethodID get_parent =
+      env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
+
+  return env->CallObjectMethod(class_loader, get_parent);
+}
+
+ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+  ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+  if (dex_path != nullptr) {
+    ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+    if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+      apk_origin = APK_ORIGIN_VENDOR;
+    }
+
+    if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+      LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+                          "Dex path contains both vendor and product partition : %s",
+                          dex_path_utf_chars.c_str());
+
+      apk_origin = APK_ORIGIN_PRODUCT;
+    }
+  }
+  return apk_origin;
+}
+
+}  // namespace
+
+void LibraryNamespaces::Initialize() {
+  // Once public namespace is initialized there is no
+  // point in running this code - it will have no effect
+  // on the current list of public libraries.
+  if (initialized_) {
+    return;
+  }
+
+  std::vector<std::string> sonames;
+  const char* android_root_env = getenv("ANDROID_ROOT");
+  std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
+  std::string public_native_libraries_system_config =
+      root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
+  std::string runtime_public_libraries = base::Join(kRuntimePublicLibraries, ":");
+  std::string llndk_native_libraries_system_config =
+      root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
+  std::string vndksp_native_libraries_system_config =
+      root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
+
+  std::string product_public_native_libraries_dir = "/product/etc";
+
+  std::string error_msg;
+  LOG_ALWAYS_FATAL_IF(
+      !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
+      "Error reading public native library list from \"%s\": %s",
+      public_native_libraries_system_config.c_str(), error_msg.c_str());
+
+  // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
+  // variable to add libraries to the list. This is intended for platform tests only.
+  if (is_debuggable()) {
+    const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
+    if (additional_libs != nullptr && additional_libs[0] != '\0') {
+      std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
+      std::copy(additional_libs_vector.begin(), additional_libs_vector.end(),
+                std::back_inserter(sonames));
+      // Apply the same list to the runtime namespace, since some libraries
+      // might reside there.
+      CHECK(sizeof(kRuntimePublicLibraries) > 0);
+      runtime_public_libraries = runtime_public_libraries + ':' + additional_libs;
+    }
+  }
+
+  // Remove the public libs in the runtime namespace.
+  // These libs are listed in public.android.txt, but we don't want the rest of android
+  // in default namespace to dlopen the libs.
+  // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+  // Unfortunately, it does not have stable C symbols, and default namespace should only use
+  // stable symbols in libandroidicu.so. http://b/120786417
+  removePublicLibsIfExistsInRuntimeApex(sonames);
+
+  // android_init_namespaces() expects all the public libraries
+  // to be loaded so that they can be found by soname alone.
+  //
+  // TODO(dimitry): this is a bit misleading since we do not know
+  // if the vendor public library is going to be opened from /vendor/lib
+  // we might as well end up loading them from /system/lib or /product/lib
+  // For now we rely on CTS test to catch things like this but
+  // it should probably be addressed in the future.
+  for (const auto& soname : sonames) {
+    LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+                        "Error preloading public library %s: %s", soname.c_str(), dlerror());
+  }
+
+  system_public_libraries_ = base::Join(sonames, ':');
+  runtime_public_libraries_ = runtime_public_libraries;
+
+  // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
+  // system libs that are exposed to apps. The libs in the txt files must be
+  // named as lib<name>.<companyname>.so.
+  sonames.clear();
+  ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
+  oem_public_libraries_ = base::Join(sonames, ':');
+
+  // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
+  // product libs that are exposed to apps.
+  sonames.clear();
+  ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
+  product_public_libraries_ = base::Join(sonames, ':');
+
+  // Insert VNDK version to llndk and vndksp config file names.
+  insert_vndk_version_str(&llndk_native_libraries_system_config);
+  insert_vndk_version_str(&vndksp_native_libraries_system_config);
+
+  sonames.clear();
+  ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
+  system_llndk_libraries_ = base::Join(sonames, ':');
+
+  sonames.clear();
+  ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
+  system_vndksp_libraries_ = base::Join(sonames, ':');
+
+  sonames.clear();
+  // This file is optional, quietly ignore if the file does not exist.
+  ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
+
+  vendor_public_libraries_ = base::Join(sonames, ':');
+}
+
+NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
+                                                 jobject class_loader, bool is_shared,
+                                                 jstring dex_path, jstring java_library_path,
+                                                 jstring java_permitted_path,
+                                                 std::string* error_msg) {
+  std::string library_path;  // empty string by default.
+
+  if (java_library_path != nullptr) {
+    ScopedUtfChars library_path_utf_chars(env, java_library_path);
+    library_path = library_path_utf_chars.c_str();
+  }
+
+  ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
+  // (http://b/27588281) This is a workaround for apps using custom
+  // classloaders and calling System.load() with an absolute path which
+  // is outside of the classloader library search path.
+  //
+  // This part effectively allows such a classloader to access anything
+  // under /data and /mnt/expand
+  std::string permitted_path = kWhitelistedDirectories;
+
+  if (java_permitted_path != nullptr) {
+    ScopedUtfChars path(env, java_permitted_path);
+    if (path.c_str() != nullptr && path.size() > 0) {
+      permitted_path = permitted_path + ":" + path.c_str();
+    }
+  }
+
+  // Initialize the anonymous namespace with the first non-empty library path.
+  if (!library_path.empty() && !initialized_ &&
+      !InitPublicNamespace(library_path.c_str(), error_msg)) {
+    return nullptr;
+  }
+
+  bool found = FindNamespaceByClassLoader(env, class_loader);
+
+  LOG_ALWAYS_FATAL_IF(found, "There is already a namespace associated with this classloader");
+
+  uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+  if (is_shared) {
+    namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
+  }
+
+  if (target_sdk_version < 24) {
+    namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+  }
+
+  NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
+
+  bool is_native_bridge = false;
+
+  if (parent_ns != nullptr) {
+    is_native_bridge = !parent_ns->is_android_namespace();
+  } else if (!library_path.empty()) {
+    is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
+  }
+
+  std::string system_exposed_libraries = system_public_libraries_;
+  const char* namespace_name = kClassloaderNamespaceName;
+  android_namespace_t* vndk_ns = nullptr;
+  if ((apk_origin == APK_ORIGIN_VENDOR ||
+       (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+      !is_shared) {
+    LOG_FATAL_IF(is_native_bridge,
+                 "Unbundled vendor / product apk must not use translated architecture");
+
+    // For vendor / product apks, give access to the vendor / product lib even though
+    // they are treated as unbundled; the libs and apks are still bundled
+    // together in the vendor / product partition.
+    const char* origin_partition;
+    const char* origin_lib_path;
+
+    switch (apk_origin) {
+      case APK_ORIGIN_VENDOR:
+        origin_partition = "vendor";
+        origin_lib_path = kVendorLibPath;
+        break;
+      case APK_ORIGIN_PRODUCT:
+        origin_partition = "product";
+        origin_lib_path = kProductLibPath;
+        break;
+      default:
+        origin_partition = "unknown";
+        origin_lib_path = "";
+    }
+
+    LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
+                 origin_partition);
+
+    library_path = library_path + ":" + origin_lib_path;
+    permitted_path = permitted_path + ":" + origin_lib_path;
+
+    // Also give access to LLNDK libraries since they are available to vendors
+    system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
+
+    // Give access to VNDK-SP libraries from the 'vndk' namespace.
+    vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
+    if (vndk_ns == nullptr) {
+      ALOGW("Cannot find \"%s\" namespace for %s apks", kVndkNamespaceName, origin_partition);
+    }
+
+    // Different name is useful for debugging
+    namespace_name = kVendorClassloaderNamespaceName;
+    ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+          origin_partition, library_path.c_str());
+  } else {
+    // oem and product public libraries are NOT available to vendor apks, otherwise it
+    // would be system->vendor violation.
+    if (!oem_public_libraries_.empty()) {
+      system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
+    }
+    if (!product_public_libraries_.empty()) {
+      system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
+    }
+  }
+  std::string runtime_exposed_libraries = runtime_public_libraries_;
+
+  NativeLoaderNamespace native_loader_ns;
+  if (!is_native_bridge) {
+    // The platform namespace is called "default" for binaries in /system and
+    // "platform" for those in the Runtime APEX. Try "platform" first since
+    // "default" always exists.
+    android_namespace_t* platform_ns = android_get_exported_namespace(kPlatformNamespaceName);
+    if (platform_ns == nullptr) {
+      platform_ns = android_get_exported_namespace(kDefaultNamespaceName);
+    }
+
+    android_namespace_t* android_parent_ns;
+    if (parent_ns != nullptr) {
+      android_parent_ns = parent_ns->get_android_ns();
+    } else {
+      // Fall back to the platform namespace if no parent is found.
+      android_parent_ns = platform_ns;
+    }
+
+    android_namespace_t* ns =
+        android_create_namespace(namespace_name, nullptr, library_path.c_str(), namespace_type,
+                                 permitted_path.c_str(), android_parent_ns);
+    if (ns == nullptr) {
+      *error_msg = dlerror();
+      return nullptr;
+    }
+
+    // Note that when vendor_ns is not configured this function will return nullptr
+    // and it will result in linking vendor_public_libraries_ to the default namespace
+    // which is expected behavior in this case.
+    android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
+
+    android_namespace_t* runtime_ns = android_get_exported_namespace(kRuntimeNamespaceName);
+
+    if (!android_link_namespaces(ns, platform_ns, system_exposed_libraries.c_str())) {
+      *error_msg = dlerror();
+      return nullptr;
+    }
+
+    // Runtime apex does not exist in host, and under certain build conditions.
+    if (runtime_ns != nullptr) {
+      if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
+        *error_msg = dlerror();
+        return nullptr;
+      }
+    }
+
+    if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
+      // vendor apks are allowed to use VNDK-SP libraries.
+      if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
+        *error_msg = dlerror();
+        return nullptr;
+      }
+    }
+
+    if (!vendor_public_libraries_.empty()) {
+      if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+        *error_msg = dlerror();
+        return nullptr;
+      }
+    }
+
+    native_loader_ns = NativeLoaderNamespace(ns);
+  } else {
+    // Same functionality as in the branch above, but calling through native bridge.
+
+    native_bridge_namespace_t* platform_ns =
+        NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
+    if (platform_ns == nullptr) {
+      platform_ns = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
+    }
+
+    native_bridge_namespace_t* native_bridge_parent_namespace;
+    if (parent_ns != nullptr) {
+      native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
+    } else {
+      native_bridge_parent_namespace = platform_ns;
+    }
+
+    native_bridge_namespace_t* ns =
+        NativeBridgeCreateNamespace(namespace_name, nullptr, library_path.c_str(), namespace_type,
+                                    permitted_path.c_str(), native_bridge_parent_namespace);
+    if (ns == nullptr) {
+      *error_msg = NativeBridgeGetError();
+      return nullptr;
+    }
+
+    native_bridge_namespace_t* vendor_ns = NativeBridgeGetExportedNamespace(kVendorNamespaceName);
+    native_bridge_namespace_t* runtime_ns = NativeBridgeGetExportedNamespace(kRuntimeNamespaceName);
+
+    if (!NativeBridgeLinkNamespaces(ns, platform_ns, system_exposed_libraries.c_str())) {
+      *error_msg = NativeBridgeGetError();
+      return nullptr;
+    }
+
+    // Runtime apex does not exist in host, and under certain build conditions.
+    if (runtime_ns != nullptr) {
+      if (!NativeBridgeLinkNamespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
+        *error_msg = NativeBridgeGetError();
+        return nullptr;
+      }
+    }
+    if (!vendor_public_libraries_.empty()) {
+      if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+        *error_msg = NativeBridgeGetError();
+        return nullptr;
+      }
+    }
+
+    native_loader_ns = NativeLoaderNamespace(ns);
+  }
+
+  namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
+
+  return &(namespaces_.back().second);
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env,
+                                                                     jobject class_loader) {
+  auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+                         [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
+                           return env->IsSameObject(value.first, class_loader);
+                         });
+  if (it != namespaces_.end()) {
+    return &it->second;
+  }
+
+  return nullptr;
+}
+
+bool LibraryNamespaces::InitPublicNamespace(const char* library_path, std::string* error_msg) {
+  // Ask native bride if this apps library path should be handled by it
+  bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
+
+  // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+  // code is one example) unknown to linker in which  case linker uses anonymous
+  // namespace. The second argument specifies the search path for the anonymous
+  // namespace which is the library_path of the classloader.
+  initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
+                                                  is_native_bridge ? nullptr : library_path);
+  if (!initialized_) {
+    *error_msg = dlerror();
+    return false;
+  }
+
+  // and now initialize native bridge namespaces if necessary.
+  if (NativeBridgeInitialized()) {
+    initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
+                                                      is_native_bridge ? library_path : nullptr);
+    if (!initialized_) {
+      *error_msg = NativeBridgeGetError();
+    }
+  }
+
+  return initialized_;
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
+                                                                           jobject class_loader) {
+  jobject parent_class_loader = GetParentClassLoader(env, class_loader);
+
+  while (parent_class_loader != nullptr) {
+    NativeLoaderNamespace* ns;
+    if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+      return ns;
+    }
+
+    parent_class_loader = GetParentClassLoader(env, parent_class_loader);
+  }
+
+  return nullptr;
+}
+
+}  // namespace android
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
new file mode 100644
index 0000000..353b5fc
--- /dev/null
+++ b/libnativeloader/library_namespaces.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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
+#if !defined(__ANDROID__)
+#error "Not available for host"
+#endif
+
+#include "native_loader_namespace.h"
+
+#include <list>
+#include <string>
+
+#include "jni.h"
+
+namespace android {
+
+// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
+// objects for an app process. Its main job is to create (and configure) a new
+// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
+// object for a given ClassLoader.
+class LibraryNamespaces {
+ public:
+  LibraryNamespaces() : initialized_(false) {}
+
+  LibraryNamespaces(LibraryNamespaces&&) = default;
+  LibraryNamespaces(const LibraryNamespaces&) = delete;
+  LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
+
+  void Initialize();
+  void Reset() { namespaces_.clear(); }
+  NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
+                                bool is_shared, jstring dex_path, jstring java_library_path,
+                                jstring java_permitted_path, std::string* error_msg);
+  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ private:
+  bool InitPublicNamespace(const char* library_path, std::string* error_msg);
+  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+  bool initialized_;
+  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+  std::string system_public_libraries_;
+  std::string runtime_public_libraries_;
+  std::string vendor_public_libraries_;
+  std::string oem_public_libraries_;
+  std::string product_public_libraries_;
+  std::string system_llndk_libraries_;
+  std::string system_vndksp_libraries_;
+};
+
+}  // namespace android
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5cc0857..ed98714 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -15,725 +15,59 @@
  */
 
 #include "nativeloader/native_loader.h"
-#include <nativehelper/ScopedUtfChars.h>
+#define LOG_TAG "nativeloader"
 
 #include <dlfcn.h>
-#ifdef __ANDROID__
-#define LOG_TAG "libnativeloader"
-#include "nativeloader/dlext_namespaces.h"
-#include "log/log.h"
-#endif
-#include <dirent.h>
 #include <sys/types.h>
-#include "nativebridge/native_bridge.h"
 
-#include <algorithm>
-#include <list>
 #include <memory>
 #include <mutex>
-#include <regex>
 #include <string>
 #include <vector>
 
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/strings.h>
-
-#ifdef __BIONIC__
-#include <android-base/properties.h>
+#include "android-base/file.h"
+#include "android-base/macros.h"
+#include "android-base/strings.h"
+#ifdef __ANDROID__
+#include "library_namespaces.h"
+#include "log/log.h"
+#include "nativeloader/dlext_namespaces.h"
 #endif
-
-#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
-                                             "%s:%d: %s CHECK '" #predicate "' failed.",\
-                                             __FILE__, __LINE__, __FUNCTION__)
-
-using namespace std::string_literals;
+#include "nativebridge/native_bridge.h"
+#include "nativehelper/ScopedUtfChars.h"
 
 namespace android {
 
+namespace {
 #if defined(__ANDROID__)
-struct NativeLoaderNamespace {
- public:
-  NativeLoaderNamespace()
-      : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+constexpr const char* kApexPath = "/apex/";
 
-  explicit NativeLoaderNamespace(android_namespace_t* ns)
-      : android_ns_(ns), native_bridge_ns_(nullptr) { }
+std::mutex g_namespaces_mutex;
+LibraryNamespaces* g_namespaces = new LibraryNamespaces;
 
-  explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
-      : android_ns_(nullptr), native_bridge_ns_(ns) { }
-
-  NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
-  NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
-
-  NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
-
-  android_namespace_t* get_android_ns() const {
-    CHECK(native_bridge_ns_ == nullptr);
-    return android_ns_;
+android_namespace_t* FindExportedNamespace(const char* caller_location) {
+  std::string location = caller_location;
+  // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+  // /apex/com.android...modulename/...
+  //
+  // And we extract from it 'modulename', which is the name of the linker namespace.
+  if (android::base::StartsWith(location, kApexPath)) {
+    size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+    LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+                        "Error finding namespace of apex: no slash in path %s", caller_location);
+    size_t dot_index = location.find_last_of('.', slash_index);
+    LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+                        "Error finding namespace of apex: no dot in apex name %s", caller_location);
+    std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+    android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+    LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+                        "Error finding namespace of apex: no namespace called %s", name.c_str());
+    return boot_namespace;
   }
-
-  native_bridge_namespace_t* get_native_bridge_ns() const {
-    CHECK(android_ns_ == nullptr);
-    return native_bridge_ns_;
-  }
-
-  bool is_android_namespace() const {
-    return native_bridge_ns_ == nullptr;
-  }
-
- private:
-  // Only one of them can be not null
-  android_namespace_t* android_ns_;
-  native_bridge_namespace_t* native_bridge_ns_;
-};
-
-static constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] =
-    "/etc/public.libraries.txt";
-static constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
-    sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
-static constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
-    sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
-static constexpr const char kPublicNativeLibrariesVendorConfig[] =
-    "/vendor/etc/public.libraries.txt";
-static constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] =
-    "/etc/llndk.libraries.txt";
-static constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] =
-    "/etc/vndksp.libraries.txt";
-
-static const std::vector<const std::string> kRuntimePublicLibraries = {
-    "libicuuc.so",
-    "libicui18n.so",
-};
-
-// The device may be configured to have the vendor libraries loaded to a separate namespace.
-// For historical reasons this namespace was named sphal but effectively it is intended
-// to use to load vendor libraries to separate namespace with controlled interface between
-// vendor and system namespaces.
-static constexpr const char* kVendorNamespaceName = "sphal";
-
-static constexpr const char* kVndkNamespaceName = "vndk";
-
-static constexpr const char* kDefaultNamespaceName = "default";
-static constexpr const char* kPlatformNamespaceName = "platform";
-static constexpr const char* kRuntimeNamespaceName = "runtime";
-
-// classloader-namespace is a linker namespace that is created for the loaded
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// default namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-static constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for vendor APKs.
-static constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-
-// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
-// System.load() with an absolute path which is outside of the classloader library search path.
-// This list includes all directories app is allowed to access this way.
-static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
-
-static constexpr const char* kApexPath = "/apex/";
-
-#if defined(__LP64__)
-static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
-static constexpr const char* kVendorLibPath = "/vendor/lib64";
-static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
-#else
-static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
-static constexpr const char* kVendorLibPath = "/vendor/lib";
-static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
-#endif
-
-static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
-static const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
-
-// Define origin of APK if it is from vendor partition or product partition
-typedef enum {
-  APK_ORIGIN_DEFAULT = 0,
-  APK_ORIGIN_VENDOR = 1,
-  APK_ORIGIN_PRODUCT = 2,
-} ApkOrigin;
-
-static bool is_debuggable() {
-  bool debuggable = false;
-#ifdef __BIONIC__
-  debuggable = android::base::GetBoolProperty("ro.debuggable", false);
-#endif
-  return debuggable;
+  return nullptr;
 }
-
-static std::string vndk_version_str() {
-#ifdef __BIONIC__
-  std::string version = android::base::GetProperty("ro.vndk.version", "");
-  if (version != "" && version != "current") {
-    return "." + version;
-  }
-#endif
-  return "";
-}
-
-static void insert_vndk_version_str(std::string* file_name) {
-  CHECK(file_name != nullptr);
-  size_t insert_pos = file_name->find_last_of(".");
-  if (insert_pos == std::string::npos) {
-    insert_pos = file_name->length();
-  }
-  file_name->insert(insert_pos, vndk_version_str());
-}
-
-static const std::function<bool(const std::string&, std::string*)> always_true =
-    [](const std::string&, std::string*) { return true; };
-
-class LibraryNamespaces {
- public:
-  LibraryNamespaces() : initialized_(false) { }
-
-  NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
-                                bool is_shared, jstring dex_path, jstring java_library_path,
-                                jstring java_permitted_path, std::string* error_msg) {
-    std::string library_path; // empty string by default.
-
-    if (java_library_path != nullptr) {
-      ScopedUtfChars library_path_utf_chars(env, java_library_path);
-      library_path = library_path_utf_chars.c_str();
-    }
-
-    ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
-
-    // (http://b/27588281) This is a workaround for apps using custom
-    // classloaders and calling System.load() with an absolute path which
-    // is outside of the classloader library search path.
-    //
-    // This part effectively allows such a classloader to access anything
-    // under /data and /mnt/expand
-    std::string permitted_path = kWhitelistedDirectories;
-
-    if (java_permitted_path != nullptr) {
-      ScopedUtfChars path(env, java_permitted_path);
-      if (path.c_str() != nullptr && path.size() > 0) {
-        permitted_path = permitted_path + ":" + path.c_str();
-      }
-    }
-
-    if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
-      return nullptr;
-    }
-
-    bool found = FindNamespaceByClassLoader(env, class_loader);
-
-    LOG_ALWAYS_FATAL_IF(found,
-                        "There is already a namespace associated with this classloader");
-
-    uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
-    if (is_shared) {
-      namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
-    }
-
-    if (target_sdk_version < 24) {
-      namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
-    }
-
-    NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
-
-    bool is_native_bridge = false;
-
-    if (parent_ns != nullptr) {
-      is_native_bridge = !parent_ns->is_android_namespace();
-    } else if (!library_path.empty()) {
-      is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
-    }
-
-    std::string system_exposed_libraries = system_public_libraries_;
-    const char* namespace_name = kClassloaderNamespaceName;
-    android_namespace_t* vndk_ns = nullptr;
-    if ((apk_origin == APK_ORIGIN_VENDOR ||
-         (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
-        !is_shared) {
-      LOG_FATAL_IF(is_native_bridge,
-                   "Unbundled vendor / product apk must not use translated architecture");
-
-      // For vendor / product apks, give access to the vendor / product lib even though
-      // they are treated as unbundled; the libs and apks are still bundled
-      // together in the vendor / product partition.
-      const char* origin_partition;
-      const char* origin_lib_path;
-
-      switch (apk_origin) {
-        case APK_ORIGIN_VENDOR:
-          origin_partition = "vendor";
-          origin_lib_path = kVendorLibPath;
-          break;
-        case APK_ORIGIN_PRODUCT:
-          origin_partition = "product";
-          origin_lib_path = kProductLibPath;
-          break;
-        default:
-          origin_partition = "unknown";
-          origin_lib_path = "";
-      }
-
-      LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
-                   origin_partition);
-
-      library_path = library_path + ":" + origin_lib_path;
-      permitted_path = permitted_path + ":" + origin_lib_path;
-
-      // Also give access to LLNDK libraries since they are available to vendors
-      system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
-
-      // Give access to VNDK-SP libraries from the 'vndk' namespace.
-      vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
-      if (vndk_ns == nullptr) {
-        ALOGW("Cannot find \"%s\" namespace for %s apks", kVndkNamespaceName, origin_partition);
-      }
-
-      // Different name is useful for debugging
-      namespace_name = kVendorClassloaderNamespaceName;
-      ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
-            origin_partition, library_path.c_str());
-    } else {
-      // oem and product public libraries are NOT available to vendor apks, otherwise it
-      // would be system->vendor violation.
-      if (!oem_public_libraries_.empty()) {
-        system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
-      }
-      if (!product_public_libraries_.empty()) {
-        system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
-      }
-    }
-
-    std::string runtime_exposed_libraries = base::Join(kRuntimePublicLibraries, ":");
-
-    NativeLoaderNamespace native_loader_ns;
-    if (!is_native_bridge) {
-      android_namespace_t* android_parent_ns;
-      if (parent_ns != nullptr) {
-        android_parent_ns = parent_ns->get_android_ns();
-      } else {
-        // Fall back to the platform namespace if no parent is found. It is
-        // called "default" for binaries in /system and "platform" for those in
-        // the Runtime APEX. Try "platform" first since "default" always exists.
-        android_parent_ns = android_get_exported_namespace(kPlatformNamespaceName);
-        if (android_parent_ns == nullptr) {
-          android_parent_ns = android_get_exported_namespace(kDefaultNamespaceName);
-        }
-      }
-
-      android_namespace_t* ns = android_create_namespace(namespace_name,
-                                                         nullptr,
-                                                         library_path.c_str(),
-                                                         namespace_type,
-                                                         permitted_path.c_str(),
-                                                         android_parent_ns);
-      if (ns == nullptr) {
-        *error_msg = dlerror();
-        return nullptr;
-      }
-
-      // Note that when vendor_ns is not configured this function will return nullptr
-      // and it will result in linking vendor_public_libraries_ to the default namespace
-      // which is expected behavior in this case.
-      android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
-
-      android_namespace_t* runtime_ns = android_get_exported_namespace(kRuntimeNamespaceName);
-
-      if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
-        *error_msg = dlerror();
-        return nullptr;
-      }
-
-      // Runtime apex does not exist in host, and under certain build conditions.
-      if (runtime_ns != nullptr) {
-        if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
-          *error_msg = dlerror();
-          return nullptr;
-        }
-      }
-
-      if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
-        // vendor apks are allowed to use VNDK-SP libraries.
-        if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
-          *error_msg = dlerror();
-          return nullptr;
-        }
-      }
-
-      if (!vendor_public_libraries_.empty()) {
-        if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
-          *error_msg = dlerror();
-          return nullptr;
-        }
-      }
-
-      native_loader_ns = NativeLoaderNamespace(ns);
-    } else {
-      native_bridge_namespace_t* native_bridge_parent_namespace;
-      if (parent_ns != nullptr) {
-        native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
-      } else {
-        native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
-        if (native_bridge_parent_namespace == nullptr) {
-          native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
-        }
-      }
-
-      native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
-                                                                  nullptr,
-                                                                  library_path.c_str(),
-                                                                  namespace_type,
-                                                                  permitted_path.c_str(),
-                                                                  native_bridge_parent_namespace);
-      if (ns == nullptr) {
-        *error_msg = NativeBridgeGetError();
-        return nullptr;
-      }
-
-      native_bridge_namespace_t* vendor_ns = NativeBridgeGetExportedNamespace(kVendorNamespaceName);
-      native_bridge_namespace_t* runtime_ns =
-          NativeBridgeGetExportedNamespace(kRuntimeNamespaceName);
-
-      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
-        *error_msg = NativeBridgeGetError();
-        return nullptr;
-      }
-
-      // Runtime apex does not exist in host, and under certain build conditions.
-      if (runtime_ns != nullptr) {
-        if (!NativeBridgeLinkNamespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
-          *error_msg = NativeBridgeGetError();
-          return nullptr;
-        }
-      }
-      if (!vendor_public_libraries_.empty()) {
-        if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
-          *error_msg = NativeBridgeGetError();
-          return nullptr;
-        }
-      }
-
-      native_loader_ns = NativeLoaderNamespace(ns);
-    }
-
-    namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
-
-    return &(namespaces_.back().second);
-  }
-
-  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
-    auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
-                [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
-                  return env->IsSameObject(value.first, class_loader);
-                });
-    if (it != namespaces_.end()) {
-      return &it->second;
-    }
-
-    return nullptr;
-  }
-
-  void Initialize() {
-    // Once public namespace is initialized there is no
-    // point in running this code - it will have no effect
-    // on the current list of public libraries.
-    if (initialized_) {
-      return;
-    }
-
-    std::vector<std::string> sonames;
-    const char* android_root_env = getenv("ANDROID_ROOT");
-    std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
-    std::string public_native_libraries_system_config =
-            root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
-    std::string llndk_native_libraries_system_config =
-            root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
-    std::string vndksp_native_libraries_system_config =
-            root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
-
-    std::string product_public_native_libraries_dir = "/product/etc";
-
-    std::string error_msg;
-    LOG_ALWAYS_FATAL_IF(
-        !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
-        "Error reading public native library list from \"%s\": %s",
-        public_native_libraries_system_config.c_str(), error_msg.c_str());
-
-    // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
-    // variable to add libraries to the list. This is intended for platform tests only.
-    if (is_debuggable()) {
-      const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
-      if (additional_libs != nullptr && additional_libs[0] != '\0') {
-        std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
-        std::copy(additional_libs_vector.begin(), additional_libs_vector.end(),
-                  std::back_inserter(sonames));
-      }
-    }
-
-    // Remove the public libs in the runtime namespace.
-    // These libs are listed in public.android.txt, but we don't want the rest of android
-    // in default namespace to dlopen the libs.
-    // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
-    // Unfortunately, it does not have stable C symbols, and default namespace should only use
-    // stable symbols in libandroidicu.so. http://b/120786417
-    removePublicLibsIfExistsInRuntimeApex(sonames);
-
-    // android_init_namespaces() expects all the public libraries
-    // to be loaded so that they can be found by soname alone.
-    //
-    // TODO(dimitry): this is a bit misleading since we do not know
-    // if the vendor public library is going to be opened from /vendor/lib
-    // we might as well end up loading them from /system/lib or /product/lib
-    // For now we rely on CTS test to catch things like this but
-    // it should probably be addressed in the future.
-    for (const auto& soname : sonames) {
-      LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
-                          "Error preloading public library %s: %s", soname.c_str(), dlerror());
-    }
-
-    system_public_libraries_ = base::Join(sonames, ':');
-
-    // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
-    // system libs that are exposed to apps. The libs in the txt files must be
-    // named as lib<name>.<companyname>.so.
-    sonames.clear();
-    ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
-    oem_public_libraries_ = base::Join(sonames, ':');
-
-    // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
-    // product libs that are exposed to apps.
-    sonames.clear();
-    ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
-    product_public_libraries_ = base::Join(sonames, ':');
-
-    // Insert VNDK version to llndk and vndksp config file names.
-    insert_vndk_version_str(&llndk_native_libraries_system_config);
-    insert_vndk_version_str(&vndksp_native_libraries_system_config);
-
-    sonames.clear();
-    ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
-    system_llndk_libraries_ = base::Join(sonames, ':');
-
-    sonames.clear();
-    ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
-    system_vndksp_libraries_ = base::Join(sonames, ':');
-
-    sonames.clear();
-    // This file is optional, quietly ignore if the file does not exist.
-    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
-
-    vendor_public_libraries_ = base::Join(sonames, ':');
-  }
-
-  void Reset() { namespaces_.clear(); }
-
- private:
-  void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
-    if (dir != nullptr) {
-      // Failing to opening the dir is not an error, which can happen in
-      // webview_zygote.
-      while (struct dirent* ent = readdir(dir.get())) {
-        if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
-          continue;
-        }
-        const std::string filename(ent->d_name);
-        if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
-            android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
-          const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
-          const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
-          const std::string company_name = filename.substr(start, end - start);
-          const std::string config_file_path = dirname + "/"s + filename;
-          LOG_ALWAYS_FATAL_IF(
-              company_name.empty(),
-              "Error extracting company name from public native library list file path \"%s\"",
-              config_file_path.c_str());
-
-          std::string error_msg;
-
-          LOG_ALWAYS_FATAL_IF(
-              !ReadConfig(
-                  config_file_path, sonames,
-                  [&company_name](const std::string& soname, std::string* error_msg) {
-                    if (android::base::StartsWith(soname, "lib") &&
-                        android::base::EndsWith(soname, "." + company_name + ".so")) {
-                      return true;
-                    } else {
-                      *error_msg = "Library name \"" + soname +
-                                   "\" does not end with the company name: " + company_name + ".";
-                      return false;
-                    }
-                  },
-                  &error_msg),
-              "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
-              error_msg.c_str());
-        }
-      }
-    }
-  }
-
-  /**
-   * Remove the public libs in runtime namespace
-   */
-  void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
-    for (const std::string& lib_name : kRuntimePublicLibraries) {
-      std::string path(kRuntimeApexLibPath);
-      path.append("/").append(lib_name);
-
-      struct stat s;
-      // Do nothing if the path in /apex does not exist.
-      // Runtime APEX must be mounted since libnativeloader is in the same APEX
-      if (stat(path.c_str(), &s) != 0) {
-        continue;
-      }
-
-      auto it = std::find(sonames.begin(), sonames.end(), lib_name);
-      if (it != sonames.end()) {
-        sonames.erase(it);
-      }
-    }
-  }
-
-  bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
-                  const std::function<bool(const std::string& /* soname */,
-                                           std::string* /* error_msg */)>& check_soname,
-                  std::string* error_msg = nullptr) {
-    // Read list of public native libraries from the config file.
-    std::string file_content;
-    if(!base::ReadFileToString(configFile, &file_content)) {
-      if (error_msg) *error_msg = strerror(errno);
-      return false;
-    }
-
-    std::vector<std::string> lines = base::Split(file_content, "\n");
-
-    for (auto& line : lines) {
-      auto trimmed_line = base::Trim(line);
-      if (trimmed_line[0] == '#' || trimmed_line.empty()) {
-        continue;
-      }
-      size_t space_pos = trimmed_line.rfind(' ');
-      if (space_pos != std::string::npos) {
-        std::string type = trimmed_line.substr(space_pos + 1);
-        if (type != "32" && type != "64") {
-          if (error_msg) *error_msg = "Malformed line: " + line;
-          return false;
-        }
-#if defined(__LP64__)
-        // Skip 32 bit public library.
-        if (type == "32") {
-          continue;
-        }
-#else
-        // Skip 64 bit public library.
-        if (type == "64") {
-          continue;
-        }
-#endif
-        trimmed_line.resize(space_pos);
-      }
-
-      if (check_soname(trimmed_line, error_msg)) {
-        sonames->push_back(trimmed_line);
-      } else {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
-    // Ask native bride if this apps library path should be handled by it
-    bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
-
-    // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
-    // code is one example) unknown to linker in which  case linker uses anonymous
-    // namespace. The second argument specifies the search path for the anonymous
-    // namespace which is the library_path of the classloader.
-    initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
-                                                    is_native_bridge ? nullptr : library_path);
-    if (!initialized_) {
-      *error_msg = dlerror();
-      return false;
-    }
-
-    // and now initialize native bridge namespaces if necessary.
-    if (NativeBridgeInitialized()) {
-      initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
-                                                        is_native_bridge ? library_path : nullptr);
-      if (!initialized_) {
-        *error_msg = NativeBridgeGetError();
-      }
-    }
-
-    return initialized_;
-  }
-
-  jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
-    jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
-    jmethodID get_parent = env->GetMethodID(class_loader_class,
-                                            "getParent",
-                                            "()Ljava/lang/ClassLoader;");
-
-    return env->CallObjectMethod(class_loader, get_parent);
-  }
-
-  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
-    jobject parent_class_loader = GetParentClassLoader(env, class_loader);
-
-    while (parent_class_loader != nullptr) {
-      NativeLoaderNamespace* ns;
-      if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
-        return ns;
-      }
-
-      parent_class_loader = GetParentClassLoader(env, parent_class_loader);
-    }
-
-    return nullptr;
-  }
-
-  ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
-    ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
-
-    if (dex_path != nullptr) {
-      ScopedUtfChars dex_path_utf_chars(env, dex_path);
-
-      if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
-        apk_origin = APK_ORIGIN_VENDOR;
-      }
-
-      if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
-        LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
-                            "Dex path contains both vendor and product partition : %s",
-                            dex_path_utf_chars.c_str());
-
-        apk_origin = APK_ORIGIN_PRODUCT;
-      }
-    }
-
-    return apk_origin;
-  }
-
-  bool initialized_;
-  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
-  std::string system_public_libraries_;
-  std::string vendor_public_libraries_;
-  std::string oem_public_libraries_;
-  std::string product_public_libraries_;
-  std::string system_llndk_libraries_;
-  std::string system_vndksp_libraries_;
-
-  DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
-};
-
-static std::mutex g_namespaces_mutex;
-static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
-#endif
+#endif  // #if defined(__ANDROID__)
+}  // namespace
 
 void InitializeNativeLoader() {
 #if defined(__ANDROID__)
@@ -767,30 +101,6 @@
   return nullptr;
 }
 
-#if defined(__ANDROID__)
-static android_namespace_t* FindExportedNamespace(const char* caller_location) {
-  std::string location = caller_location;
-  // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
-  // /apex/com.android...modulename/...
-  //
-  // And we extract from it 'modulename', which is the name of the linker namespace.
-  if (android::base::StartsWith(location, kApexPath)) {
-    size_t slash_index = location.find_first_of('/', strlen(kApexPath));
-    LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
-                        "Error finding namespace of apex: no slash in path %s", caller_location);
-    size_t dot_index = location.find_last_of('.', slash_index);
-    LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
-                        "Error finding namespace of apex: no dot in apex name %s", caller_location);
-    std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
-    android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
-    LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
-                        "Error finding namespace of apex: no namespace called %s", name.c_str());
-    return boot_namespace;
-  }
-  return nullptr;
-}
-#endif
-
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
                         jobject class_loader, const char* caller_location, jstring library_path,
                         bool* needs_native_bridge, char** error_msg) {
@@ -941,10 +251,11 @@
 
   return nullptr;
 }
+
 NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
   return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
 }
 #endif
 
-}; //  android namespace
+};  // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
new file mode 100644
index 0000000..71b60d8
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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
+#if defined(__ANDROID__)
+
+#include <dlfcn.h>
+
+#include "android-base/logging.h"
+#include "android/dlext.h"
+#include "log/log.h"
+#include "nativebridge/native_bridge.h"
+
+namespace android {
+
+// NativeLoaderNamespace abstracts a linker namespace for the native
+// architecture (ex: arm on arm) or the translated architecture (ex: arm on
+// x86). Instances of this class are managed by LibraryNamespaces object.
+struct NativeLoaderNamespace {
+ public:
+  NativeLoaderNamespace() : android_ns_(nullptr), native_bridge_ns_(nullptr) {}
+
+  explicit NativeLoaderNamespace(android_namespace_t* ns)
+      : android_ns_(ns), native_bridge_ns_(nullptr) {}
+
+  explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
+      : android_ns_(nullptr), native_bridge_ns_(ns) {}
+
+  NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
+  NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
+  NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
+
+  android_namespace_t* get_android_ns() const {
+    CHECK(native_bridge_ns_ == nullptr);
+    return android_ns_;
+  }
+
+  native_bridge_namespace_t* get_native_bridge_ns() const {
+    CHECK(android_ns_ == nullptr);
+    return native_bridge_ns_;
+  }
+
+  bool is_android_namespace() const { return native_bridge_ns_ == nullptr; }
+
+ private:
+  // Only one of them can be not null
+  android_namespace_t* android_ns_;
+  native_bridge_namespace_t* native_bridge_ns_;
+};
+
+}  // namespace android
+#endif  // #if defined(__ANDROID__)
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 86e6035..7e6bf45 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,16 +24,20 @@
 __BEGIN_DECLS
 
 static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
-static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
 
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
 bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
 
-bool UsePerAppMemcg();
+bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+                        bool use_fd_cache = false);
 
-bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles);
-bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+#ifndef __ANDROID_VNDK__
+
+static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
+
+bool UsePerAppMemcg();
 
 // Return 0 and removes the cgroup if there are no longer any processes in it.
 // Returns -1 in the case of an error occurring or if there are processes still running
@@ -54,4 +58,6 @@
 
 void removeAllProcessGroups(void);
 
+#endif // __ANDROID_VNDK__
+
 __END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index abe63dd..1485ae9 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -112,12 +112,16 @@
     return memcg_supported;
 }
 
-bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+                        bool use_fd_cache) {
     const TaskProfiles& tp = TaskProfiles::GetInstance();
 
     for (const auto& name : profiles) {
-        const TaskProfile* profile = tp.GetProfile(name);
+        TaskProfile* profile = tp.GetProfile(name);
         if (profile != nullptr) {
+            if (use_fd_cache) {
+                profile->EnableResourceCaching();
+            }
             if (!profile->ExecuteForProcess(uid, pid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
             }
@@ -129,12 +133,15 @@
     return true;
 }
 
-bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles) {
+bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
     const TaskProfiles& tp = TaskProfiles::GetInstance();
 
     for (const auto& name : profiles) {
-        const TaskProfile* profile = tp.GetProfile(name);
+        TaskProfile* profile = tp.GetProfile(name);
         if (profile != nullptr) {
+            if (use_fd_cache) {
+                profile->EnableResourceCaching();
+            }
             if (!profile->ExecuteForTask(tid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " task profile";
             }
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index ab0f1ca..fe4f93b 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -46,26 +46,34 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
-                                         "TimerSlackHigh"})
+            return SetTaskProfiles(tid,
+                                   {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
+                                    "TimerSlackHigh"},
+                                   true)
                            ? 0
                            : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
-                                         "TimerSlackNormal"})
+            return SetTaskProfiles(tid,
+                                   {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
+                                    "TimerSlackNormal"},
+                                   true)
                            ? 0
                            : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
-                                         "TimerSlackNormal"})
+            return SetTaskProfiles(tid,
+                                   {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
+                                    "TimerSlackNormal"},
+                                   true)
                            ? 0
                            : -1;
         case SP_SYSTEM:
-            return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1;
         case SP_RESTRICTED:
-            return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true)
+                           ? 0
+                           : -1;
         default:
             break;
     }
@@ -126,17 +134,17 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}, true) ? 0 : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
         case SP_RT_APP:
-            return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}, true) ? 0 : -1;
         default:
-            return SetTaskProfiles(tid, {"TimerSlackNormal"}) ? 0 : -1;
+            return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
     }
 
     return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 4b45c87..40d8d90 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -138,31 +138,38 @@
 
 SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
     : controller_(c), path_(p) {
-#ifdef CACHE_FILE_DESCRIPTORS
-    // cache file descriptor only if path is app independent
+    // file descriptors for app-dependent paths can't be cached
     if (IsAppDependentPath(path_)) {
         // file descriptor is not cached
-        fd_.reset(-2);
+        fd_.reset(FDS_APP_DEPENDENT);
         return;
     }
 
-    std::string tasks_path = c.GetTasksFilePath(p);
+    // file descriptor can be cached later on request
+    fd_.reset(FDS_NOT_CACHED);
+}
+
+void SetCgroupAction::EnableResourceCaching() {
+    if (fd_ != FDS_NOT_CACHED) {
+        return;
+    }
+
+    std::string tasks_path = controller_.GetTasksFilePath(path_);
 
     if (access(tasks_path.c_str(), W_OK) != 0) {
         // file is not accessible
-        fd_.reset(-1);
+        fd_.reset(FDS_INACCESSIBLE);
         return;
     }
 
     unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (fd < 0) {
         PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'";
-        fd_.reset(-1);
+        fd_.reset(FDS_INACCESSIBLE);
         return;
     }
 
     fd_ = std::move(fd);
-#endif
 }
 
 bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
@@ -184,8 +191,7 @@
 }
 
 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
-#ifdef CACHE_FILE_DESCRIPTORS
-    if (fd_ >= 0) {
+    if (IsFdValid()) {
         // fd is cached, reuse it
         if (!AddTidToCgroup(pid, fd_)) {
             LOG(ERROR) << "Failed to add task into cgroup";
@@ -194,12 +200,12 @@
         return true;
     }
 
-    if (fd_ == -1) {
+    if (fd_ == FDS_INACCESSIBLE) {
         // no permissions to access the file, ignore
         return true;
     }
 
-    // this is app-dependent path, file descriptor is not cached
+    // this is app-dependent path and fd is not cached or cached fd can't be used
     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
@@ -212,25 +218,10 @@
     }
 
     return true;
-#else
-    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
-    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
-    if (tmp_fd < 0) {
-        // no permissions to access the file, ignore
-        return true;
-    }
-    if (!AddTidToCgroup(pid, tmp_fd)) {
-        LOG(ERROR) << "Failed to add task into cgroup";
-        return false;
-    }
-
-    return true;
-#endif
 }
 
 bool SetCgroupAction::ExecuteForTask(int tid) const {
-#ifdef CACHE_FILE_DESCRIPTORS
-    if (fd_ >= 0) {
+    if (IsFdValid()) {
         // fd is cached, reuse it
         if (!AddTidToCgroup(tid, fd_)) {
             LOG(ERROR) << "Failed to add task into cgroup";
@@ -239,20 +230,23 @@
         return true;
     }
 
-    if (fd_ == -1) {
+    if (fd_ == FDS_INACCESSIBLE) {
         // no permissions to access the file, ignore
         return true;
     }
 
-    // application-dependent path can't be used with tid
-    LOG(ERROR) << "Application profile can't be applied to a thread";
-    return false;
-#else
+    if (fd_ == FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        return false;
+    }
+
+    // fd was not cached because cached fd can't be used
     std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        // no permissions to access the file, ignore
-        return true;
+        PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
+        return false;
     }
     if (!AddTidToCgroup(tid, tmp_fd)) {
         LOG(ERROR) << "Failed to add task into cgroup";
@@ -260,7 +254,6 @@
     }
 
     return true;
-#endif
 }
 
 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
@@ -284,6 +277,18 @@
     return true;
 }
 
+void TaskProfile::EnableResourceCaching() {
+    if (res_cached_) {
+        return;
+    }
+
+    for (auto& element : elements_) {
+        element->EnableResourceCaching();
+    }
+
+    res_cached_ = true;
+}
+
 TaskProfiles& TaskProfiles::GetInstance() {
     // Deliberately leak this object to avoid a race between destruction on
     // process exit and concurrent access from another thread.
@@ -411,7 +416,7 @@
     return true;
 }
 
-const TaskProfile* TaskProfiles::GetProfile(const std::string& name) const {
+TaskProfile* TaskProfiles::GetProfile(const std::string& name) const {
     auto iter = profiles_.find(name);
 
     if (iter != profiles_.end()) {
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 37cc305..445647d 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -48,6 +48,8 @@
     // Default implementations will fail
     virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
     virtual bool ExecuteForTask(int) const { return false; };
+
+    virtual void EnableResourceCaching() {}
 };
 
 // Profile actions
@@ -110,31 +112,40 @@
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
+    virtual void EnableResourceCaching();
 
     const CgroupController* controller() const { return &controller_; }
     std::string path() const { return path_; }
 
   private:
+    enum FdState {
+        FDS_INACCESSIBLE = -1,
+        FDS_APP_DEPENDENT = -2,
+        FDS_NOT_CACHED = -3,
+    };
+
     CgroupController controller_;
     std::string path_;
-#ifdef CACHE_FILE_DESCRIPTORS
     android::base::unique_fd fd_;
-#endif
 
     static bool IsAppDependentPath(const std::string& path);
     static bool AddTidToCgroup(int tid, int fd);
+
+    bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
 };
 
 class TaskProfile {
   public:
-    TaskProfile() {}
+    TaskProfile() : res_cached_(false) {}
 
     void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
 
     bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     bool ExecuteForTask(int tid) const;
+    void EnableResourceCaching();
 
   private:
+    bool res_cached_;
     std::vector<std::unique_ptr<ProfileAction>> elements_;
 };
 
@@ -143,7 +154,7 @@
     // Should be used by all users
     static TaskProfiles& GetInstance();
 
-    const TaskProfile* GetProfile(const std::string& name) const;
+    TaskProfile* GetProfile(const std::string& name) const;
     const ProfileAttribute* GetAttribute(const std::string& name) const;
 
   private:
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index cb288c5..24c6379 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -188,7 +188,7 @@
                               int (*write)(void* priv, const void* data, size_t len,
                                            unsigned int block, unsigned int nr_blocks),
                               void* priv) {
-  int ret;
+  int ret = 0;
   int chunks;
   struct chunk_data chk;
   struct output_file* out;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 26626b5..37323dc 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -25,6 +25,7 @@
 #include <algorithm>
 
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 #include <demangle.h>
 
@@ -168,7 +169,7 @@
       // If this elf is memory backed, and there is a valid file, then set
       // an indicator that we couldn't open the file.
       if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
-          map_info->name[0] != '[') {
+          map_info->name[0] != '[' && !android::base::StartsWith(map_info->name, "/memfd:")) {
         elf_from_memory_not_file_ = true;
       }
       step_pc = regs_->pc();
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index f635021..30e57a1 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -126,6 +126,12 @@
     const auto& info5 = *--maps_->end();
     info5->memory_backed_elf = true;
 
+    elf = new ElfFake(new MemoryFake);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/memfd:/jit-cache", elf);
+    const auto& info6 = *--maps_->end();
+    info6->memory_backed_elf = true;
+
     process_memory_.reset(new MemoryFake);
   }
 
@@ -1234,6 +1240,36 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0xc3050);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x50U, frame->rel_pc);
+  EXPECT_EQ(0xc3050U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/memfd:/jit-cache", frame->map_name);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
+  EXPECT_EQ(0xc3000U, frame->map_start);
+  EXPECT_EQ(0xc4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
 // Verify format frame code.
 TEST_F(UnwinderTest, format_frame) {
   RegsFake regs_arm(10);
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 2de7378..48140b8 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -189,8 +189,8 @@
 /* vmpressure event handler data */
 static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
 
-/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
-#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket, 1 lmk events */
+#define MAX_EPOLL_EVENTS (2 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
 static int epollfd;
 static int maxevents;
 
@@ -1863,6 +1863,74 @@
     return false;
 }
 
+#ifdef LMKD_LOG_STATS
+static int kernel_poll_fd = -1;
+
+static void poll_kernel() {
+    if (kernel_poll_fd == -1) {
+        // not waiting
+        return;
+    }
+
+    while (1) {
+        char rd_buf[256];
+        int bytes_read =
+                TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
+        if (bytes_read <= 0) break;
+        rd_buf[bytes_read] = '\0';
+
+        int64_t pid;
+        int64_t uid;
+        int64_t group_leader_pid;
+        int64_t min_flt;
+        int64_t maj_flt;
+        int64_t rss_in_pages;
+        int16_t oom_score_adj;
+        int16_t min_score_adj;
+        int64_t starttime;
+        char* taskname = 0;
+        int fields_read = sscanf(rd_buf,
+                                 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+                                 " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
+                                 &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
+                                 &oom_score_adj, &min_score_adj, &starttime, &taskname);
+
+        /* only the death of the group leader process is logged */
+        if (fields_read == 10 && group_leader_pid == pid) {
+            int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+            stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, oom_score_adj,
+                                          min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
+                                          process_start_time_ns, min_score_adj);
+        }
+
+        free(taskname);
+    }
+}
+
+static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
+
+static void init_poll_kernel() {
+    struct epoll_event epev;
+    kernel_poll_fd =
+            TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+
+    if (kernel_poll_fd < 0) {
+        ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
+        return;
+    }
+
+    epev.events = EPOLLIN;
+    epev.data.ptr = (void*)&kernel_poll_hinfo;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
+        ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
+        close(kernel_poll_fd);
+        kernel_poll_fd = -1;
+    } else {
+        maxevents++;
+    }
+}
+#endif
+
 static int init(void) {
     struct epoll_event epev;
     int i;
@@ -1910,6 +1978,11 @@
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
+#ifdef LMKD_LOG_STATS
+        if (enable_stats_log) {
+            init_poll_kernel();
+        }
+#endif
     } else {
         /* Try to use psi monitor first if kernel has it */
         use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index a21555c..5a375ec 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -229,70 +229,17 @@
         static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
         static const char newline[] = "\n";
 
-        // Dedupe messages, checking for identical messages starting with avc:
-        static unsigned count;
-        static char* last_str;
-        static bool last_info;
+        auditParse(str, uid, &denial_metadata);
+        iov[0].iov_base = info ? const_cast<char*>(log_info) : const_cast<char*>(log_warning);
+        iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
+        iov[1].iov_base = str;
+        iov[1].iov_len = strlen(str);
+        iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+        iov[2].iov_len = denial_metadata.length();
+        iov[3].iov_base = const_cast<char*>(newline);
+        iov[3].iov_len = strlen(newline);
 
-        if (last_str != nullptr) {
-            static const char avc[] = "): avc: ";
-            char* avcl = strstr(last_str, avc);
-            bool skip = false;
-
-            if (avcl) {
-                char* avcr = strstr(str, avc);
-
-                skip = avcr &&
-                       !fastcmp<strcmp>(avcl + strlen(avc), avcr + strlen(avc));
-                if (skip) {
-                    ++count;
-                    free(last_str);
-                    last_str = strdup(str);
-                    last_info = info;
-                }
-            }
-            if (!skip) {
-                static const char resume[] = " duplicate messages suppressed\n";
-                iov[0].iov_base = last_info ? const_cast<char*>(log_info)
-                                            : const_cast<char*>(log_warning);
-                iov[0].iov_len =
-                    last_info ? sizeof(log_info) : sizeof(log_warning);
-                iov[1].iov_base = last_str;
-                iov[1].iov_len = strlen(last_str);
-                iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
-                iov[2].iov_len = denial_metadata.length();
-                if (count > 1) {
-                    iov[3].iov_base = const_cast<char*>(resume);
-                    iov[3].iov_len = strlen(resume);
-                } else {
-                    iov[3].iov_base = const_cast<char*>(newline);
-                    iov[3].iov_len = strlen(newline);
-                }
-
-                writev(fdDmesg, iov, arraysize(iov));
-                free(last_str);
-                last_str = nullptr;
-            }
-        }
-        if (last_str == nullptr) {
-            count = 0;
-            last_str = strdup(str);
-            last_info = info;
-        }
-        if (count == 0) {
-            auditParse(str, uid, &denial_metadata);
-            iov[0].iov_base = info ? const_cast<char*>(log_info)
-                                   : const_cast<char*>(log_warning);
-            iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
-            iov[1].iov_base = str;
-            iov[1].iov_len = strlen(str);
-            iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
-            iov[2].iov_len = denial_metadata.length();
-            iov[3].iov_base = const_cast<char*>(newline);
-            iov[3].iov_len = strlen(newline);
-
-            writev(fdDmesg, iov, arraysize(iov));
-        }
+        writev(fdDmesg, iov, arraysize(iov));
     }
 
     if (!main && !events) {
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 0044534..f084cd2 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -97,7 +97,7 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    dev proc sys system data odm oem acct config storage mnt apex $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
@@ -377,4 +377,13 @@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
 		echo $(lib).so >> $@;)
 
+#######################################
+# adb_debug.prop in debug ramdisk
+include $(CLEAR_VARS)
+LOCAL_MODULE := adb_debug.prop
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT)
+include $(BUILD_PREBUILT)
+
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/adb_debug.prop b/rootdir/adb_debug.prop
new file mode 100644
index 0000000..37e2f2d
--- /dev/null
+++ b/rootdir/adb_debug.prop
@@ -0,0 +1,12 @@
+# Note: This file will be loaded with highest priority to override
+# other system properties, if a special ramdisk with "/force_debuggable"
+# is used and the device is unlocked.
+
+# Disable adb authentication to allow test automation on user build GSI
+ro.adb.secure=0
+
+# Allow 'adb root' on user build GSI
+ro.debuggable=1
+
+# Introduce this property to indicate that init has loaded adb_debug.prop
+ro.force.debuggable=1
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index e081bdf..4b07478 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -20,6 +20,9 @@
 
 [legacy]
 namespace.default.isolated = false
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
 
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /product/${LIB}
@@ -41,7 +44,7 @@
 
 additional.namespaces = runtime,conscrypt,media,resolv
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
 # to load the shared library from the runtime namespace. And then, if the
@@ -50,14 +53,12 @@
 # Finally, if all attempts fail, the dynamic linker returns an error.
 namespace.default.links = runtime,resolv
 namespace.default.asan.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
 namespace.default.link.runtime.shared_libs += libpac.so
@@ -71,11 +72,13 @@
 # "runtime" APEX namespace
 #
 # This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
@@ -99,7 +102,7 @@
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
 namespace.media.links = default
-namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs  = libbinder_ndk.so
 namespace.media.link.default.shared_libs += libc.so
 namespace.media.link.default.shared_libs += libcgrouprc.so
 namespace.media.link.default.shared_libs += libdl.so
@@ -119,11 +122,11 @@
 # "conscrypt" APEX namespace
 #
 # This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.conscrypt.isolated = true
 namespace.conscrypt.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.links = runtime,default
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 3f9882a..f4710d8 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -43,6 +43,9 @@
 # can't be loaded in this namespace.
 ###############################################################################
 namespace.default.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
 
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
@@ -121,7 +124,7 @@
 namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
 namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
 # to load the shared library from the runtime namespace. And then, if the
@@ -129,14 +132,12 @@
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
 namespace.default.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
 namespace.default.link.runtime.shared_libs += libpac.so
@@ -150,11 +151,13 @@
 # "runtime" APEX namespace
 #
 # This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
@@ -187,11 +190,11 @@
 # "conscrypt" APEX namespace
 #
 # This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.conscrypt.isolated = true
 namespace.conscrypt.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.links = runtime,default
@@ -234,6 +237,8 @@
 # Note that there is no link from the default namespace to this namespace.
 ###############################################################################
 namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.sphal.visible = true
 
 namespace.sphal.search.paths  = /odm/${LIB}
@@ -323,6 +328,8 @@
 # This namespace is exclusively for vndk-sp libs.
 ###############################################################################
 namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.vndk.visible = true
 
 namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
@@ -430,10 +437,10 @@
 # "runtime" APEX namespace
 #
 # This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.runtime.isolated = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = system
@@ -504,6 +511,7 @@
 
 namespace.system.links = runtime
 namespace.system.link.runtime.shared_libs  = libdexfile_external.so
+namespace.system.link.runtime.shared_libs += libdexfiled_external.so
 namespace.system.link.runtime.shared_libs += libnativebridge.so
 namespace.system.link.runtime.shared_libs += libnativehelper.so
 namespace.system.link.runtime.shared_libs += libnativeloader.so
@@ -564,6 +572,10 @@
 [unrestricted]
 additional.namespaces = runtime,media,conscrypt,resolv
 
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 namespace.default.search.paths += /vendor/${LIB}
@@ -575,15 +587,14 @@
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 namespace.default.links = runtime,resolv
-namespace.default.visible = true
-
-namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
 namespace.default.link.runtime.shared_libs += libpac.so
@@ -594,11 +605,13 @@
 # "runtime" APEX namespace
 #
 # This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
@@ -629,11 +642,11 @@
 # "conscrypt" APEX namespace
 #
 # This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.conscrypt.isolated = true
 namespace.conscrypt.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.links = runtime,default
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 6d89886..4ce7f52 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -43,6 +43,9 @@
 # partitions are also allowed temporarily.
 ###############################################################################
 namespace.default.isolated = false
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
 
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /odm/${LIB}
@@ -61,8 +64,7 @@
 namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
-# Keep in sync with the platform namespace in the com.android.runtime APEX
-# ld.config.txt.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
 # to load the shared library from the runtime namespace. And then, if the
@@ -70,14 +72,12 @@
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
 namespace.default.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
 namespace.default.link.runtime.shared_libs += libpac.so
@@ -91,12 +91,13 @@
 # "runtime" APEX namespace
 #
 # This namespace pulls in externally accessible libs from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# Keep in sync with the default namespace in the com.android.runtime APEX
-# ld.config.txt.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
@@ -129,11 +130,11 @@
 # "conscrypt" APEX namespace
 #
 # This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.conscrypt.isolated = true
 namespace.conscrypt.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.links = runtime,default
@@ -176,6 +177,8 @@
 # Note that there is no link from the default namespace to this namespace.
 ###############################################################################
 namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.sphal.visible = true
 
 namespace.sphal.search.paths  = /odm/${LIB}
@@ -265,6 +268,8 @@
 # This namespace is exclusively for vndk-sp libs.
 ###############################################################################
 namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.vndk.visible = true
 
 namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
@@ -357,6 +362,7 @@
 
 namespace.default.links = runtime
 namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
@@ -367,10 +373,10 @@
 # "runtime" APEX namespace
 #
 # This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.runtime.isolated = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
@@ -387,6 +393,10 @@
 [unrestricted]
 additional.namespaces = runtime,media,conscrypt,resolv
 
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 namespace.default.search.paths += /vendor/${LIB}
@@ -398,15 +408,14 @@
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 namespace.default.links = runtime,resolv
-namespace.default.visible = true
-
-namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+namespace.default.link.runtime.shared_libs  = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
 namespace.default.link.runtime.shared_libs += libpac.so
@@ -417,11 +426,13 @@
 # "runtime" APEX namespace
 #
 # This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
@@ -452,11 +463,11 @@
 # "conscrypt" APEX namespace
 #
 # This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
 namespace.conscrypt.isolated = true
 namespace.conscrypt.visible = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.links = runtime,default
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9bb624a..295e704 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -608,6 +608,10 @@
     # IOCTLs on ashmem fds any more.
     setprop sys.use_memfd false
 
+    # Set fscklog permission
+    chown root system /dev/fscklogs/log
+    chmod 0770 /dev/fscklogs/log
+
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start && property:ro.crypto.state=unencrypted