Merge "Support for stopping/starting post-data-mount class subsets."
diff --git a/adb/Android.bp b/adb/Android.bp
index eec1335..87e4adc 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -509,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 552df41..f39aa58 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -904,12 +904,12 @@
         }
         buf[8] = '\0';
 
-        if (strcmp(kSideloadServiceExitSuccess, buf) == 0 ||
-            strcmp(kSideloadServiceExitFailure, 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(kSideloadServiceExitFailure, buf) == 0) {
+            if (strcmp(kMinadbdServicesExitFailure, buf) == 0) {
                 return 1;
             }
             return 0;
@@ -961,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:
@@ -1115,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;
@@ -1643,6 +1670,7 @@
     } 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]));
@@ -1650,6 +1678,8 @@
             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");
         }
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 1e37015..0a116ab 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -310,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;
@@ -324,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 46cab6e..6185aa6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -202,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));
@@ -212,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;
@@ -243,7 +243,7 @@
         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 8f3919b..6fc89d7 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -23,9 +23,10 @@
 constexpr char kShellServiceArgPty[] = "pty";
 constexpr char kShellServiceArgShellProtocol[] = "v2";
 
-// Special flags sent by minadbd that indicate the end of sideload transfer and install result.
-constexpr char kSideloadServiceExitSuccess[] = "DONEDONE";
-constexpr char kSideloadServiceExitFailure[] = "FAILFAIL";
+// 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/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index f8f7eb3..25df451 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -502,9 +502,8 @@
 
 static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
                           std::vector<char>* out) {
-    ZipString zip_entry_name(entry_name.c_str());
     ZipEntry zip_entry;
-    if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+    if (FindEntry(zip, entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
         return false;
     }
@@ -614,9 +613,8 @@
 static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
     unique_fd fd(make_temporary_fd(entry_name));
 
-    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
-    if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+    if (FindEntry(zip, entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
         errno = ENOENT;
         return -1;
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.cpp b/fs_mgr/fs_mgr.cpp
index c1aafda..6f24fe1 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1610,38 +1610,6 @@
     return ret;
 }
 
-bool fs_mgr_load_verity_state(int* mode) {
-    /* return the default mode, unless any of the verified partitions are in
-     * logging mode, in which case return that */
-    *mode = VERITY_MODE_DEFAULT;
-
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
-        LERROR << "Failed to read default fstab";
-        return false;
-    }
-
-    for (const auto& entry : fstab) {
-        if (entry.fs_mgr_flags.avb) {
-            *mode = VERITY_MODE_RESTART;  // avb only supports restart mode.
-            break;
-        } else if (!entry.fs_mgr_flags.verify) {
-            continue;
-        }
-
-        int current;
-        if (load_verity_state(entry, &current) < 0) {
-            continue;
-        }
-        if (current != VERITY_MODE_DEFAULT) {
-            *mode = current;
-            break;
-        }
-    }
-
-    return true;
-}
-
 bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
     if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
         return false;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index da049ef..78455d4 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -261,10 +261,6 @@
                     LWARNING << "Warning: zramsize= flag malformed: " << arg;
                 }
             }
-        } else if (StartsWith(flag, "verify=")) {
-            // If the verify flag is followed by an = and the location for the verity state.
-            entry->fs_mgr_flags.verify = true;
-            entry->verity_loc = arg;
         } else if (StartsWith(flag, "forceencrypt=")) {
             // The forceencrypt flag is followed by an = and the location of the keys.
             entry->fs_mgr_flags.force_crypt = true;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index e46e497..71c4072 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -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_priv.h b/fs_mgr/fs_mgr_priv.h
index 70abf5b..c36fd3d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -99,7 +99,6 @@
 bool fs_mgr_is_device_unlocked();
 const std::string& get_android_dt_dir();
 bool is_dt_compatible();
-int load_verity_state(const android::fs_mgr::FstabEntry& entry, int* mode);
 
 bool fs_mgr_is_ext4(const std::string& blk_device);
 bool fs_mgr_is_f2fs(const std::string& blk_device);
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/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 3f09157..1deb1ac 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -275,248 +275,6 @@
     return 0;
 }
 
-static int check_verity_restart(const char *fname)
-{
-    char buffer[VERITY_KMSG_BUFSIZE + 1];
-    int fd;
-    int rc = 0;
-    ssize_t size;
-    struct stat s;
-
-    fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
-    if (fd == -1) {
-        if (errno != ENOENT) {
-            PERROR << "Failed to open " << fname;
-        }
-        goto out;
-    }
-
-    if (fstat(fd, &s) == -1) {
-        PERROR << "Failed to fstat " << fname;
-        goto out;
-    }
-
-    size = VERITY_KMSG_BUFSIZE;
-
-    if (size > s.st_size) {
-        size = s.st_size;
-    }
-
-    if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
-        PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
-        goto out;
-    }
-
-    if (!android::base::ReadFully(fd, buffer, size)) {
-        PERROR << "Failed to read " << size << " bytes from " << fname;
-        goto out;
-    }
-
-    buffer[size] = '\0';
-
-    if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
-        rc = 1;
-    }
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-
-    return rc;
-}
-
-static int was_verity_restart()
-{
-    static const char* files[] = {
-        // clang-format off
-        "/sys/fs/pstore/console-ramoops-0",
-        "/sys/fs/pstore/console-ramoops",
-        "/proc/last_kmsg",
-        NULL
-        // clang-format on
-    };
-    int i;
-
-    for (i = 0; files[i]; ++i) {
-        if (check_verity_restart(files[i])) {
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-static int metadata_add(FILE *fp, long start, const char *tag,
-        unsigned int length, off64_t *offset)
-{
-    if (fseek(fp, start, SEEK_SET) < 0 ||
-        fprintf(fp, "%s %u\n", tag, length) < 0) {
-        return -1;
-    }
-
-    *offset = ftell(fp);
-
-    if (fseek(fp, length, SEEK_CUR) < 0 ||
-        fprintf(fp, METADATA_EOD " 0\n") < 0) {
-        return -1;
-    }
-
-    return 0;
-}
-
-static int metadata_find(const char *fname, const char *stag,
-        unsigned int slength, off64_t *offset)
-{
-    char tag[METADATA_TAG_MAX_LENGTH + 1];
-    int rc = -1;
-    int n;
-    long start = 0x4000; /* skip cryptfs metadata area */
-    uint32_t magic;
-    unsigned int length = 0;
-
-    if (!fname) {
-        return -1;
-    }
-
-    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
-
-    if (!fp) {
-        PERROR << "Failed to open " << fname;
-        return -1;
-    }
-
-    /* check magic */
-    if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
-        PERROR << "Failed to read magic from " << fname;
-        return -1;
-    }
-
-    if (magic != METADATA_MAGIC) {
-        magic = METADATA_MAGIC;
-
-        if (fseek(fp.get(), start, SEEK_SET) < 0 ||
-            fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
-            PERROR << "Failed to write magic to " << fname;
-            return -1;
-        }
-
-        rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
-        if (rc < 0) {
-            PERROR << "Failed to add metadata to " << fname;
-        }
-
-        return rc;
-    }
-
-    start += sizeof(magic);
-
-    while (1) {
-        n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
-
-        if (n == 2 && strcmp(tag, METADATA_EOD)) {
-            /* found a tag */
-            start = ftell(fp.get());
-
-            if (!strcmp(tag, stag) && length == slength) {
-                *offset = start;
-                return 0;
-            }
-
-            start += length;
-
-            if (fseek(fp.get(), length, SEEK_CUR) < 0) {
-                PERROR << "Failed to seek " << fname;
-                return -1;
-            }
-        } else {
-            rc = metadata_add(fp.get(), start, stag, slength, offset);
-            if (rc < 0) {
-                PERROR << "Failed to write metadata to " << fname;
-            }
-            return rc;
-        }
-    }
-}
-
-static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
-{
-    int fd;
-    int rc = -1;
-    struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
-
-    fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
-
-    if (fd == -1) {
-        PERROR << "Failed to open " << fname;
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
-        PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
-               << " to offset " << offset;
-        goto out;
-    }
-
-    rc = 0;
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-
-    return rc;
-}
-
-static int read_verity_state(const char *fname, off64_t offset, int *mode)
-{
-    int fd = -1;
-    int rc = -1;
-    struct verity_state s;
-
-    fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
-    if (fd == -1) {
-        PERROR << "Failed to open " << fname;
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
-        PERROR << "Failed to read " <<  sizeof(s) << " bytes from " << fname
-               << " offset " << offset;
-        goto out;
-    }
-
-    if (s.header != VERITY_STATE_HEADER) {
-        /* space allocated, but no state written. write default state */
-        *mode = VERITY_MODE_DEFAULT;
-        rc = write_verity_state(fname, offset, *mode);
-        goto out;
-    }
-
-    if (s.version != VERITY_STATE_VERSION) {
-        LERROR << "Unsupported verity state version (" << s.version << ")";
-        goto out;
-    }
-
-    if (s.mode < VERITY_MODE_EIO ||
-        s.mode > VERITY_MODE_LAST) {
-        LERROR << "Unsupported verity mode (" << s.mode << ")";
-        goto out;
-    }
-
-    *mode = s.mode;
-    rc = 0;
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-
-    return rc;
-}
-
 static int read_partition(const char *path, uint64_t size)
 {
     char buf[READ_BUF_SIZE];
@@ -540,119 +298,23 @@
     return 0;
 }
 
-static int compare_last_signature(const FstabEntry& entry, int* match) {
-    char tag[METADATA_TAG_MAX_LENGTH + 1];
-    int fd = -1;
-    int rc = -1;
-    off64_t offset = 0;
-    struct fec_handle *f = NULL;
-    struct fec_verity_metadata verity;
-    uint8_t curr[SHA256_DIGEST_LENGTH];
-    uint8_t prev[SHA256_DIGEST_LENGTH];
-
-    *match = 1;
-
-    if (fec_open(&f, entry.blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) ==
-        -1) {
-        PERROR << "Failed to open '" << entry.blk_device << "'";
-        return rc;
-    }
-
-    // read verity metadata
-    if (fec_verity_get_metadata(f, &verity) == -1) {
-        PERROR << "Failed to get verity metadata '" << entry.blk_device << "'";
-        goto out;
-    }
-
-    SHA256(verity.signature, sizeof(verity.signature), curr);
-
-    if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s", basename(entry.mount_point.c_str())) >=
-        (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << entry.mount_point;
-        goto out;
-    }
-
-    if (metadata_find(entry.verity_loc.c_str(), tag, SHA256_DIGEST_LENGTH, &offset) < 0) {
-        goto out;
-    }
-
-    fd = TEMP_FAILURE_RETRY(open(entry.verity_loc.c_str(), O_RDWR | O_SYNC | O_CLOEXEC));
-
-    if (fd == -1) {
-        PERROR << "Failed to open " << entry.verity_loc;
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev), offset)) != sizeof(prev)) {
-        PERROR << "Failed to read " << sizeof(prev) << " bytes from " << entry.verity_loc
-               << " offset " << offset;
-        goto out;
-    }
-
-    *match = !memcmp(curr, prev, SHA256_DIGEST_LENGTH);
-
-    if (!*match) {
-        /* update current signature hash */
-        if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
-                offset)) != sizeof(curr)) {
-            PERROR << "Failed to write " << sizeof(curr) << " bytes to " << entry.verity_loc
-                   << " offset " << offset;
-            goto out;
-        }
-    }
-
-    rc = 0;
-
-out:
-    fec_close(f);
-    return rc;
-}
-
-static int get_verity_state_offset(const FstabEntry& entry, off64_t* offset) {
-    char tag[METADATA_TAG_MAX_LENGTH + 1];
-
-    if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s", basename(entry.mount_point.c_str())) >=
-        (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << entry.mount_point;
-        return -1;
-    }
-
-    return metadata_find(entry.verity_loc.c_str(), tag, sizeof(struct verity_state), offset);
-}
-
-int load_verity_state(const FstabEntry& entry, int* mode) {
+bool fs_mgr_load_verity_state(int* mode) {
     // unless otherwise specified, use EIO mode.
     *mode = VERITY_MODE_EIO;
 
-    // use the kernel parameter if set.
-    std::string veritymode;
-    if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
-        if (veritymode == "enforcing") {
-            *mode = VERITY_MODE_DEFAULT;
-        }
-        return 0;
+    // The bootloader communicates verity mode via the kernel commandline
+    std::string verity_mode;
+    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+        return false;
     }
 
-    off64_t offset = 0;
-    if (get_verity_state_offset(entry, &offset) < 0) {
-        /* fall back to stateless behavior */
-        return 0;
-    }
-
-    if (was_verity_restart()) {
-        /* device was restarted after dm-verity detected a corrupted
-         * block, so use EIO mode */
-        return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
-    }
-
-    int match = 0;
-    if (!compare_last_signature(entry, &match) && !match) {
-        /* partition has been reflashed, reset dm-verity state */
+    if (verity_mode == "enforcing") {
         *mode = VERITY_MODE_DEFAULT;
-        return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
+    } else if (verity_mode == "logging") {
+        *mode = VERITY_MODE_LOGGING;
     }
 
-    return read_verity_state(entry.verity_loc.c_str(), offset, mode);
+    return true;
 }
 
 // Update the verity table using the actual block device path.
@@ -759,7 +421,7 @@
 
     params.ecc_dev = entry->blk_device.c_str();
 
-    if (load_verity_state(*entry, &params.mode) < 0) {
+    if (!fs_mgr_load_verity_state(&params.mode)) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
          * restart loop, and no corrupted data will be exposed to userspace
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index d7afed6..c7193ab 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,6 @@
     std::string fs_options;
     std::string key_loc;
     std::string key_dir;
-    std::string verity_loc;
     off64_t length = 0;
     std::string label;
     int partnum = -1;
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a4614d0..c2917a4 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -210,6 +210,20 @@
     return true;
 }
 
+bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
+    std::vector<DmTargetTypeInfo> targets;
+    if (!GetAvailableTargets(&targets)) {
+        return false;
+    }
+    for (const auto& target : targets) {
+        if (target.name() == name) {
+            if (info) *info = target;
+            return true;
+        }
+    }
+    return false;
+}
+
 bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
     devices->clear();
 
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index cb33eea..f440e6d 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/parseint.h>
 #include <android-base/strings.h>
 
 #include <libdm/dm.h>
@@ -115,5 +116,82 @@
     return keyid_ + " " + block_device_;
 }
 
+std::string DmTargetSnapshot::name() const {
+    if (mode_ == SnapshotStorageMode::Merge) {
+        return "snapshot-merge";
+    }
+    return "snapshot";
+}
+
+std::string DmTargetSnapshot::GetParameterString() const {
+    std::string mode;
+    switch (mode_) {
+        case SnapshotStorageMode::Persistent:
+        case SnapshotStorageMode::Merge:
+            // Note: "O" lets us query for overflow in the status message. This
+            // is only supported on kernels 4.4+. On earlier kernels, an overflow
+            // will be reported as "Invalid" in the status string.
+            mode = "P";
+            if (ReportsOverflow(name())) {
+                mode += "O";
+            }
+            break;
+        case SnapshotStorageMode::Transient:
+            mode = "N";
+            break;
+        default:
+            LOG(ERROR) << "DmTargetSnapshot unknown mode";
+            break;
+    }
+    return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
+}
+
+bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    DmTargetTypeInfo info;
+    if (!dm.GetTargetByName(target_type, &info)) {
+        return false;
+    }
+    if (target_type == "snapshot") {
+        return info.IsAtLeast(1, 15, 0);
+    }
+    if (target_type == "snapshot-merge") {
+        return info.IsAtLeast(1, 4, 0);
+    }
+    return false;
+}
+
+bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+    auto sections = android::base::Split(text, " ");
+    if (sections.size() == 1) {
+        // This is probably an error code, "Invalid" is possible as is "Overflow"
+        // on 4.4+.
+        status->error = text;
+        return true;
+    }
+    if (sections.size() != 2) {
+        LOG(ERROR) << "snapshot status should have two components";
+        return false;
+    }
+    auto sector_info = android::base::Split(sections[0], "/");
+    if (sector_info.size() != 2) {
+        LOG(ERROR) << "snapshot sector info should have two components";
+        return false;
+    }
+    if (!android::base::ParseUint(sections[1], &status->metadata_sectors)) {
+        LOG(ERROR) << "could not parse metadata sectors";
+        return false;
+    }
+    if (!android::base::ParseUint(sector_info[0], &status->sectors_allocated)) {
+        LOG(ERROR) << "could not parse sectors allocated";
+        return false;
+    }
+    if (!android::base::ParseUint(sector_info[1], &status->total_sectors)) {
+        LOG(ERROR) << "could not parse total sectors";
+        return false;
+    }
+    return true;
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 70823c6..72a0e11 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -24,6 +24,7 @@
 
 #include <chrono>
 #include <ctime>
+#include <iostream>
 #include <map>
 #include <thread>
 
@@ -35,21 +36,15 @@
 #include "test_util.h"
 
 using namespace std;
+using namespace std::chrono_literals;
 using namespace android::dm;
 using unique_fd = android::base::unique_fd;
 
 TEST(libdm, HasMinimumTargets) {
+    DmTargetTypeInfo info;
+
     DeviceMapper& dm = DeviceMapper::Instance();
-    vector<DmTargetTypeInfo> targets;
-    ASSERT_TRUE(dm.GetAvailableTargets(&targets));
-
-    map<string, DmTargetTypeInfo> by_name;
-    for (const auto& target : targets) {
-        by_name[target.name()] = target;
-    }
-
-    auto iter = by_name.find("linear");
-    EXPECT_NE(iter, by_name.end());
+    ASSERT_TRUE(dm.GetTargetByName("linear", &info));
 }
 
 // Helper to ensure that device mapper devices are released.
@@ -201,3 +196,245 @@
             "2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
     EXPECT_EQ(target.GetParameterString(), expected);
 }
+
+TEST(libdm, DmSnapshotArgs) {
+    DmTargetSnapshot target1(0, 512, "base", "cow", SnapshotStorageMode::Persistent, 8);
+    if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+        EXPECT_EQ(target1.GetParameterString(), "base cow PO 8");
+    } else {
+        EXPECT_EQ(target1.GetParameterString(), "base cow P 8");
+    }
+    EXPECT_EQ(target1.name(), "snapshot");
+
+    DmTargetSnapshot target2(0, 512, "base", "cow", SnapshotStorageMode::Transient, 8);
+    EXPECT_EQ(target2.GetParameterString(), "base cow N 8");
+    EXPECT_EQ(target2.name(), "snapshot");
+
+    DmTargetSnapshot target3(0, 512, "base", "cow", SnapshotStorageMode::Merge, 8);
+    if (DmTargetSnapshot::ReportsOverflow("snapshot-merge")) {
+        EXPECT_EQ(target3.GetParameterString(), "base cow PO 8");
+    } else {
+        EXPECT_EQ(target3.GetParameterString(), "base cow P 8");
+    }
+    EXPECT_EQ(target3.name(), "snapshot-merge");
+}
+
+TEST(libdm, DmSnapshotOriginArgs) {
+    DmTargetSnapshotOrigin target(0, 512, "base");
+    EXPECT_EQ(target.GetParameterString(), "base");
+    EXPECT_EQ(target.name(), "snapshot-origin");
+}
+
+class SnapshotTestHarness final {
+  public:
+    bool Setup();
+    bool Merge();
+
+    std::string origin_dev() const { return origin_dev_->path(); }
+    std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+    int base_fd() const { return base_fd_; }
+
+    static const uint64_t kBaseDeviceSize = 1024 * 1024;
+    static const uint64_t kCowDeviceSize = 1024 * 64;
+    static const uint64_t kSectorSize = 512;
+
+  private:
+    void SetupImpl();
+    void MergeImpl();
+
+    unique_fd base_fd_;
+    unique_fd cow_fd_;
+    unique_ptr<LoopDevice> base_loop_;
+    unique_ptr<LoopDevice> cow_loop_;
+    unique_ptr<TempDevice> origin_dev_;
+    unique_ptr<TempDevice> snapshot_dev_;
+    bool setup_ok_ = false;
+    bool merge_ok_ = false;
+};
+
+bool SnapshotTestHarness::Setup() {
+    SetupImpl();
+    return setup_ok_;
+}
+
+void SnapshotTestHarness::SetupImpl() {
+    base_fd_ = CreateTempFile("base_device", kBaseDeviceSize);
+    ASSERT_GE(base_fd_, 0);
+    cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
+    ASSERT_GE(cow_fd_, 0);
+
+    base_loop_ = std::make_unique<LoopDevice>(base_fd_);
+    ASSERT_TRUE(base_loop_->valid());
+    cow_loop_ = std::make_unique<LoopDevice>(cow_fd_);
+    ASSERT_TRUE(cow_loop_->valid());
+
+    DmTable origin_table;
+    ASSERT_TRUE(origin_table.AddTarget(make_unique<DmTargetSnapshotOrigin>(
+            0, kBaseDeviceSize / kSectorSize, base_loop_->device())));
+    ASSERT_TRUE(origin_table.valid());
+
+    origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
+    ASSERT_TRUE(origin_dev_->valid());
+    ASSERT_FALSE(origin_dev_->path().empty());
+    ASSERT_TRUE(origin_dev_->WaitForUdev());
+
+    // chunk size = 4K blocks.
+    DmTable snap_table;
+    ASSERT_TRUE(snap_table.AddTarget(make_unique<DmTargetSnapshot>(
+            0, kBaseDeviceSize / kSectorSize, base_loop_->device(), cow_loop_->device(),
+            SnapshotStorageMode::Persistent, 8)));
+    ASSERT_TRUE(snap_table.valid());
+
+    snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
+    ASSERT_TRUE(snapshot_dev_->valid());
+    ASSERT_FALSE(snapshot_dev_->path().empty());
+    ASSERT_TRUE(snapshot_dev_->WaitForUdev());
+
+    setup_ok_ = true;
+}
+
+bool SnapshotTestHarness::Merge() {
+    MergeImpl();
+    return merge_ok_;
+}
+
+void SnapshotTestHarness::MergeImpl() {
+    DmTable merge_table;
+    ASSERT_TRUE(merge_table.AddTarget(
+            make_unique<DmTargetSnapshot>(0, kBaseDeviceSize / kSectorSize, base_loop_->device(),
+                                          cow_loop_->device(), SnapshotStorageMode::Merge, 8)));
+    ASSERT_TRUE(merge_table.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    ASSERT_TRUE(dm.LoadTableAndActivate("libdm-test-dm-snapshot", merge_table));
+
+    while (true) {
+        vector<DeviceMapper::TargetInfo> status;
+        ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &status));
+        ASSERT_EQ(status.size(), 1);
+        ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+                  0);
+
+        DmTargetSnapshot::Status merge_status;
+        ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+        ASSERT_TRUE(merge_status.error.empty());
+        if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+            break;
+        }
+
+        std::this_thread::sleep_for(250ms);
+    }
+
+    merge_ok_ = true;
+}
+
+bool CheckSnapshotAvailability() {
+    DmTargetTypeInfo info;
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.GetTargetByName("snapshot", &info)) {
+        cout << "snapshot module not enabled; skipping test" << std::endl;
+        return false;
+    }
+    if (!dm.GetTargetByName("snapshot-merge", &info)) {
+        cout << "snapshot-merge module not enabled; skipping test" << std::endl;
+        return false;
+    }
+    if (!dm.GetTargetByName("snapshot-origin", &info)) {
+        cout << "snapshot-origin module not enabled; skipping test" << std::endl;
+        return false;
+    }
+    return true;
+}
+
+TEST(libdm, DmSnapshot) {
+    if (!CheckSnapshotAvailability()) {
+        return;
+    }
+
+    SnapshotTestHarness harness;
+    ASSERT_TRUE(harness.Setup());
+
+    // Open the dm devices.
+    unique_fd origin_fd(open(harness.origin_dev().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(origin_fd, 0);
+    unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+    ASSERT_GE(snapshot_fd, 0);
+
+    // Write to the first block of the snapshot device.
+    std::string data("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+    ASSERT_TRUE(android::base::WriteFully(snapshot_fd, data.data(), data.size()));
+    ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+
+    // We should get the same data back from the snapshot device.
+    std::string read(data.size(), '\0');
+    ASSERT_TRUE(android::base::ReadFully(snapshot_fd, read.data(), read.size()));
+    ASSERT_EQ(read, data);
+
+    // We should see the original data from the origin device.
+    std::string zeroes(data.size(), '\0');
+    ASSERT_TRUE(android::base::ReadFully(origin_fd, read.data(), read.size()));
+    ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+    ASSERT_EQ(read, zeroes);
+
+    // We should also see the original data from the base device.
+    ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+    ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+    ASSERT_EQ(read, zeroes);
+
+    // Now, perform the merge and wait.
+    ASSERT_TRUE(harness.Merge());
+
+    // Reading from the base device should give us the modified data.
+    ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+    ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+    ASSERT_EQ(read, data);
+}
+
+TEST(libdm, DmSnapshotOverflow) {
+    if (!CheckSnapshotAvailability()) {
+        return;
+    }
+
+    SnapshotTestHarness harness;
+    ASSERT_TRUE(harness.Setup());
+
+    // Open the dm devices.
+    unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC));
+    ASSERT_GE(snapshot_fd, 0);
+
+    // Fill the copy-on-write device until it overflows.
+    uint64_t bytes_remaining = SnapshotTestHarness::kCowDeviceSize;
+    uint8_t byte = 1;
+    while (bytes_remaining) {
+        std::string data(4096, char(byte));
+        if (!android::base::WriteFully(snapshot_fd, data.data(), data.size())) {
+            ASSERT_EQ(errno, EIO);
+            break;
+        }
+        bytes_remaining -= data.size();
+    }
+
+    // If writes succeed (because they are buffered), then we should expect an
+    // fsync to fail with EIO.
+    if (!bytes_remaining) {
+        ASSERT_EQ(fsync(snapshot_fd), -1);
+        ASSERT_EQ(errno, EIO);
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    vector<DeviceMapper::TargetInfo> target_status;
+    ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &target_status));
+    ASSERT_EQ(target_status.size(), 1);
+    ASSERT_EQ(strncmp(target_status[0].spec.target_type, "snapshot", strlen("snapshot")), 0);
+
+    DmTargetSnapshot::Status status;
+    ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(target_status[0].data, &status));
+    if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+        ASSERT_EQ(status.error, "Overflow");
+    } else {
+        ASSERT_EQ(status.error, "Invalid");
+    }
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 28e6e01..d7e8aa9 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -96,6 +96,10 @@
     // successfully read and stored in 'targets'. Returns 'false' otherwise.
     bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
 
+    // Finds a target by name and returns its information if found. |info| may
+    // be null to check for the existence of a target.
+    bool GetTargetByName(const std::string& name, DmTargetTypeInfo* info);
+
     // Return 'true' if it can successfully read the list of device mapper block devices
     // currently created. 'devices' will be empty if the kernel interactions
     // were successful and there are no block devices at the moment. Returns
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 175b0f0..fce1175 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -40,6 +40,18 @@
         return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
     }
 
+    uint32_t major_version() const { return major_; }
+    uint32_t minor_version() const { return minor_; }
+    uint32_t patch_level() const { return patch_; }
+
+    bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch) const {
+        if (major_ > major) return true;
+        if (major_ < major) return false;
+        if (minor_ > minor) return true;
+        if (minor_ < minor) return false;
+        return patch_ >= patch;
+    }
+
   private:
     std::string name_;
     uint32_t major_;
@@ -170,6 +182,65 @@
     std::string target_string_;
 };
 
+enum class SnapshotStorageMode {
+    // The snapshot will be persisted to the COW device.
+    Persistent,
+    // The snapshot will be lost on reboot.
+    Transient,
+    // The snapshot will be merged from the COW device into the base device,
+    // in the background.
+    Merge
+};
+
+// Writes to a snapshot device will be written to the given COW device. Reads
+// will read from the COW device or base device. The chunk size is specified
+// in sectors.
+class DmTargetSnapshot final : public DmTarget {
+  public:
+    DmTargetSnapshot(uint64_t start, uint64_t length, const std::string& base_device,
+                     const std::string& cow_device, SnapshotStorageMode mode, uint64_t chunk_size)
+        : DmTarget(start, length),
+          base_device_(base_device),
+          cow_device_(cow_device),
+          mode_(mode),
+          chunk_size_(chunk_size) {}
+
+    std::string name() const override;
+    std::string GetParameterString() const override;
+    bool Valid() const override { return true; }
+
+    struct Status {
+        uint64_t sectors_allocated;
+        uint64_t total_sectors;
+        uint64_t metadata_sectors;
+        std::string error;
+    };
+
+    static bool ParseStatusText(const std::string& text, Status* status);
+    static bool ReportsOverflow(const std::string& target_type);
+
+  private:
+    std::string base_device_;
+    std::string cow_device_;
+    SnapshotStorageMode mode_;
+    uint64_t chunk_size_;
+};
+
+// snapshot-origin will read/write directly to the backing device, updating any
+// snapshot devices with a matching origin.
+class DmTargetSnapshotOrigin final : public DmTarget {
+  public:
+    DmTargetSnapshotOrigin(uint64_t start, uint64_t length, const std::string& device)
+        : DmTarget(start, length), device_(device) {}
+
+    std::string name() const override { return "snapshot-origin"; }
+    std::string GetParameterString() const override { return device_; }
+    bool Valid() const override { return true; }
+
+  private:
+    std::string device_;
+};
+
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 8fc02cb..fcef1f0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,6 +21,8 @@
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
 
@@ -702,3 +704,19 @@
     ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
     EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
 }
+
+TEST(liblp, ReadSuperPartition) {
+    auto slot_suffix = fs_mgr_get_slot_suffix();
+    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto super_name = fs_mgr_get_super_partition_name(slot_number);
+    auto metadata = ReadMetadata(super_name, slot_number);
+    ASSERT_NE(metadata, nullptr);
+
+    if (!slot_suffix.empty()) {
+        auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+        auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+        auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+        auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+        ASSERT_NE(other_metadata, nullptr);
+    }
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 72afa69..6d87594 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -394,7 +394,7 @@
     std::string fstab_contents = R"fs(
 source none0       swap   defaults      encryptable,forceencrypt,fileencryption,forcefdeorfbe,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_loopback_path,zram_loopback_size,zram_backing_dev_path
 
-source none1       swap   defaults      encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,verify=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
+source none1       swap   defaults      encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
 
 source none2       swap   defaults      forcefdeorfbe=
 
@@ -413,7 +413,6 @@
     }
     EXPECT_EQ("", entry->key_loc);
     EXPECT_EQ("", entry->key_dir);
-    EXPECT_EQ("", entry->verity_loc);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
     EXPECT_EQ(-1, entry->partnum);
@@ -437,13 +436,11 @@
         flags.crypt = true;
         flags.force_crypt = true;
         flags.file_encryption = true;
-        flags.verify = true;
         flags.avb = true;
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
     EXPECT_EQ("", entry->key_loc);
     EXPECT_EQ("", entry->key_dir);
-    EXPECT_EQ("", entry->verity_loc);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
     EXPECT_EQ(-1, entry->partnum);
@@ -639,29 +636,6 @@
     EXPECT_EQ(0, entry->zram_size);
 }
 
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Verify) {
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
-    std::string fstab_contents = R"fs(
-source none0       swap   defaults      verify=/dir/key
-)fs";
-
-    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
-    Fstab fstab;
-    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    ASSERT_EQ(1U, fstab.size());
-
-    auto entry = fstab.begin();
-    EXPECT_EQ("none0", entry->mount_point);
-
-    FstabEntry::FsMgrFlags flags = {};
-    flags.verify = true;
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-
-    EXPECT_EQ("/dir/key", entry->verity_loc);
-}
-
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 9309aad..7e6ad5b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,15 +38,7 @@
 #include <vector>
 
 using namespace std::literals::string_literals;
-
-using DeviceMapper = ::android::dm::DeviceMapper;
-using DmTable = ::android::dm::DmTable;
-using DmTarget = ::android::dm::DmTarget;
-using DmTargetLinear = ::android::dm::DmTargetLinear;
-using DmTargetZero = ::android::dm::DmTargetZero;
-using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
-using DmTargetBow = ::android::dm::DmTargetBow;
-using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using namespace android::dm;
 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
 
 static int Usage(void) {
@@ -57,6 +49,7 @@
     std::cerr << "  delete <dm-name>" << std::endl;
     std::cerr << "  list <devices | targets> [-v]" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  status <dm-name>" << std::endl;
     std::cerr << "  table <dm-name>" << std::endl;
     std::cerr << "  help" << std::endl;
     std::cerr << std::endl;
@@ -122,6 +115,62 @@
             }
             std::string block_device = NextArg();
             return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
+        } else if (target_type == "snapshot-origin") {
+            if (!HasArgs(1)) {
+                std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
+                return nullptr;
+            }
+            std::string block_device = NextArg();
+            return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
+                                                            block_device);
+        } else if (target_type == "snapshot") {
+            if (!HasArgs(4)) {
+                std::cerr
+                        << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
+                        << std::endl;
+                return nullptr;
+            }
+            std::string base_device = NextArg();
+            std::string cow_device = NextArg();
+            std::string mode_str = NextArg();
+            std::string chunk_size_str = NextArg();
+
+            SnapshotStorageMode mode;
+            if (mode_str == "P") {
+                mode = SnapshotStorageMode::Persistent;
+            } else if (mode_str == "N") {
+                mode = SnapshotStorageMode::Transient;
+            } else {
+                std::cerr << "Unrecognized mode: " << mode_str << "\n";
+                return nullptr;
+            }
+
+            uint32_t chunk_size;
+            if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+                std::cerr << "Chunk size must be an unsigned integer.\n";
+                return nullptr;
+            }
+            return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+                                                      cow_device, mode, chunk_size);
+        } else if (target_type == "snapshot-merge") {
+            if (!HasArgs(3)) {
+                std::cerr
+                        << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
+                        << std::endl;
+                return nullptr;
+            }
+            std::string base_device = NextArg();
+            std::string cow_device = NextArg();
+            std::string chunk_size_str = NextArg();
+            SnapshotStorageMode mode = SnapshotStorageMode::Merge;
+
+            uint32_t chunk_size;
+            if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+                std::cerr << "Chunk size must be an unsigned integer.\n";
+                return nullptr;
+            }
+            return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+                                                      cow_device, mode, chunk_size);
         } else {
             std::cerr << "Unrecognized target type: " << target_type << std::endl;
             return nullptr;
@@ -308,7 +357,7 @@
     return 0;
 }
 
-static int TableCmdHandler(int argc, char** argv) {
+static int DumpTable(const std::string& mode, int argc, char** argv) {
     if (argc != 1) {
         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
         return -EINVAL;
@@ -316,9 +365,18 @@
 
     DeviceMapper& dm = DeviceMapper::Instance();
     std::vector<DeviceMapper::TargetInfo> table;
-    if (!dm.GetTableInfo(argv[0], &table)) {
-        std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
-        return -EINVAL;
+    if (mode == "status") {
+        if (!dm.GetTableStatus(argv[0], &table)) {
+            std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+                      << std::endl;
+            return -EINVAL;
+        }
+    } else if (mode == "table") {
+        if (!dm.GetTableInfo(argv[0], &table)) {
+            std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+                      << std::endl;
+            return -EINVAL;
+        }
     }
     std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
     for (const auto& target : table) {
@@ -333,6 +391,14 @@
     return 0;
 }
 
+static int TableCmdHandler(int argc, char** argv) {
+    return DumpTable("table", argc, argv);
+}
+
+static int StatusCmdHandler(int argc, char** argv) {
+    return DumpTable("status", argc, argv);
+}
+
 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
         // clang-format off
         {"create", DmCreateCmdHandler},
@@ -341,6 +407,7 @@
         {"help", HelpCmdHandler},
         {"getpath", GetPathCmdHandler},
         {"table", TableCmdHandler},
+        {"status", StatusCmdHandler},
         // clang-format on
 };
 
diff --git a/init/README.md b/init/README.md
index 5f8abf2..79c6237 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.
@@ -598,9 +598,6 @@
 `umount <path>`
 > Unmount the filesystem mounted at that path.
 
-`verity_load_state`
-> Internal implementation detail used to load dm-verity state.
-
 `verity_update_state <mount-point>`
 > Internal implementation detail used to update dm-verity state and
   set the partition._mount-point_.verified properties used by adb remount
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8bd2718..ba1c94d 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -729,17 +729,6 @@
     return Success();
 }
 
-static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
-    int mode = -1;
-    bool loaded = fs_mgr_load_verity_state(&mode);
-    if (loaded && mode != VERITY_MODE_DEFAULT) {
-        ActionManager::GetInstance().QueueEventTrigger("verity-logging");
-    }
-    if (!loaded) return Error() << "Could not load verity state";
-
-    return Success();
-}
-
 static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
     int mode;
     if (!fs_mgr_load_verity_state(&mode)) {
@@ -1181,7 +1170,6 @@
         {"symlink",                 {2,     2,    {true,   do_symlink}}},
         {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
         {"trigger",                 {1,     1,    {false,  do_trigger}}},
-        {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
         {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
         {"wait",                    {1,     2,    {true,   do_wait}}},
         {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 3e76556..85fa874 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -644,7 +644,6 @@
 }
 
 bool FirstStageMountVBootV1::GetDmVerityDevices() {
-    std::string verity_loc_device;
     need_dm_verity_ = false;
 
     for (const auto& fstab_entry : fstab_) {
@@ -657,21 +656,9 @@
         if (fstab_entry.fs_mgr_flags.verify) {
             need_dm_verity_ = true;
         }
-        // Checks if verity metadata is on a separate partition. Note that it is
-        // not partition specific, so there must be only one additional partition
-        // that carries verity state.
-        if (!fstab_entry.verity_loc.empty()) {
-            if (verity_loc_device.empty()) {
-                verity_loc_device = fstab_entry.verity_loc;
-            } else if (verity_loc_device != fstab_entry.verity_loc) {
-                LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
-                           << fstab_entry.verity_loc;
-                return false;
-            }
-        }
     }
 
-    // Includes the partition names of fstab records and verity_loc_device (if any).
+    // Includes the partition names of fstab records.
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
     for (const auto& fstab_entry : fstab_) {
         if (!fstab_entry.fs_mgr_flags.logical) {
@@ -679,10 +666,6 @@
         }
     }
 
-    if (!verity_loc_device.empty()) {
-        required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
-    }
-
     return true;
 }
 
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 132fc13..c49dc9f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -331,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();
@@ -376,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 8c3e228..2f96681 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/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/logprint.cpp b/liblog/logprint.cpp
index cec5d96..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.
- */
-static 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/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/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index ab38dfd..32c6ea8 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,6 +25,8 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+#include <string_view>
+
 #include "android-base/off64_t.h"
 
 /* Zip compression methods we support */
@@ -39,10 +41,7 @@
 
   ZipString() {}
 
-  /*
-   * entry_name has to be an c-style string with only ASCII characters.
-   */
-  explicit ZipString(const char* entry_name);
+  explicit ZipString(std::string_view entry_name);
 
   bool operator==(const ZipString& rhs) const {
     return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
@@ -149,8 +148,7 @@
 void CloseArchive(ZipArchiveHandle archive);
 
 /*
- * Find an entry in the Zip archive, by name. |entryName| must be a null
- * terminated string, and |data| must point to a writeable memory location.
+ * Find an entry in the Zip archive, by name. |data| must be non-null.
  *
  * Returns 0 if an entry is found, and populates |data| with information
  * about this entry. Returns negative values otherwise.
@@ -164,6 +162,8 @@
  * On non-Windows platforms this method does not modify internal state and
  * can be called concurrently.
  */
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
+// TODO: remove this internally, where there is a new user.
 int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
 
 /*
@@ -179,6 +179,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
+// TODO: switch these ZipStrings to std::string_view.
 int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
                        const ZipString* optional_prefix, const ZipString* optional_suffix);
 
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 596786a..bc7103b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -690,8 +690,7 @@
 
 struct IterationHandle {
   uint32_t position;
-  // We're not using vector here because this code is used in the Windows SDK
-  // where the STL is not available.
+  // TODO: switch these to std::string now that Windows uses libc++ too.
   ZipString prefix;
   ZipString suffix;
   ZipArchive* archive;
@@ -742,6 +741,7 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
+// TODO: remove this internally.
 int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
   if (entryName.name_length == 0) {
     ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
@@ -758,6 +758,23 @@
   return FindEntry(archive, static_cast<uint32_t>(ent), data);
 }
 
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+                  ZipEntry* data) {
+  if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
+    ALOGW("Zip: Invalid filename of length %zu", entryName.size());
+    return kInvalidEntryName;
+  }
+
+  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size,
+                                   ZipString(entryName), archive->central_directory.GetBasePtr());
+  if (ent < 0) {
+    ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
+    return static_cast<int32_t>(ent);  // kEntryNotFound is safe to truncate.
+  }
+  // We know there are at most hast_table_size entries, safe to truncate.
+  return FindEntry(archive, static_cast<uint32_t>(ent), data);
+}
+
 int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
@@ -1152,8 +1169,9 @@
   return archive->mapped_zip.GetFileDescriptor();
 }
 
-ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
-  size_t len = strlen(entry_name);
+ZipString::ZipString(std::string_view entry_name)
+    : name(reinterpret_cast<const uint8_t*>(entry_name.data())) {
+  size_t len = entry_name.size();
   CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
   name_length = static_cast<uint16_t>(len);
 }
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 46aa5a6..52166a4 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -55,7 +55,7 @@
 
   // In order to walk through all file names in the archive, look for a name
   // that does not exist in the archive.
-  ZipString name("thisFileNameDoesNotExist");
+  std::string_view name("thisFileNameDoesNotExist");
 
   // Start the benchmark.
   while (state.KeepRunning()) {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index e471d5e..cfbce1c 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -64,12 +64,6 @@
   return OpenArchive(abs_path.c_str(), handle);
 }
 
-static void SetZipString(ZipString* zip_str, const std::string& str) {
-  zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
-  CHECK_LE(str.size(), std::numeric_limits<uint16_t>::max());
-  zip_str->name_length = static_cast<uint16_t>(str.size());
-}
-
 TEST(ziparchive, Open) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -192,9 +186,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry data;
-  ZipString name;
-  SetZipString(&name, kATxtName);
-  ASSERT_EQ(0, FindEntry(handle, name, &data));
+  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
 
   // Known facts about a.txt, from zipinfo -v.
   ASSERT_EQ(63, data.offset);
@@ -205,9 +197,28 @@
   ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
-  ZipString absent_name;
-  SetZipString(&absent_name, kNonexistentTxtName);
-  ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
+  ASSERT_LT(FindEntry(handle, kNonexistentTxtName, &data), 0);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_empty) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  ZipEntry data;
+  ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_too_long) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  std::string very_long_name(65536, 'x');
+  ZipEntry data;
+  ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
 
   CloseArchive(handle);
 }
@@ -234,9 +245,7 @@
 
   // An entry that's deflated.
   ZipEntry data;
-  ZipString a_name;
-  SetZipString(&a_name, kATxtName);
-  ASSERT_EQ(0, FindEntry(handle, a_name, &data));
+  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
   const uint32_t a_size = data.uncompressed_length;
   ASSERT_EQ(a_size, kATxtContents.size());
   uint8_t* buffer = new uint8_t[a_size];
@@ -245,9 +254,7 @@
   delete[] buffer;
 
   // An entry that's stored.
-  ZipString b_name;
-  SetZipString(&b_name, kBTxtName);
-  ASSERT_EQ(0, FindEntry(handle, b_name, &data));
+  ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
   const uint32_t b_size = data.uncompressed_length;
   ASSERT_EQ(b_size, kBTxtContents.size());
   buffer = new uint8_t[b_size];
@@ -302,9 +309,7 @@
   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
 
   ZipEntry entry;
-  ZipString empty_name;
-  SetZipString(&empty_name, kEmptyTxtName);
-  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
   uint8_t buffer[1];
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -327,9 +332,7 @@
   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
 
   ZipEntry entry;
-  ZipString ab_name;
-  SetZipString(&ab_name, kAbTxtName);
-  ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
 
   // Extract the entry to memory.
@@ -386,9 +389,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry entry;
-  ZipString name;
-  SetZipString(&name, kATxtName);
-  ASSERT_EQ(0, FindEntry(handle, name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
 
   // Assert that the first 8 bytes of the file haven't been clobbered.
@@ -424,10 +425,8 @@
             OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
 
   // Assert one entry can be found and extracted correctly.
-  std::string BINARY_PATH("META-INF/com/google/android/update-binary");
-  ZipString binary_path(BINARY_PATH.c_str());
   ZipEntry binary_entry;
-  ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+  ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
   TemporaryFile tmp_binary;
   ASSERT_NE(-1, tmp_binary.fd);
   ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
@@ -436,9 +435,7 @@
 
 static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
                                  bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
-  ZipString name;
-  SetZipString(&name, entry_name);
-  ASSERT_EQ(0, FindEntry(handle, name, entry));
+  ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
   std::unique_ptr<ZipArchiveStreamEntry> stream;
   if (raw) {
     stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
@@ -589,11 +586,7 @@
   // an entry whose name is "name" and whose size is 12 (contents =
   // "abdcdefghijk").
   ZipEntry entry;
-  ZipString name;
-  std::string name_str = "name";
-  SetZipString(&name, name_str);
-
-  ASSERT_EQ(0, FindEntry(handle, name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, "name", &entry));
   ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
 
   entry_out->resize(12);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 63adbbc..c3da23c 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -62,7 +62,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(0u, data.has_data_descriptor);
   EXPECT_EQ(strlen(expected), data.compressed_length);
@@ -95,19 +95,19 @@
 
   ZipEntry data;
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(2u, data.compressed_length);
   ASSERT_EQ(2u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(3u, data.compressed_length);
   ASSERT_EQ(3u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(0u, data.compressed_length);
   EXPECT_EQ(0u, data.uncompressed_length);
@@ -129,7 +129,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
   CloseArchive(handle);
@@ -163,7 +163,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
   struct tm mod = data.GetModificationTime();
@@ -191,7 +191,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
   CloseArchive(handle);
@@ -213,7 +213,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
   struct tm mod = data.GetModificationTime();
@@ -241,7 +241,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressDeflated, data.method);
   ASSERT_EQ(4u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
@@ -273,7 +273,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressDeflated, data.method);
   EXPECT_EQ(kBufSize, data.uncompressed_length);
 
@@ -340,12 +340,12 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
   ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
 
-  ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+  ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
 
-  ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+  ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
   ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
 
   CloseArchive(handle);
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/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index aa392ce..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
@@ -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