Merge "Add missing <android/log.h> documentation."
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index cf22efa..0008f72 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -392,7 +392,8 @@
         }
 
         if (android::base::EndsWithIgnoreCase(file, ".apk") ||
-            android::base::EndsWithIgnoreCase(file, ".dm")) {
+            android::base::EndsWithIgnoreCase(file, ".dm") ||
+            android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
             struct stat sb;
             if (stat(file, &sb) != -1) total_size += sb.st_size;
             first_apk = i;
diff --git a/adb/client/console.cpp b/adb/client/console.cpp
index 9563eac..4e8a3f8 100644
--- a/adb/client/console.cpp
+++ b/adb/client/console.cpp
@@ -135,6 +135,7 @@
     // preventing the emulator from reading the command that adb has sent.
     // https://code.google.com/p/android/issues/detail?id=21021
     int result;
+    std::string emulator_output;
     do {
         char buf[BUFSIZ];
         result = adb_read(fd, buf, sizeof(buf));
@@ -146,8 +147,37 @@
         // appended above, and that causes the emulator to close the socket
         // which should cause zero bytes (orderly/graceful shutdown) to be
         // returned.
+        if (result > 0) emulator_output.append(buf, result);
     } while (result > 0);
 
+    // Note: the following messages are expected to be quite stable from emulator.
+    //
+    // Emulator console will send the following message upon connection:
+    //
+    // Android Console: Authentication required
+    // Android Console: type 'auth <auth_token>' to authenticate
+    // Android Console: you can find your <auth_token> in
+    // '/<path-to-home>/.emulator_console_auth_token'
+    // OK\r\n
+    //
+    // and the following after authentication:
+    // Android Console: type 'help' for a list of commands
+    // OK\r\n
+    //
+    // So try search and skip first two "OK\r\n", print the rest.
+    //
+    const std::string delims = "OK\r\n";
+    size_t found = 0;
+    for (int i = 0; i < 2; ++i) {
+        const size_t result = emulator_output.find(delims, found);
+        if (result == std::string::npos) {
+            break;
+        } else {
+            found = result + delims.size();
+        }
+    }
+
+    printf("%s", emulator_output.c_str() + found);
     adb_close(fd);
 
     return 0;
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 9c35560..fc5c1ce 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -61,6 +61,7 @@
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
 bool StartsWith(const std::string& s, const std::string& prefix);
 bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool StartsWith(const std::string& s, char prefix);
 
 // Tests whether 's' ends with 'suffix'.
 // TODO: string_view
@@ -68,6 +69,7 @@
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
 bool EndsWith(const std::string& s, const std::string& suffix);
 bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
+bool EndsWith(const std::string& s, char suffix);
 
 // Tests whether 'lhs' equals 'rhs', ignoring case.
 bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index cd2dc04..4e3879b 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 
 #if !defined(_WIN32)
+#include <dirent.h>
 #include <sys/socket.h>
 #endif
 
@@ -211,6 +212,17 @@
   return file;
 }
 
+// Using fdopendir with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline DIR* Fdopendir(unique_fd&& ufd) {
+  int fd = ufd.release();
+  DIR* dir = fdopendir(fd);
+  if (dir == nullptr) {
+    close(fd);
+  }
+  return dir;
+}
+
 #endif  // !defined(_WIN32)
 
 }  // namespace base
diff --git a/base/strings.cpp b/base/strings.cpp
index a8bb2a9..2d6eef0 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -95,6 +95,10 @@
   return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
 }
 
+bool StartsWith(const std::string& s, char prefix) {
+  return *s.c_str() == prefix;  // Use c_str() to guarantee there is at least a '\0'.
+}
+
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
   return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
 }
@@ -121,6 +125,10 @@
   return EndsWith(s, suffix.c_str(), suffix.size(), true);
 }
 
+bool EndsWith(const std::string& s, char suffix) {
+  return EndsWith(s, &suffix, 1, true);
+}
+
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
   return EndsWith(s, suffix, strlen(suffix), false);
 }
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index b8639ea..9d74094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -198,6 +198,12 @@
   ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
 }
 
+TEST(strings, StartsWith_char) {
+  ASSERT_FALSE(android::base::StartsWith("", 'f'));
+  ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
+  ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
+}
+
 TEST(strings, EndsWith_empty) {
   ASSERT_FALSE(android::base::EndsWith("", "foo"));
   ASSERT_TRUE(android::base::EndsWith("", ""));
@@ -273,6 +279,12 @@
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
 }
 
+TEST(strings, EndsWith_char) {
+  ASSERT_FALSE(android::base::EndsWith("", 'o'));
+  ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
+  ASSERT_FALSE(android::base::EndsWith("foo", "f"));
+}
+
 TEST(strings, EqualsIgnoreCase) {
   ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
   ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
diff --git a/cpio/Android.bp b/cpio/Android.bp
new file mode 100644
index 0000000..847e0f1
--- /dev/null
+++ b/cpio/Android.bp
@@ -0,0 +1,8 @@
+// Copyright 2005 The Android Open Source Project
+
+cc_binary_host {
+    name: "mkbootfs",
+    srcs: ["mkbootfs.c"],
+    cflags: ["-Werror"],
+    shared_libs: ["libcutils"],
+}
diff --git a/cpio/Android.mk b/cpio/Android.mk
index 2aa7297..fc3551b 100644
--- a/cpio/Android.mk
+++ b/cpio/Android.mk
@@ -1,17 +1,3 @@
 # Copyright 2005 The Android Open Source Project
 
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-	mkbootfs.c
-
-LOCAL_MODULE := mkbootfs
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
+$(call dist-for-goals,dist_files,$(ALL_MODULES.mkbootfs.BUILT))
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index ed7423b..15c0265 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -250,11 +250,12 @@
         }
 
         uint64_t expected = pack_thread_fd(-1, -1);
-        if (!trace_output.compare_exchange_strong(expected,
-                                                  pack_thread_fd(tid, pipe_write.release()))) {
+        int sent_fd = pipe_write.release();
+        if (!trace_output.compare_exchange_strong(expected, pack_thread_fd(tid, sent_fd))) {
           auto [tid, fd] = unpack_thread_fd(expected);
           async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                                 "thread %d is already outputting to fd %d?", tid, fd);
+          close(sent_fd);
           return false;
         }
 
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 8bdc02f..d0c5234 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -382,6 +382,8 @@
         case TRAP_TRACE: return "TRAP_TRACE";
         case TRAP_BRANCH: return "TRAP_BRANCH";
         case TRAP_HWBKPT: return "TRAP_HWBKPT";
+        case TRAP_UNK:
+          return "TRAP_UNDIAGNOSED";
       }
       if ((si->si_code & 0xff) == SIGTRAP) {
         switch ((si->si_code >> 8) & 0xff) {
@@ -403,7 +405,7 @@
             return "PTRACE_EVENT_STOP";
         }
       }
-      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+      static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
       break;
   }
   // Then the other codes...
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 6e45133..11c838a 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -329,7 +329,6 @@
 
   private:
     std::string super_device_;
-    uint32_t slot_number_;
     std::unique_ptr<MetadataBuilder> builder_;
 };
 
@@ -341,8 +340,8 @@
     super_device_ = *super_device;
 
     std::string slot = device->GetCurrentSlot();
-    slot_number_ = SlotNumberForSlotSuffix(slot);
-    builder_ = MetadataBuilder::New(super_device_, slot_number_);
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot);
+    builder_ = MetadataBuilder::New(super_device_, slot_number);
 }
 
 bool PartitionBuilder::Write() {
@@ -350,7 +349,11 @@
     if (!metadata) {
         return false;
     }
-    return UpdatePartitionTable(super_device_, *metadata.get(), slot_number_);
+    bool ok = true;
+    for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
+        ok &= UpdatePartitionTable(super_device_, *metadata.get(), i);
+    }
+    return ok;
 }
 
 bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3cce0e8..6c8a943 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -46,6 +46,7 @@
         "fs_mgr_avb_ops.cpp",
         "fs_mgr_dm_linear.cpp",
         "fs_mgr_overlayfs.cpp",
+        "fs_mgr_vendor_overlay.cpp",
     ],
     shared_libs: [
         "libbase",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a6ef35b..ae2e2fe 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -50,7 +50,6 @@
 #include <cutils/partition_utils.h>
 #include <cutils/properties.h>
 #include <ext4_utils/ext4.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 6ddd5a8..4dacebf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -75,14 +75,9 @@
                 target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
                 break;
             case LP_TARGET_TYPE_LINEAR: {
-                auto block_device = GetMetadataSuperBlockDevice(metadata);
-                if (!block_device) {
-                    LOG(ERROR) << "Could not identify the super block device";
-                    return false;
-                }
-
+                const auto& block_device = metadata.block_devices[extent.target_source];
                 std::string path;
-                if (!GetPhysicalPartitionDevicePath(*block_device, &path)) {
+                if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
                     LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
                     return false;
                 }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index ad488a9..f06b819 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -41,6 +41,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
@@ -54,13 +55,28 @@
 using namespace android::dm;
 using namespace android::fs_mgr;
 
+static bool fs_mgr_access(const std::string& path) {
+    auto save_errno = errno;
+    auto ret = access(path.c_str(), F_OK) == 0;
+    errno = save_errno;
+    return ret;
+}
+
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
-bool fs_mgr_overlayfs_mount_all(const fstab*) {
+bool fs_mgr_overlayfs_mount_all(fstab*) {
     return false;
 }
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab*) {
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>&) {
+    return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
+    return {};
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const std::vector<const fstab_rec*>&) {
     return {};
 }
 
@@ -103,17 +119,6 @@
     return ret | !rmdir(test_directory.c_str());
 }
 
-std::string fs_mgr_get_context(const std::string& mount_point) {
-    char* ctx = nullptr;
-    auto len = getfilecon(mount_point.c_str(), &ctx);
-    if ((len > 0) && ctx) {
-        std::string context(ctx, len);
-        free(ctx);
-        return context;
-    }
-    return "";
-}
-
 // At less than 1% free space return value of false,
 // means we will try to wrap with overlayfs.
 bool fs_mgr_filesystem_has_space(const char* mount_point) {
@@ -127,138 +132,17 @@
     return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
-bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
     // readonly filesystem, can not be mount -o remount,rw
     // if squashfs or if free space is (near) zero making such a remount
     // virtually useless, or if there are shared blocks that prevent remount,rw
-    return ("squashfs"s == fsrec->fs_type) ||
-           fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
-           !fs_mgr_filesystem_has_space(fsrec->mount_point);
-}
-
-const auto kUpperName = "upper"s;
-const auto kWorkName = "work"s;
-const auto kOverlayTopDir = "/overlay"s;
-
-std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
-    if (!fs_mgr_is_dir(mount_point)) return "";
-    const auto base = android::base::Basename(mount_point) + "/";
-    for (const auto& overlay_mount_point : kOverlayMountPoints) {
-        auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
-        auto upper = dir + kUpperName;
-        if (!fs_mgr_is_dir(upper)) continue;
-        auto work = dir + kWorkName;
-        if (!fs_mgr_is_dir(work)) continue;
-        if (!fs_mgr_dir_is_writable(work)) continue;
-        return dir;
+    if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+        return true;
     }
-    return "";
-}
-
-const auto kLowerdirOption = "lowerdir="s;
-const auto kUpperdirOption = "upperdir="s;
-
-// default options for mount_point, returns empty string for none available.
-std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
-    auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
-    if (candidate.empty()) return "";
-
-    return "override_creds=off," + kLowerdirOption + mount_point + "," + kUpperdirOption +
-           candidate + kUpperName + ",workdir=" + candidate + kWorkName;
-}
-
-const char* fs_mgr_mount_point(const char* mount_point) {
-    if (!mount_point) return mount_point;
-    if ("/"s != mount_point) return mount_point;
-    return "/system";
-}
-
-bool fs_mgr_access(const std::string& path) {
-    auto save_errno = errno;
-    auto ret = access(path.c_str(), F_OK) == 0;
-    errno = save_errno;
-    return ret;
-}
-
-bool fs_mgr_rw_access(const std::string& path) {
-    if (path.empty()) return false;
-    auto save_errno = errno;
-    auto ret = access(path.c_str(), R_OK | W_OK) == 0;
-    errno = save_errno;
-    return ret;
-}
-
-// return true if system supports overlayfs
-bool fs_mgr_wants_overlayfs() {
-    // Properties will return empty on init first_stage_mount, so speculative
-    // determination, empty (unset) _or_ "1" is true which differs from the
-    // official ro.debuggable policy.  ALLOW_ADBD_DISABLE_VERITY == 0 should
-    // protect us from false in any case, so this is insurance.
-    auto debuggable = android::base::GetProperty("ro.debuggable", "1");
-    if (debuggable != "1") return false;
-
-    // Overlayfs available in the kernel, and patched for override_creds?
-    return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
-}
-
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) return false;
-    const auto lowerdir = kLowerdirOption + mount_point;
-    for (auto i = 0; i < fstab->num_entries; ++i) {
-        const auto fsrec = &fstab->recs[i];
-        const auto fs_type = fsrec->fs_type;
-        if (!fs_type) continue;
-        if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
-        auto fsrec_mount_point = fsrec->mount_point;
-        if (!fsrec_mount_point) continue;
-        if (mount_point != fsrec_mount_point) continue;
-        if (!overlay_only) return true;
-        const auto fs_options = fsrec->fs_options;
-        if (!fs_options) continue;
-        const auto options = android::base::Split(fs_options, ",");
-        for (const auto& opt : options) {
-            if (opt == lowerdir) {
-                return true;
-            }
-        }
+    if (fs_mgr_is_logical(fsrec)) {
+        fs_mgr_update_logical_partition(fsrec);
     }
-    return false;
-}
-
-std::vector<std::string> fs_mgr_overlayfs_verity_enabled_list() {
-    std::vector<std::string> ret;
-    fs_mgr_update_verity_state([&ret](fstab_rec*, const char* mount_point, int, int) {
-        ret.emplace_back(mount_point);
-    });
-    return ret;
-}
-
-bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
-    if (!fsrec) return false;
-
-    auto fsrec_mount_point = fsrec->mount_point;
-    if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
-    if (!fsrec->blk_device) return false;
-
-    if (!fsrec->fs_type) return false;
-
-    // Don't check entries that are managed by vold.
-    if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
-
-    // Only concerned with readonly partitions.
-    if (!(fsrec->flags & MS_RDONLY)) return false;
-
-    // If unbindable, do not allow overlayfs as this could expose us to
-    // security issues.  On Android, this could also be used to turn off
-    // the ability to overlay an otherwise acceptable filesystem since
-    // /system and /vendor are never bound(sic) to.
-    if (fsrec->flags & MS_UNBINDABLE) return false;
-
-    if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
-
-    return true;
+    return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
 }
 
 bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
@@ -307,6 +191,110 @@
     return ret;
 }
 
+const auto kUpperName = "upper"s;
+const auto kWorkName = "work"s;
+const auto kOverlayTopDir = "/overlay"s;
+
+std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
+    if (!fs_mgr_is_dir(mount_point)) return "";
+    const auto base = android::base::Basename(mount_point) + "/";
+    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+        auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
+        auto upper = dir + kUpperName;
+        if (!fs_mgr_is_dir(upper)) continue;
+        auto work = dir + kWorkName;
+        if (!fs_mgr_is_dir(work)) continue;
+        if (!fs_mgr_dir_is_writable(work)) continue;
+        return dir;
+    }
+    return "";
+}
+
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
+
+// default options for mount_point, returns empty string for none available.
+std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
+    auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
+    if (candidate.empty()) return "";
+
+    return "override_creds=off," + kLowerdirOption + mount_point + "," + kUpperdirOption +
+           candidate + kUpperName + ",workdir=" + candidate + kWorkName;
+}
+
+const char* fs_mgr_mount_point(const char* mount_point) {
+    if (!mount_point) return mount_point;
+    if ("/"s != mount_point) return mount_point;
+    return "/system";
+}
+
+bool fs_mgr_rw_access(const std::string& path) {
+    if (path.empty()) return false;
+    auto save_errno = errno;
+    auto ret = access(path.c_str(), R_OK | W_OK) == 0;
+    errno = save_errno;
+    return ret;
+}
+
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
+                                                               fs_mgr_free_fstab);
+    if (!fstab) return false;
+    const auto lowerdir = kLowerdirOption + mount_point;
+    for (auto i = 0; i < fstab->num_entries; ++i) {
+        const auto fsrec = &fstab->recs[i];
+        const auto fs_type = fsrec->fs_type;
+        if (!fs_type) continue;
+        if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+        auto fsrec_mount_point = fsrec->mount_point;
+        if (!fsrec_mount_point) continue;
+        if (mount_point != fsrec_mount_point) continue;
+        if (!overlay_only) return true;
+        const auto fs_options = fsrec->fs_options;
+        if (!fs_options) continue;
+        const auto options = android::base::Split(fs_options, ",");
+        for (const auto& opt : options) {
+            if (opt == lowerdir) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_verity_enabled_list() {
+    std::vector<std::string> ret;
+    fs_mgr_update_verity_state([&ret](fstab_rec*, const char* mount_point, int, int) {
+        ret.emplace_back(mount_point);
+    });
+    return ret;
+}
+
+bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
+    if (!fsrec) return false;
+
+    auto fsrec_mount_point = fsrec->mount_point;
+    if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
+    if (!fsrec->blk_device) return false;
+
+    if (!fsrec->fs_type) return false;
+
+    // Don't check entries that are managed by vold.
+    if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
+
+    // Only concerned with readonly partitions.
+    if (!(fsrec->flags & MS_RDONLY)) return false;
+
+    // If unbindable, do not allow overlayfs as this could expose us to
+    // security issues.  On Android, this could also be used to turn off
+    // the ability to overlay an otherwise acceptable filesystem since
+    // /system and /vendor are never bound(sic) to.
+    if (fsrec->flags & MS_UNBINDABLE) return false;
+
+    if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
+
+    return true;
+}
 constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
 
 bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
@@ -532,8 +520,7 @@
     }
 }
 
-std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
-                                               const char* mount_point = nullptr) {
+std::vector<std::string> fs_mgr_candidate_list(fstab* fstab, const char* mount_point = nullptr) {
     std::vector<std::string> mounts;
     if (!fstab) return mounts;
 
@@ -734,10 +721,10 @@
 
 }  // namespace
 
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
     auto ret = false;
 
-    if (!fs_mgr_wants_overlayfs()) return ret;
+    if (!fs_mgr_overlayfs_supports_override_creds()) return ret;
 
     if (!fstab) return ret;
 
@@ -761,7 +748,14 @@
     return ret;
 }
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fsrecs) {
+    std::vector<fstab_rec> recs;
+    for (const auto& rec : fsrecs) recs.push_back(*rec);
+    fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+    return fs_mgr_overlayfs_mount_all(&fstab);
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
     if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
         return {};
     }
@@ -775,12 +769,20 @@
     return {};
 }
 
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+        const std::vector<const fstab_rec*>& fsrecs) {
+    std::vector<fstab_rec> recs;
+    for (const auto& rec : fsrecs) recs.push_back(*rec);
+    fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+    return fs_mgr_overlayfs_required_devices(&fstab);
+}
+
 // Returns false if setup not permitted, errno set to last error.
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
     if (change) *change = false;
     auto ret = false;
-    if (!fs_mgr_wants_overlayfs()) return ret;
+    if (!fs_mgr_overlayfs_supports_override_creds()) return ret;
     if (!fs_mgr_boot_completed()) {
         errno = EBUSY;
         PERROR << "setup";
@@ -843,7 +845,7 @@
     for (const auto& overlay_mount_point : kOverlayMountPoints) {
         ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
     }
-    if (!fs_mgr_wants_overlayfs()) {
+    if (!fs_mgr_overlayfs_supports_override_creds()) {
         // After obligatory teardown to make sure everything is clean, but if
         // we didn't want overlayfs in the the first place, we do not want to
         // waste time on a reboot (or reboot request message).
@@ -882,3 +884,19 @@
 
     return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
 }
+
+std::string fs_mgr_get_context(const std::string& mount_point) {
+    char* ctx = nullptr;
+    if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+        return "";
+    }
+
+    std::string context(ctx);
+    free(ctx);
+    return context;
+}
+
+bool fs_mgr_overlayfs_supports_override_creds() {
+    // Overlayfs available in the kernel, and patched for override_creds?
+    return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
+}
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
new file mode 100644
index 0000000..360a117
--- /dev/null
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <selinux/selinux.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_vendor_overlay.h>
+#include <fstab/fstab.h>
+
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+
+namespace {
+
+const auto kVendorOverlaySourceDir = "/system/vendor_overlay/"s;
+const auto kVndkVersionPropertyName = "ro.vndk.version"s;
+const auto kVendorTopDir = "/vendor/"s;
+const auto kLowerdirOption = "lowerdir="s;
+
+std::string fs_mgr_get_vendor_overlay_top_dir() {
+    // VNDK version is provided by the /vendor/default.prop
+    // To read the property, it must be called at the second init stage after the default
+    // properties are loaded.
+    std::string vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
+    if (vndk_version.empty()) {
+        return "";
+    }
+    return kVendorOverlaySourceDir + vndk_version;
+}
+
+std::vector<std::string> fs_mgr_get_vendor_overlay_dirs(const std::string& overlay_top) {
+    std::vector<std::string> vendor_overlay_dirs;
+    std::unique_ptr<DIR, decltype(&closedir)> vendor_overlay_top(opendir(overlay_top.c_str()),
+                                                                 closedir);
+    if (!vendor_overlay_top) return vendor_overlay_dirs;
+
+    // Vendor overlay root for current vendor version found!
+    LINFO << "vendor overlay root: " << overlay_top;
+    struct dirent* dp;
+    while ((dp = readdir(vendor_overlay_top.get())) != nullptr) {
+        if (dp->d_type != DT_DIR || dp->d_name[0] == '.') {
+            continue;
+        }
+        vendor_overlay_dirs.push_back(dp->d_name);
+    }
+
+    return vendor_overlay_dirs;
+}
+
+bool fs_mgr_vendor_overlay_mount(const std::string& overlay_top, const std::string& mount_point) {
+    const auto vendor_mount_point = kVendorTopDir + mount_point;
+    LINFO << "vendor overlay mount on " << vendor_mount_point;
+
+    auto context = fs_mgr_get_context(vendor_mount_point);
+    if (!context.empty()) {
+        context = ",rootcontext="s + context;
+    } else {
+        PERROR << " result: cannot find the mount point";
+        return false;
+    }
+
+    auto options = "override_creds=off,"s + kLowerdirOption + overlay_top + "/" + mount_point +
+                   ":" + vendor_mount_point + context;
+    auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
+                  options + ")=";
+    auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+                     options.c_str());
+    if (ret) {
+        PERROR << report << ret;
+        return false;
+    } else {
+        LINFO << report << ret;
+        return true;
+    }
+}
+
+}  // namespace
+
+// Since the vendor overlay requires to know the version of the vendor partition,
+// it is not possible to mount vendor overlay at the first stage that cannot
+// initialize properties.
+// To read the properties, vendor overlay must be mounted at the second stage, right
+// after "property_load_boot_defaults()" is called.
+bool fs_mgr_vendor_overlay_mount_all() {
+    const auto overlay_top = fs_mgr_get_vendor_overlay_top_dir();
+    if (overlay_top.empty()) {
+        LINFO << "vendor overlay: vndk version not defined";
+        return false;
+    }
+    const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(overlay_top);
+    if (vendor_overlay_dirs.empty()) return true;
+    if (!fs_mgr_overlayfs_supports_override_creds()) {
+        LINFO << "vendor overlay: kernel does not support overlayfs";
+        return false;
+    }
+
+    // Mount each directory in /system/vendor_overlay/<ver> on /vendor
+    auto ret = true;
+    for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {
+        if (!fs_mgr_vendor_overlay_mount(overlay_top, vendor_overlay_dir)) {
+            ret = false;
+        }
+    }
+    return ret;
+}
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 3274e0e..72202ab 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,9 +21,14 @@
 #include <string>
 #include <vector>
 
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+        const std::vector<const fstab_rec*>& fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
 bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
+std::string fs_mgr_get_context(const std::string& mount_point);
+bool fs_mgr_overlayfs_supports_override_creds();
diff --git a/fs_mgr/include/fs_mgr_vendor_overlay.h b/fs_mgr/include/fs_mgr_vendor_overlay.h
new file mode 100644
index 0000000..9771a0c
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_vendor_overlay.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fstab/fstab.h>
+
+bool fs_mgr_vendor_overlay_mount_all();
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 1b8ed57..3cd33b1 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -29,12 +29,19 @@
 namespace android {
 namespace fs_mgr {
 
-void LinearExtent::AddTo(LpMetadata* out) const {
-    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+bool LinearExtent::AddTo(LpMetadata* out) const {
+    if (device_index_ >= out->block_devices.size()) {
+        LERROR << "Extent references unknown block device.";
+        return false;
+    }
+    out->extents.emplace_back(
+            LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
+    return true;
 }
 
-void ZeroExtent::AddTo(LpMetadata* out) const {
-    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+bool ZeroExtent::AddTo(LpMetadata* out) const {
+    out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
+    return true;
 }
 
 Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
@@ -44,15 +51,17 @@
     size_ += extent->num_sectors() * LP_SECTOR_SIZE;
 
     if (LinearExtent* new_extent = extent->AsLinearExtent()) {
-        if (!extents_.empty() && extents_.back()->AsLinearExtent() &&
-            extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) {
-            // If the previous extent can be merged into this new one, do so
-            // to avoid creating unnecessary extents.
+        if (!extents_.empty() && extents_.back()->AsLinearExtent()) {
             LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
-            extent = std::make_unique<LinearExtent>(
-                    prev_extent->num_sectors() + new_extent->num_sectors(),
-                    prev_extent->physical_sector());
-            extents_.pop_back();
+            if (prev_extent->end_sector() == new_extent->physical_sector() &&
+                prev_extent->device_index() == new_extent->device_index()) {
+                // If the previous extent can be merged into this new one, do so
+                // to avoid creating unnecessary extents.
+                extent = std::make_unique<LinearExtent>(
+                        prev_extent->num_sectors() + new_extent->num_sectors(),
+                        prev_extent->device_index(), prev_extent->physical_sector());
+                extents_.pop_back();
+            }
         }
     }
     extents_.push_back(std::move(extent));
@@ -108,9 +117,12 @@
     if (!builder) {
         return nullptr;
     }
-    BlockDeviceInfo device_info;
-    if (opener.GetInfo(super_partition, &device_info)) {
-        builder->UpdateBlockDeviceInfo(device_info);
+    for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+        std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+        BlockDeviceInfo device_info;
+        if (opener.GetInfo(partition_name, &device_info)) {
+            builder->UpdateBlockDeviceInfo(i, device_info);
+        }
     }
     return builder;
 }
@@ -120,11 +132,11 @@
     return New(PartitionOpener(), super_partition, slot_number);
 }
 
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
-                                                      uint32_t metadata_max_size,
-                                                      uint32_t metadata_slot_count) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
+        const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+        uint32_t metadata_max_size, uint32_t metadata_slot_count) {
     std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
-    if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
+    if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {
         return nullptr;
     }
     return builder;
@@ -156,6 +168,7 @@
 
 bool MetadataBuilder::Init(const LpMetadata& metadata) {
     geometry_ = metadata.geometry;
+    block_devices_ = metadata.block_devices;
 
     for (const auto& group : metadata.groups) {
         std::string group_name = GetPartitionGroupName(group);
@@ -164,10 +177,6 @@
         }
     }
 
-    for (const auto& block_device : metadata.block_devices) {
-        block_devices_.push_back(block_device);
-    }
-
     for (const auto& partition : metadata.partitions) {
         std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
         Partition* builder =
@@ -179,7 +188,8 @@
         for (size_t i = 0; i < partition.num_extents; i++) {
             const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
             if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
-                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+                                                           extent.target_data);
                 builder->AddExtent(std::move(copy));
             } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
                 auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
@@ -190,7 +200,37 @@
     return true;
 }
 
-bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
+static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+    if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " logical block size must be a multiple of 512.";
+        return false;
+    }
+    if (device_info.size % device_info.logical_block_size != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " size must be a multiple of its block size.";
+        return false;
+    }
+    if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " alignment offset is not sector-aligned.";
+        return false;
+    }
+    if (device_info.alignment % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " partition alignment is not sector-aligned.";
+        return false;
+    }
+    if (device_info.alignment_offset > device_info.alignment) {
+        LERROR << "Block device " << device_info.partition_name
+               << " partition alignment offset is greater than its alignment.";
+        return false;
+    }
+    return true;
+}
+
+bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
+                           const std::string& super_partition, uint32_t metadata_max_size,
                            uint32_t metadata_slot_count) {
     if (metadata_max_size < sizeof(LpMetadataHeader)) {
         LERROR << "Invalid metadata maximum size.";
@@ -200,70 +240,102 @@
         LERROR << "Invalid metadata slot count.";
         return false;
     }
+    if (block_devices.empty()) {
+        LERROR << "No block devices were specified.";
+        return false;
+    }
 
     // Align the metadata size up to the nearest sector.
     metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
 
-    // Check that device properties are sane.
-    if (device_info.size % LP_SECTOR_SIZE != 0) {
-        LERROR << "Block device size must be a multiple of 512.";
+    // Validate and build the block device list.
+    uint32_t logical_block_size = 0;
+    for (const auto& device_info : block_devices) {
+        if (!VerifyDeviceProperties(device_info)) {
+            return false;
+        }
+
+        if (!logical_block_size) {
+            logical_block_size = device_info.logical_block_size;
+        }
+        if (logical_block_size != device_info.logical_block_size) {
+            LERROR << "All partitions must have the same logical block size.";
+            return false;
+        }
+
+        LpMetadataBlockDevice out = {};
+        out.alignment = device_info.alignment;
+        out.alignment_offset = device_info.alignment_offset;
+        out.size = device_info.size;
+        if (device_info.partition_name.size() >= sizeof(out.partition_name)) {
+            LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
+            return false;
+        }
+        strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));
+
+        // In the case of the super partition, this field will be adjusted
+        // later. For all partitions, the first 512 bytes are considered
+        // untouched to be compatible code that looks for an MBR. Thus we
+        // start counting free sectors at sector 1, not 0.
+        uint64_t free_area_start = LP_SECTOR_SIZE;
+        if (out.alignment || out.alignment_offset) {
+            free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+        } else {
+            free_area_start = AlignTo(free_area_start, logical_block_size);
+        }
+        out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+        // There must be one logical block of space available.
+        uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;
+        if (device_info.size < minimum_size) {
+            LERROR << "Block device " << device_info.partition_name
+                   << " is too small to hold any logical partitions.";
+            return false;
+        }
+
+        // The "root" of the super partition is always listed first.
+        if (device_info.partition_name == super_partition) {
+            block_devices_.emplace(block_devices_.begin(), out);
+        } else {
+            block_devices_.emplace_back(out);
+        }
+    }
+    if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+        LERROR << "No super partition was specified.";
         return false;
     }
-    if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
-        LERROR << "Logical block size must be a multiple of 512.";
-        return false;
-    }
-    if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
-        LERROR << "Alignment offset is not sector-aligned.";
-        return false;
-    }
-    if (device_info.alignment % LP_SECTOR_SIZE != 0) {
-        LERROR << "Partition alignment is not sector-aligned.";
-        return false;
-    }
-    if (device_info.alignment_offset > device_info.alignment) {
-        LERROR << "Partition alignment offset is greater than its alignment.";
-        return false;
-    }
+
+    LpMetadataBlockDevice& super = block_devices_[0];
 
     // We reserve a geometry block (4KB) plus space for each copy of the
     // maximum size of a metadata blob. Then, we double that space since
     // we store a backup copy of everything.
     uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
-    if (device_info.size < total_reserved) {
+    if (super.size < total_reserved) {
         LERROR << "Attempting to create metadata on a block device that is too small.";
         return false;
     }
 
     // Compute the first free sector, factoring in alignment.
     uint64_t free_area_start = total_reserved;
-    if (device_info.alignment || device_info.alignment_offset) {
-        free_area_start =
-                AlignTo(free_area_start, device_info.alignment, device_info.alignment_offset);
+    if (super.alignment || super.alignment_offset) {
+        free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
     } else {
-        free_area_start = AlignTo(free_area_start, device_info.logical_block_size);
+        free_area_start = AlignTo(free_area_start, logical_block_size);
     }
-    uint64_t first_sector = free_area_start / LP_SECTOR_SIZE;
+    super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
 
     // There must be one logical block of free space remaining (enough for one partition).
-    uint64_t minimum_disk_size = (first_sector * LP_SECTOR_SIZE) + device_info.logical_block_size;
-    if (device_info.size < minimum_disk_size) {
+    uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;
+    if (super.size < minimum_disk_size) {
         LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has "
-               << device_info.size;
+               << super.size;
         return false;
     }
 
-    block_devices_.push_back(LpMetadataBlockDevice{
-            first_sector,
-            device_info.alignment,
-            device_info.alignment_offset,
-            device_info.size,
-            "super",
-    });
-
     geometry_.metadata_max_size = metadata_max_size;
     geometry_.metadata_slot_count = metadata_slot_count;
-    geometry_.logical_block_size = device_info.logical_block_size;
+    geometry_.logical_block_size = logical_block_size;
 
     if (!AddGroup("default", 0)) {
         return false;
@@ -347,8 +419,9 @@
     for (size_t i = 1; i < extents.size(); i++) {
         const Interval& previous = extents[i - 1];
         const Interval& current = extents[i];
+        DCHECK(previous.device_index == current.device_index);
 
-        uint64_t aligned = AlignSector(previous.end);
+        uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
         if (aligned >= current.start) {
             // There is no gap between these two extents, try the next one.
             // Note that we check with >= instead of >, since alignment may
@@ -358,37 +431,43 @@
 
         // The new interval represents the free space starting at the end of
         // the previous interval, and ending at the start of the next interval.
-        free_regions->emplace_back(aligned, current.start);
+        free_regions->emplace_back(current.device_index, aligned, current.start);
     }
 }
 
 auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
     std::vector<Interval> free_regions;
 
-    // Collect all extents in the partition table, then sort them by starting
-    // sector.
-    std::vector<Interval> extents;
+    // Collect all extents in the partition table, per-device, then sort them
+    // by starting sector.
+    std::vector<std::vector<Interval>> device_extents(block_devices_.size());
     for (const auto& partition : partitions_) {
         for (const auto& extent : partition->extents()) {
             LinearExtent* linear = extent->AsLinearExtent();
             if (!linear) {
                 continue;
             }
-            extents.emplace_back(linear->physical_sector(),
+            CHECK(linear->device_index() < device_extents.size());
+            auto& extents = device_extents[linear->device_index()];
+            extents.emplace_back(linear->device_index(), linear->physical_sector(),
                                  linear->physical_sector() + extent->num_sectors());
         }
     }
 
     // Add 0-length intervals for the first and last sectors. This will cause
     // ExtentToFreeList() to treat the space in between as available.
-    uint64_t first_sector = super_device().first_logical_sector;
-    uint64_t last_sector = super_device().size / LP_SECTOR_SIZE;
-    extents.emplace_back(first_sector, first_sector);
-    extents.emplace_back(last_sector, last_sector);
+    for (size_t i = 0; i < device_extents.size(); i++) {
+        auto& extents = device_extents[i];
+        const auto& block_device = block_devices_[i];
 
-    std::sort(extents.begin(), extents.end());
+        uint64_t first_sector = block_device.first_logical_sector;
+        uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;
+        extents.emplace_back(i, first_sector, first_sector);
+        extents.emplace_back(i, last_sector, last_sector);
 
-    ExtentsToFreeList(extents, &free_regions);
+        std::sort(extents.begin(), extents.end());
+        ExtentsToFreeList(extents, &free_regions);
+    }
     return free_regions;
 }
 
@@ -443,7 +522,7 @@
         uint64_t sectors = std::min(sectors_needed, region.length());
         CHECK(sectors % sectors_per_block == 0);
 
-        auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+        auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
         new_extents.push_back(std::move(extent));
         sectors_needed -= sectors;
         if (!sectors_needed) {
@@ -471,6 +550,9 @@
     metadata->header = header_;
     metadata->geometry = geometry_;
 
+    // Assign this early so the extent table can read it.
+    metadata->block_devices = block_devices_;
+
     std::map<std::string, size_t> group_indices;
     for (const auto& group : groups_) {
         LpMetadataPartitionGroup out = {};
@@ -515,13 +597,13 @@
         part.group_index = iter->second;
 
         for (const auto& extent : partition->extents()) {
-            extent->AddTo(metadata.get());
+            if (!extent->AddTo(metadata.get())) {
+                return nullptr;
+            }
         }
         metadata->partitions.push_back(part);
     }
 
-    metadata->block_devices = block_devices_;
-
     metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
     metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
     metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
@@ -531,7 +613,11 @@
 }
 
 uint64_t MetadataBuilder::AllocatableSpace() const {
-    return super_device().size - (super_device().first_logical_sector * LP_SECTOR_SIZE);
+    uint64_t total_size = 0;
+    for (const auto& block_device : block_devices_) {
+        total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE);
+    }
+    return total_size;
 }
 
 uint64_t MetadataBuilder::UsedSpace() const {
@@ -542,26 +628,58 @@
     return size;
 }
 
-uint64_t MetadataBuilder::AlignSector(uint64_t sector) const {
+uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
+                                      uint64_t sector) const {
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
     uint64_t lba = sector * LP_SECTOR_SIZE;
-    uint64_t aligned = AlignTo(lba, super_device().alignment, super_device().alignment_offset);
+    uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
     return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
 }
 
-bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const {
-    info->size = super_device().size;
-    info->alignment = super_device().alignment;
-    info->alignment_offset = super_device().alignment_offset;
+bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
+                                            uint32_t* index) const {
+    for (size_t i = 0; i < block_devices_.size(); i++) {
+        if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+            *index = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,
+                                         BlockDeviceInfo* info) const {
+    uint32_t index;
+    if (!FindBlockDeviceByName(partition_name, &index)) {
+        LERROR << "No device named " << partition_name;
+        return false;
+    }
+    info->size = block_devices_[index].size;
+    info->alignment = block_devices_[index].alignment;
+    info->alignment_offset = block_devices_[index].alignment_offset;
     info->logical_block_size = geometry_.logical_block_size;
+    info->partition_name = partition_name;
     return true;
 }
 
-bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) {
-    if (device_info.size != super_device().size) {
+bool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name,
+                                            const BlockDeviceInfo& device_info) {
+    uint32_t index;
+    if (!FindBlockDeviceByName(partition_name, &index)) {
+        LERROR << "No device named " << partition_name;
+        return false;
+    }
+    return UpdateBlockDeviceInfo(index, device_info);
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {
+    CHECK(index < block_devices_.size());
+
+    LpMetadataBlockDevice& block_device = block_devices_[index];
+    if (device_info.size != block_device.size) {
         LERROR << "Device size does not match (got " << device_info.size << ", expected "
-               << super_device().size << ")";
+               << block_device.size << ")";
         return false;
     }
     if (device_info.logical_block_size != geometry_.logical_block_size) {
@@ -573,10 +691,10 @@
     // The kernel does not guarantee these values are present, so we only
     // replace existing values if the new values are non-zero.
     if (device_info.alignment) {
-        super_device().alignment = device_info.alignment;
+        block_device.alignment = device_info.alignment;
     }
     if (device_info.alignment_offset) {
-        super_device().alignment_offset = device_info.alignment_offset;
+        block_device.alignment_offset = device_info.alignment_offset;
     }
     return true;
 }
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index c02242a..c27e300 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -27,6 +27,7 @@
 
 TEST(liblp, BuildBasic) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
 
     Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(partition, nullptr);
@@ -41,6 +42,7 @@
 
 TEST(liblp, ResizePartition) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
 
     Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
@@ -94,6 +96,7 @@
 
 TEST(liblp, PartitionAlignment) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
 
     // Test that we align up to one sector.
     Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -120,6 +123,7 @@
 TEST(liblp, MetadataAlignment) {
     // Make sure metadata sizes get aligned up.
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+    ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
@@ -127,7 +131,7 @@
 
 TEST(liblp, InternalAlignment) {
     // Test the metadata fitting within alignment.
-    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
@@ -174,7 +178,7 @@
 }
 
 TEST(liblp, InternalPartitionAlignment) {
-    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
+    BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
 
     Partition* a = builder->AddPartition("a", 0);
@@ -394,7 +398,7 @@
     static const size_t kMetadataSize = 64 * 1024;
 
     // No space to store metadata + geometry.
-    BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
@@ -441,12 +445,12 @@
 }
 
 TEST(liblp, UpdateBlockDeviceInfo) {
-    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
     BlockDeviceInfo new_info;
-    ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
 
     EXPECT_EQ(new_info.size, device_info.size);
     EXPECT_EQ(new_info.alignment, device_info.alignment);
@@ -455,37 +459,37 @@
 
     device_info.alignment = 0;
     device_info.alignment_offset = 2048;
-    ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
-    ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
     EXPECT_EQ(new_info.alignment, 4096);
     EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
 
     device_info.alignment = 8192;
     device_info.alignment_offset = 0;
-    ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
-    ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
     EXPECT_EQ(new_info.alignment, 8192);
     EXPECT_EQ(new_info.alignment_offset, 2048);
 
     new_info.size += 4096;
-    ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info));
-    ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+    ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
     EXPECT_EQ(new_info.size, 1024 * 1024);
 
     new_info.logical_block_size = 512;
-    ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info));
-    ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+    ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
     EXPECT_EQ(new_info.logical_block_size, 4096);
 }
 
 TEST(liblp, InvalidBlockSize) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     EXPECT_EQ(builder, nullptr);
 }
 
 TEST(liblp, AlignedExtentSize) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
@@ -497,13 +501,13 @@
 
 TEST(liblp, AlignedFreeSpace) {
     // Only one sector free - at least one block is required.
-    BlockDeviceInfo device_info(10240, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
     ASSERT_EQ(builder, nullptr);
 }
 
 TEST(liblp, HasDefaultGroup) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
@@ -511,7 +515,7 @@
 }
 
 TEST(liblp, GroupSizeLimits) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
@@ -530,6 +534,9 @@
 constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
     return x << 30;
 }
+constexpr unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT
+    return x << 20;
+}
 
 TEST(liblp, RemoveAndAddFirstPartition) {
     auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
@@ -555,7 +562,7 @@
 }
 
 TEST(liblp, ListGroups) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(builder->AddGroup("example", 0));
@@ -565,7 +572,7 @@
 }
 
 TEST(liblp, RemoveGroupAndPartitions) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(builder->AddGroup("example", 0));
@@ -580,3 +587,48 @@
     builder->RemoveGroupAndPartitions("default");
     ASSERT_NE(builder->FindPartition("system"), nullptr);
 }
+
+TEST(liblp, MultipleBlockDevices) {
+    std::vector<BlockDeviceInfo> partitions = {
+            BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
+            BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
+            BlockDeviceInfo("product_a", 64_MiB, 786432, 753664, 4096),
+    };
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
+    ASSERT_NE(builder, nullptr);
+    EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+
+    // Create a partition that spans 3 devices.
+    Partition* p = builder->AddPartition("system_a", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(p, 466976768));
+
+    unique_ptr<LpMetadata> metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+    ASSERT_EQ(metadata->block_devices.size(), 3);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "system_a");
+    EXPECT_EQ(metadata->block_devices[0].size, 256_MiB);
+    EXPECT_EQ(metadata->block_devices[0].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), "vendor_a");
+    EXPECT_EQ(metadata->block_devices[1].size, 128_MiB);
+    EXPECT_EQ(metadata->block_devices[1].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), "product_a");
+    EXPECT_EQ(metadata->block_devices[2].size, 64_MiB);
+    EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
+    ASSERT_EQ(metadata->extents.size(), 3);
+    EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+    EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[0].target_data, 1984);
+    EXPECT_EQ(metadata->extents[0].target_source, 0);
+    EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+    EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[1].target_data, 1472);
+    EXPECT_EQ(metadata->extents[1].target_source, 1);
+    EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+    EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[2].target_data, 1472);
+    EXPECT_EQ(metadata->extents[2].target_source, 2);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a090889..f9de106 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -41,7 +41,7 @@
     explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
     virtual ~Extent() {}
 
-    virtual void AddTo(LpMetadata* out) const = 0;
+    virtual bool AddTo(LpMetadata* out) const = 0;
     virtual LinearExtent* AsLinearExtent() { return nullptr; }
 
     uint64_t num_sectors() const { return num_sectors_; }
@@ -54,16 +54,18 @@
 // This corresponds to a dm-linear target.
 class LinearExtent final : public Extent {
   public:
-    LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
-        : Extent(num_sectors), physical_sector_(physical_sector) {}
+    LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector)
+        : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}
 
-    void AddTo(LpMetadata* metadata) const override;
+    bool AddTo(LpMetadata* metadata) const override;
     LinearExtent* AsLinearExtent() override { return this; }
 
     uint64_t physical_sector() const { return physical_sector_; }
     uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
+    uint32_t device_index() const { return device_index_; }
 
   private:
+    uint32_t device_index_;
     uint64_t physical_sector_;
 };
 
@@ -72,7 +74,7 @@
   public:
     explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
 
-    void AddTo(LpMetadata* out) const override;
+    bool AddTo(LpMetadata* out) const override;
 };
 
 class PartitionGroup final {
@@ -122,15 +124,17 @@
 
 class MetadataBuilder {
   public:
-    // Construct an empty logical partition table builder. The block device size
-    // and maximum metadata size must be specified, as this will determine which
-    // areas of the physical partition can be flashed for metadata vs for logical
-    // partitions.
+    // Construct an empty logical partition table builder given the specified
+    // map of partitions that are available for storing logical partitions.
+    //
+    // At least one partition in the list must be the "super" device, where
+    // metadata will be stored.
     //
     // If the parameters would yield invalid metadata, nullptr is returned. This
-    // could happen if the block device size is too small to store the metadata
-    // and backup copies.
-    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+    // could happen if the super device is too small to store all required
+    // metadata.
+    static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,
+                                                const std::string& super_partition,
                                                 uint32_t metadata_max_size,
                                                 uint32_t metadata_slot_count);
 
@@ -150,11 +154,20 @@
     // This method is for testing or changing off-line tables.
     static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
 
+    // Helper function for a single super partition, for tests.
+    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count) {
+        return New({device_info}, device_info.partition_name, metadata_max_size,
+                   metadata_slot_count);
+    }
+
     // Wrapper around New() with a BlockDeviceInfo that only specifies a device
     // size. This is a convenience method for tests.
     static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
                                                 uint32_t metadata_slot_count) {
-        BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
+        BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0,
+                                    kDefaultBlockSize);
         return New(device_info, metadata_max_size, metadata_slot_count);
     }
 
@@ -209,8 +222,8 @@
     // Remove all partitions belonging to a group, then remove the group.
     void RemoveGroupAndPartitions(const std::string& group_name);
 
-    bool GetBlockDeviceInfo(BlockDeviceInfo* info) const;
-    bool UpdateBlockDeviceInfo(const BlockDeviceInfo& info);
+    bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
+    bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
 
   private:
     MetadataBuilder();
@@ -218,19 +231,27 @@
     MetadataBuilder(MetadataBuilder&&) = delete;
     MetadataBuilder& operator=(const MetadataBuilder&) = delete;
     MetadataBuilder& operator=(MetadataBuilder&&) = delete;
-    bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+    bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+              uint32_t metadata_max_size, uint32_t metadata_slot_count);
     bool Init(const LpMetadata& metadata);
     bool GrowPartition(Partition* partition, uint64_t aligned_size);
     void ShrinkPartition(Partition* partition, uint64_t aligned_size);
-    uint64_t AlignSector(uint64_t sector) const;
+    uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
     uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
+    bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
+    bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
 
     struct Interval {
+        uint32_t device_index;
         uint64_t start;
         uint64_t end;
 
-        Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+        Interval(uint32_t device_index, uint64_t start, uint64_t end)
+            : device_index(device_index), start(start), end(end) {}
         uint64_t length() const { return end - start; }
+
+        // Note: the device index is not included in sorting (intervals are
+        // sorted in per-device lists).
         bool operator<(const Interval& other) const {
             return (start == other.start) ? end < other.end : start < other.start;
         }
@@ -239,9 +260,6 @@
     void ExtentsToFreeList(const std::vector<Interval>& extents,
                            std::vector<Interval>* free_regions) const;
 
-    const LpMetadataBlockDevice& super_device() const { return block_devices_[0]; }
-    LpMetadataBlockDevice& super_device() { return block_devices_[0]; }
-
     LpMetadataGeometry geometry_;
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 8a309be..1e40df3 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
 #define LP_METADATA_HEADER_MAGIC 0x414C5030
 
 /* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 7
+#define LP_METADATA_MAJOR_VERSION 8
 #define LP_METADATA_MINOR_VERSION 0
 
 /* Attributes for the LpMetadataPartition::attributes field.
@@ -240,6 +240,13 @@
      * ZERO: This field must be 0.
      */
     uint64_t target_data;
+
+    /* 20: Contents depends on target_type.
+     *
+     * LINEAR: Must be an index into the block devices table.
+     * ZERO: This field must be 0.
+     */
+    uint32_t target_source;
 } __attribute__((packed)) LpMetadataExtent;
 
 /* This struct defines an entry in the groups table. Each group has a maximum
@@ -255,8 +262,9 @@
     uint64_t maximum_size;
 } LpMetadataPartitionGroup;
 
-/* This struct defines an entry in the block_devices table. There must be
- * exactly one device, corresponding to the super partition.
+/* This struct defines an entry in the block_devices table. There must be at
+ * least one device, and the first device must represent the partition holding
+ * the super metadata.
  */
 typedef struct LpMetadataBlockDevice {
     /* 0: First usable sector for allocating logical partitions. this will be
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
index fe61b9c..e506bd5 100644
--- a/fs_mgr/liblp/include/liblp/partition_opener.h
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -27,12 +27,13 @@
 
 struct BlockDeviceInfo {
     BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
-    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
-                    uint32_t logical_block_size)
+    BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,
+                    uint32_t alignment_offset, uint32_t logical_block_size)
         : size(size),
           alignment(alignment),
           alignment_offset(alignment_offset),
-          logical_block_size(logical_block_size) {}
+          logical_block_size(logical_block_size),
+          partition_name(partition_name) {}
     // Size of the block device, in bytes.
     uint64_t size;
     // Optimal target alignment, in bytes. Partition extents will be aligned to
@@ -44,6 +45,9 @@
     uint32_t alignment_offset;
     // Block size, for aligning extent sizes and partition sizes.
     uint32_t logical_block_size;
+    // The physical partition name for this block device, as it would appear in
+    // the GPT or under /dev/block/by-name.
+    std::string partition_name;
 };
 
 // Test-friendly interface for interacting with partitions.
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 9c675fe..603e5c0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -128,7 +128,7 @@
 // Flashing metadata should not work if the metadata was created for a larger
 // disk than the destination disk.
 TEST(liblp, ExportDiskTooSmall) {
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -581,7 +581,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    BlockDeviceInfo device_info(kDiskSize, 0, 0, 512);
+    BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 512);
     unique_ptr<MetadataBuilder> builder =
             MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
     ASSERT_NE(builder, nullptr);
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 7381eed..77b0e62 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -24,6 +24,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+
 #include "utility.h"
 
 namespace android {
@@ -68,6 +70,7 @@
 
     device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
     device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+    device_info->partition_name = android::base::Basename(block_device);
     return true;
 #else
     (void)block_device;
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 070573c..a02e746 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -274,6 +274,12 @@
         memcpy(&extent, cursor, sizeof(extent));
         cursor += header.extents.entry_size;
 
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
+            extent.target_source >= header.block_devices.num_entries) {
+            LERROR << "Logical partition extent has invalid block device.";
+            return nullptr;
+        }
+
         metadata->extents.push_back(extent);
     }
 
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 5ef4794..b6a8eef 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1,14 +1,21 @@
 #! /bin/bash
-#
-# adb remount tests (overlayfs focus)
-#
-# Conditions:
-#  - Must be a userdebug build.
-#  - Must be in adb mode.
-#  - Kernel must have overlayfs enabled and patched to support override_creds.
-#  - Must have either squashfs, ext4-dedupe or right-sized partitions.
-#  - Minimum expectation system and vender are overlayfs covered partitions.
-#
+
+USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+
+adb remount tests (overlayfs focus)
+
+Conditions:
+ - Must be a userdebug build.
+ - Must be in adb mode.
+ - Kernel must have overlayfs enabled and patched to support override_creds.
+ - Must have either squashfs, ext4-dedupe or right-sized partitions.
+ - Minimum expectation system and vender are overlayfs covered partitions.
+"
+
+if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
+  echo "${USAGE}" >&2
+  exit 0
+fi
 
 # Helper Variables
 
@@ -86,11 +93,15 @@
   adb reboot remount-test
 }
 
-[ "USAGE: adb_wait
+[ "USAGE: adb_wait [timeout]
 
-Returns: waits until the device has returned" ]
+Returns: waits until the device has returned or the optional timeout" ]
 adb_wait() {
-  adb wait-for-device
+  if [ -n "${1}" ]; then
+    timeout --preserve-status --signal=KILL ${1} adb wait-for-device
+  else
+    adb wait-for-device
+  fi
 }
 
 [ "USAGE: adb_root
@@ -178,6 +189,10 @@
 fi
 
 inFastboot && die "device in fastboot mode"
+if ! inAdb; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} device not in adb mode ... waiting 2 minutes"
+  adb_wait 2m
+fi
 inAdb || die "device not in adb mode"
 isDebuggable || die "device not a debug build"
 
@@ -205,8 +220,9 @@
 adb_reboot &&
   adb_wait &&
   adb_sh df -k </dev/null | head -1 &&
-  adb_sh df -k </dev/null | grep "^overlay " &&
-  adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  adb_sh df -k </dev/null | grep "^overlay " ||
+  die "overlay takeover failed"
+adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
   echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
 
 adb_root &&
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
new file mode 100644
index 0000000..8e9c7ea
--- /dev/null
+++ b/gatekeeperd/Android.bp
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2015 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.
+//
+
+cc_binary {
+    name: "gatekeeperd",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunused",
+    ],
+    srcs: [
+        "SoftGateKeeperDevice.cpp",
+        "IGateKeeperService.cpp",
+        "gatekeeperd.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libgatekeeper",
+        "liblog",
+        "libhardware",
+        "libbase",
+        "libutils",
+        "libcrypto",
+        "libkeystore_aidl",
+        "libkeystore_binder",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "android.hardware.gatekeeper@1.0",
+    ],
+
+    static_libs: ["libscrypt_static"],
+    include_dirs: ["external/scrypt/lib/crypto"],
+    init_rc: ["gatekeeperd.rc"],
+}
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
deleted file mode 100644
index 6d5d1ea..0000000
--- a/gatekeeperd/Android.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Copyright (C) 2015 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
-LOCAL_SRC_FILES := \
-	SoftGateKeeperDevice.cpp \
-	IGateKeeperService.cpp \
-	gatekeeperd.cpp
-
-LOCAL_MODULE := gatekeeperd
-LOCAL_SHARED_LIBRARIES := \
-	libbinder \
-	libgatekeeper \
-	liblog \
-	libhardware \
-	libbase \
-	libutils \
-	libcrypto \
-	libkeystore_aidl \
-	libkeystore_binder \
-	libhidlbase \
-	libhidltransport \
-	libhwbinder \
-	android.hardware.gatekeeper@1.0 \
-
-LOCAL_STATIC_LIBRARIES := libscrypt_static
-LOCAL_C_INCLUDES := external/scrypt/lib/crypto
-LOCAL_INIT_RC := gatekeeperd.rc
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/gatekeeperd/tests/Android.bp b/gatekeeperd/tests/Android.bp
new file mode 100644
index 0000000..d4cf93b
--- /dev/null
+++ b/gatekeeperd/tests/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2015 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.
+//
+
+cc_test {
+    name: "gatekeeperd-unit-tests",
+
+    cflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+    ],
+    shared_libs: [
+        "libgatekeeper",
+        "libcrypto",
+        "libbase",
+    ],
+    static_libs: ["libscrypt_static"],
+    include_dirs: ["external/scrypt/lib/crypto"],
+    srcs: ["gatekeeper_test.cpp"],
+}
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
deleted file mode 100644
index c38c64b..0000000
--- a/gatekeeperd/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2015 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := gatekeeperd-unit-tests
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
-LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
-LOCAL_STATIC_LIBRARIES := libscrypt_static
-LOCAL_C_INCLUDES := external/scrypt/lib/crypto
-LOCAL_SRC_FILES := \
-	gatekeeper_test.cpp
-include $(BUILD_NATIVE_TEST)
-
diff --git a/init/Android.bp b/init/Android.bp
index c793971..ff3b61f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -74,6 +74,7 @@
         "libdl",
         "libext4_utils",
         "libfs_mgr",
+        "libfscrypt",
         "libhidl-gen-utils",
         "libkeyutils",
         "liblog",
diff --git a/init/Android.mk b/init/Android.mk
index ef08329..dc46d21 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -73,6 +73,7 @@
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
+    libfscrypt \
     libseccomp_policy \
     libcrypto_utils \
     libsparse \
@@ -89,6 +90,8 @@
     libcap \
 
 LOCAL_SANITIZE := signed-integer-overflow
+# First stage init is weird: it may start without stdout/stderr, and no /proc.
+LOCAL_NOSANITIZE := hwaddress
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
diff --git a/init/README.md b/init/README.md
index 6c51b37..2c531df 100644
--- a/init/README.md
+++ b/init/README.md
@@ -235,9 +235,16 @@
   to "123,124,125". Since keycodes are handled very early in init,
   only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
 
-`memcg.limit_in_bytes <value>`
-> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
+`memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>`
+> Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes`
+  bytes and `limit_percent` which is interpreted as a percentage of the size
+  of the device's physical memory (only if memcg is mounted).
+  Values must be equal or greater than 0.
+
+`memcg.limit_property <value>`
+> Sets the child's memory.limit_in_bytes to the value of the specified property
+  (only if memcg is mounted). This property will override the values specified
+  via `memcg.limit_in_bytes` and `memcg.limit_percent`.
 
 `memcg.soft_limit_in_bytes <value>`
 > Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7da2526..5d62c0b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -50,9 +50,9 @@
 #include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_crypt.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <fs_mgr.h>
+#include <fscrypt/fscrypt.h>
+#include <fscrypt/fscrypt_init_extensions.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -307,8 +307,8 @@
         }
     }
 
-    if (e4crypt_is_native()) {
-        if (e4crypt_set_directory_policy(args[1].c_str())) {
+    if (fscrypt_is_native()) {
+        if (fscrypt_set_directory_policy(args[1].c_str())) {
             return reboot_into_recovery(
                 {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
         }
@@ -517,8 +517,8 @@
         return reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -528,8 +528,8 @@
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -539,8 +539,8 @@
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -1016,7 +1016,7 @@
     }
     service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            if (e4crypt_is_native()) {
+            if (fscrypt_is_native()) {
                 LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
                 if (auto result = reboot_into_recovery(
                             {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
@@ -1038,7 +1038,7 @@
 static Result<Success> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return Success();
 
-    auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+    auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
     if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
         return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
diff --git a/init/devices.cpp b/init/devices.cpp
index 58c8b2e..45b17a2 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -419,7 +419,7 @@
 }
 
 void DeviceHandler::ColdbootDone() {
-    skip_restorecon_ = true;
+    skip_restorecon_ = false;
 }
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
diff --git a/init/init.cpp b/init/init.cpp
index b12ba8c..90803f7 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -39,6 +39,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
+#include <fs_mgr_vendor_overlay.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
 #include <selinux/android.h>
@@ -717,6 +718,7 @@
     InstallSignalFdHandler(&epoll);
 
     property_load_boot_defaults();
+    fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
     StartPropertyService(&epoll);
     set_usb_controller();
diff --git a/init/service.cpp b/init/service.cpp
index 7f49423..1bda7ec 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -235,9 +235,6 @@
       ioprio_pri_(0),
       priority_(0),
       oom_score_adjust_(-1000),
-      swappiness_(-1),
-      soft_limit_in_bytes_(-1),
-      limit_in_bytes_(-1),
       start_order_(0),
       args_(args) {}
 
@@ -630,6 +627,18 @@
     return Success();
 }
 
+Result<Success> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &limit_percent_, 0)) {
+        return Error() << "limit_percent value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+    limit_property_ = std::move(args[1]);
+    return Success();
+}
+
 Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
         return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
@@ -783,6 +792,10 @@
         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
         {"memcg.limit_in_bytes",
                         {1,     1,    &Service::ParseMemcgLimitInBytes}},
+        {"memcg.limit_percent",
+                        {1,     1,    &Service::ParseMemcgLimitPercent}},
+        {"memcg.limit_property",
+                        {1,     1,    &Service::ParseMemcgLimitProperty}},
         {"memcg.soft_limit_in_bytes",
                         {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
         {"memcg.swappiness",
@@ -1001,11 +1014,13 @@
     start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
-    errno = -createProcessGroup(uid_, pid_);
+    bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+                      limit_percent_ != -1 || !limit_property_.empty();
+    errno = -createProcessGroup(uid_, pid_, use_memcg);
     if (errno != 0) {
         PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
                     << name_ << "'";
-    } else {
+    } else if (use_memcg) {
         if (swappiness_ != -1) {
             if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
                 PLOG(ERROR) << "setProcessGroupSwappiness failed";
@@ -1018,8 +1033,29 @@
             }
         }
 
-        if (limit_in_bytes_ != -1) {
-            if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+        size_t computed_limit_in_bytes = limit_in_bytes_;
+        if (limit_percent_ != -1) {
+            long page_size = sysconf(_SC_PAGESIZE);
+            long num_pages = sysconf(_SC_PHYS_PAGES);
+            if (page_size > 0 && num_pages > 0) {
+                size_t max_mem = SIZE_MAX;
+                if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+                    max_mem = size_t(num_pages) * size_t(page_size);
+                }
+                computed_limit_in_bytes =
+                        std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+            }
+        }
+
+        if (!limit_property_.empty()) {
+            // This ends up overwriting computed_limit_in_bytes but only if the
+            // property is defined.
+            computed_limit_in_bytes = android::base::GetUintProperty(
+                    limit_property_, computed_limit_in_bytes, SIZE_MAX);
+        }
+
+        if (computed_limit_in_bytes != size_t(-1)) {
+            if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) {
                 PLOG(ERROR) << "setProcessGroupLimit failed";
             }
         }
diff --git a/init/service.h b/init/service.h
index c7beee9..49b09ce 100644
--- a/init/service.h
+++ b/init/service.h
@@ -154,6 +154,8 @@
     Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
     Result<Success> ParseOverride(std::vector<std::string>&& args);
     Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitPercent(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitProperty(std::vector<std::string>&& args);
     Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
     Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
     Result<Success> ParseNamespace(std::vector<std::string>&& args);
@@ -213,9 +215,12 @@
 
     int oom_score_adjust_;
 
-    int swappiness_;
-    int soft_limit_in_bytes_;
-    int limit_in_bytes_;
+    int swappiness_ = -1;
+    int soft_limit_in_bytes_ = -1;
+
+    int limit_in_bytes_ = -1;
+    int limit_percent_ = -1;
+    std::string limit_property_;
 
     bool process_cgroup_empty_ = false;
 
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 4e7f761..fe28eba 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -49,6 +49,7 @@
   unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
                                  regs, stack_map->process_memory());
   unwinder.SetResolveNames(stack_map->ResolveNames());
+  stack_map->SetArch(regs->Arch());
   if (stack_map->GetJitDebug() != nullptr) {
     unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
   }
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 52dd441..9d15af2 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -25,6 +25,7 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
 
 #include "UnwindStackMap.h"
 
@@ -106,7 +107,17 @@
     return "";
   }
 
-  unwindstack::Elf* elf = map_info->GetElf(process_memory());
+  if (arch_ == unwindstack::ARCH_UNKNOWN) {
+    if (pid_ == getpid()) {
+      arch_ = unwindstack::Regs::CurrentArch();
+    } else {
+      // Create a remote regs, to figure out the architecture.
+      std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid_));
+      arch_ = regs->Arch();
+    }
+  }
+
+  unwindstack::Elf* elf = map_info->GetElf(process_memory(), arch_);
 
   std::string name;
   uint64_t func_offset;
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 039f4a2..e19b605 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -30,6 +30,7 @@
 #if !defined(NO_LIBDEXFILE_SUPPORT)
 #include <unwindstack/DexFiles.h>
 #endif
+#include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 
@@ -58,6 +59,8 @@
   unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
 #endif
 
+  void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
+
  protected:
   uint64_t GetLoadBias(size_t index) override;
 
@@ -67,6 +70,8 @@
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   std::unique_ptr<unwindstack::DexFiles> dex_files_;
 #endif
+
+  unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
 };
 
 class UnwindStackOfflineMap : public UnwindStackMap {
diff --git a/libcrypto_utils/tests/Android.bp b/libcrypto_utils/tests/Android.bp
new file mode 100644
index 0000000..5aadfe2
--- /dev/null
+++ b/libcrypto_utils/tests/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2016 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.
+//
+
+cc_test_host {
+    name: "libcrypto_utils_test",
+    srcs: ["android_pubkey_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libcrypto_utils",
+        "libcrypto",
+    ],
+}
diff --git a/libcrypto_utils/tests/Android.mk b/libcrypto_utils/tests/Android.mk
deleted file mode 100644
index ef3d0cf..0000000
--- a/libcrypto_utils/tests/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcrypto_utils_test
-LOCAL_SRC_FILES := android_pubkey_test.cpp
-LOCAL_CFLAGS := -Wall -Werror -Wextra
-LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/liblog/Android.mk b/liblog/Android.mk
deleted file mode 100644
index 6c4dff5..0000000
--- a/liblog/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
index c3ed8a2..bf0e4fe 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.c
@@ -269,6 +269,14 @@
   }
 }
 
+static void* realloc_or_free(void* ptr, size_t new_size) {
+  void* result = realloc(ptr, new_size);
+  if (!result) {
+    free(ptr);
+  }
+  return result;
+}
+
 LIBLOG_ABI_PRIVATE ssize_t
 __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
                              __android_log_pmsg_file_read_fn fn, void* arg) {
@@ -541,7 +549,7 @@
       /* Missing sequence numbers */
       while (sequence < content->entry.nsec) {
         /* plus space for enforced nul */
-        buf = realloc(buf, len + sizeof(char) + sizeof(char));
+        buf = realloc_or_free(buf, len + sizeof(char) + sizeof(char));
         if (!buf) {
           break;
         }
@@ -556,7 +564,7 @@
         continue;
       }
       /* plus space for enforced nul */
-      buf = realloc(buf, len + add_len + sizeof(char));
+      buf = realloc_or_free(buf, len + add_len + sizeof(char));
       if (!buf) {
         ret = -ENOMEM;
         list_remove(content_node);
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
new file mode 100644
index 0000000..e6a9c0c
--- /dev/null
+++ b/liblog/tests/Android.bp
@@ -0,0 +1,98 @@
+//
+// Copyright (C) 2013-2014 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.
+//
+
+// -----------------------------------------------------------------------------
+// Benchmarks.
+// -----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+//   adb shell liblog-benchmarks
+cc_benchmark {
+    name: "liblog-benchmarks",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+    ],
+    shared_libs: [
+        "liblog",
+        "libm",
+        "libbase",
+    ],
+    srcs: ["liblog_benchmark.cpp"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+    name: "liblog-tests-defaults",
+
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+    ],
+    srcs: [
+        "libc_test.cpp",
+        "liblog_test_default.cpp",
+        "liblog_test_local.cpp",
+        "liblog_test_stderr.cpp",
+        "liblog_test_stderr_local.cpp",
+        "log_id_test.cpp",
+        "log_radio_test.cpp",
+        "log_read_test.cpp",
+        "log_system_test.cpp",
+        "log_time_test.cpp",
+        "log_wrap_test.cpp",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libbase",
+    ],
+}
+
+// Build tests for the device (with .so). Run with:
+//   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
+cc_test {
+    name: "liblog-unit-tests",
+    defaults: ["liblog-tests-defaults"],
+}
+
+cc_test {
+    name: "CtsLiblogTestCases",
+    defaults: ["liblog-tests-defaults"],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    cflags: ["-DNO_PSTORE"],
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
deleted file mode 100644
index cfa849b..0000000
--- a/liblog/tests/Android.mk
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# Copyright (C) 2013-2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# -----------------------------------------------------------------------------
-# Benchmarks.
-# -----------------------------------------------------------------------------
-
-test_module_prefix := liblog-
-test_tags := tests
-
-benchmark_c_flags := \
-    -Wall \
-    -Wextra \
-    -Werror \
-    -fno-builtin \
-
-benchmark_src_files := \
-    liblog_benchmark.cpp
-
-# Build benchmarks for the device. Run with:
-#   adb shell liblog-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(benchmark_c_flags)
-LOCAL_SHARED_LIBRARIES += liblog libm libbase
-LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-
-cts_src_files := \
-    libc_test.cpp \
-    liblog_test_default.cpp \
-    liblog_test_local.cpp \
-    liblog_test_stderr.cpp \
-    liblog_test_stderr_local.cpp \
-    log_id_test.cpp \
-    log_radio_test.cpp \
-    log_read_test.cpp \
-    log_system_test.cpp \
-    log_time_test.cpp \
-    log_wrap_test.cpp
-
-test_src_files := \
-    $(cts_src_files) \
-
-# Build tests for the device (with .so). Run with:
-#   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLiblogTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags) -DNO_PSTORE
-LOCAL_SRC_FILES := $(cts_src_files)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.core.liblog
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_LDLIBS_linux := -lrt
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif  # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 2d327ee..7d7554b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -34,15 +34,6 @@
     export_shared_lib_headers: ["libstatssocket"],
 }
 
-// static version of libmetricslogger, needed by a few art static binaries
-// TODO(b/117829226): Remove once dependencies are cleaned up.
-cc_library_static {
-    name: "libmetricslogger_static",
-    srcs: metricslogger_lib_src_files,
-    defaults: ["metricslogger_defaults"],
-    export_shared_lib_headers: ["libstatssocket"],
-}
-
 // metricslogger shared library, debug
 // -----------------------------------------------------------------------------
 cc_library_shared {
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
deleted file mode 100644
index 3887b1b..0000000
--- a/libnativebridge/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 9e2e641..222bc4c 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -44,3 +44,45 @@
     srcs: ["DummyNativeBridge3.cpp"],
     defaults: ["libnativebridge-dummy-defaults"],
 }
+
+// Build the unit tests.
+cc_test {
+    name: "libnativebridge-tests",
+    host_supported: true,
+    test_per_src: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: [
+        "CodeCacheCreate_test.cpp",
+        "CodeCacheExists_test.cpp",
+        "CodeCacheStatFail_test.cpp",
+        "CompleteFlow_test.cpp",
+        "InvalidCharsNativeBridge_test.cpp",
+        "NativeBridge2Signal_test.cpp",
+        "NativeBridgeVersion_test.cpp",
+        "NeedsNativeBridge_test.cpp",
+        "PreInitializeNativeBridge_test.cpp",
+        "PreInitializeNativeBridgeFail1_test.cpp",
+        "PreInitializeNativeBridgeFail2_test.cpp",
+        "ReSetupNativeBridge_test.cpp",
+        "UnavailableNativeBridge_test.cpp",
+        "ValidNameNativeBridge_test.cpp",
+        "NativeBridge3UnloadLibrary_test.cpp",
+        "NativeBridge3GetError_test.cpp",
+        "NativeBridge3IsPathSupported_test.cpp",
+        "NativeBridge3InitAnonymousNamespace_test.cpp",
+        "NativeBridge3CreateNamespace_test.cpp",
+        "NativeBridge3LoadLibraryExt_test.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libnativebridge",
+        "libnativebridge-dummy",
+    ],
+    header_libs: ["libbase_headers"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
deleted file mode 100644
index 4ed6e20..0000000
--- a/libnativebridge/tests/Android.mk
+++ /dev/null
@@ -1,60 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the unit tests.
-test_src_files := \
-    CodeCacheCreate_test.cpp \
-    CodeCacheExists_test.cpp \
-    CodeCacheStatFail_test.cpp \
-    CompleteFlow_test.cpp \
-    InvalidCharsNativeBridge_test.cpp \
-    NativeBridge2Signal_test.cpp \
-    NativeBridgeVersion_test.cpp \
-    NeedsNativeBridge_test.cpp \
-    PreInitializeNativeBridge_test.cpp \
-    PreInitializeNativeBridgeFail1_test.cpp \
-    PreInitializeNativeBridgeFail2_test.cpp \
-    ReSetupNativeBridge_test.cpp \
-    UnavailableNativeBridge_test.cpp \
-    ValidNameNativeBridge_test.cpp \
-    NativeBridge3UnloadLibrary_test.cpp \
-    NativeBridge3GetError_test.cpp \
-    NativeBridge3IsPathSupported_test.cpp \
-    NativeBridge3InitAnonymousNamespace_test.cpp \
-    NativeBridge3CreateNamespace_test.cpp \
-    NativeBridge3LoadLibraryExt_test.cpp
-
-
-shared_libraries := \
-    liblog \
-    libnativebridge \
-    libnativebridge-dummy
-
-header_libraries := \
-    libbase_headers
-
-libnativebridge_tests_common_cflags := \
-    -Wall \
-    -Werror \
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval include $(BUILD_HOST_NATIVE_TEST)) \
-)
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 9fa4154..2412f3c 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -31,8 +31,10 @@
 // that it only returns 0 in the case that the cgroup exists and it contains no processes.
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
 
-int createProcessGroup(uid_t uid, int initialPid);
+int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
 
+// Set various properties of a process group. For these functions to work, the process group must
+// have been created by passing memControl=true to createProcessGroup.
 bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
 bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
 bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0a42053..9df8dd9 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -53,49 +53,31 @@
 
 using namespace std::chrono_literals;
 
-#define MEM_CGROUP_PATH "/dev/memcg/apps"
-#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
-#define ACCT_CGROUP_PATH "/acct"
+static const char kCpuacctCgroup[] = "/acct";
+static const char kMemoryCgroup[] = "/dev/memcg/apps";
 
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
 
-std::once_flag init_path_flag;
-
-static const std::string& GetCgroupRootPath() {
-    static std::string cgroup_root_path;
-    std::call_once(init_path_flag, [&]() {
-        // low-ram devices use per-app memcg by default, unlike high-end ones
-        bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
-        bool per_app_memcg =
-            GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
-        if (per_app_memcg) {
-            // Check if mem cgroup is mounted, only then check for
-            // write-access to avoid SELinux denials
-            cgroup_root_path =
-                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
-                ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
-        } else {
-            cgroup_root_path = ACCT_CGROUP_PATH;
-        }
-    });
-    return cgroup_root_path;
+static bool isMemoryCgroupSupported() {
+    static bool memcg_supported = !access("/dev/memcg/memory.limit_in_bytes", F_OK);
+    return memcg_supported;
 }
 
-static std::string ConvertUidToPath(uid_t uid) {
-    return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
+static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
+    return StringPrintf("%s/uid_%d", cgroup, uid);
 }
 
-static std::string ConvertUidPidToPath(uid_t uid, int pid) {
-    return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
+static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
+    return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
 }
 
-static int RemoveProcessGroup(uid_t uid, int pid) {
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
     int ret;
 
-    auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+    auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
     ret = rmdir(uid_pid_path.c_str());
 
-    auto uid_path = ConvertUidToPath(uid);
+    auto uid_path = ConvertUidToPath(cgroup, uid);
     rmdir(uid_path.c_str());
 
     return ret;
@@ -124,25 +106,26 @@
 void removeAllProcessGroups()
 {
     LOG(VERBOSE) << "removeAllProcessGroups()";
-    const auto& cgroup_root_path = GetCgroupRootPath();
-    std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
-    if (root == NULL) {
-        PLOG(ERROR) << "Failed to open " << cgroup_root_path;
-    } else {
-        dirent* dir;
-        while ((dir = readdir(root.get())) != nullptr) {
-            if (dir->d_type != DT_DIR) {
-                continue;
-            }
+    for (const char* cgroup_root_path : {kCpuacctCgroup, kMemoryCgroup}) {
+        std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
+        if (root == NULL) {
+            PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+        } else {
+            dirent* dir;
+            while ((dir = readdir(root.get())) != nullptr) {
+                if (dir->d_type != DT_DIR) {
+                    continue;
+                }
 
-            if (!StartsWith(dir->d_name, "uid_")) {
-                continue;
-            }
+                if (!StartsWith(dir->d_name, "uid_")) {
+                    continue;
+                }
 
-            auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
-            RemoveUidProcessGroups(path);
-            LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+                auto path = StringPrintf("%s/%s", cgroup_root_path, dir->d_name);
+                RemoveUidProcessGroups(path);
+                LOG(VERBOSE) << "Removing " << path;
+                if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            }
         }
     }
 }
@@ -150,8 +133,8 @@
 // Returns number of processes killed on success
 // Returns 0 if there are no processes in the process cgroup left to kill
 // Returns -1 on error
-static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) {
+    auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
     std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
     if (!fd) {
         PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
@@ -217,11 +200,16 @@
 }
 
 static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+    const char* cgroup =
+            (!access(ConvertUidPidToPath(kCpuacctCgroup, uid, initialPid).c_str(), F_OK))
+                    ? kCpuacctCgroup
+                    : kMemoryCgroup;
+
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
     int retry = retries;
     int processes;
-    while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
+    while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
         LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
@@ -251,7 +239,7 @@
             LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
                       << " in " << static_cast<int>(ms) << "ms";
         }
-        return RemoveProcessGroup(uid, initialPid);
+        return RemoveProcessGroup(cgroup, uid, initialPid);
     } else {
         if (retries > 0) {
             LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -285,16 +273,29 @@
     return true;
 }
 
-int createProcessGroup(uid_t uid, int initialPid)
+static bool isPerAppMemcgEnabled() {
+    static bool per_app_memcg =
+            GetBoolProperty("ro.config.per_app_memcg", GetBoolProperty("ro.config.low_ram", false));
+    return per_app_memcg;
+}
+
+int createProcessGroup(uid_t uid, int initialPid, bool memControl)
 {
-    auto uid_path = ConvertUidToPath(uid);
+    const char* cgroup;
+    if (isMemoryCgroupSupported() && (memControl || isPerAppMemcgEnabled())) {
+        cgroup = kMemoryCgroup;
+    } else {
+        cgroup = kCpuacctCgroup;
+    }
+
+    auto uid_path = ConvertUidToPath(cgroup, uid);
 
     if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_path;
         return -errno;
     }
 
-    auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
+    auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, initialPid);
 
     if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
@@ -313,12 +314,12 @@
 }
 
 static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
-    if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+    if (!isMemoryCgroupSupported()) {
         PLOG(ERROR) << "Memcg is not mounted.";
         return false;
     }
 
-    auto path = ConvertUidPidToPath(uid, pid) + file_name;
+    auto path = ConvertUidPidToPath(kMemoryCgroup, uid, pid) + file_name;
 
     if (!WriteStringToFile(std::to_string(value), path)) {
         PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 3771f9f..0fc4201 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -22,6 +22,7 @@
 
 #include <functional>
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
 
@@ -147,5 +148,23 @@
   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
 }
 
+struct MapInfo {
+  uint64_t start;
+  uint64_t end;
+  uint16_t flags;
+  uint64_t pgoff;
+  std::string name;
+
+  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
+};
+
+inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
+  return ReadProcessMaps(
+      pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+        maps->emplace_back(start, end, flags, pgoff, name);
+      });
+}
+
 } /* namespace procinfo */
 } /* namespace android */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
index d9e8a4d..04995d4 100644
--- a/libprocinfo/process_map_benchmark.cpp
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -27,21 +27,10 @@
 
 #include <benchmark/benchmark.h>
 
-struct MapInfo {
-  uint64_t start;
-  uint64_t end;
-  uint16_t flags;
-  uint64_t pgoff;
-  const std::string name;
-
-  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
-      : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
-};
-
 static void BM_ReadMapFile(benchmark::State& state) {
   std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
   for (auto _ : state) {
-    std::vector<MapInfo> maps;
+    std::vector<android::procinfo::MapInfo> maps;
     android::procinfo::ReadMapFile(
         map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
                       const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 4b93c5b..170a806 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -22,20 +22,9 @@
 
 #include <gtest/gtest.h>
 
-struct MapInfo {
-  uint64_t start;
-  uint64_t end;
-  uint16_t flags;
-  uint64_t pgoff;
-  const std::string name;
-
-  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
-      : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
-};
-
-TEST(process_map, smoke) {
+TEST(process_map, ReadMapFile) {
   std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
-  std::vector<MapInfo> maps;
+  std::vector<android::procinfo::MapInfo> maps;
   ASSERT_TRUE(android::procinfo::ReadMapFile(
       map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
                     const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
@@ -58,3 +47,14 @@
             "[anon:dalvik-classes.dex extracted in memory from "
             "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
 }
+
+TEST(process_map, ReadProcessMaps) {
+  std::vector<android::procinfo::MapInfo> maps;
+  ASSERT_TRUE(android::procinfo::ReadProcessMaps(
+      getpid(), [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                    const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+  ASSERT_GT(maps.size(), 0u);
+  maps.clear();
+  ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
+  ASSERT_GT(maps.size(), 0u);
+}
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index ac55fee..451a0b9 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -54,8 +54,8 @@
   }
 }
 
-void DexFiles::SetArch(ArchEnum arch) {
-  switch (arch) {
+void DexFiles::ProcessArch() {
+  switch (arch()) {
     case ARCH_ARM:
     case ARCH_MIPS:
     case ARCH_X86:
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 60ef796..4d72ead 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -88,6 +88,11 @@
   }
 }
 
+void Elf::Invalidate() {
+  interface_.reset(nullptr);
+  valid_ = false;
+}
+
 bool Elf::GetSoname(std::string* name) {
   std::lock_guard<std::mutex> guard(lock_);
   return valid_ && interface_->GetSoname(name);
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index f0ad2b6..7a3de01 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -31,6 +31,13 @@
 Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
     : memory_(memory), search_libs_(search_libs) {}
 
+void Global::SetArch(ArchEnum arch) {
+  if (arch_ == ARCH_UNKNOWN) {
+    arch_ = arch;
+    ProcessArch();
+  }
+}
+
 uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
   if (!search_libs_.empty()) {
     bool found = false;
@@ -46,7 +53,7 @@
     }
   }
 
-  Elf* elf = info->GetElf(memory_);
+  Elf* elf = info->GetElf(memory_, arch());
   uint64_t ptr;
   // Find first non-empty list (libraries might be loaded multiple times).
   if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) {
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index d6af49e..20bc4b9 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -141,8 +141,8 @@
   return code.next;
 }
 
-void JitDebug::SetArch(ArchEnum arch) {
-  switch (arch) {
+void JitDebug::ProcessArch() {
+  switch (arch()) {
     case ARCH_X86:
       read_descriptor_func_ = &JitDebug::ReadDescriptor32;
       read_entry_func_ = &JitDebug::ReadEntry32Pack;
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 31337a9..5b2fadf 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -88,6 +88,7 @@
 bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
   std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
   unwindstack::RegsGetLocal(regs.get());
+  ArchEnum arch = regs->Arch();
 
   size_t num_frames = 0;
   bool adjust_pc = false;
@@ -100,7 +101,7 @@
       break;
     }
 
-    Elf* elf = map_info->GetElf(process_memory_);
+    Elf* elf = map_info->GetElf(process_memory_, arch);
     uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
     uint64_t step_pc = rel_pc;
     uint64_t pc_adjustment;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 8527797..fe32b5e 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -146,7 +146,7 @@
   return nullptr;
 }
 
-Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
   // Make sure no other thread is trying to add the elf to this map.
   std::lock_guard<std::mutex> guard(mutex_);
 
@@ -176,6 +176,10 @@
   // If the init fails, keep the elf around as an invalid object so we
   // don't try to reinit the object.
   elf->Init();
+  if (elf->valid() && expected_arch != elf->arch()) {
+    // Make the elf invalid, mismatch between arch and expected arch.
+    elf->Invalidate();
+  }
 
   if (locked) {
     Elf::CacheAdd(this);
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index b8b0ac6..b3c5549 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -135,6 +135,8 @@
   last_error_.code = ERROR_NONE;
   last_error_.address = 0;
 
+  ArchEnum arch = regs_->Arch();
+
   bool return_address_attempt = false;
   bool adjust_pc = false;
   std::unique_ptr<JitDebug> jit_debug;
@@ -155,7 +157,7 @@
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
       }
-      elf = map_info->GetElf(process_memory_);
+      elf = map_info->GetElf(process_memory_, arch);
       step_pc = regs_->pc();
       rel_pc = elf->GetRelPc(step_pc, map_info);
       // Everyone except elf data in gdb jit debug maps uses the relative pc.
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index c2fde74..c202a33 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -47,8 +47,6 @@
   void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
                             uint64_t* method_offset);
 
-  void SetArch(ArchEnum arch);
-
  private:
   void Init(Maps* maps);
 
@@ -64,6 +62,8 @@
 
   bool ReadVariableData(uint64_t ptr_offset) override;
 
+  void ProcessArch() override;
+
   std::mutex lock_;
   bool initialized_ = false;
   std::unordered_map<uint64_t, DexFile*> files_;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 24cabf2..e5b0a89 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -57,6 +57,8 @@
 
   void InitGnuDebugdata();
 
+  void Invalidate();
+
   bool GetSoname(std::string* name);
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
index 70e3ddd..a7e6c15 100644
--- a/libunwindstack/include/unwindstack/Global.h
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -25,6 +25,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include <unwindstack/Elf.h>
 #include <unwindstack/Memory.h>
 
 namespace unwindstack {
@@ -39,12 +40,20 @@
   Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
   virtual ~Global() = default;
 
+  void SetArch(ArchEnum arch);
+
+  ArchEnum arch() { return arch_; }
+
  protected:
   uint64_t GetVariableOffset(MapInfo* info, const std::string& variable);
   void FindAndReadVariable(Maps* maps, const char* variable);
 
   virtual bool ReadVariableData(uint64_t offset) = 0;
 
+  virtual void ProcessArch() = 0;
+
+  ArchEnum arch_ = ARCH_UNKNOWN;
+
   std::shared_ptr<Memory> memory_;
   std::vector<std::string> search_libs_;
 };
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index ccb473f..f64b04f 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -42,17 +42,9 @@
 
   Elf* GetElf(Maps* maps, uint64_t pc);
 
-  void SetArch(ArchEnum arch);
-
  private:
   void Init(Maps* maps);
 
-  uint64_t entry_addr_ = 0;
-  bool initialized_ = false;
-  std::vector<Elf*> elf_list_;
-
-  std::mutex lock_;
-
   uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
   uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
 
@@ -64,6 +56,14 @@
   uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
 
   bool ReadVariableData(uint64_t ptr_offset) override;
+
+  void ProcessArch() override;
+
+  uint64_t entry_addr_ = 0;
+  bool initialized_ = false;
+  std::vector<Elf*> elf_list_;
+
+  std::mutex lock_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index ff634f2..9c6b552 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -72,7 +72,7 @@
   std::atomic_uint64_t load_bias;
 
   // This function guarantees it will never return nullptr.
-  Elf* GetElf(const std::shared_ptr<Memory>& process_memory);
+  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
 
   uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
 
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 3ac3ca6..1ea9e5c 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -36,12 +36,20 @@
 
 class DexFilesTest : public ::testing::Test {
  protected:
-  void SetUp() override {
-    memory_ = new MemoryFake;
-    process_memory_.reset(memory_);
+  void CreateFakeElf(MapInfo* map_info) {
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
 
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+  }
+
+  void Init(ArchEnum arch) {
     dex_files_.reset(new DexFiles(process_memory_));
-    dex_files_->SetArch(ARCH_ARM);
+    dex_files_->SetArch(arch);
 
     maps_.reset(
         new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
@@ -58,35 +66,24 @@
     // Global variable in a section that is not readable.
     MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
     ASSERT_TRUE(map_info != nullptr);
-    MemoryFake* memory = new MemoryFake;
-    ElfFake* elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
 
     // Global variable not set by default.
     map_info = maps_->Get(kMapGlobalSetToZero);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
 
     // Global variable set in this map.
     map_info = maps_->Get(kMapGlobal);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
+  }
+
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    Init(ARCH_ARM);
   }
 
   void WriteDescriptor32(uint64_t addr, uint32_t head);
@@ -169,11 +166,12 @@
 }
 
 TEST_F(DexFilesTest, get_method_information_64) {
+  Init(ARCH_ARM64);
+
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  dex_files_->SetArch(ARCH_ARM64);
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x301000);
   WriteDex(0x301000);
@@ -199,11 +197,12 @@
 }
 
 TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+  Init(ARCH_ARM64);
+
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  dex_files_->SetArch(ARCH_ARM64);
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0x200100, 0, 0x100000);
   WriteEntry64(0x200100, 0, 0x200000, 0x300000);
@@ -297,6 +296,8 @@
 }
 
 TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+  Init(ARCH_ARM64);
+
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
@@ -308,7 +309,6 @@
   WriteEntry64(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->SetArch(ARCH_ARM64);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(0U, method_offset);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 8ed697c..d9acdec 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -82,9 +82,9 @@
   MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
   MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
 
-  Elf* elf1 = info1.GetElf(memory_);
+  Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf1->valid());
-  Elf* elf2 = info2.GetElf(memory_);
+  Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf2->valid());
 
   if (cache_enabled) {
@@ -132,10 +132,10 @@
   MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
   MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
 
-  Elf* elf0_1 = info0_1.GetElf(memory_);
+  Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_1->valid());
   EXPECT_EQ(ARCH_ARM, elf0_1->arch());
-  Elf* elf0_2 = info0_2.GetElf(memory_);
+  Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_2->valid());
   EXPECT_EQ(ARCH_ARM, elf0_2->arch());
   EXPECT_EQ(0U, info0_1.elf_offset);
@@ -146,10 +146,10 @@
     EXPECT_NE(elf0_1, elf0_2);
   }
 
-  Elf* elf100_1 = info100_1.GetElf(memory_);
+  Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
   ASSERT_TRUE(elf100_1->valid());
   EXPECT_EQ(ARCH_X86, elf100_1->arch());
-  Elf* elf100_2 = info100_2.GetElf(memory_);
+  Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
   ASSERT_TRUE(elf100_2->valid());
   EXPECT_EQ(ARCH_X86, elf100_2->arch());
   EXPECT_EQ(0U, info100_1.elf_offset);
@@ -160,10 +160,10 @@
     EXPECT_NE(elf100_1, elf100_2);
   }
 
-  Elf* elf200_1 = info200_1.GetElf(memory_);
+  Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
   ASSERT_TRUE(elf200_1->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
-  Elf* elf200_2 = info200_2.GetElf(memory_);
+  Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
   ASSERT_TRUE(elf200_2->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
   EXPECT_EQ(0U, info200_1.elf_offset);
@@ -174,10 +174,10 @@
     EXPECT_NE(elf200_1, elf200_2);
   }
 
-  Elf* elf300_1 = info300_1.GetElf(memory_);
+  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_);
+  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -222,10 +222,10 @@
   MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
   MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
 
-  Elf* elf300_1 = info300_1.GetElf(memory_);
+  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_);
+  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -236,10 +236,10 @@
     EXPECT_NE(elf300_1, elf300_2);
   }
 
-  Elf* elf400_1 = info400_1.GetElf(memory_);
+  Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf400_1->valid());
   EXPECT_EQ(ARCH_ARM, elf400_1->arch());
-  Elf* elf400_2 = info400_2.GetElf(memory_);
+  Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf400_2->valid());
   EXPECT_EQ(ARCH_ARM, elf400_2->arch());
   EXPECT_EQ(0x400U, info400_1.elf_offset);
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index 4598526..b1ca111 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -35,12 +35,19 @@
 
 class JitDebugTest : public ::testing::Test {
  protected:
-  void SetUp() override {
-    memory_ = new MemoryFake;
-    process_memory_.reset(memory_);
+  void CreateFakeElf(MapInfo* map_info) {
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+  }
 
+  void Init(ArchEnum arch) {
     jit_debug_.reset(new JitDebug(process_memory_));
-    jit_debug_->SetArch(ARCH_ARM);
+    jit_debug_->SetArch(arch);
 
     maps_.reset(
         new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
@@ -57,33 +64,22 @@
 
     MapInfo* map_info = maps_->Get(3);
     ASSERT_TRUE(map_info != nullptr);
-    MemoryFake* memory = new MemoryFake;
-    ElfFake* elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
 
     map_info = maps_->Get(5);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
 
     map_info = maps_->Get(7);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
+  }
+
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    Init(ARCH_ARM);
   }
 
   template <typename EhdrType, typename ShdrType>
@@ -326,6 +322,8 @@
 }
 
 TEST_F(JitDebugTest, get_elf_x86) {
+  Init(ARCH_X86);
+
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
   WriteDescriptor32(0xf800, 0x200000);
@@ -343,12 +341,13 @@
 }
 
 TEST_F(JitDebugTest, get_elf_64) {
+  Init(ARCH_ARM64);
+
   CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
 
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
 
-  jit_debug_->SetArch(ARCH_ARM64);
   Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf != nullptr);
 
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index c6c1c34..4d74696 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -72,7 +72,7 @@
   MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
@@ -84,7 +84,7 @@
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -98,13 +98,25 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
 }
 
+TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
+  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+
+  Elf* elf = info.GetElf(process_memory_, ARCH_X86);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
+}
+
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
   MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
@@ -113,7 +125,7 @@
                                                  memory_->SetMemory(0x2000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -129,7 +141,7 @@
                                                  memory_->SetMemory(0x5000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -144,20 +156,20 @@
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   info.elf.reset();
   info.end = 0xfff;
-  elf = info.GetElf(process_memory_);
+  elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Make sure this test is valid.
   info.elf.reset();
   info.end = 0x2000;
-  elf = info.GetElf(process_memory_);
+  elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 }
@@ -174,7 +186,7 @@
   memcpy(buffer.data(), &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -203,7 +215,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -236,7 +248,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -264,7 +276,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -290,13 +302,13 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   info.elf.reset();
   info.flags = PROT_READ;
-  elf = info.GetElf(process_memory_);
+  elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -313,20 +325,20 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_);
+  Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Set the name to nothing to verify that it still fails.
   info.elf.reset();
   info.name = "";
-  elf = info.GetElf(process_memory_);
+  elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_FALSE(elf->valid());
 
   // Change the flags and verify the elf is valid now.
   info.elf.reset();
   info.flags = PROT_READ;
-  elf = info.GetElf(process_memory_);
+  elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -352,7 +364,7 @@
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
         ;
-      Elf* elf = info.GetElf(process_memory_);
+      Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
       elf_in_threads[i] = elf;
     });
     threads.push_back(thread);
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index af9f4c8..0ea7d5d 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -348,7 +348,7 @@
   // Read the file back to a buffer and make sure the contents are
   // the same as the memory buffer we extracted directly to.
   std::vector<uint8_t> file_contents(kAbUncompressedSize);
-  ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
   ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
   ASSERT_EQ(file_contents, buffer);
 
@@ -392,7 +392,7 @@
 
   // Assert that the first 8 bytes of the file haven't been clobbered.
   uint8_t read_buffer[data_size];
-  ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
 
@@ -404,7 +404,7 @@
 
   // Assert that the total length of the file is sane
   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
-            lseek64(tmp_file.fd, 0, SEEK_END));
+            lseek(tmp_file.fd, 0, SEEK_END));
 }
 
 #if !defined(_WIN32)
diff --git a/llkd/README.md b/llkd/README.md
index e5be850..3da7a2f 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -44,7 +44,8 @@
 ABA detection since forward scheduling progress is allowed, thus the condition
 for the symbols are:
 
-- Check is looking for " " + __symbol__+ "0x" in /proc/<pid>/stack.
+- Check is looking for " __symbol__+0x" or " __symbol__.cfi+0x" in
+  /proc/__pid__/stack.
 - The __symbol__ should be rare and short lived enough that on a typical
   system the function is seen at most only once in a sample over the timeout
   period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
@@ -88,7 +89,14 @@
 Android Properties llkd respond to (*prop*_ms parms are in milliseconds):
 
 #### ro.config.low_ram
-default false, if true do not sysrq t (dump all threads).
+device is configured with limited memory.
+
+#### ro.debuggable
+device is configured for userdebug or eng build.
+
+#### ro.llk.sysrq_t
+default not ro.config.low_ram, or ro.debuggable if property is "eng".
+if true do sysrq t (dump all threads).
 
 #### ro.llk.enable
 default false, allow live-lock daemon to be enabled.
@@ -121,14 +129,14 @@
 #### ro.llk.stack.timeout_ms
 default ro.llk.timeout_ms,
 checking for persistent stack symbols maximum timelimit.
-Only active on userdebug and eng builds.
+Only active on userdebug or eng builds.
 
 #### ro.llk.check_ms
 default 2 minutes samples of threads for D or Z.
 
 #### ro.llk.stack
-default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
-The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io comma separated list of kernel
+symbols.  The string "*false*" is the equivalent to an *empty* list.
 Look for kernel stack symbols that if ever persistently present can
 indicate a subsystem is locked up.
 Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -136,11 +144,14 @@
 should be exceptionally rare and fleeting.
 One must be convinced that it is virtually *impossible* for symbol to show up
 persistently in all samples of the stack.
-Only active on userdebug and eng builds.
+Again, looks for a match for either " **symbol**+0x" or " **symbol**.cfi+0x"
+in stack expansion.
+Only available on userdebug or eng builds, limited privileges due to security
+concerns on user builds prevents this checking.
 
 #### ro.llk.blacklist.process
 default 0,1,2 (kernel, init and [kthreadd]) plus process names
-init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
+init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
 [watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
 The string "*false*" is the equivalent to an *empty* list.
 Do not watch these processes.  A process can be comm, cmdline or pid reference.
@@ -160,7 +171,7 @@
 Do not watch processes that match this uid.
 
 #### ro.llk.blacklist.process.stack
-default process names init,lmkd,lmkd.llkd,llkd,keystore,logd.
+default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
 The string "*false*" is the equivalent to an *empty* list.
 This subset of processes are not monitored for live lock stack signatures.
 Also prevents the sepolicy violation associated with processes that block
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 2c62fca..b16b1d8 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -35,6 +35,8 @@
 #define LLK_ENABLE_DEFAULT             false /* "eng" and userdebug true */
 #define KHT_ENABLE_WRITEABLE_PROPERTY  "khungtask.enable"
 #define KHT_ENABLE_PROPERTY            "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_SYSRQ_T_PROPERTY    "ro.llk.sysrq_t"
+#define LLK_ENABLE_SYSRQ_T_DEFAULT     true
 #define LLK_MLOCKALL_PROPERTY          "ro.llk.mlockall"
 #define LLK_MLOCKALL_DEFAULT           true
 #define LLK_KILLTEST_PROPERTY          "ro.llk.killtest"
@@ -48,7 +50,7 @@
 /* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
 #define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
 #define LLK_CHECK_STACK_PROPERTY       "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages"
+#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages,bit_wait_io"
 #define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
     "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 2727aab..0827470 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -85,6 +85,7 @@
 milliseconds llkCheckMs;                             // checking interval to inspect any
                                                      // persistent live-locked states
 bool llkLowRam;                                      // ro.config.low_ram
+bool llkEnableSysrqT = LLK_ENABLE_SYSRQ_T_DEFAULT;   // sysrq stack trace dump
 bool khtEnable = LLK_ENABLE_DEFAULT;                 // [khungtaskd] panic
 // [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.
 // Provides a wide angle of margin b/c khtTimeout is also its granularity.
@@ -509,8 +510,10 @@
     return android::base::Trim(content) == string;
 }
 
-void llkPanicKernel(bool dump, pid_t tid, const char* state) __noreturn;
-void llkPanicKernel(bool dump, pid_t tid, const char* state) {
+void llkPanicKernel(bool dump, pid_t tid, const char* state,
+                    const std::string& message = "") __noreturn;
+void llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message) {
+    if (!message.empty()) LOG(ERROR) << message;
     auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
     if (sysrqTriggerFd < 0) {
         // DYB
@@ -523,14 +526,24 @@
     if (dump) {
         // Show all locks that are held
         android::base::WriteStringToFd("d", sysrqTriggerFd);
+        // Show all waiting tasks
+        android::base::WriteStringToFd("w", sysrqTriggerFd);
         // This can trigger hardware watchdog, that is somewhat _ok_.
         // But useless if pstore configured for <256KB, low ram devices ...
-        if (!llkLowRam) {
+        if (llkEnableSysrqT) {
             android::base::WriteStringToFd("t", sysrqTriggerFd);
+            // Show all locks that are held (in case 't' overflows ramoops)
+            android::base::WriteStringToFd("d", sysrqTriggerFd);
+            // Show all waiting tasks (in case 't' overflows ramoops)
+            android::base::WriteStringToFd("w", sysrqTriggerFd);
         }
         ::usleep(200000);  // let everything settle
     }
-    llkWriteStringToFile("SysRq : Trigger a crash : 'livelock,"s + state + "'\n", "/dev/kmsg");
+    // SysRq message matches kernel format, and propagates through bootstat
+    // ultimately to the boot reason into panic,livelock,<state>.
+    llkWriteStringToFile(message + (message.empty() ? "" : "\n") +
+                                 "SysRq : Trigger a crash : 'livelock,"s + state + "'\n",
+                         "/dev/kmsg");
     android::base::WriteStringToFd("c", sysrqTriggerFd);
     // NOTREACHED
     // DYB
@@ -726,7 +739,8 @@
     char match = -1;
     for (const auto& stack : llkCheckStackSymbols) {
         if (++idx < 0) break;
-        if (kernel_stack.find(" "s + stack + "+0x") != std::string::npos) {
+        if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
+            (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
             match = idx;
             break;
         }
@@ -798,6 +812,7 @@
 
 void llkLogConfig(void) {
     LOG(INFO) << "ro.config.low_ram=" << llkFormat(llkLowRam) << "\n"
+              << LLK_ENABLE_SYSRQ_T_PROPERTY "=" << llkFormat(llkEnableSysrqT) << "\n"
               << LLK_ENABLE_PROPERTY "=" << llkFormat(llkEnable) << "\n"
               << KHT_ENABLE_PROPERTY "=" << llkFormat(khtEnable) << "\n"
               << LLK_MLOCKALL_PROPERTY "=" << llkFormat(llkMlockall) << "\n"
@@ -1089,10 +1104,12 @@
                 }
             }
             // We are here because we have confirmed kernel live-lock
-            LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
-                       << "->" << tid << ' ' << procp->getComm() << " [panic]";
+            const auto message = state + " "s + llkFormat(procp->count) + " " +
+                                 std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
+                                 std::to_string(tid) + " " + procp->getComm() + " [panic]";
             llkPanicKernel(true, tid,
-                           (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping");
+                           (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
+                           message);
         }
         LOG(VERBOSE) << "+closedir()";
     }
@@ -1149,13 +1166,22 @@
     return duration_cast<milliseconds>(llkCheck()).count();
 }
 
+bool llkCheckEng(const std::string& property) {
+    return android::base::GetProperty(property, "eng") == "eng";
+}
+
 bool llkInit(const char* threadname) {
     auto debuggable = android::base::GetBoolProperty("ro.debuggable", false);
     llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
-    if (!LLK_ENABLE_DEFAULT && debuggable) {
-        llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
-        khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
+    llkEnableSysrqT &= !llkLowRam;
+    if (debuggable) {
+        llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);
+        if (!LLK_ENABLE_DEFAULT) {  // NB: default is currently true ...
+            llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY);
+            khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);
+        }
     }
+    llkEnableSysrqT = android::base::GetBoolProperty(LLK_ENABLE_SYSRQ_T_PROPERTY, llkEnableSysrqT);
     llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);
     if (llkEnable && !llkTopDirectory.reset(procdir)) {
         // Most likely reason we could be here is llkd was started
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index e3c4ccc..c9c9e8e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -1260,6 +1260,8 @@
     return maxprocp;
 }
 
+static int last_killed_pid = -1;
+
 /* Kill one process specified by procp.  Returns the size of the process killed */
 static int kill_one_process(struct proc* procp) {
     int pid = procp->pid;
@@ -1304,6 +1306,8 @@
 
     TRACE_KILL_END();
 
+    last_killed_pid = pid;
+
     if (r) {
         ALOGE("kill(%d): errno=%d", pid, errno);
         goto out;
@@ -1331,14 +1335,12 @@
 }
 
 /*
- * Find processes to kill to free required number of pages.
- * If pages_to_free is set to 0 only one process will be killed.
- * Returns the size of the killed processes.
+ * Find one process to kill at or above the given oom_adj level.
+ * Returns size of the killed process.
  */
-static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
+static int find_and_kill_process(int min_score_adj) {
     int i;
-    int killed_size;
-    int pages_freed = 0;
+    int killed_size = 0;
 
 #ifdef LMKD_LOG_STATS
     bool lmk_state_change_start = false;
@@ -1363,20 +1365,12 @@
                                                   LMK_STATE_CHANGE_START);
                 }
 #endif
-
-                pages_freed += killed_size;
-                if (pages_freed >= pages_to_free) {
-
-#ifdef LMKD_LOG_STATS
-                    if (enable_stats_log && lmk_state_change_start) {
-                        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
-                                LMK_STATE_CHANGE_STOP);
-                    }
-#endif
-                    return pages_freed;
-                }
+                break;
             }
         }
+        if (killed_size) {
+            break;
+        }
     }
 
 #ifdef LMKD_LOG_STATS
@@ -1385,7 +1379,7 @@
     }
 #endif
 
-    return pages_freed;
+    return killed_size;
 }
 
 static int64_t get_memory_usage(struct reread_data *file_data) {
@@ -1445,6 +1439,23 @@
         level - 1 : level);
 }
 
+static bool is_kill_pending(void) {
+    char buf[24];
+
+    if (last_killed_pid < 0) {
+        return false;
+    }
+
+    snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
+    if (access(buf, F_OK) == 0) {
+        return true;
+    }
+
+    // reset last killed PID because there's nothing pending
+    last_killed_pid = -1;
+    return false;
+}
+
 static void mp_event_common(int data, uint32_t events __unused) {
     int ret;
     unsigned long long evcount;
@@ -1459,7 +1470,6 @@
     enum vmpressure_level level = (enum vmpressure_level)data;
     long other_free = 0, other_file = 0;
     int min_score_adj;
-    int pages_to_free = 0;
     int minfree = 0;
     static struct reread_data mem_usage_file_data = {
         .filename = MEMCG_MEMORY_USAGE,
@@ -1490,7 +1500,11 @@
     }
 
     if (kill_timeout_ms) {
-        if (get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+        // If we're within the timeout, see if there's pending reclaim work
+        // from the last killed process. If there is (as evidenced by
+        // /proc/<pid> continuing to exist), skip killing for now.
+        if ((get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) &&
+            (low_ram_device || is_kill_pending())) {
             kill_skip_count++;
             return;
         }
@@ -1537,9 +1551,6 @@
             return;
         }
 
-        /* Free up enough pages to push over the highest minfree level */
-        pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
-            ((other_free < other_file) ? other_free : other_file);
         goto do_kill;
     }
 
@@ -1595,7 +1606,7 @@
 do_kill:
     if (low_ram_device) {
         /* For Go devices kill only one task */
-        if (find_and_kill_processes(level_oomadj[level], 0) == 0) {
+        if (find_and_kill_process(level_oomadj[level]) == 0) {
             if (debug_process_killing) {
                 ALOGI("Nothing to kill");
             }
@@ -1619,10 +1630,7 @@
                 return;
             }
             /* Free up enough memory to downgrate the memory pressure to low level */
-            if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) {
-                pages_to_free = low_pressure_mem.max_nr_free_pages -
-                    mi.field.nr_free_pages;
-            } else {
+            if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
                 if (debug_process_killing) {
                     ALOGI("Ignoring pressure since more memory is "
                         "available (%" PRId64 ") than watermark (%" PRId64 ")",
@@ -1633,7 +1641,7 @@
             min_score_adj = level_oomadj[level];
         }
 
-        pages_freed = find_and_kill_processes(min_score_adj, pages_to_free);
+        pages_freed = find_and_kill_process(min_score_adj);
 
         if (pages_freed == 0) {
             /* Rate limit kill reports when nothing was reclaimed */
@@ -1641,25 +1649,24 @@
                 report_skip_count++;
                 return;
             }
+        } else {
+            /* If we killed anything, update the last killed timestamp. */
+            last_kill_tm = curr_tm;
         }
 
         /* Log meminfo whenever we kill or when report rate limit allows */
         meminfo_log(&mi);
-        if (pages_freed >= pages_to_free) {
-            /* Reset kill time only if reclaimed enough memory */
-            last_kill_tm = curr_tm;
-        }
 
         if (use_minfree_levels) {
-            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+            ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
                 "free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
-                pages_to_free * page_k, pages_freed * page_k,
+                pages_freed * page_k,
                 other_file * page_k, mi.field.nr_free_pages * page_k,
                 zi.field.totalreserve_pages * page_k,
                 minfree * page_k, min_score_adj);
         } else {
-            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
-                pages_to_free * page_k, pages_freed * page_k, min_score_adj);
+            ALOGI("Reclaimed %ldkB at oom_adj %d",
+                pages_freed * page_k, min_score_adj);
         }
 
         if (report_skip_count > 0) {
diff --git a/logcat/Android.mk b/logcat/Android.mk
deleted file mode 100644
index a716993..0000000
--- a/logcat/Android.mk
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2006-2014 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/tests/Android.bp b/logcat/tests/Android.bp
new file mode 100644
index 0000000..e1f4d6f
--- /dev/null
+++ b/logcat/tests/Android.bp
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2013-2014 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.
+//
+
+cc_defaults {
+    name: "logcat-tests-defaults",
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+    ],
+}
+
+// -----------------------------------------------------------------------------
+// Benchmarks
+// ----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+//   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
+cc_benchmark {
+    name: "logcat-benchmarks",
+    defaults: ["logcat-tests-defaults"],
+    srcs: ["logcat_benchmark.cpp"],
+    shared_libs: ["libbase"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+// Build tests for the device (with .so). Run with:
+//   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
+cc_test {
+    name: "logcat-unit-tests",
+    defaults: ["logcat-tests-defaults"],
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
+    srcs: [
+        "logcat_test.cpp",
+        "logcatd_test.cpp",
+    ],
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
deleted file mode 100644
index 66f6724..0000000
--- a/logcat/tests/Android.mk
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# Copyright (C) 2013-2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-test_module_prefix := logcat-
-test_tags := tests
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-
-# -----------------------------------------------------------------------------
-# Benchmarks
-# ----------------------------------------------------------------------------
-
-benchmark_src_files := \
-    logcat_benchmark.cpp \
-
-# Build benchmarks for the device. Run with:
-#   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_src_files := \
-    logcat_test.cpp \
-    logcatd_test.cpp \
-
-# Build tests for the device (with .so). Run with:
-#   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
diff --git a/logd/Android.mk b/logd/Android.mk
index 1bca891..b3ce560 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -9,5 +9,3 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
 
 include $(BUILD_PREBUILT)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
new file mode 100644
index 0000000..f15beb2
--- /dev/null
+++ b/logd/tests/Android.bp
@@ -0,0 +1,68 @@
+//
+// Copyright (C) 2014 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.
+//
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+    name: "logd-unit-test-defaults",
+
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+
+        "-DAUDITD_LOG_TAG=1003",
+        "-DCHATTY_LOG_TAG=1004",
+    ],
+
+    srcs: ["logd_test.cpp"],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libselinux",
+    ],
+}
+
+// Build tests for the logger. Run with:
+//   adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+    name: "logd-unit-tests",
+    defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+    name: "CtsLogdTestCases",
+    defaults: ["logd-unit-test-defaults"],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+}
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
deleted file mode 100644
index a0875ea..0000000
--- a/logd/tests/Android.mk
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# -----------------------------------------------------------------------------
-# Benchmarks. (see ../../liblog/tests)
-# -----------------------------------------------------------------------------
-
-test_module_prefix := logd-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-    $(event_flag)
-
-test_src_files := \
-    logd_test.cpp
-
-# Build tests for the logger. Run with:
-#   adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLogdTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.core.logd
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif  # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))