Merge "libbase: add ConsumePrefix/ConsumeSuffix."
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/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 e2a17c5..f39aa58 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1142,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;
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/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/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_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_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/init/README.md b/init/README.md
index 51deb5a..28a106a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -191,7 +191,7 @@
 
 `critical`
 > This is a device-critical service. If it exits more than four times in
-  four minutes, the device will reboot into bootloader.
+  four minutes or before boot completes, the device will reboot into bootloader.
 
 `disabled`
 > This service will not automatically start with its class.
diff --git a/init/service.cpp b/init/service.cpp
index f5c13b9..276c2aa 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -372,16 +372,20 @@
         return;
     }
 
-    // If we crash > 4 times in 4 minutes, reboot into bootloader or set crashing property
+    // If we crash > 4 times in 4 minutes or before boot_completed,
+    // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
     if (((flags_ & SVC_CRITICAL) || !pre_apexd_) && !(flags_ & SVC_RESTART)) {
-        if (now < time_crashed_ + 4min) {
+        bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+        if (now < time_crashed_ + 4min || !boot_completed) {
             if (++crash_count_ > 4) {
                 if (flags_ & SVC_CRITICAL) {
                     // Aborts into bootloader
-                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
                 } else {
-                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times in 4 minutes";
+                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
                     // Notifies update_verifier and apexd
                     property_set("ro.init.updatable_crashing", "1");
                 }
diff --git a/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 1012ea0..ed98714 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -15,742 +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
-// 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.
-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();
-      }
-    }
-
-    // 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* 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 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, ':');
-  }
-
-  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 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_;
-
-  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__)
@@ -784,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) {
@@ -958,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/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 85b8cdc..4b07478 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -53,11 +53,12 @@
 # Finally, if all attempts fail, the dynamic linker returns an error.
 namespace.default.links = runtime,resolv
 namespace.default.asan.links = runtime,resolv
-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
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index b37a551..f4710d8 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -132,11 +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
-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
@@ -510,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
@@ -587,11 +589,12 @@
 
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 namespace.default.links = runtime,resolv
-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
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 04fbd82..4ce7f52 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -72,11 +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
-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
@@ -361,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
@@ -408,11 +410,12 @@
 
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 namespace.default.links = runtime,resolv
-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