Merge "debuggerd_fallback: fix fd leak."
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..c47230f
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "adbd_test"
+    }
+  ]
+}
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index ad77064..6960345 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -293,6 +293,9 @@
     struct passwd pwent;
     struct passwd* result;
     int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+    if (pwent_max == -1) {
+        pwent_max = 16384;
+    }
     std::vector<char> buf(pwent_max);
     int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
     if (rc == 0 && result) {
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/commandline.cpp b/adb/client/commandline.cpp
index 3124852..b5bed28 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -204,8 +204,8 @@
         " sideload OTAPACKAGE      sideload the given full OTA package\n"
         " root                     restart adbd with root permissions\n"
         " unroot                   restart adbd without root permissions\n"
-        " usb                      restart adb server listening on USB\n"
-        " tcpip PORT               restart adb server listening on TCP on PORT\n"
+        " usb                      restart adbd listening on USB\n"
+        " tcpip PORT               restart adbd listening on TCP on PORT\n"
         "\n"
         "internal debugging:\n"
         " start-server             ensure that there is a server running\n"
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/Android.bp b/base/Android.bp
index daa820a..741664b 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -47,6 +47,7 @@
         "chrono_utils.cpp",
         "file.cpp",
         "logging.cpp",
+        "mapped_file.cpp",
         "parsenetaddress.cpp",
         "properties.cpp",
         "quick_exit.cpp",
@@ -124,6 +125,7 @@
         "file_test.cpp",
         "logging_test.cpp",
         "macros_test.cpp",
+        "mapped_file_test.cpp",
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index d6fe753..3834ed4 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -38,7 +38,6 @@
 #endif
 #if defined(_WIN32)
 #include <windows.h>
-#define O_CLOEXEC O_NOINHERIT
 #define O_NOFOLLOW 0
 #endif
 
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 908690b..86d537d 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -20,13 +20,16 @@
 #include <sys/types.h>
 #include <string>
 
+#include "android-base/off64_t.h"
+
 #if !defined(_WIN32) && !defined(O_BINARY)
+/** Windows needs O_BINARY, but Unix never mangles line endings. */
 #define O_BINARY 0
 #endif
 
-#if defined(__APPLE__)
-/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-typedef off_t off64_t;
+#if defined(_WIN32) && !defined(O_CLOEXEC)
+/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
+#define O_CLOEXEC O_NOINHERIT
 #endif
 
 namespace android {
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 1748665..5abf514 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -75,46 +75,6 @@
 
 #define arraysize(array) (sizeof(ArraySizeHelper(array)))
 
-// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions.  It's less safe than arraysize as it accepts some
-// (although not all) pointers.  Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE_UNSAFE catches a few type errors.  If you see a compiler error
-//
-//   "warning: division by zero in ..."
-//
-// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element).  If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array.  Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size.  Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-#define ARRAYSIZE_UNSAFE(a)     \
-  ((sizeof(a) / sizeof(*(a))) / \
-    static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-
 #define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
 
 // Changing this definition will cause you a lot of pain.  A majority of
@@ -153,23 +113,23 @@
 //    case 42:
 //      ...
 //
-//  As shown in the example above, the FALLTHROUGH_INTENDED macro should be
-//  followed by a semicolon. It is designed to mimic control-flow statements
-//  like 'break;', so it can be placed in most places where 'break;' can, but
-//  only if there are no statements on the execution path between it and the
-//  next switch label.
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
 //
-//  When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
-//  [[clang::fallthrough]] attribute, which is analysed when performing switch
-//  labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
-//  documentation on language extensions for details:
-//  http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+// [[clang::fallthrough]] attribute, which is analysed when performing switch
+// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+// documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
 //
-//  When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
-//  effect on diagnostics.
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
 //
-//  In either case this macro has no effect on runtime behavior and performance
-//  of code.
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
 #ifndef FALLTHROUGH_INTENDED
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
 #endif
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
new file mode 100644
index 0000000..80513b1
--- /dev/null
+++ b/base/include/android-base/mapped_file.h
@@ -0,0 +1,75 @@
+/*
+ * 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 "android-base/macros.h"
+#include "android-base/off64_t.h"
+
+#include <sys/types.h>
+
+#include <memory>
+
+#if defined(_WIN32)
+#include <windows.h>
+#define PROT_READ 1
+#define PROT_WRITE 2
+#else
+#include <sys/mman.h>
+#endif
+
+namespace android {
+namespace base {
+
+/**
+ * A region of a file mapped into memory.
+ */
+class MappedFile {
+ public:
+  /**
+   * Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives,
+   * `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
+   * will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
+   */
+  static std::unique_ptr<MappedFile> FromFd(int fd, off64_t offset, size_t length, int prot);
+
+  /**
+   * Removes the mapping.
+   */
+  ~MappedFile();
+
+  char* data() { return base_ + offset_; }
+  size_t size() { return size_; }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
+
+  char* base_;
+  size_t size_;
+
+  size_t offset_;
+
+#if defined(_WIN32)
+  MappedFile(char* base, size_t size, size_t offset, HANDLE handle)
+      : base_(base), size_(size), offset_(offset), handle_(handle) {}
+  HANDLE handle_;
+#else
+  MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {}
+#endif
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/off64_t.h b/base/include/android-base/off64_t.h
new file mode 100644
index 0000000..e6b71b8
--- /dev/null
+++ b/base/include/android-base/off64_t.h
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+#if defined(__APPLE__)
+/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
new file mode 100644
index 0000000..f7901af
--- /dev/null
+++ b/base/mapped_file.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 "android-base/mapped_file.h"
+
+namespace android {
+namespace base {
+
+static off64_t InitPageSize() {
+#if defined(_WIN32)
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return si.dwAllocationGranularity;
+#else
+  return sysconf(_SC_PAGE_SIZE);
+#endif
+}
+
+std::unique_ptr<MappedFile> MappedFile::FromFd(int fd, off64_t offset, size_t length, int prot) {
+  static off64_t page_size = InitPageSize();
+  size_t slop = offset % page_size;
+  off64_t file_offset = offset - slop;
+  off64_t file_length = length + slop;
+
+#if defined(_WIN32)
+  HANDLE handle =
+      CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
+                        (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+  if (handle == nullptr) return nullptr;
+  void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
+                             file_offset, file_length);
+  if (base == nullptr) {
+    CloseHandle(handle);
+    return nullptr;
+  }
+  return std::unique_ptr<MappedFile>(
+      new MappedFile{static_cast<char*>(base), length, slop, handle});
+#else
+  void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd, file_offset);
+  if (base == MAP_FAILED) return nullptr;
+  return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
+#endif
+}
+
+MappedFile::~MappedFile() {
+#if defined(_WIN32)
+  if (base_ != nullptr) UnmapViewOfFile(base_);
+  if (handle_ != nullptr) CloseHandle(handle_);
+#else
+  if (base_ != nullptr) munmap(base_, size_);
+#endif
+
+  base_ = nullptr;
+  offset_ = size_ = 0;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
new file mode 100644
index 0000000..57fde6f
--- /dev/null
+++ b/base/mapped_file_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/mapped_file.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include "android-base/unique_fd.h"
+
+TEST(mapped_file, smoke) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_TRUE(android::base::WriteStringToFd("hello world", tf.fd));
+
+  auto m = android::base::MappedFile::FromFd(tf.fd, 3, 2, PROT_READ);
+  ASSERT_EQ(2u, m->size());
+  ASSERT_EQ('l', m->data()[0]);
+  ASSERT_EQ('o', m->data()[1]);
+}
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/flashing.cpp b/fastboot/device/flashing.cpp
index 2347496..66b90bf 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -21,10 +21,15 @@
 
 #include <algorithm>
 #include <memory>
+#include <set>
+#include <string>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <sparse/sparse.h>
@@ -32,13 +37,35 @@
 #include "fastboot_device.h"
 #include "utility.h"
 
+using namespace android::fs_mgr;
+using namespace std::literals;
+
 namespace {
 
 constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a;
 
-}  // namespace
+void WipeOverlayfsForPartition(FastbootDevice* device, const std::string& partition_name) {
+    // May be called, in the case of sparse data, multiple times so cache/skip.
+    static std::set<std::string> wiped;
+    if (wiped.find(partition_name) != wiped.end()) return;
+    wiped.insert(partition_name);
+    // Following appears to have a first time 2% impact on flashing speeds.
 
-using namespace android::fs_mgr;
+    // Convert partition_name to a validated mount point and wipe.
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    for (auto i = 0; i < fstab->num_entries; i++) {
+        const auto mount_point = fstab->recs[i].mount_point;
+        if (!mount_point) continue;
+        auto partition = android::base::Basename(mount_point);
+        if ("/"s == mount_point) partition = "system";
+        if ((partition + device->GetCurrentSlot()) == partition_name) {
+            fs_mgr_overlayfs_teardown(mount_point);
+        }
+    }
+}
+
+}  // namespace
 
 int FlashRawDataChunk(int fd, const char* data, size_t len) {
     size_t ret = 0;
@@ -101,15 +128,11 @@
     } else if (data.size() > get_block_device_size(handle.fd())) {
         return -EOVERFLOW;
     }
+    WipeOverlayfsForPartition(device, partition_name);
     return FlashBlockDevice(handle.fd(), data);
 }
 
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe) {
-    std::optional<std::string> super = FindPhysicalPartition(partition_name);
-    if (!super) {
-        return device->WriteFail("Could not find partition: " + partition_name);
-    }
-
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
     std::vector<char> data = std::move(device->download_data());
     if (data.empty()) {
         return device->WriteFail("No data available");
@@ -125,47 +148,17 @@
     // image.
     std::string slot_suffix = device->GetCurrentSlot();
     uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
-    std::unique_ptr<LpMetadata> metadata = ReadMetadata(super->c_str(), slot_number);
-    if (!metadata || wipe) {
-        if (!FlashPartitionTable(super.value(), *new_metadata.get())) {
+    if (wipe || !ReadMetadata(super_name, slot_number)) {
+        if (!FlashPartitionTable(super_name, *new_metadata.get())) {
             return device->WriteFail("Unable to flash new partition table");
         }
         return device->WriteOkay("Successfully flashed partition table");
     }
 
-    // There's a working super partition, and we don't want to wipe it - it may
-    // may contain partitions created for the user. Instead, we create a zero-
-    // sized partition for each entry in the new partition table. It is then
-    // the host's responsibility to size it correctly via resize-logical-partition.
-    std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*metadata.get());
-    if (!builder) {
-        return device->WriteFail("Unable to create a metadata builder");
-    }
-    for (const auto& partition : new_metadata->partitions) {
-        std::string name = GetPartitionName(partition);
-        if (builder->FindPartition(name)) {
-            continue;
-        }
-        if (!builder->AddPartition(name, partition.attributes)) {
-            return device->WriteFail("Unable to add partition: " + name);
-        }
-    }
-
-    // The scratch partition may exist as temporary storage, created for
-    // use by adb remount for overlayfs. If we're performing a flashall
-    // operation then we want to start over with a clean slate, so we
-    // remove the scratch partition until it is requested again.
-    builder->RemovePartition("scratch");
-
-    new_metadata = builder->Export();
-    if (!new_metadata) {
-        return device->WriteFail("Unable to export new partition table");
-    }
-
     // Write the new table to every metadata slot.
     bool ok = true;
     for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
-        ok &= UpdatePartitionTable(super.value(), *new_metadata.get(), i);
+        ok &= UpdatePartitionTable(super_name, *new_metadata.get(), i);
     }
 
     if (!ok) {
diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h
index 89e20fc..b15f28b 100644
--- a/fastboot/device/flashing.h
+++ b/fastboot/device/flashing.h
@@ -22,4 +22,4 @@
 class FastbootDevice;
 
 int Flash(FastbootDevice* device, const std::string& partition_name);
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe);
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6b6e659..3c6b1b7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -79,10 +79,6 @@
 using android::base::Trim;
 using android::base::unique_fd;
 
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
 static const char* serial = nullptr;
 
 static bool g_long_listing = false;
@@ -856,7 +852,7 @@
         buf->image_size = sz;
     }
 
-    lseek64(fd, 0, SEEK_SET);
+    lseek(fd, 0, SEEK_SET);
     int64_t limit = get_sparse_limit(sz);
     if (limit) {
         sparse_file** s = load_sparse_files(fd, limit);
@@ -1096,12 +1092,20 @@
     }
 }
 
+static bool is_logical(const std::string& partition) {
+    std::string value;
+    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+}
+
 static void do_flash(const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
     if (!load_buf(fname, &buf)) {
         die("cannot load '%s': %s", fname, strerror(errno));
     }
+    if (is_logical(pname)) {
+        fb->ResizePartition(pname, std::to_string(buf.image_size));
+    }
     flash_buf(pname, &buf);
 }
 
@@ -1144,11 +1148,6 @@
     return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
 }
 
-static bool is_logical(const std::string& partition) {
-    std::string value;
-    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
-}
-
 static void reboot_to_userspace_fastboot() {
     fb->RebootTo("fastboot");
 
@@ -1316,6 +1315,9 @@
     if (!is_userspace_fastboot()) {
         reboot_to_userspace_fastboot();
     }
+    if (!is_userspace_fastboot()) {
+        die("Failed to boot into userspace; one or more components might be unbootable.");
+    }
     fb->Download("super", fd, get_file_size(fd));
 
     std::string command = "update-super:super";
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index b1f3bc9..65a5247 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -41,10 +41,10 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/mapped_file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <utils/FileMap.h>
 
 #include "constants.h"
 #include "transport.h"
@@ -467,15 +467,14 @@
 
     while (remaining) {
         // Memory map the file
-        android::FileMap filemap;
         size_t len = std::min(remaining, MAX_MAP_SIZE);
-
-        if (!filemap.create(NULL, fd, offset, len, true)) {
+        auto mapping{android::base::MappedFile::FromFd(fd, offset, len, PROT_READ)};
+        if (!mapping) {
             error_ = "Creating filemap failed";
             return IO_ERROR;
         }
 
-        if ((ret = SendBuffer(filemap.getDataPtr(), len))) {
+        if ((ret = SendBuffer(mapping->data(), mapping->size()))) {
             return ret;
         }
 
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 804069a..4dacebf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -33,6 +33,7 @@
 
 #include <sstream>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -50,8 +51,21 @@
 using DmTargetZero = android::dm::DmTargetZero;
 using DmTargetLinear = android::dm::DmTargetLinear;
 
-static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
-                          const LpMetadataPartition& partition, DmTable* table) {
+bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device,
+                                    std::string* result) {
+    // Note: device-mapper will not accept symlinks, so we must use realpath
+    // here.
+    std::string name = GetBlockDevicePartitionName(block_device);
+    std::string path = "/dev/block/by-name/" + name;
+    if (!android::base::Realpath(path, result)) {
+        PERROR << "realpath: " << path;
+        return false;
+    }
+    return true;
+}
+
+static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
+                          DmTable* table) {
     uint64_t sector = 0;
     for (size_t i = 0; i < partition.num_extents; i++) {
         const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -60,10 +74,17 @@
             case LP_TARGET_TYPE_ZERO:
                 target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
                 break;
-            case LP_TARGET_TYPE_LINEAR:
-                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+            case LP_TARGET_TYPE_LINEAR: {
+                const auto& block_device = metadata.block_devices[extent.target_source];
+                std::string path;
+                if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
+                    LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
+                    return false;
+                }
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
                                                           extent.target_data);
                 break;
+            }
             default:
                 LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
                 return false;
@@ -79,13 +100,13 @@
     return true;
 }
 
-static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
-                                   const LpMetadataPartition& partition, bool force_writable,
-                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {
+static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
+                                   bool force_writable, const std::chrono::milliseconds& timeout_ms,
+                                   std::string* path) {
     DeviceMapper& dm = DeviceMapper::Instance();
 
     DmTable table;
-    if (!CreateDmTable(block_device, metadata, partition, &table)) {
+    if (!CreateDmTable(metadata, partition, &table)) {
         return false;
     }
     if (force_writable) {
@@ -122,7 +143,7 @@
             continue;
         }
         std::string path;
-        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) {
+        if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -140,8 +161,8 @@
     }
     for (const auto& partition : metadata->partitions) {
         if (GetPartitionName(partition) == partition_name) {
-            return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
-                                          timeout_ms, path);
+            return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms,
+                                          path);
         }
     }
     LERROR << "Could not find any partition with name: " << partition_name;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 64e9fb4..79957f6 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>
@@ -56,11 +57,11 @@
 
 #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*) {
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
     return {};
 }
 
@@ -127,13 +128,63 @@
     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);
+    if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+        return true;
+    }
+    if (fs_mgr_is_logical(fsrec)) {
+        fs_mgr_update_logical_partition(fsrec);
+    }
+    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) {
+    auto save_errno = errno;
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+    if (!dir) {
+        if (errno == ENOENT) {
+            errno = save_errno;
+            return true;
+        }
+        PERROR << "opendir " << path << " depth=" << level;
+        if ((errno == EPERM) && (level != 0)) {
+            errno = save_errno;
+            return true;
+        }
+        return false;
+    }
+    dirent* entry;
+    auto ret = true;
+    while ((entry = readdir(dir.get()))) {
+        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+        auto file = path + "/" + entry->d_name;
+        if (entry->d_type == DT_UNKNOWN) {
+            struct stat st;
+            save_errno = errno;
+            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+            errno = save_errno;
+        }
+        if (entry->d_type == DT_DIR) {
+            ret &= fs_mgr_rm_all(file, change, level + 1);
+            if (!rmdir(file.c_str())) {
+                if (change) *change = true;
+            } else {
+                if (errno != ENOENT) ret = false;
+                PERROR << "rmdir " << file << " depth=" << level;
+            }
+            continue;
+        }
+        if (!unlink(file.c_str())) {
+            if (change) *change = true;
+        } else {
+            if (errno != ENOENT) ret = false;
+            PERROR << "rm " << file << " depth=" << level;
+        }
+    }
+    return ret;
 }
 
 const auto kUpperName = "upper"s;
@@ -235,7 +286,7 @@
     return ret;
 }
 
-bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
+bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
     if (!fsrec) return false;
 
     auto fsrec_mount_point = fsrec->mount_point;
@@ -260,53 +311,6 @@
 
     return true;
 }
-
-bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
-    auto save_errno = errno;
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
-    if (!dir) {
-        if (errno == ENOENT) {
-            errno = save_errno;
-            return true;
-        }
-        PERROR << "opendir " << path << " depth=" << level;
-        if ((errno == EPERM) && (level != 0)) {
-            errno = save_errno;
-            return true;
-        }
-        return false;
-    }
-    dirent* entry;
-    auto ret = true;
-    while ((entry = readdir(dir.get()))) {
-        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
-        auto file = path + "/" + entry->d_name;
-        if (entry->d_type == DT_UNKNOWN) {
-            struct stat st;
-            save_errno = errno;
-            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
-            errno = save_errno;
-        }
-        if (entry->d_type == DT_DIR) {
-            ret &= fs_mgr_rm_all(file, change, level + 1);
-            if (!rmdir(file.c_str())) {
-                if (change) *change = true;
-            } else {
-                if (errno != ENOENT) ret = false;
-                PERROR << "rmdir " << file << " depth=" << level;
-            }
-            continue;
-        }
-        if (!unlink(file.c_str())) {
-            if (change) *change = true;
-        } else {
-            if (errno != ENOENT) ret = false;
-            PERROR << "rm " << file << " depth=" << level;
-        }
-    }
-    return ret;
-}
-
 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 +536,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;
 
@@ -590,6 +593,7 @@
 
 // Mount kScratchMountPoint
 bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type) {
+    if (!fs_mgr_rw_access(device_path)) return false;
     if (setfscreatecon(kOverlayfsFileContext)) {
         PERROR << "setfscreatecon " << kOverlayfsFileContext;
     }
@@ -733,7 +737,7 @@
 
 }  // 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;
@@ -760,7 +764,7 @@
     return ret;
 }
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* 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 {};
     }
@@ -826,6 +830,19 @@
     if (change) *change = false;
     mount_point = fs_mgr_mount_point(mount_point);
     auto ret = true;
+    // If scratch exists, but is not mounted, lets gain access to clean
+    // specific override entries.
+    if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        auto scratch_device = fs_mgr_overlayfs_scratch_device();
+        if (scratch_device.empty()) {
+            auto slot_number = fs_mgr_overlayfs_slot_number();
+            auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+            const auto partition_name = android::base::Basename(kScratchMountPoint);
+            CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+                                   &scratch_device);
+        }
+        fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type());
+    }
     for (const auto& overlay_mount_point : kOverlayMountPoints) {
         ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
     }
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 3274e0e..550dd18 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,8 +21,8 @@
 #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);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* 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);
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 4953655..5689bdf 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -25,6 +25,7 @@
     srcs: [
         "builder.cpp",
         "images.cpp",
+        "partition_opener.cpp",
         "reader.cpp",
         "utility.cpp",
         "writer.cpp",
@@ -59,6 +60,7 @@
     srcs: [
         "builder_test.cpp",
         "io_test.cpp",
+        "test_partition_opener.cpp",
         "utility_test.cpp",
     ],
 }
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2c57a35..3cd33b1 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -16,11 +16,7 @@
 
 #include "liblp/builder.h"
 
-#if defined(__linux__)
-#include <linux/fs.h>
-#endif
 #include <string.h>
-#include <sys/ioctl.h>
 
 #include <algorithm>
 
@@ -33,49 +29,19 @@
 namespace android {
 namespace fs_mgr {
 
-bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
-#if defined(__linux__)
-    android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+bool LinearExtent::AddTo(LpMetadata* out) const {
+    if (device_index_ >= out->block_devices.size()) {
+        LERROR << "Extent references unknown block device.";
         return false;
     }
-    if (!GetDescriptorSize(fd, &device_info->size)) {
-        return false;
-    }
-    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
-        return false;
-    }
-
-    int alignment_offset;
-    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
-        return false;
-    }
-    int logical_block_size;
-    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
-        return false;
-    }
-
-    device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
-    device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+    out->extents.emplace_back(
+            LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
     return true;
-#else
-    (void)block_device;
-    (void)device_info;
-    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
-    return false;
-#endif
 }
 
-void LinearExtent::AddTo(LpMetadata* out) const {
-    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
-}
-
-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)
@@ -85,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));
@@ -138,9 +106,10 @@
     return sectors * LP_SECTOR_SIZE;
 }
 
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& opener,
+                                                      const std::string& super_partition,
                                                       uint32_t slot_number) {
-    std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(opener, super_partition, slot_number);
     if (!metadata) {
         return nullptr;
     }
@@ -148,18 +117,26 @@
     if (!builder) {
         return nullptr;
     }
-    BlockDeviceInfo device_info;
-    if (fs_mgr::GetBlockDeviceInfo(block_device, &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;
 }
 
-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::string& super_partition,
+                                                      uint32_t slot_number) {
+    return New(PartitionOpener(), super_partition, slot_number);
+}
+
+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;
@@ -186,10 +163,12 @@
     header_.partitions.entry_size = sizeof(LpMetadataPartition);
     header_.extents.entry_size = sizeof(LpMetadataExtent);
     header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
+    header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);
 }
 
 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);
@@ -209,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);
@@ -220,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.";
@@ -230,68 +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 reserved =
-            LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
-    uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved * 2;
-    if (device_info.size < total_reserved) {
+    uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
+    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;
     }
 
-    geometry_.first_logical_sector = first_sector;
     geometry_.metadata_max_size = metadata_max_size;
     geometry_.metadata_slot_count = metadata_slot_count;
-    geometry_.alignment = device_info.alignment;
-    geometry_.alignment_offset = device_info.alignment_offset;
-    geometry_.block_device_size = device_info.size;
-    geometry_.logical_block_size = device_info.logical_block_size;
+    geometry_.logical_block_size = logical_block_size;
 
     if (!AddGroup("default", 0)) {
         return false;
@@ -375,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
@@ -386,36 +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
-    // ExtentsToFreeList() to treat the space in between as available.
-    uint64_t last_sector = geometry_.block_device_size / LP_SECTOR_SIZE;
-    extents.emplace_back(geometry_.first_logical_sector, geometry_.first_logical_sector);
-    extents.emplace_back(last_sector, last_sector);
+    // ExtentToFreeList() to treat the space in between as available.
+    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;
 }
 
@@ -470,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) {
@@ -498,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 = {};
@@ -542,7 +597,9 @@
         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);
     }
@@ -550,11 +607,17 @@
     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());
+    metadata->header.block_devices.num_entries =
+            static_cast<uint32_t>(metadata->block_devices.size());
     return metadata;
 }
 
 uint64_t MetadataBuilder::AllocatableSpace() const {
-    return geometry_.block_device_size - (geometry_.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 {
@@ -565,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, geometry_.alignment, geometry_.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 = geometry_.block_device_size;
-    info->alignment = geometry_.alignment;
-    info->alignment_offset = geometry_.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 != geometry_.block_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 "
-               << geometry_.block_device_size << ")";
+               << block_device.size << ")";
         return false;
     }
     if (device_info.logical_block_size != geometry_.logical_block_size) {
@@ -596,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) {
-        geometry_.alignment = device_info.alignment;
+        block_device.alignment = device_info.alignment;
     }
     if (device_info.alignment_offset) {
-        geometry_.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 27ad250..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,12 +131,14 @@
 
 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();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 1536);
 
     // Test a large alignment offset thrown in.
     device_info.alignment_offset = 753664;
@@ -140,7 +146,9 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 1472);
 
     // Alignment offset without alignment doesn't mean anything.
     device_info.alignment = 0;
@@ -154,7 +162,9 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 174);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 174);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -162,11 +172,13 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 160);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 160);
 }
 
 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);
@@ -292,6 +304,9 @@
     unique_ptr<LpMetadata> exported = builder->Export();
     EXPECT_NE(exported, nullptr);
 
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+
     // Verify geometry. Some details of this may change if we change the
     // metadata structures. So in addition to checking the exact values, we
     // also check that they are internally consistent after.
@@ -300,11 +315,11 @@
     EXPECT_EQ(geometry.struct_size, sizeof(geometry));
     EXPECT_EQ(geometry.metadata_max_size, 1024);
     EXPECT_EQ(geometry.metadata_slot_count, 2);
-    EXPECT_EQ(geometry.first_logical_sector, 32);
+    EXPECT_EQ(super_device->first_logical_sector, 32);
 
     static const size_t kMetadataSpace =
             ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
-    EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+    EXPECT_GE(super_device->first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
 
     // Verify header.
     const LpMetadataHeader& header = exported->header;
@@ -383,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);
 
@@ -413,13 +428,10 @@
                                                                fs_mgr_free_fstab);
     ASSERT_NE(fstab, nullptr);
 
-    // This should read from the "super" partition once we have a well-defined
-    // way to access it.
-    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data");
-    ASSERT_NE(rec, nullptr);
+    PartitionOpener opener;
 
     BlockDeviceInfo device_info;
-    ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
 
     // Sanity check that the device doesn't give us some weird inefficient
     // alignment.
@@ -433,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);
@@ -447,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);
 
@@ -489,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);
 
@@ -503,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);
 
@@ -522,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);
@@ -547,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));
@@ -557,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));
@@ -572,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/images.cpp b/fs_mgr/liblp/images.cpp
index dfa37fe..46bdfa4 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -99,11 +99,12 @@
       block_size_(block_size),
       file_(nullptr, sparse_file_destroy),
       images_(images) {
+    uint64_t total_size = GetTotalSuperPartitionSize(metadata);
     if (block_size % LP_SECTOR_SIZE != 0) {
         LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
         return;
     }
-    if (metadata.geometry.block_device_size % block_size != 0) {
+    if (total_size % block_size != 0) {
         LERROR << "Device size must be a multiple of the block size, " << block_size;
         return;
     }
@@ -120,7 +121,7 @@
         return;
     }
 
-    uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
+    uint64_t num_blocks = total_size % block_size;
     if (num_blocks >= UINT_MAX) {
         // libsparse counts blocks in unsigned 32-bit integers, so we check to
         // make sure we're not going to overflow.
@@ -128,7 +129,10 @@
         return;
     }
 
-    file_.reset(sparse_file_new(block_size_, geometry_.block_device_size));
+    file_.reset(sparse_file_new(block_size_, total_size));
+    if (!file_) {
+        LERROR << "Could not allocate sparse file of size " << total_size;
+    }
 }
 
 bool SparseBuilder::Export(const char* file) {
@@ -333,14 +337,7 @@
 bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
                        const std::map<std::string, std::string>& images) {
     SparseBuilder builder(metadata, block_size, images);
-    if (!builder.IsValid()) {
-        LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
-        return false;
-    }
-    if (!builder.Build()) {
-        return false;
-    }
-    return builder.Export(file);
+    return builder.IsValid() && builder.Build() && builder.Export(file);
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 7e07df4..f9de106 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,6 +24,7 @@
 #include <memory>
 
 #include "liblp.h"
+#include "partition_opener.h"
 
 namespace android {
 namespace fs_mgr {
@@ -34,34 +35,13 @@
 static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
 static const uint32_t kDefaultBlockSize = 4096;
 
-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)
-        : size(size),
-          alignment(alignment),
-          alignment_offset(alignment_offset),
-          logical_block_size(logical_block_size) {}
-    // Size of the block device, in bytes.
-    uint64_t size;
-    // Optimal target alignment, in bytes. Partition extents will be aligned to
-    // this value by default. This value must be 0 or a multiple of 512.
-    uint32_t alignment;
-    // Alignment offset to parent device (if any), in bytes. The sector at
-    // |alignment_offset| on the target device is correctly aligned on its
-    // parent device. This value must be 0 or a multiple of 512.
-    uint32_t alignment_offset;
-    // Block size, for aligning extent sizes and partition sizes.
-    uint32_t logical_block_size;
-};
-
 // Abstraction around dm-targets that can be encoded into logical partition tables.
 class Extent {
   public:
     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_; }
@@ -74,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_;
 };
 
@@ -92,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 {
@@ -142,22 +124,29 @@
 
 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);
 
     // Import an existing table for modification. This reads metadata off the
     // given block device and imports it. It also adjusts alignment information
     // based on run-time values in the operating system.
-    static std::unique_ptr<MetadataBuilder> New(const std::string& block_device,
+    static std::unique_ptr<MetadataBuilder> New(const IPartitionOpener& opener,
+                                                const std::string& super_partition,
+                                                uint32_t slot_number);
+
+    // Same as above, but use the default PartitionOpener.
+    static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
                                                 uint32_t slot_number);
 
     // Import an existing table for modification. If the table is not valid, for
@@ -165,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);
     }
 
@@ -224,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();
@@ -233,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;
         }
@@ -258,6 +264,7 @@
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
     std::vector<std::unique_ptr<PartitionGroup>> groups_;
+    std::vector<LpMetadataBlockDevice> block_devices_;
 };
 
 // Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 5f95dca..4669cea 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -24,7 +24,10 @@
 #include <memory>
 #include <string>
 
+#include <android-base/unique_fd.h>
+
 #include "metadata_format.h"
+#include "partition_opener.h"
 
 namespace android {
 namespace fs_mgr {
@@ -37,13 +40,15 @@
     std::vector<LpMetadataPartition> partitions;
     std::vector<LpMetadataExtent> extents;
     std::vector<LpMetadataPartitionGroup> groups;
+    std::vector<LpMetadataBlockDevice> block_devices;
 };
 
 // Place an initial partition table on the device. This will overwrite the
 // existing geometry, and should not be used for normal partition table
 // updates. False can be returned if the geometry is incompatible with the
 // block device or an I/O error occurs.
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata);
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                         const LpMetadata& metadata);
 
 // Update the partition table for a given metadata slot number. False is
 // returned if an error occurs, which can include:
@@ -51,12 +56,19 @@
 //  - I/O error.
 //  - Corrupt or missing metadata geometry on disk.
 //  - Incompatible geometry.
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                          uint32_t slot_number);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number);
 
 // Read logical partition metadata from its predetermined location on a block
 // device. If readback fails, we also attempt to load from a backup copy.
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+                                         const std::string& super_partition, uint32_t slot_number);
+
+// Helper functions that use the default PartitionOpener.
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata);
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+                          uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
 
 // Read/Write logical partition metadata to an image file, for diagnostics or
 // flashing.
@@ -69,6 +81,14 @@
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
 std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
+
+// Return the block device that houses the super partition metadata; returns
+// null on failure.
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
+
+// Return the total size of all partitions comprising the super partition.
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
 
 // Helper to return a slot number for a slot suffix.
 uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 89b219c..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 6
+#define LP_METADATA_MAJOR_VERSION 8
 #define LP_METADATA_MINOR_VERSION 0
 
 /* Attributes for the LpMetadataPartition::attributes field.
@@ -103,42 +103,10 @@
      */
     uint32_t metadata_slot_count;
 
-    /* 48: First usable sector for allocating logical partitions. this will be
-     * the first sector after the initial geometry blocks, followed by the
-     * space consumed by metadata_max_size*metadata_slot_count*2.
-     */
-    uint64_t first_logical_sector;
-
-    /* 64: Alignment for defining partitions or partition extents. For example,
-     * an alignment of 1MiB will require that all partitions have a size evenly
-     * divisible by 1MiB, and that the smallest unit the partition can grow by
-     * is 1MiB.
-     *
-     * Alignment is normally determined at runtime when growing or adding
-     * partitions. If for some reason the alignment cannot be determined, then
-     * this predefined alignment in the geometry is used instead. By default
-     * it is set to 1MiB.
-     */
-    uint32_t alignment;
-
-    /* 68: Alignment offset for "stacked" devices. For example, if the "super"
-     * partition itself is not aligned within the parent block device's
-     * partition table, then we adjust for this in deciding where to place
-     * |first_logical_sector|.
-     *
-     * Similar to |alignment|, this will be derived from the operating system.
-     * If it cannot be determined, it is assumed to be 0.
-     */
-    uint32_t alignment_offset;
-
-    /* 72: Block device size, as specified when the metadata was created. This
-     * can be used to verify the geometry against a target device.
-     */
-    uint64_t block_device_size;
-
-    /* 76: Logical block size of the super partition block device. This is the
-     * minimal alignment for partition and extent sizes, and it must be a
-     * multiple of LP_SECTOR_SIZE.
+    /* 48: Logical block size. This is the minimal alignment for partition and
+     * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
+     * this must be equal across all LUNs that comprise the super partition,
+     * and thus this field is stored in the geometry, not per-device.
      */
     uint32_t logical_block_size;
 } __attribute__((packed)) LpMetadataGeometry;
@@ -217,6 +185,8 @@
     LpMetadataTableDescriptor extents;
     /* 104: Updateable group descriptor. */
     LpMetadataTableDescriptor groups;
+    /* 116: Block device table. */
+    LpMetadataTableDescriptor block_devices;
 } __attribute__((packed)) LpMetadataHeader;
 
 /* This struct defines a logical partition entry, similar to what would be
@@ -270,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
@@ -285,6 +262,48 @@
     uint64_t maximum_size;
 } LpMetadataPartitionGroup;
 
+/* 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
+     * the first sector after the initial geometry blocks, followed by the
+     * space consumed by metadata_max_size*metadata_slot_count*2.
+     */
+    uint64_t first_logical_sector;
+
+    /* 8: Alignment for defining partitions or partition extents. For example,
+     * an alignment of 1MiB will require that all partitions have a size evenly
+     * divisible by 1MiB, and that the smallest unit the partition can grow by
+     * is 1MiB.
+     *
+     * Alignment is normally determined at runtime when growing or adding
+     * partitions. If for some reason the alignment cannot be determined, then
+     * this predefined alignment in the geometry is used instead. By default
+     * it is set to 1MiB.
+     */
+    uint32_t alignment;
+
+    /* 12: Alignment offset for "stacked" devices. For example, if the "super"
+     * partition itself is not aligned within the parent block device's
+     * partition table, then we adjust for this in deciding where to place
+     * |first_logical_sector|.
+     *
+     * Similar to |alignment|, this will be derived from the operating system.
+     * If it cannot be determined, it is assumed to be 0.
+     */
+    uint32_t alignment_offset;
+
+    /* 16: Block device size, as specified when the metadata was created. This
+     * can be used to verify the geometry against a target device.
+     */
+    uint64_t size;
+
+    /* 24: Partition name in the GPT. Any unused characters must be 0. */
+    char partition_name[36];
+} LpMetadataBlockDevice;
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
new file mode 100644
index 0000000..e506bd5
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -0,0 +1,77 @@
+//
+// 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 <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct BlockDeviceInfo {
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+    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),
+          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
+    // this value by default. This value must be 0 or a multiple of 512.
+    uint32_t alignment;
+    // Alignment offset to parent device (if any), in bytes. The sector at
+    // |alignment_offset| on the target device is correctly aligned on its
+    // parent device. This value must be 0 or a multiple of 512.
+    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.
+class IPartitionOpener {
+  public:
+    virtual ~IPartitionOpener() = default;
+
+    // Open the given named physical partition with the provided open() flags.
+    // The name can be an absolute path if the full path is already known.
+    virtual android::base::unique_fd Open(const std::string& partition_name, int flags) const = 0;
+
+    // Return block device information about the given named physical partition.
+    // The name can be an absolute path if the full path is already known.
+    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+};
+
+// Helper class to implement IPartitionOpener. If |partition_name| is not an
+// absolute path, /dev/block/by-name/ will be prepended.
+class PartitionOpener : public IPartitionOpener {
+  public:
+    virtual android::base::unique_fd Open(const std::string& partition_name,
+                                          int flags) const override;
+    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 2aa41f3..603e5c0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -26,6 +26,7 @@
 
 #include "images.h"
 #include "reader.h"
+#include "test_partition_opener.h"
 #include "utility.h"
 #include "writer.h"
 
@@ -101,7 +102,9 @@
     if (!exported) {
         return {};
     }
-    if (!FlashPartitionTable(fd, *exported.get())) {
+
+    TestPartitionOpener opener({{"super", fd}});
+    if (!FlashPartitionTable(opener, "super", *exported.get())) {
         return {};
     }
     return fd;
@@ -116,14 +119,16 @@
     ASSERT_TRUE(GetDescriptorSize(fd, &size));
     ASSERT_EQ(size, kDiskSize);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Verify that we can't read unwritten metadata.
-    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+    ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
 }
 
 // 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);
@@ -133,7 +138,9 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get()));
+    TestPartitionOpener opener({{"super", fd}});
+
+    EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
 }
 
 // Test the basics of flashing a partition and reading it back.
@@ -145,22 +152,23 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
 
     // Read back. Note that some fields are only filled in during
     // serialization, so exported and imported will not be identical. For
     // example, table sizes and checksums are computed in WritePartitionTable.
     // Therefore we check on a field-by-field basis.
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 
     // Check geometry and header.
     EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
     EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
-    EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
     EXPECT_EQ(exported->header.major_version, imported->header.major_version);
     EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
     EXPECT_EQ(exported->header.header_size, imported->header.header_size);
@@ -178,6 +186,11 @@
     EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
     EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
     EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+
+    // Check block devices table.
+    ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size());
+    EXPECT_EQ(exported->block_devices[0].first_logical_sector,
+              imported->block_devices[0].first_logical_sector);
 }
 
 // Test that we can update metadata slots without disturbing others.
@@ -185,35 +198,40 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    TestPartitionOpener opener({{"super", fd}});
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
 
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
-    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
     // Read back the original slot, make sure it hasn't changed.
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
 
     // Now read back the new slot, and verify that it has a different name.
-    imported = ReadMetadata(fd, 1);
+    imported = ReadMetadata(opener, "super", 1);
     ASSERT_NE(imported, nullptr);
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
 
-    uint64_t last_sector = imported->geometry.block_device_size / LP_SECTOR_SIZE;
+    auto super_device = GetMetadataSuperBlockDevice(*imported.get());
+    ASSERT_NE(super_device, nullptr);
+
+    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
 
     // Verify that we didn't overwrite anything in the logical paritition area.
     // We expect the disk to be filled with 0xcc on creation so we can read
     // this back and compare it.
     char expected[LP_SECTOR_SIZE];
     memset(expected, 0xcc, sizeof(expected));
-    for (uint64_t i = imported->geometry.first_logical_sector; i < last_sector; i++) {
+    for (uint64_t i = super_device->first_logical_sector; i < last_sector; i++) {
         char buffer[LP_SECTOR_SIZE];
         ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
         ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
@@ -225,15 +243,17 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Make sure all slots are filled.
-    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
     ASSERT_NE(metadata, nullptr);
     for (uint32_t i = 1; i < kMetadataSlots; i++) {
-        ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
+        ASSERT_TRUE(UpdatePartitionTable(opener, "super", *metadata.get(), i));
     }
 
     // Verify that we can't read unavailable slots.
-    EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+    EXPECT_EQ(ReadMetadata(opener, "super", kMetadataSlots), nullptr);
 }
 
 // Test that updating a metadata slot does not allow it to be computed based
@@ -242,24 +262,27 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    TestPartitionOpener opener({{"super", fd}});
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
     imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.metadata_slot_count++;
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    imported->geometry.first_logical_sector++;
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_EQ(imported->block_devices.size(), 1);
+    imported->block_devices[0].first_logical_sector++;
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 }
 
@@ -268,6 +291,8 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     LpMetadataGeometry geometry;
     ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
     ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
@@ -276,7 +301,7 @@
     bad_geometry.metadata_slot_count++;
     ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
 
-    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
     ASSERT_NE(metadata, nullptr);
     EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
 }
@@ -285,25 +310,29 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     char corruption[LP_METADATA_GEOMETRY_SIZE];
     memset(corruption, 0xff, sizeof(corruption));
 
     // Corrupt the primary geometry.
     ASSERT_GE(lseek(fd, GetPrimaryGeometryOffset(), SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+    EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
 
     // Corrupt the backup geometry.
     ASSERT_GE(lseek(fd, GetBackupGeometryOffset(), SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+    EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
 }
 
 TEST(liblp, ReadBackupMetadata) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    TestPartitionOpener opener({{"super", fd}});
+
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
 
     char corruption[kMetadataSize];
     memset(corruption, 0xff, sizeof(corruption));
@@ -312,14 +341,14 @@
 
     ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+    EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
 
     offset = GetBackupMetadataOffset(metadata->geometry, 0);
 
     // Corrupt the backup metadata.
     ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+    EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
 }
 
 // Test that we don't attempt to write metadata if it would overflow its
@@ -329,9 +358,11 @@
     ASSERT_NE(builder, nullptr);
 
     // Compute the maximum number of partitions we can fit in 512 bytes of
-    // metadata. By default there is the header, and one partition group.
-    static const size_t kMaxPartitionTableSize =
-            kMetadataSize - sizeof(LpMetadataHeader) - sizeof(LpMetadataPartitionGroup);
+    // metadata. By default there is the header, one partition group, and a
+    // block device entry.
+    static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) -
+                                                 sizeof(LpMetadataPartitionGroup) -
+                                                 sizeof(LpMetadataBlockDevice);
     size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
 
     // Add this number of partitions.
@@ -347,8 +378,10 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Check that we are able to write our table.
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
 
     // Check that adding one more partition overflows the metadata allotment.
     partition = builder->AddPartition("final", LP_PARTITION_ATTR_NONE);
@@ -358,14 +391,17 @@
     ASSERT_NE(exported, nullptr);
 
     // The new table should be too large to be written.
-    ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *exported.get(), 1));
+
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
 
     // Check that the first and last logical sectors weren't touched when we
     // wrote this almost-full metadata.
     char expected[LP_SECTOR_SIZE];
     memset(expected, 0xcc, sizeof(expected));
     char buffer[LP_SECTOR_SIZE];
-    ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+    ASSERT_GE(lseek(fd, super_device->first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
     ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
     EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
 }
@@ -451,23 +487,25 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     BadWriter writer;
 
     // Read and write it back.
     writer.FailOnWrite(1);
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
 
     // We should still be able to read the backup copy.
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 
     // Flash again, this time fail the backup copy. We should still be able
     // to read the primary.
     writer.FailOnWrite(3);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
-    imported = ReadMetadata(fd, 0);
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 }
 
@@ -477,23 +515,25 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     BadWriter writer;
 
     // Read and write it back.
     writer.FailOnWrite(2);
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
 
     // We should still be able to read the primary copy.
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 
     // Flash again, this time fail the primary copy. We should still be able
     // to read the primary.
     writer.FailOnWrite(2);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
-    imported = ReadMetadata(fd, 0);
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 }
 
@@ -504,20 +544,22 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     BadWriter writer;
 
     // Change the name of the existing partition.
-    unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> new_table = ReadMetadata(opener, "super", 0);
     ASSERT_NE(new_table, nullptr);
     ASSERT_GE(new_table->partitions.size(), 1);
     new_table->partitions[0].name[0]++;
 
     // Flash it, but fail to write the backup copy.
     writer.FailAfterWrite(2);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
 
     // When we read back, we should get the updated primary copy.
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_GE(new_table->partitions.size(), 1);
     ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
@@ -526,9 +568,9 @@
     // Note that the sync step should have used the primary to sync, not
     // the backup.
     writer.Reset();
-    ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_GE(new_table->partitions.size(), 1);
     ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
@@ -539,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
new file mode 100644
index 0000000..77b0e62
--- /dev/null
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "liblp/partition_opener.h"
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string GetPartitionAbsolutePath(const std::string& path) {
+    if (path[0] == '/') {
+        return path;
+    }
+    return "/dev/block/by-name/" + path;
+}
+
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+    unique_fd fd(open(block_device.c_str(), O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+        return false;
+    }
+    if (!GetDescriptorSize(fd, &device_info->size)) {
+        return false;
+    }
+    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+
+    int alignment_offset;
+    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    int logical_block_size;
+    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+        return false;
+    }
+
+    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;
+    (void)device_info;
+    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+    return false;
+#endif
+}
+
+}  // namespace
+
+unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
+    std::string path = GetPartitionAbsolutePath(partition_name);
+    return unique_fd{open(path.c_str(), flags)};
+}
+
+bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+    std::string path = GetPartitionAbsolutePath(partition_name);
+    return GetBlockDeviceInfo(path, info);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 43d8076..a02e746 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -108,15 +108,6 @@
         LERROR << "Metadata max size is not sector-aligned.";
         return false;
     }
-
-    // Check that the metadata area and logical partition areas don't overlap.
-    int64_t end_of_metadata =
-            GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
-            geometry->metadata_max_size;
-    if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
-        LERROR << "Logical partition metadata overlaps with logical partition contents.";
-        return false;
-    }
     return true;
 }
 
@@ -195,7 +186,8 @@
     }
     if (!ValidateTableBounds(header, header.partitions) ||
         !ValidateTableBounds(header, header.extents) ||
-        !ValidateTableBounds(header, header.groups)) {
+        !ValidateTableBounds(header, header.groups) ||
+        !ValidateTableBounds(header, header.block_devices)) {
         LERROR << "Logical partition metadata has invalid table bounds.";
         return false;
     }
@@ -282,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);
     }
 
@@ -294,6 +292,28 @@
         metadata->groups.push_back(group);
     }
 
+    cursor = buffer.get() + header.block_devices.offset;
+    for (size_t i = 0; i < header.block_devices.num_entries; i++) {
+        LpMetadataBlockDevice device = {};
+        memcpy(&device, cursor, sizeof(device));
+        cursor += header.block_devices.entry_size;
+
+        metadata->block_devices.push_back(device);
+    }
+
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
+    if (!super_device) {
+        LERROR << "Metadata does not specify a super device.";
+        return nullptr;
+    }
+
+    // Check that the metadata area and logical partition areas don't overlap.
+    uint64_t metadata_region =
+            GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
+    if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+        return nullptr;
+    }
     return metadata;
 }
 
@@ -328,7 +348,14 @@
     return ParseMetadata(geometry, fd);
 }
 
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+                                         const std::string& super_partition, uint32_t slot_number) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return nullptr;
+    }
+
     LpMetadataGeometry geometry;
     if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
         return nullptr;
@@ -347,13 +374,8 @@
     return ReadBackupMetadata(fd, geometry, slot_number);
 }
 
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
-    android::base::unique_fd fd(open(block_device, O_RDONLY));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
-        return nullptr;
-    }
-    return ReadMetadata(fd, slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
+    return ReadMetadata(PartitionOpener(), super_partition, slot_number);
 }
 
 static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
@@ -374,5 +396,9 @@
     return NameFromFixedArray(group.name, sizeof(group.name));
 }
 
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
+    return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 24b2611..d5d5188 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -31,7 +31,6 @@
 bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
 
 // Helper functions for manually reading geometry and metadata.
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
 std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
 std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
                                           size_t size);
diff --git a/fs_mgr/liblp/test_partition_opener.cpp b/fs_mgr/liblp/test_partition_opener.cpp
new file mode 100644
index 0000000..c796f6c
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "test_partition_opener.h"
+
+#include <errno.h>
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+TestPartitionOpener::TestPartitionOpener(
+        const std::map<std::string, int>& partition_map,
+        const std::map<std::string, BlockDeviceInfo>& partition_info)
+    : partition_map_(partition_map), partition_info_(partition_info) {}
+
+unique_fd TestPartitionOpener::Open(const std::string& partition_name, int flags) const {
+    auto iter = partition_map_.find(partition_name);
+    if (iter == partition_map_.end()) {
+        errno = ENOENT;
+        return {};
+    }
+    return unique_fd{dup(iter->second)};
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+    auto iter = partition_info_.find(partition_name);
+    if (iter == partition_info_.end()) {
+        errno = ENOENT;
+        return false;
+    }
+    *info = iter->second;
+    return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/test_partition_opener.h b/fs_mgr/liblp/test_partition_opener.h
new file mode 100644
index 0000000..b90fee7
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <liblp/partition_opener.h>
+
+namespace android {
+namespace fs_mgr {
+
+class TestPartitionOpener : public PartitionOpener {
+  public:
+    explicit TestPartitionOpener(const std::map<std::string, int>& partition_map,
+                                 const std::map<std::string, BlockDeviceInfo>& partition_info = {});
+
+    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+    bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+
+  private:
+    std::map<std::string, int> partition_map_;
+    std::map<std::string, BlockDeviceInfo> partition_info_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 0556833..742ad82 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -16,7 +16,6 @@
 
 #include <fcntl.h>
 #include <stdint.h>
-#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -66,11 +65,8 @@
 
 int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
     CHECK(slot_number < geometry.metadata_slot_count);
-
     int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
                      geometry.metadata_max_size * slot_number;
-    CHECK(offset + geometry.metadata_max_size <=
-          int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
     return offset;
 }
 
@@ -81,6 +77,18 @@
     return start + int64_t(geometry.metadata_max_size * slot_number);
 }
 
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
+    return LP_PARTITION_RESERVED_BYTES +
+           (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
+}
+
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
+    if (metadata.block_devices.empty()) {
+        return nullptr;
+    }
+    return &metadata.block_devices[0];
+}
+
 void SHA256(const void* data, size_t length, uint8_t out[32]) {
     SHA256_CTX c;
     SHA256_Init(&c);
@@ -100,5 +108,13 @@
     return suffix[1] - 'a';
 }
 
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
+    uint64_t size = 0;
+    for (const auto& block_device : metadata.block_devices) {
+        size += block_device.size;
+    }
+    return size;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 61e7d31..65e643b 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -23,7 +23,7 @@
 
 #include <android-base/logging.h>
 
-#include "liblp/metadata_format.h"
+#include "liblp/liblp.h"
 
 #define LP_TAG "[liblp]"
 #define LWARN LOG(WARNING) << LP_TAG
@@ -50,6 +50,10 @@
 // device.
 int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
 
+// Return the total space at the start of the super partition that must be set
+// aside from headers/metadata and backups.
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
+
 // Cross-platform helper for lseek64().
 int64_t SeekFile64(int fd, int64_t offset, int whence);
 
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 8baf9e7..bdf6dfd 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -36,10 +36,6 @@
                                    {0},
                                    16384,
                                    4,
-                                   10000,
-                                   0,
-                                   0,
-                                   1024 * 1024,
                                    4096};
     static const uint64_t start = LP_PARTITION_RESERVED_BYTES;
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index f857d8c..c740bd4 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -43,9 +43,7 @@
 static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
     return g1.metadata_max_size == g2.metadata_max_size &&
            g1.metadata_slot_count == g2.metadata_slot_count &&
-           g1.block_device_size == g2.block_device_size &&
-           g1.logical_block_size == g2.logical_block_size &&
-           g1.first_logical_sector == g2.first_logical_sector;
+           g1.logical_block_size == g2.logical_block_size;
 }
 
 std::string SerializeMetadata(const LpMetadata& input) {
@@ -59,15 +57,18 @@
                         metadata.extents.size() * sizeof(LpMetadataExtent));
     std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
                        metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
+    std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
+                              metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
 
     // Compute positions of tables.
     header.partitions.offset = 0;
     header.extents.offset = header.partitions.offset + partitions.size();
     header.groups.offset = header.extents.offset + extents.size();
-    header.tables_size = header.groups.offset + groups.size();
+    header.block_devices.offset = header.groups.offset + groups.size();
+    header.tables_size = header.block_devices.offset + block_devices.size();
 
     // Compute payload checksum.
-    std::string tables = partitions + extents + groups;
+    std::string tables = partitions + extents + groups + block_devices;
     SHA256(tables.data(), tables.size(), header.tables_checksum);
 
     // Compute header checksum.
@@ -105,14 +106,20 @@
                              uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
     uint64_t total_reserved = reserved_size * 2;
 
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << "Logical partition metadata does not have a super block device.";
+        return false;
+    }
+
     if (total_reserved > blockdevice_size ||
-        total_reserved > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+        total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
         LERROR << "Not enough space to store all logical partition metadata slots.";
         return false;
     }
-    if (blockdevice_size != metadata.geometry.block_device_size) {
+    if (blockdevice_size != super_device->size) {
         LERROR << "Block device size " << blockdevice_size
-               << " does not match metadata requested size " << metadata.geometry.block_device_size;
+               << " does not match metadata requested size " << super_device->size;
         return false;
     }
 
@@ -125,11 +132,11 @@
     }
 
     // Make sure all linear extents have a valid range.
-    uint64_t last_sector = geometry.block_device_size / LP_SECTOR_SIZE;
+    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
     for (const auto& extent : metadata.extents) {
         if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
             uint64_t physical_sector = extent.target_data;
-            if (physical_sector < geometry.first_logical_sector ||
+            if (physical_sector < super_device->first_logical_sector ||
                 physical_sector + extent.num_sectors > last_sector) {
                 LERROR << "Extent table entry is out of bounds.";
                 return false;
@@ -139,10 +146,28 @@
     return true;
 }
 
-static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+// Check that the given region is within metadata bounds.
+static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
+        return false;
+    }
+    if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
+               << " overlaps with logical partition contents";
+        return false;
+    }
+    return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
                                  const std::string& blob,
                                  const std::function<bool(int, const std::string&)>& writer) {
-    int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
+        return false;
+    }
     if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
         return false;
@@ -154,18 +179,15 @@
     return true;
 }
 
-static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
                                 const std::string& blob,
                                 const std::function<bool(int, const std::string&)>& writer) {
-    int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
-    int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_SET);
-    if (abs_offset == (int64_t)-1) {
-        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
+    int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
         return false;
     }
-    if (abs_offset >= int64_t(geometry.first_logical_sector) * LP_SECTOR_SIZE) {
-        PERROR << __PRETTY_FUNCTION__ << " backup offset " << abs_offset
-               << " is within logical partition bounds, sector " << geometry.first_logical_sector;
+    if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
         return false;
     }
     if (!writer(fd, blob)) {
@@ -175,18 +197,18 @@
     return true;
 }
 
-static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
                           const std::string& blob,
                           const std::function<bool(int, const std::string&)>& writer) {
     // Make sure we're writing to a valid metadata slot.
-    if (slot_number >= geometry.metadata_slot_count) {
+    if (slot_number >= metadata.geometry.metadata_slot_count) {
         LERROR << "Invalid logical partition metadata slot number.";
         return false;
     }
-    if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+    if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
         return false;
     }
-    if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
+    if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
         return false;
     }
     return true;
@@ -196,7 +218,14 @@
     return android::base::WriteFully(fd, blob.data(), blob.size());
 }
 
-bool FlashPartitionTable(int fd, const LpMetadata& metadata) {
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                         const LpMetadata& metadata) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
     // Before writing geometry and/or logical partition tables, perform some
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
@@ -216,6 +245,8 @@
         return false;
     }
 
+    LWARN << "Flashing new logical partition geometry to " << super_partition;
+
     // Write geometry to the primary and backup locations.
     std::string blob = SerializeGeometry(metadata.geometry);
     if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
@@ -237,18 +268,29 @@
 
     bool ok = true;
     for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
-        ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter);
+        ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
     }
     return ok;
 }
 
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {
+    return FlashPartitionTable(PartitionOpener(), super_partition, metadata);
+}
+
 static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
     return !memcmp(a.header.header_checksum, b.header.header_checksum,
                    sizeof(a.header.header_checksum));
 }
 
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number,
                           const std::function<bool(int, const std::string&)>& writer) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
     // Before writing geometry and/or logical partition tables, perform some
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
@@ -289,7 +331,7 @@
             LERROR << "Error serializing primary metadata to repair corrupted backup";
             return false;
         }
-        if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+        if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {
             LERROR << "Error writing primary metadata to repair corrupted backup";
             return false;
         }
@@ -301,46 +343,31 @@
             LERROR << "Error serializing primary metadata to repair corrupted backup";
             return false;
         }
-        if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+        if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
             LERROR << "Error writing primary metadata to repair corrupted backup";
             return false;
         }
     }
 
     // Both copies should now be in sync, so we can continue the update.
-    return WriteMetadata(fd, geometry, slot_number, blob, writer);
-}
+    if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {
+        return false;
+    }
 
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {
-    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
-        return false;
-    }
-    if (!FlashPartitionTable(fd, metadata)) {
-        return false;
-    }
-    LWARN << "Flashed new logical partition geometry to " << block_device;
-    return true;
-}
-
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                          uint32_t slot_number) {
-    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
-        return false;
-    }
-    if (!UpdatePartitionTable(fd, metadata, slot_number)) {
-        return false;
-    }
     LINFO << "Updated logical partition table at slot " << slot_number << " on device "
-          << block_device;
+          << super_partition;
     return true;
 }
 
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
-    return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number) {
+    return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
+}
+
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    PartitionOpener opener;
+    return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
index ab18d45..6f1da0f 100644
--- a/fs_mgr/liblp/writer.h
+++ b/fs_mgr/liblp/writer.h
@@ -30,10 +30,8 @@
 
 // These variants are for testing only. The path-based functions should be used
 // for actual operation, so that open() is called with the correct flags.
-bool FlashPartitionTable(int fd, const LpMetadata& metadata);
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
-
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number,
                           const std::function<bool(int, const std::string&)>& writer);
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index d4ca574..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 &&
@@ -251,8 +267,33 @@
   B="`adb_cat /vendor/hello`" ||
   die "re-read vendor hello after reboot"
 check_eq "${A}" "${B}" vendor after reboot
+
+adb reboot-fastboot &&
+  fastboot flash vendor &&
+  fastboot reboot ||
+  die "fastbootd flash vendor"
+adb_wait &&
+  adb_root &&
+  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 ||
+  die  "overlay system takeover after flash vendor"
+adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
+  die  "overlay minus vendor takeover after flash vendor"
+B="`adb_cat /system/hello`" ||
+  die "re-read system hello after flash vendor"
+check_eq "${A}" "${B}" system after flash vendor
+adb_root &&
+  adb_wait ||
+  die "adb root"
+B="`adb_cat /vendor/hello`" &&
+  die "re-read vendor hello after flash vendor"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+
 adb remount &&
-  adb_sh rm /system/hello /vendor/hello </dev/null ||
+  ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
+  adb_sh rm /system/hello </dev/null ||
   die "cleanup hello"
 B="`adb_cat /system/hello`" &&
   die "re-read system hello after rm"
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..c85727c 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -73,6 +73,7 @@
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
+    libfscrypt \
     libseccomp_policy \
     libcrypto_utils \
     libsparse \
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 1481162..4f8bd16 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -19,7 +19,11 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 
-#include "stable_properties.h"
+#if defined(__ANDROID__)
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
 
 using android::base::GetBoolProperty;
 using android::base::StartsWith;
@@ -36,15 +40,19 @@
         return true;
     }
 
-    if (kExportedActionableProperties.count(prop_name) == 1) {
-        return true;
-    }
+    static constexpr const char* kPartnerPrefixes[] = {
+            "init.svc.vendor.", "ro.vendor.",    "persist.vendor.",
+            "vendor.",          "init.svc.odm.", "ro.odm.",
+            "persist.odm.",     "odm.",          "ro.boot.",
+    };
+
     for (const auto& prefix : kPartnerPrefixes) {
         if (android::base::StartsWith(prop_name, prefix)) {
             return true;
         }
     }
-    return false;
+
+    return CanReadProperty(subcontext->context(), prop_name);
 }
 
 Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
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/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 8866bdc..b85e54a 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -30,6 +30,9 @@
 std::string default_console = "/dev/console";
 
 // property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    return true;
+}
 uint32_t SetProperty(const std::string& key, const std::string& value) {
     android::base::SetProperty(key, value);
     return 0;
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 0af11f6..63ceead 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -39,6 +39,7 @@
 extern std::string default_console;
 
 // property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name);
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                            const std::string& source_context, const ucred& cr, std::string* error);
diff --git a/init/parser.cpp b/init/parser.cpp
index fa0fd11..bbfbdc6 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -53,7 +53,12 @@
     int section_start_line = -1;
     std::vector<std::string> args;
 
+    // If we encounter a bad section start, there is no valid parser object to parse the subsequent
+    // sections, so we must suppress errors until the next valid section is found.
+    bool bad_section_found = false;
+
     auto end_section = [&] {
+        bad_section_found = false;
         if (section_parser == nullptr) return;
 
         if (auto result = section_parser->EndSection(); !result) {
@@ -101,6 +106,7 @@
                         parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
+                        bad_section_found = true;
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
@@ -108,7 +114,7 @@
                         parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
-                } else {
+                } else if (!bad_section_found) {
                     parse_error_count_++;
                     LOG(ERROR) << filename << ": " << state.line
                                << ": Invalid section keyword found";
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 6aed0a3..5328869 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -111,6 +111,22 @@
         LOG(FATAL) << "Failed to load serialized property info file";
     }
 }
+
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    const char* target_context = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
+
+    PropertyAuditData audit_data;
+
+    audit_data.name = name.c_str();
+
+    ucred cr = {.pid = 0, .uid = 0, .gid = 0};
+    audit_data.cr = &cr;
+
+    return selinux_check_access(source_context.c_str(), target_context, "file", "read",
+                                &audit_data) == 0;
+}
+
 static bool CheckMacPerms(const std::string& name, const char* target_context,
                           const char* source_context, const ucred& cr) {
     if (!target_context || !source_context) {
diff --git a/init/property_service.h b/init/property_service.h
index cacd987..9022f5a 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,6 +26,8 @@
 namespace android {
 namespace init {
 
+bool CanReadProperty(const std::string& source_context, const std::string& name);
+
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/stable_properties.h b/init/stable_properties.h
deleted file mode 100644
index baef833..0000000
--- a/init/stable_properties.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _INIT_STABLE_PROPERTIES_H
-#define _INIT_STABLE_PROPERTIES_H
-
-#include <set>
-#include <string>
-
-namespace android {
-namespace init {
-
-static constexpr const char* kPartnerPrefixes[] = {
-    "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.", "init.svc.odm.", "ro.odm.",
-    "persist.odm.",     "odm.",       "ro.boot.",
-};
-
-static const std::set<std::string> kExportedActionableProperties = {
-    "dev.bootcomplete",
-    "init.svc.console",
-    "init.svc.dumpstatez",
-    "init.svc.mediadrm",
-    "init.svc.surfaceflinger",
-    "init.svc.zygote",
-    "persist.bluetooth.btsnoopenable",
-    "persist.sys.crash_rcu",
-    "persist.sys.usb.usbradio.config",
-    "persist.sys.zram_enabled",
-    "ro.board.platform",
-    "ro.bootmode",
-    "ro.build.type",
-    "ro.crypto.state",
-    "ro.crypto.type",
-    "ro.debuggable",
-    "sys.boot_completed",
-    "sys.boot_from_charger_mode",
-    "sys.retaildemo.enabled",
-    "sys.shutdown.requested",
-    "sys.usb.config",
-    "sys.usb.configfs",
-    "sys.usb.ffs.mtp.ready",
-    "sys.usb.ffs.ready",
-    "sys.user.0.ce_available",
-    "sys.vdso",
-    "vold.decrypt",
-    "vold.post_fs_data_done",
-    "vts.native_server.on",
-    "wlan.driver.status",
-};
-
-}  // namespace init
-}  // namespace android
-
-#endif
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 d2d6ab8..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(), true);
+  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/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index b24468b..285f150 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __CUTILS_SOCKETS_H
-#define __CUTILS_SOCKETS_H
+#pragma once
 
 #include <errno.h>
 #include <limits.h>
@@ -141,19 +140,6 @@
                             const cutils_socket_buffer_t* buffers,
                             size_t num_buffers);
 
-/*
- * socket_peer_is_trusted - Takes a socket which is presumed to be a
- * connected local socket (e.g. AF_LOCAL) and returns whether the peer
- * (the userid that owns the process on the other end of that socket)
- * is one of the two trusted userids, root or shell.
- *
- * Note: This only works as advertised on the Android OS and always
- * just returns true when called on other operating systems.
- */
-extern bool socket_peer_is_trusted(int fd);
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* __CUTILS_SOCKETS_H */
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 0cb8a4d..2248817 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -32,34 +32,6 @@
 
 #include "android_get_control_env.h"
 
-#if defined(__ANDROID__)
-/* For the socket trust (credentials) check */
-#include <private/android_filesystem_config.h>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-bool socket_peer_is_trusted(int fd __android_unused) {
-#if defined(__ANDROID__)
-    ucred cr;
-    socklen_t len = sizeof(cr);
-    int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-
-    if (n != 0) {
-        ALOGE("could not get socket credentials: %s\n", strerror(errno));
-        return false;
-    }
-
-    if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
-        ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
-        return false;
-    }
-#endif
-
-    return true;
-}
-
 int socket_close(int sock) {
     return close(sock);
 }
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 1551b5b..2d327ee 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -11,7 +11,11 @@
 
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libstatssocket",
+    ],
     whole_static_libs: ["libgtest_prod"],
 
     cflags: [
@@ -23,17 +27,20 @@
 
 // metricslogger shared library
 // -----------------------------------------------------------------------------
-cc_library_shared {
+cc_library {
     name: "libmetricslogger",
     srcs: metricslogger_lib_src_files,
     defaults: ["metricslogger_defaults"],
+    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
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index c305db2..56bd6c4 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -15,6 +15,7 @@
  */
 
 #include <log/log_event_list.h>
+#include <stats_event_list.h>
 #include <cstdint>
 #include <string>
 
@@ -43,6 +44,7 @@
 class ComplexEventLogger {
   private:
     android_log_event_list logger;
+    stats_event_list stats_logger;
 
   public:
     // Create a complex event with category|category|.
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6a32153..2a1b137 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,11 +18,15 @@
 
 #include <cstdlib>
 
+#include <android-base/chrono_utils.h>
 #include <log/event_tag_map.h>
-#include <log/log_event_list.h>
+
+using namespace android;
 
 namespace {
 
+const static int kStatsEventTag = 1937006964;
+const static int kKeyValuePairAtomId = 83;
 #ifdef __ANDROID__
 EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
 const int kSysuiMultiActionTag = android_lookupEventTagNum(
@@ -32,6 +36,12 @@
 const int kSysuiMultiActionTag = 0;
 #endif
 
+int64_t getElapsedTimeNanoSinceBoot() {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(
+                   android::base::boot_clock::now().time_since_epoch())
+            .count();
+}
+
 }  // namespace
 
 namespace android {
@@ -42,6 +52,12 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
         << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event << LOGBUILDER_BUCKET << data
+              << LOGBUILDER_VALUE << 1;
+    stats_log.write(LOG_ID_STATS);
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#count().
@@ -49,6 +65,11 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
         << val << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE << val;
+    stats_log.write(LOG_ID_STATS);
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#action().
@@ -56,34 +77,48 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
         << field << value << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << category << LOGBUILDER_TYPE << TYPE_ACTION << field << value;
+    stats_log.write(LOG_ID_STATS);
 }
 
-ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+ComplexEventLogger::ComplexEventLogger(int category)
+    : logger(kSysuiMultiActionTag), stats_logger(kStatsEventTag) {
     logger << LOGBUILDER_CATEGORY << category;
+    stats_logger << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+                 << category;
 }
 
 void ComplexEventLogger::SetPackageName(const std::string& package_name) {
     logger << LOGBUILDER_PACKAGENAME << package_name;
+    stats_logger << LOGBUILDER_PACKAGENAME << package_name;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, float value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::Record() {
     logger << LOG_ID_EVENTS;
+    stats_logger.write(LOG_ID_STATS);
 }
 
 }  // namespace metricslogger
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 6e63b74..92fd2a2 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -10,9 +10,11 @@
 
     host_supported: true,
     srcs: ["native_bridge.cc"],
+    header_libs: [
+        "libbase_headers",
+    ],
     shared_libs: [
         "liblog",
-        "libbase",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 5b9ba1c..4ed6e20 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -29,10 +29,12 @@
 
 shared_libraries := \
     liblog \
-    libbase \
     libnativebridge \
     libnativebridge-dummy
 
+header_libraries := \
+    libbase_headers
+
 libnativebridge_tests_common_cflags := \
     -Wall \
     -Werror \
@@ -40,6 +42,7 @@
 $(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=%))) \
@@ -49,6 +52,7 @@
 $(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=%))) \
diff --git a/libstats/Android.bp b/libstats/Android.bp
index d58f294..f5ee1da 100644
--- a/libstats/Android.bp
+++ b/libstats/Android.bp
@@ -17,12 +17,13 @@
 // ==========================================================
 // Native library to write stats log to statsd socket
 // ==========================================================
-cc_library_static {
+cc_library {
     name: "libstatssocket",
     srcs: [
         "stats_event_list.c",
         "statsd_writer.c",
     ],
+    host_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
@@ -32,6 +33,7 @@
     ],
     export_include_dirs: ["include"],
     shared_libs: [
+        "libcutils",
         "liblog",
     ],
 }
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 8eedc60..72770d4 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -17,6 +17,7 @@
 #include "include/stats_event_list.h"
 
 #include <string.h>
+#include <sys/time.h>
 #include "statsd_writer.h"
 
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
@@ -156,7 +157,14 @@
     }
 
     save_errno = errno;
+#if defined(__ANDROID__)
     clock_gettime(CLOCK_REALTIME, &ts);
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
+#endif
 
     int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
     errno = save_errno;
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index afe401f..88f7d44 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -15,8 +15,9 @@
  */
 #include "statsd_writer.h"
 
+#include <cutils/fs.h>
 #include <cutils/sockets.h>
-#include <endian.h>
+#include <cutils/threads.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -37,6 +38,14 @@
 /* branchless on many architectures. */
 #define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
+#ifndef htole32
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole32(x) (x)
+#else
+#define htole32(x) __bswap_32(x)
+#endif
+#endif
+
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
 static atomic_int dropped = 0;
 
@@ -78,7 +87,14 @@
 
     i = atomic_load(&statsdLoggerWrite.sock);
     if (i < 0) {
-        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+        int flags = SOCK_DGRAM;
+#ifdef SOCK_CLOEXEC
+        flags |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+        flags |= SOCK_NONBLOCK;
+#endif
+        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
         if (sock < 0) {
             ret = -errno;
         } else {
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 56a0679..4d72ead 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -40,7 +40,7 @@
 std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
 std::mutex* Elf::cache_lock_;
 
-bool Elf::Init(bool init_gnu_debugdata) {
+bool Elf::Init() {
   load_bias_ = 0;
   if (!memory_) {
     return false;
@@ -54,11 +54,7 @@
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
     interface_->InitHeaders(load_bias_);
-    if (init_gnu_debugdata) {
-      InitGnuDebugdata();
-    } else {
-      gnu_debugdata_interface_.reset(nullptr);
-    }
+    InitGnuDebugdata();
   } else {
     interface_.reset(nullptr);
   }
@@ -92,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 b449c7e..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_, true);
+  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 fe680d7..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;
@@ -201,7 +201,7 @@
     entry_addr_ = (this->*read_entry_func_)(&start, &size);
 
     Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
-    elf->Init(true);
+    elf->Init();
     if (!elf->valid()) {
       // The data is not formatted in a way we understand, do not attempt
       // to process any other entries.
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 952b332..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_, true);
+    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 52b7535..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, bool init_gnu_debugdata) {
+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_);
 
@@ -175,7 +175,11 @@
   elf.reset(new Elf(memory));
   // If the init fails, keep the elf around as an invalid object so we
   // don't try to reinit the object.
-  elf->Init(init_gnu_debugdata);
+  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/RegsInfo.h b/libunwindstack/RegsInfo.h
index e6dd33c..e445a91 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -41,7 +41,7 @@
   }
 
   inline AddressType* Save(uint32_t reg) {
-    if (reg > MAX_REGISTERS) {
+    if (reg >= MAX_REGISTERS) {
       // This should never happen since all currently supported
       // architectures have < 64 total registers.
       abort();
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index ee1cd1a..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_, true);
+      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 9af859d..e5b0a89 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -53,10 +53,12 @@
   Elf(Memory* memory) : memory_(memory) {}
   virtual ~Elf() = default;
 
-  bool Init(bool init_gnu_debugdata);
+  bool Init();
 
   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 9755c48..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, bool init_gnu_debugdata = false);
+  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 1afd4ef..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_, true);
+  Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf1->valid());
-  Elf* elf2 = info2.GetElf(memory_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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_, true);
+  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/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 9a117b2..ccf8927 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -110,7 +110,7 @@
 TEST_F(ElfTest, invalid_memory) {
   Elf elf(memory_);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
 }
 
@@ -122,7 +122,7 @@
   // Corrupt the ELF signature.
   memory_->SetData32(0, 0x7f000000);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
   ASSERT_TRUE(elf.interface() == nullptr);
 
@@ -142,7 +142,7 @@
   InitElf32(EM_PPC);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
@@ -155,7 +155,7 @@
   InitElf64(EM_PPC64);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
@@ -167,7 +167,7 @@
 
   InitElf32(EM_ARM);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -179,7 +179,7 @@
 
   InitElf32(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -191,7 +191,7 @@
 
   InitElf32(EM_386);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -203,7 +203,7 @@
 
   InitElf64(EM_AARCH64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -215,7 +215,7 @@
 
   InitElf64(EM_X86_64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -227,41 +227,13 @@
 
   InitElf64(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
   ASSERT_TRUE(elf.interface() != nullptr);
 }
 
-TEST_F(ElfTest, gnu_debugdata_init_fail32) {
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(false));
-  ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
-  EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
-TEST_F(ElfTest, gnu_debugdata_init_fail64) {
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(false));
-  ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
-  EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
 TEST_F(ElfTest, gnu_debugdata_init32) {
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -269,7 +241,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
@@ -283,7 +255,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_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 918c028..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_, false);
+  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_, false);
+  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,43 +98,23 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  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, gnu_debugdata_do_not_init32) {
-  MapInfo info(nullptr, 0x4000, 0x8000, 0, PROT_READ, "");
+TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
+  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(0x4000 + offset, ptr, size);
-                                               });
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_X86);
   ASSERT_TRUE(elf != nullptr);
-  ASSERT_TRUE(elf->valid());
-  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
-  EXPECT_EQ(ELFCLASS32, elf->class_type());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
-}
-
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
-  MapInfo info(nullptr, 0x6000, 0x8000, 0, PROT_READ, "");
-
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(0x6000 + offset, ptr, size);
-                                               });
-
-  Elf* elf = info.GetElf(process_memory_, false);
-  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());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+  ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
@@ -145,7 +125,7 @@
                                                  memory_->SetMemory(0x2000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, true);
+  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());
@@ -161,7 +141,7 @@
                                                  memory_->SetMemory(0x5000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, true);
+  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());
@@ -176,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_, false);
+  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_, false);
+  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_, false);
+  elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 }
@@ -206,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_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -235,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_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -268,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_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -296,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_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -322,13 +302,13 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  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_, false);
+  elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -345,20 +325,20 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  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_, false);
+  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_, false);
+  elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -384,7 +364,7 @@
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
         ;
-      Elf* elf = info.GetElf(process_memory_, false);
+      Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
       elf_in_threads[i] = elf;
     });
     threads.push_back(thread);
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
index 052b5bf..a6bc2c5 100644
--- a/libunwindstack/tests/RegsInfoTest.cpp
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -82,4 +82,11 @@
   }
 }
 
+TEST(RegsInfoTest, invalid_register) {
+  RegsImplFake<uint64_t> regs(64);
+  RegsInfo<uint64_t> info(&regs);
+
+  EXPECT_DEATH(info.Save(RegsInfo<uint64_t>::MAX_REGISTERS), "");
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 266a6db..aebeb95 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -113,7 +113,7 @@
   }
 
   Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 0f01566..4b6f49a 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -173,7 +173,7 @@
   }
 
   Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index f8e3e92..9128430 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -66,7 +66,7 @@
   }
 
   unwindstack::Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", argv[1]);
     return 1;
   }
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 3308adf..2095189 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -73,6 +73,7 @@
         enabled: true,
     },
     double_loadable: true,
+    export_shared_lib_headers: ["libbase"],
 
     defaults: [
         "libziparchive_defaults",
@@ -84,14 +85,6 @@
         "libz",
     ],
     target: {
-        android: {
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        host: {
-            static_libs: ["libutils"],
-        },
         linux_bionic: {
             enabled: true,
         },
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 32d7901..ab38dfd 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
+#pragma once
+
 /*
  * Read-only access to Zip archives, with minimal heap allocation.
  */
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_H_
 
 #include <stdint.h>
 #include <string.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
-#include <utils/Compat.h>
+
+#include "android-base/off64_t.h"
 
 /* Zip compression methods we support */
 enum {
@@ -273,5 +274,3 @@
 int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
                 const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
 }  // namespace zip_archive
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
index b4766f8..8c6ca79 100644
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -15,12 +15,13 @@
  */
 
 // Read-only stream access to Zip archives entries.
-#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
-#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
 
 #include <vector>
 
-#include <ziparchive/zip_archive.h>
+#include "android-base/off64_t.h"
 
 class ZipArchiveStreamEntry {
  public:
@@ -43,5 +44,3 @@
   off64_t offset_ = 0;
   uint32_t crc32_ = 0u;
 };
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index 0e0caf1..f6c8427 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
-#define LIBZIPARCHIVE_ZIPWRITER_H_
+#pragma once
 
 #include <cstdio>
 #include <ctime>
@@ -25,7 +24,7 @@
 #include <vector>
 
 #include "android-base/macros.h"
-#include "utils/Compat.h"
+#include "android-base/off64_t.h"
 
 struct z_stream_s;
 typedef struct z_stream_s z_stream;
@@ -183,5 +182,3 @@
   std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
 };
-
-#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 4221ee7..9eb7f2c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -20,7 +20,8 @@
 
 #define LOG_TAG "ziparchive"
 
-#include <assert.h>
+#include "ziparchive/zip_archive.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -33,6 +34,10 @@
 #include <memory>
 #include <vector>
 
+#if defined(__APPLE__)
+#define lseek64 lseek
+#endif
+
 #if defined(__BIONIC__)
 #include <android/fdsan.h>
 #endif
@@ -40,12 +45,10 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
+#include <android-base/mapped_file.h>
 #include <android-base/memory.h>
 #include <android-base/utf8.h>
 #include <log/log.h>
-#include <utils/Compat.h>
-#include <utils/FileMap.h>
-#include "ziparchive/zip_archive.h"
 #include "zlib.h"
 
 #include "entry_name_utils-inl.h"
@@ -58,12 +61,6 @@
 // specified in the local file header and the central directory.
 static const bool kCrcChecksEnabled = false;
 
-// This is for windows. If we don't open a file in binary mode, weird
-// things will happen.
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
 // The maximum number of bytes to scan backwards for the EOCD start.
 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
 
@@ -196,7 +193,7 @@
       close_file(assume_ownership),
       directory_offset(0),
       central_directory(),
-      directory_map(new android::FileMap()),
+      directory_map(),
       num_entries(0),
       hash_table_size(0),
       hash_table(nullptr) {
@@ -212,7 +209,7 @@
       close_file(false),
       directory_offset(0),
       central_directory(),
-      directory_map(new android::FileMap()),
+      directory_map(),
       num_entries(0),
       hash_table_size(0),
       hash_table(nullptr) {}
@@ -303,8 +300,7 @@
    * in archive.
    */
 
-  if (!archive->InitializeCentralDirectory(debug_file_name,
-                                           static_cast<off64_t>(eocd->cd_start_offset),
+  if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
                                            static_cast<size_t>(eocd->cd_size))) {
     ALOGE("Zip: failed to intialize central directory.\n");
     return kMmapFailed;
@@ -823,7 +819,7 @@
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (bytes_written_ + buf_size > size_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+      ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
             bytes_written_ + buf_size);
       return false;
     }
@@ -910,7 +906,7 @@
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (total_bytes_written_ + buf_size > declared_length_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
+      ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
             total_bytes_written_ + buf_size);
       return false;
     }
@@ -919,7 +915,7 @@
     if (result) {
       total_bytes_written_ += buf_size;
     } else {
-      ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+      ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
     }
 
     return result;
@@ -1048,7 +1044,7 @@
     }
   } while (zerr == Z_OK);
 
-  assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+  CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
 
   // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
   // "feature" of zlib to tell it there won't be a zlib file header. zlib
@@ -1255,16 +1251,14 @@
   length_ = cd_size;
 }
 
-bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
-                                            size_t cd_size) {
+bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
   if (mapped_zip.HasFd()) {
-    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
-                               cd_size, true /* read only */)) {
-      return false;
-    }
+    directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
+                                                      cd_start_offset, cd_size, PROT_READ);
+    if (!directory_map) return false;
 
-    CHECK_EQ(directory_map->getDataLength(), cd_size);
-    central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
+    CHECK_EQ(directory_map->size(), cd_size);
+    central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
   } else {
     if (mapped_zip.GetBasePtr() == nullptr) {
       ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 83cb11f..330a02a 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
 
 #include <stdint.h>
 #include <stdlib.h>
@@ -24,9 +25,8 @@
 #include <memory>
 #include <vector>
 
-#include <utils/FileMap.h>
-#include <ziparchive/zip_archive.h>
 #include "android-base/macros.h"
+#include "android-base/mapped_file.h"
 
 static const char* kErrorMessages[] = {
     "Success",
@@ -164,7 +164,7 @@
   // mapped central directory area
   off64_t directory_offset;
   CentralDirectory central_directory;
-  std::unique_ptr<android::FileMap> directory_map;
+  std::unique_ptr<android::base::MappedFile> directory_map;
 
   // number of entries in the Zip archive
   uint16_t num_entries;
@@ -180,8 +180,5 @@
   ZipArchive(void* address, size_t length);
   ~ZipArchive();
 
-  bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
-                                  size_t cd_size);
+  bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
 };
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 23dd5dc..0ea7d5d 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -27,10 +27,10 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/mapped_file.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
-#include <utils/FileMap.h>
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_archive_stream_entry.h>
 
@@ -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)
@@ -416,11 +416,10 @@
   ASSERT_EQ(0, fstat(fd, &sb));
 
   // Memory map the file first and open the archive from the memory region.
-  android::FileMap file_map;
-  file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
+  auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)};
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
-                                     zip_path.c_str(), &handle));
+  ASSERT_EQ(0,
+            OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
 
   // Assert one entry can be found and extracted correctly.
   std::string BINARY_PATH("META-INF/com/google/android/update-binary");
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index ed1d135..981df3a 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -26,8 +26,6 @@
 #include <vector>
 
 #include "android-base/logging.h"
-#include "utils/Compat.h"
-#include "utils/Log.h"
 
 #include "entry_name_utils-inl.h"
 #include "zip_archive_common.h"
@@ -303,10 +301,10 @@
 
   if (zerr != Z_OK) {
     if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+      LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
       return HandleError(kZlibError);
     } else {
-      ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+      LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
       return HandleError(kZlibError);
     }
   }
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index e8f51da..e72d159 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -31,6 +31,7 @@
     LMK_PROCPRIO,    /* Register a process and set its oom_adj_score */
     LMK_PROCREMOVE,  /* Unregister a process */
     LMK_PROCPURGE,   /* Purge all registered processes */
+    LMK_GETKILLCNT,  /* Get number of kills */
 };
 
 /*
@@ -152,6 +153,44 @@
     return sizeof(int);
 }
 
+/* LMK_GETKILLCNT packet payload */
+struct lmk_getkillcnt {
+    int min_oomadj;
+    int max_oomadj;
+};
+
+/*
+ * For LMK_GETKILLCNT packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
+                                   struct lmk_getkillcnt *params) {
+    params->min_oomadj = ntohl(packet[1]);
+    params->max_oomadj = ntohl(packet[2]);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
+                                       struct lmk_getkillcnt *params) {
+    packet[0] = htonl(LMK_GETKILLCNT);
+    packet[1] = htonl(params->min_oomadj);
+    packet[2] = htonl(params->max_oomadj);
+    return 3 * sizeof(int);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT reply packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
+    packet[0] = htonl(LMK_GETKILLCNT);
+    packet[1] = htonl(kill_cnt);
+    return 2 * sizeof(int);
+}
+
 __END_DECLS
 
 #endif /* _LMKD_H_ */
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 0a469e8..c9c9e8e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -313,7 +313,20 @@
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
 
 #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
-static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
+#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
+static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
+
+#define MAX_DISTINCT_OOM_ADJ 32
+#define KILLCNT_INVALID_IDX 0xFF
+/*
+ * Because killcnt array is sparse a two-level indirection is used
+ * to keep the size small. killcnt_idx stores index of the element in
+ * killcnt array. Index KILLCNT_INVALID_IDX indicates an unused slot.
+ */
+static uint8_t killcnt_idx[ADJTOSLOT_COUNT];
+static uint16_t killcnt[MAX_DISTINCT_OOM_ADJ];
+static int killcnt_free_idx = 0;
+static uint32_t killcnt_total = 0;
 
 /* PAGE_SIZE / 1024 */
 static long page_k;
@@ -644,6 +657,67 @@
     memset(&pidhash[0], 0, sizeof(pidhash));
 }
 
+static void inc_killcnt(int oomadj) {
+    int slot = ADJTOSLOT(oomadj);
+    uint8_t idx = killcnt_idx[slot];
+
+    if (idx == KILLCNT_INVALID_IDX) {
+        /* index is not assigned for this oomadj */
+        if (killcnt_free_idx < MAX_DISTINCT_OOM_ADJ) {
+            killcnt_idx[slot] = killcnt_free_idx;
+            killcnt[killcnt_free_idx] = 1;
+            killcnt_free_idx++;
+        } else {
+            ALOGW("Number of distinct oomadj levels exceeds %d",
+                MAX_DISTINCT_OOM_ADJ);
+        }
+    } else {
+        /*
+         * wraparound is highly unlikely and is detectable using total
+         * counter because it has to be equal to the sum of all counters
+         */
+        killcnt[idx]++;
+    }
+    /* increment total kill counter */
+    killcnt_total++;
+}
+
+static int get_killcnt(int min_oomadj, int max_oomadj) {
+    int slot;
+    int count = 0;
+
+    if (min_oomadj > max_oomadj)
+        return 0;
+
+    /* special case to get total kill count */
+    if (min_oomadj > OOM_SCORE_ADJ_MAX)
+        return killcnt_total;
+
+    while (min_oomadj <= max_oomadj &&
+           (slot = ADJTOSLOT(min_oomadj)) < ADJTOSLOT_COUNT) {
+        uint8_t idx = killcnt_idx[slot];
+        if (idx != KILLCNT_INVALID_IDX) {
+            count += killcnt[idx];
+        }
+        min_oomadj++;
+    }
+
+    return count;
+}
+
+static int cmd_getkillcnt(LMKD_CTRL_PACKET packet) {
+    struct lmk_getkillcnt params;
+
+    if (use_inkernel_interface) {
+        /* kernel driver does not expose this information */
+        return 0;
+    }
+
+    lmkd_pack_get_getkillcnt(packet, &params);
+
+    return get_killcnt(params.min_oomadj, params.max_oomadj);
+}
+
 static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
     struct lmk_target target;
@@ -748,12 +822,28 @@
     return ret;
 }
 
+static int ctrl_data_write(int dsock_idx, char *buf, size_t bufsz) {
+    int ret = 0;
+
+    ret = TEMP_FAILURE_RETRY(write(data_sock[dsock_idx].sock, buf, bufsz));
+
+    if (ret == -1) {
+        ALOGE("control data socket write failed; errno=%d", errno);
+    } else if (ret == 0) {
+        ALOGE("Got EOF on control data socket");
+        ret = -1;
+    }
+
+    return ret;
+}
+
 static void ctrl_command_handler(int dsock_idx) {
     LMKD_CTRL_PACKET packet;
     int len;
     enum lmk_cmd cmd;
     int nargs;
     int targets;
+    int kill_cnt;
 
     len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
     if (len <= 0)
@@ -791,6 +881,14 @@
             goto wronglen;
         cmd_procpurge();
         break;
+    case LMK_GETKILLCNT:
+        if (nargs != 2)
+            goto wronglen;
+        kill_cnt = cmd_getkillcnt(packet);
+        len = lmkd_pack_set_getkillcnt_repl(packet, kill_cnt);
+        if (ctrl_data_write(dsock_idx, (char *)packet, len) != len)
+            return;
+        break;
     default:
         ALOGE("Received unknown command code %d", cmd);
         return;
@@ -1162,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;
@@ -1200,11 +1300,14 @@
 
     /* CAP_KILL required */
     r = kill(pid, SIGKILL);
+    inc_killcnt(procp->oomadj);
     ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
         taskname, pid, uid, procp->oomadj, tasksize * page_k);
 
     TRACE_KILL_END();
 
+    last_killed_pid = pid;
+
     if (r) {
         ALOGE("kill(%d): errno=%d", pid, errno);
         goto out;
@@ -1232,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;
@@ -1264,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
@@ -1286,7 +1379,7 @@
     }
 #endif
 
-    return pages_freed;
+    return killed_size;
 }
 
 static int64_t get_memory_usage(struct reread_data *file_data) {
@@ -1346,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;
@@ -1360,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,
@@ -1391,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;
         }
@@ -1438,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;
     }
 
@@ -1496,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");
             }
@@ -1520,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 ")",
@@ -1534,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 */
@@ -1542,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) {
@@ -1700,6 +1806,8 @@
         procadjslot_list[i].prev = &procadjslot_list[i];
     }
 
+    memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
+
     return 0;
 }
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ea83ef9..025e3c3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -489,6 +489,8 @@
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
 
+    mkdir /data/preloads 0775 system system
+
     mkdir /data/vendor 0771 root root
     mkdir /data/vendor_ce 0771 root root
     mkdir /data/vendor_de 0771 root root