Merge "Call markBootAttempt when serice manager available"
diff --git a/adb/Android.bp b/adb/Android.bp
index a9ccc84..53f4404 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -24,7 +24,6 @@
         "-Wno-missing-field-initializers",
         "-Wvla",
     ],
-    cpp_std: "gnu++17",
     rtti: true,
 
     use_version_lib: true,
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 22f8e8d..07b5747 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1579,15 +1579,20 @@
         }
         return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
     }
+    // clang-format off
     else if (!strcmp(argv[0], "remount") ||
              !strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
+             !strcmp(argv[0], "reboot-fastboot") ||
              !strcmp(argv[0], "usb") ||
              !strcmp(argv[0], "disable-verity") ||
              !strcmp(argv[0], "enable-verity")) {
+        // clang-format on
         std::string command;
         if (!strcmp(argv[0], "reboot-bootloader")) {
             command = "reboot:bootloader";
+        } else if (!strcmp(argv[0], "reboot-fastboot")) {
+            command = "reboot:fastboot";
         } else if (argc > 1) {
             command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
         } else {
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index dfd9a0a..1534792 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -26,14 +26,10 @@
 #include <unistd.h>
 
 #include <algorithm>
-#include <map>
 #include <mutex>
 #include <string>
-#include <thread>
 #include <vector>
 
-#include <android-base/thread_annotations.h>
-
 #if !ADB_HOST
 #include <android-base/properties.h>
 #include <log/log_properties.h>
@@ -41,150 +37,9 @@
 
 #include "adb.h"
 #include "adb_io.h"
-#include "adb_utils.h"
-#include "sysdeps/chrono.h"
 #include "transport.h"
 #include "types.h"
 
-// The standard (RFC 1122 - 4.2.2.13) says that if we call close on a
-// socket while we have pending data, a TCP RST should be sent to the
-// other end to notify it that we didn't read all of its data. However,
-// this can result in data that we've successfully written out to be dropped
-// on the other end. To avoid this, instead of immediately closing a
-// socket, call shutdown on it instead, and then read from the file
-// descriptor until we hit EOF or an error before closing.
-struct LingeringSocketCloser {
-    LingeringSocketCloser() = default;
-    ~LingeringSocketCloser() = delete;
-
-    // Defer thread creation until it's needed, because we need for there to
-    // only be one thread when dropping privileges in adbd.
-    void Start() {
-        CHECK(!thread_.joinable());
-
-        int fds[2];
-        if (adb_socketpair(fds) != 0) {
-            PLOG(FATAL) << "adb_socketpair failed";
-        }
-
-        set_file_block_mode(fds[0], false);
-        set_file_block_mode(fds[1], false);
-
-        notify_fd_read_.reset(fds[0]);
-        notify_fd_write_.reset(fds[1]);
-
-        thread_ = std::thread([this]() { Run(); });
-    }
-
-    void EnqueueSocket(unique_fd socket) {
-        // Shutdown the socket in the outgoing direction only, so that
-        // we don't have the same problem on the opposite end.
-        adb_shutdown(socket.get(), SHUT_WR);
-        set_file_block_mode(socket.get(), false);
-
-        std::lock_guard<std::mutex> lock(mutex_);
-        int fd = socket.get();
-        SocketInfo info = {
-                .fd = std::move(socket),
-                .deadline = std::chrono::steady_clock::now() + 1s,
-        };
-
-        D("LingeringSocketCloser received fd %d", fd);
-
-        fds_.emplace(fd, std::move(info));
-        if (adb_write(notify_fd_write_, "", 1) == -1 && errno != EAGAIN) {
-            PLOG(FATAL) << "failed to write to LingeringSocketCloser notify fd";
-        }
-    }
-
-  private:
-    std::vector<adb_pollfd> GeneratePollFds() {
-        std::lock_guard<std::mutex> lock(mutex_);
-        std::vector<adb_pollfd> result;
-        result.push_back(adb_pollfd{.fd = notify_fd_read_, .events = POLLIN});
-        for (auto& [fd, _] : fds_) {
-            result.push_back(adb_pollfd{.fd = fd, .events = POLLIN});
-        }
-        return result;
-    }
-
-    void Run() {
-        while (true) {
-            std::vector<adb_pollfd> pfds = GeneratePollFds();
-            int rc = adb_poll(pfds.data(), pfds.size(), 1000);
-            if (rc == -1) {
-                PLOG(FATAL) << "poll failed in LingeringSocketCloser";
-            }
-
-            std::lock_guard<std::mutex> lock(mutex_);
-            if (rc == 0) {
-                // Check deadlines.
-                auto now = std::chrono::steady_clock::now();
-                for (auto it = fds_.begin(); it != fds_.end();) {
-                    if (now > it->second.deadline) {
-                        D("LingeringSocketCloser closing fd %d due to deadline", it->first);
-                        it = fds_.erase(it);
-                    } else {
-                        D("deadline still not expired for fd %d", it->first);
-                        ++it;
-                    }
-                }
-                continue;
-            }
-
-            for (auto& pfd : pfds) {
-                if ((pfd.revents & POLLIN) == 0) {
-                    continue;
-                }
-
-                // Empty the fd.
-                ssize_t rc;
-                char buf[32768];
-                while ((rc = adb_read(pfd.fd, buf, sizeof(buf))) > 0) {
-                    continue;
-                }
-
-                if (pfd.fd == notify_fd_read_) {
-                    continue;
-                }
-
-                auto it = fds_.find(pfd.fd);
-                if (it == fds_.end()) {
-                    LOG(FATAL) << "fd is missing";
-                }
-
-                if (rc == -1 && errno == EAGAIN) {
-                    if (std::chrono::steady_clock::now() > it->second.deadline) {
-                        D("LingeringSocketCloser closing fd %d due to deadline", pfd.fd);
-                    } else {
-                        continue;
-                    }
-                } else if (rc == -1) {
-                    D("LingeringSocketCloser closing fd %d due to error %d", pfd.fd, errno);
-                } else {
-                    D("LingeringSocketCloser closing fd %d due to EOF", pfd.fd);
-                }
-
-                fds_.erase(it);
-            }
-        }
-    }
-
-    std::thread thread_;
-    unique_fd notify_fd_read_;
-    unique_fd notify_fd_write_;
-
-    struct SocketInfo {
-        unique_fd fd;
-        std::chrono::steady_clock::time_point deadline;
-    };
-
-    std::mutex mutex_;
-    std::map<int, SocketInfo> fds_ GUARDED_BY(mutex_);
-};
-
-static auto& socket_closer = *new LingeringSocketCloser();
-
 static std::recursive_mutex& local_socket_list_lock = *new std::recursive_mutex();
 static unsigned local_socket_next_id = 1;
 
@@ -388,12 +243,10 @@
 
     D("LS(%d): destroying fde.fd=%d", s->id, s->fd);
 
-    // Defer thread creation until it's needed, because we need for there to
-    // only be one thread when dropping privileges in adbd.
-    static std::once_flag once;
-    std::call_once(once, []() { socket_closer.Start(); });
-
-    socket_closer.EnqueueSocket(fdevent_release(s->fde));
+    /* IMPORTANT: the remove closes the fd
+    ** that belongs to this socket
+    */
+    fdevent_destroy(s->fde);
 
     remove_socket(s);
     delete s;
diff --git a/adb/test_device.py b/adb/test_device.py
index 4c45a73..c3166ff 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -35,8 +35,6 @@
 import time
 import unittest
 
-from datetime import datetime
-
 import adb
 
 def requires_root(func):
@@ -1337,63 +1335,6 @@
             self.device.forward_remove("tcp:{}".format(local_port))
 
 
-class SocketTest(DeviceTest):
-    def test_socket_flush(self):
-        """Test that we handle socket closure properly.
-
-        If we're done writing to a socket, closing before the other end has
-        closed will send a TCP_RST if we have incoming data queued up, which
-        may result in data that we've written being discarded.
-
-        Bug: http://b/74616284
-        """
-        s = socket.create_connection(("localhost", 5037))
-
-        def adb_length_prefixed(string):
-            encoded = string.encode("utf8")
-            result = b"%04x%s" % (len(encoded), encoded)
-            return result
-
-        if "ANDROID_SERIAL" in os.environ:
-            transport_string = "host:transport:" + os.environ["ANDROID_SERIAL"]
-        else:
-            transport_string = "host:transport-any"
-
-        s.sendall(adb_length_prefixed(transport_string))
-        response = s.recv(4)
-        self.assertEquals(b"OKAY", response)
-
-        shell_string = "shell:sleep 0.5; dd if=/dev/zero bs=1m count=1 status=none; echo foo"
-        s.sendall(adb_length_prefixed(shell_string))
-
-        response = s.recv(4)
-        self.assertEquals(b"OKAY", response)
-
-        # Spawn a thread that dumps garbage into the socket until failure.
-        def spam():
-            buf = b"\0" * 16384
-            try:
-                while True:
-                    s.sendall(buf)
-            except Exception as ex:
-                print(ex)
-
-        thread = threading.Thread(target=spam)
-        thread.start()
-
-        time.sleep(1)
-
-        received = b""
-        while True:
-            read = s.recv(512)
-            if len(read) == 0:
-                break
-            received += read
-
-        self.assertEquals(1024 * 1024 + len("foo\n"), len(received))
-        thread.join()
-
-
 if sys.platform == "win32":
     # From https://stackoverflow.com/a/38749458
     import os
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index ff7a75c..4934f5a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -556,17 +556,11 @@
         return mounts;
     }
 
-    // Manually check dm state because stunted fstab (w/o system as root) borken
-    auto& dm = DeviceMapper::Instance();
-    auto found = false;
-    for (auto& system : {"system", "vroot"}) {
-        if (dm.GetState(system) == DmDeviceState::INVALID) continue;
-        std::vector<DeviceMapper::TargetInfo> table;
-        found = !dm.GetTableStatus(system, &table) || table.empty() || table[0].data.empty() ||
-                (table[0].data[0] == 'C') || (table[0].data[0] == 'V');
-        if (found) break;
-    }
-    if (!found) mounts.emplace_back("/system");
+    // We have a stunted fstab (w/o system or / ) passed in by the caller,
+    // verity claims are assumed accurate because they are collected internally
+    // from fs_mgr_fstab_default() from within fs_mgr_update_verity_state(),
+    // Can (re)evaluate /system with impunity since we know it is ever-present.
+    mounts.emplace_back("/system");
     return mounts;
 }
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 69dc065..bbdec5b 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -51,6 +51,7 @@
         "liblp",
         "libbase",
         "libfs_mgr",
+        "libsparse",
     ],
     srcs: [
         "builder_test.cpp",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 97b15bd..743a3fe 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -150,7 +150,7 @@
     }
     BlockDeviceInfo device_info;
     if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
-        builder->set_block_device_info(device_info);
+        builder->UpdateBlockDeviceInfo(device_info);
     }
     return builder;
 }
@@ -217,10 +217,6 @@
             }
         }
     }
-
-    device_info_.alignment = geometry_.alignment;
-    device_info_.alignment_offset = geometry_.alignment_offset;
-    device_info_.logical_block_size = geometry_.logical_block_size;
     return true;
 }
 
@@ -239,24 +235,23 @@
     metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
 
     // Check that device properties are sane.
-    device_info_ = device_info;
-    if (device_info_.size % LP_SECTOR_SIZE != 0) {
+    if (device_info.size % LP_SECTOR_SIZE != 0) {
         LERROR << "Block device size must be a multiple of 512.";
         return false;
     }
-    if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) {
+    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) {
+    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) {
+    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) {
+    if (device_info.alignment_offset > device_info.alignment) {
         LERROR << "Partition alignment offset is greater than its alignment.";
         return false;
     }
@@ -267,20 +262,21 @@
     uint64_t reserved =
             LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
     uint64_t total_reserved = reserved * 2;
-    if (device_info_.size < total_reserved) {
+    if (device_info.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 = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
+    uint64_t free_area =
+            AlignTo(total_reserved, device_info.alignment, device_info.alignment_offset);
     uint64_t first_sector = free_area / LP_SECTOR_SIZE;
 
     // Compute the last free sector, which is inclusive. We subtract 1 to make
     // sure that logical partitions won't overlap with the same sector as the
     // backup metadata, which could happen if the block device was not aligned
     // to LP_SECTOR_SIZE.
-    uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
+    uint64_t last_sector = (device_info.size / LP_SECTOR_SIZE) - 1;
 
     // If this check fails, it means either (1) we did not have free space to
     // allocate a single sector, or (2) we did, but the alignment was high
@@ -296,7 +292,7 @@
     // computation, then we abort. Note that the last sector is inclusive,
     // so we have to account for that.
     uint64_t num_free_sectors = last_sector - first_sector + 1;
-    uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    uint64_t sectors_per_block = device_info.logical_block_size / LP_SECTOR_SIZE;
     if (num_free_sectors < sectors_per_block) {
         LERROR << "Not enough space to allocate any partition tables.";
         return false;
@@ -307,9 +303,9 @@
     geometry_.last_logical_sector = last_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_.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;
 
     if (!AddGroup("default", 0)) {
@@ -460,7 +456,7 @@
         free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1);
     }
 
-    const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
     CHECK_NE(sectors_per_block, 0);
     CHECK(sectors_needed % sectors_per_block == 0);
 
@@ -578,32 +574,44 @@
     // 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, device_info_.alignment, device_info_.alignment_offset);
+    uint64_t aligned = AlignTo(lba, geometry_.alignment, geometry_.alignment_offset);
     return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
 }
 
-void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
-    device_info_.size = device_info.size;
+bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const {
+    info->size = geometry_.block_device_size;
+    info->alignment = geometry_.alignment;
+    info->alignment_offset = geometry_.alignment_offset;
+    info->logical_block_size = geometry_.logical_block_size;
+    return true;
+}
 
-    // Note that if the logical block size changes, we're probably in trouble:
-    // we could have already built extents that will only work on the previous
-    // size.
-    DCHECK(partitions_.empty() ||
-           device_info_.logical_block_size == device_info.logical_block_size);
+bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) {
+    if (device_info.size != geometry_.block_device_size) {
+        LERROR << "Device size does not match (got " << device_info.size << ", expected "
+               << geometry_.block_device_size << ")";
+        return false;
+    }
+    if (device_info.logical_block_size != geometry_.logical_block_size) {
+        LERROR << "Device logical block size does not match (got " << device_info.logical_block_size
+               << ", expected " << geometry_.logical_block_size << ")";
+        return false;
+    }
 
     // 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) {
-        device_info_.alignment = device_info.alignment;
+        geometry_.alignment = device_info.alignment;
     }
     if (device_info.alignment_offset) {
-        device_info_.alignment_offset = device_info.alignment_offset;
+        geometry_.alignment_offset = device_info.alignment_offset;
     }
+    return true;
 }
 
 bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
     // Align the space needed up to the nearest sector.
-    uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size);
+    uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
     uint64_t old_size = partition->size();
 
     if (aligned_size > old_size) {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index c916b44..ffa7d3b 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -48,8 +48,8 @@
     LinearExtent* extent = system->extents()[0]->AsLinearExtent();
     ASSERT_NE(extent, nullptr);
     EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    // The first logical sector will be (4096+1024*2)/512 = 12.
-    EXPECT_EQ(extent->physical_sector(), 12);
+    // The first logical sector will be (8192+1024*4)/512 = 12.
+    EXPECT_EQ(extent->physical_sector(), 24);
 
     // Test resizing to the same size.
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -78,7 +78,7 @@
     extent = system->extents()[0]->AsLinearExtent();
     ASSERT_NE(extent, nullptr);
     EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(extent->physical_sector(), 12);
+    EXPECT_EQ(extent->physical_sector(), 24);
 
     // Test shrinking to 0.
     builder->ResizePartition(system, 0);
@@ -127,7 +127,7 @@
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2047);
 
     // Test a large alignment offset thrown in.
     device_info.alignment_offset = 753664;
@@ -136,7 +136,7 @@
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2047);
 
     // Alignment offset without alignment doesn't mean anything.
     device_info.alignment = 0;
@@ -150,8 +150,8 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 78);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 1973);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 150);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2045);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -159,8 +159,8 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 72);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 160);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2047);
 }
 
 TEST(liblp, InternalPartitionAlignment) {
@@ -247,11 +247,11 @@
     ASSERT_NE(system2, nullptr);
     ASSERT_NE(vendor1, nullptr);
     EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system1->physical_sector(), 24);
     EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(system2->physical_sector(), 216);
     EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(vendor1->physical_sector(), 140);
+    EXPECT_EQ(vendor1->physical_sector(), 152);
     EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
     EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
 }
@@ -297,13 +297,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, 12);
-    EXPECT_EQ(geometry.last_logical_sector, 2035);
+    EXPECT_EQ(geometry.first_logical_sector, 24);
+    EXPECT_EQ(geometry.last_logical_sector, 2047);
 
     static const size_t kMetadataSpace =
-            (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
-    uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
-    EXPECT_GE(space_at_end, kMetadataSpace);
+            ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
     EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
 
     // Verify header.
@@ -361,9 +359,9 @@
     LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
     LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
     EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system1->physical_sector(), 24);
     EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(system2->physical_sector(), 216);
     EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
 }
 
@@ -437,22 +435,37 @@
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
-    EXPECT_EQ(builder->block_device_info().size, device_info.size);
-    EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
-    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
-    EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size);
+    BlockDeviceInfo new_info;
+    ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+
+    EXPECT_EQ(new_info.size, device_info.size);
+    EXPECT_EQ(new_info.alignment, device_info.alignment);
+    EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
+    EXPECT_EQ(new_info.logical_block_size, device_info.logical_block_size);
 
     device_info.alignment = 0;
     device_info.alignment_offset = 2048;
-    builder->set_block_device_info(device_info);
-    EXPECT_EQ(builder->block_device_info().alignment, 4096);
-    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo(&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;
-    builder->set_block_device_info(device_info);
-    EXPECT_EQ(builder->block_device_info().alignment, 8192);
-    EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo(&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));
+    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));
+    EXPECT_EQ(new_info.logical_block_size, 4096);
 }
 
 TEST(liblp, InvalidBlockSize) {
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 742b1d0..8716988 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -19,8 +19,6 @@
 #include <limits.h>
 
 #include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
 
 #include "reader.h"
 #include "utility.h"
@@ -89,41 +87,36 @@
     return WriteToImageFile(fd, input);
 }
 
-// We use an object to build the sparse file since it requires that data
-// pointers be held alive until the sparse file is destroyed. It's easier
-// to do this when the data pointers are all in one place.
-class SparseBuilder {
-  public:
-    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
-                  const std::map<std::string, std::string>& images);
-
-    bool Build();
-    bool Export(const char* file);
-    bool IsValid() const { return file_ != nullptr; }
-
-  private:
-    bool AddData(const std::string& blob, uint64_t sector);
-    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
-    int OpenImageFile(const std::string& file);
-    bool SectorToBlock(uint64_t sector, uint32_t* block);
-
-    const LpMetadata& metadata_;
-    const LpMetadataGeometry& geometry_;
-    uint32_t block_size_;
-    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
-    std::string primary_blob_;
-    std::string backup_blob_;
-    std::map<std::string, std::string> images_;
-    std::vector<android::base::unique_fd> temp_fds_;
-};
-
 SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
                              const std::map<std::string, std::string>& images)
     : metadata_(metadata),
       geometry_(metadata.geometry),
       block_size_(block_size),
-      file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy),
-      images_(images) {}
+      file_(nullptr, sparse_file_destroy),
+      images_(images) {
+    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) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return;
+    }
+    if (metadata.geometry.metadata_max_size % block_size != 0) {
+        LERROR << "Metadata max size must be a multiple of the block size, " << block_size;
+        return;
+    }
+
+    uint64_t num_blocks = metadata.geometry.block_device_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.
+        LERROR << "Block device is too large to encode with libsparse.";
+        return;
+    }
+
+    file_.reset(sparse_file_new(block_size_, geometry_.block_device_size));
+}
 
 bool SparseBuilder::Export(const char* file) {
     android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
@@ -174,16 +167,12 @@
     std::string metadata_blob = SerializeMetadata(metadata_);
     metadata_blob.resize(geometry_.metadata_max_size);
 
-    std::string all_metadata;
-    for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
-        all_metadata += metadata_blob;
+    // Two copies of geometry, then two copies of each metadata slot.
+    all_metadata_ += geometry_blob + geometry_blob;
+    for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
+        all_metadata_ += metadata_blob;
     }
-
-    // Metadata immediately follows geometry, and we write the same metadata
-    // to all slots. Note that we don't bother trying to write skip chunks
-    // here since it's a small amount of data.
-    primary_blob_ = geometry_blob + all_metadata;
-    if (!AddData(primary_blob_, 0)) {
+    if (!AddData(all_metadata_, 0)) {
         return false;
     }
 
@@ -202,17 +191,6 @@
         LERROR << "Partition image was specified but no partition was found.";
         return false;
     }
-
-    // The backup area contains all metadata slots, and then geometry. Similar
-    // to before we write the metadata to every slot.
-    int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0);
-    uint64_t backups_start = geometry_.block_device_size + backup_offset;
-    uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
-
-    backup_blob_ = all_metadata + geometry_blob;
-    if (!AddData(backup_blob_, backup_sector)) {
-        return false;
-    }
     return true;
 }
 
@@ -336,22 +314,6 @@
 
 bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
                        const std::map<std::string, std::string>& images) {
-    if (block_size % LP_SECTOR_SIZE != 0) {
-        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
-        return false;
-    }
-    if (metadata.geometry.block_device_size % block_size != 0) {
-        LERROR << "Device size must be a multiple of the block size, " << block_size;
-        return false;
-    }
-    uint64_t num_blocks = metadata.geometry.block_device_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.
-        LERROR << "Block device is too large to encode with libsparse.";
-        return false;
-    }
-
     SparseBuilder builder(metadata, block_size, images);
     if (!builder.IsValid()) {
         LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 3a999b8..a9ef8ce 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -14,7 +14,14 @@
  * limitations under the License.
  */
 
+#include <stdint.h>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
 #include <liblp/liblp.h>
+#include <sparse/sparse.h>
 
 namespace android {
 namespace fs_mgr {
@@ -25,5 +32,34 @@
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 bool WriteToImageFile(int fd, const LpMetadata& metadata);
 
+// We use an object to build the sparse file since it requires that data
+// pointers be held alive until the sparse file is destroyed. It's easier
+// to do this when the data pointers are all in one place.
+class SparseBuilder {
+  public:
+    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                  const std::map<std::string, std::string>& images);
+
+    bool Build();
+    bool Export(const char* file);
+    bool IsValid() const { return file_ != nullptr; }
+
+    sparse_file* file() const { return file_.get(); }
+
+  private:
+    bool AddData(const std::string& blob, uint64_t sector);
+    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
+    int OpenImageFile(const std::string& file);
+    bool SectorToBlock(uint64_t sector, uint32_t* block);
+
+    const LpMetadata& metadata_;
+    const LpMetadataGeometry& geometry_;
+    uint32_t block_size_;
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+    std::string all_metadata_;
+    std::map<std::string, std::string> images_;
+    std::vector<android::base::unique_fd> temp_fds_;
+};
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a6044d0..8dbba84 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -215,10 +215,8 @@
     uint64_t AllocatableSpace() const;
     uint64_t UsedSpace() const;
 
-    // Merge new block device information into previous values. Alignment values
-    // are only overwritten if the new values are non-zero.
-    void set_block_device_info(const BlockDeviceInfo& device_info);
-    const BlockDeviceInfo& block_device_info() const { return device_info_; }
+    bool GetBlockDeviceInfo(BlockDeviceInfo* info) const;
+    bool UpdateBlockDeviceInfo(const BlockDeviceInfo& info);
 
   private:
     MetadataBuilder();
@@ -238,7 +236,6 @@
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
     std::vector<std::unique_ptr<PartitionGroup>> groups_;
-    BlockDeviceInfo device_info_;
 };
 
 // Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 7d1a2a9..a4ff1a7 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 3
+#define LP_METADATA_MAJOR_VERSION 4
 #define LP_METADATA_MINOR_VERSION 0
 
 /* Attributes for the LpMetadataPartition::attributes field.
@@ -58,13 +58,13 @@
  *     +--------------------+
  *     | Disk Geometry      |
  *     +--------------------+
- *     | Metadata           |
+ *     | Geometry Backup    |
  *     +--------------------+
- *     | Logical Partitions |
+ *     | Metadata           |
  *     +--------------------+
  *     | Backup Metadata    |
  *     +--------------------+
- *     | Geometry Backup    |
+ *     | Logical Partitions |
  *     +--------------------+
  */
 #define LP_METADATA_DEFAULT_PARTITION_NAME "super"
@@ -72,8 +72,8 @@
 /* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
 #define LP_SECTOR_SIZE 512
 
-/* This structure is stored at sector 0 in the first 4096 bytes of the
- * partition, and again in the very last 4096 bytes. It is never modified and
+/* This structure is stored at block 0 in the first 4096 bytes of the
+ * partition, and again in the following block. It is never modified and
  * describes how logical partition information can be located.
  */
 typedef struct LpMetadataGeometry {
@@ -99,8 +99,8 @@
     uint32_t metadata_slot_count;
 
     /* 48: First usable sector for allocating logical partitions. this will be
-     * the first sector after the initial 4096 geometry block, followed by the
-     * space consumed by metadata_max_size*metadata_slot_count.
+     * 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;
 
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 01de3ac..220d651 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -290,13 +290,13 @@
     char corruption[LP_METADATA_GEOMETRY_SIZE];
     memset(corruption, 0xff, sizeof(corruption));
 
-    // Corrupt the first 4096 bytes of the disk.
-    ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+    // 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);
 
-    // Corrupt the last 4096 bytes too.
-    ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+    // 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);
 }
@@ -310,14 +310,16 @@
     char corruption[kMetadataSize];
     memset(corruption, 0xff, sizeof(corruption));
 
-    ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
+    off_t offset = GetPrimaryMetadataOffset(metadata->geometry, 0);
+
+    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
     EXPECT_NE(ReadMetadata(fd, 0), nullptr);
 
-    off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+    offset = GetBackupMetadataOffset(metadata->geometry, 0);
 
     // Corrupt the backup metadata.
-    ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
     EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
 }
@@ -535,3 +537,36 @@
     ASSERT_GE(new_table->partitions.size(), 1);
     ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
 }
+
+// Test that writing a sparse image can be read back.
+TEST(liblp, FlashSparseImage) {
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    BlockDeviceInfo device_info(kDiskSize, 0, 0, 512);
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Build the sparse file.
+    SparseBuilder sparse(*exported.get(), 512, {});
+    ASSERT_TRUE(sparse.IsValid());
+    sparse_file_verbose(sparse.file());
+    ASSERT_TRUE(sparse.Build());
+
+    // Write it to the fake disk.
+    ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);
+    int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false);
+    ASSERT_EQ(ret, 0);
+
+    // Verify that we can read both sets of metadata.
+    LpMetadataGeometry geometry;
+    ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry));
+    ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry));
+    ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);
+    ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 005d493..835df9b 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -120,13 +120,9 @@
     return true;
 }
 
-// Read and validate geometry information from a block device that holds
-// logical partitions. If the information is corrupted, this will attempt
-// to read it from a secondary backup location.
-bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
-    // Read the first 4096 bytes.
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
-    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed";
         return false;
     }
@@ -134,12 +130,12 @@
         PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
         return false;
     }
-    if (ParseGeometry(buffer.get(), geometry)) {
-        return true;
-    }
+    return ParseGeometry(buffer.get(), geometry);
+}
 
-    // Try the backup copy in the last 4096 bytes.
-    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
         return false;
     }
@@ -151,6 +147,16 @@
     return ParseGeometry(buffer.get(), geometry);
 }
 
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+    if (ReadPrimaryGeometry(fd, geometry)) {
+        return true;
+    }
+    return ReadBackupGeometry(fd, geometry);
+}
+
 static bool ValidateTableBounds(const LpMetadataHeader& header,
                                 const LpMetadataTableDescriptor& table) {
     if (table.offset > header.tables_size) {
@@ -315,7 +321,7 @@
 std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
                                                uint32_t slot_number) {
     int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
-    if (SeekFile64(fd, offset, SEEK_END) < 0) {
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
         return nullptr;
     }
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 9f6ca6e..24b2611 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -36,6 +36,8 @@
 std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
                                           size_t size);
 bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
 
 // These functions assume a valid geometry and slot number.
 std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index b08f96c..2f7692f 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -56,10 +56,18 @@
     return lseek(fd, offset, whence);
 }
 
+int64_t GetPrimaryGeometryOffset() {
+    return 0;
+}
+
+int64_t GetBackupGeometryOffset() {
+    return LP_METADATA_GEOMETRY_SIZE;
+}
+
 int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
     CHECK(slot_number < geometry.metadata_slot_count);
 
-    int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
+    int64_t offset = (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;
@@ -67,7 +75,7 @@
 
 int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
     CHECK(slot_number < geometry.metadata_slot_count);
-    int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+    int64_t start = LP_METADATA_GEOMETRY_SIZE * 2 +
                     int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
     return start + int64_t(geometry.metadata_max_size * slot_number);
 }
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 6ef5124..61e7d31 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -38,6 +38,10 @@
 // error. After calling this, the position of |fd| may have changed.
 bool GetDescriptorSize(int fd, uint64_t* size);
 
+// Return the offset of the primary or backup geometry.
+int64_t GetPrimaryGeometryOffset();
+int64_t GetBackupGeometryOffset();
+
 // Return the offset of a primary metadata slot, relative to the start of the
 // device.
 int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 7bf42ae..ff50e09 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -42,15 +42,17 @@
                                    0,
                                    1024 * 1024,
                                    4096};
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 8192);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 8192 + 16384);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 8192 + 16384 * 2);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 8192 + 16384 * 3);
 
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+    static const uint64_t backup_start = 8192 + 16384 * 4;
+
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), backup_start + 16384 * 3);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), backup_start + 16384 * 2);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), backup_start + 16384 * 1);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
 }
 
 TEST(liblp, AlignTo) {
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 2415629..0c0cc49 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -106,15 +106,13 @@
     // metadata.
     uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
                              uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
-    if (reserved_size > blockdevice_size ||
-        reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+    uint64_t total_reserved = reserved_size * 2;
+
+    if (total_reserved > blockdevice_size ||
+        total_reserved > geometry.first_logical_sector * LP_SECTOR_SIZE) {
         LERROR << "Not enough space to store all logical partition metadata slots.";
         return false;
     }
-    if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
-        LERROR << "Not enough space to backup all logical partition metadata slots.";
-        return false;
-    }
     if (blockdevice_size != metadata.geometry.block_device_size) {
         LERROR << "Block device size " << blockdevice_size
                << " does not match metadata requested size " << metadata.geometry.block_device_size;
@@ -162,12 +160,12 @@
                                 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_END);
+    int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_SET);
     if (abs_offset == (int64_t)-1) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
         return false;
     }
-    if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
+    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.last_logical_sector;
         return false;
@@ -211,7 +209,7 @@
 
     // Write geometry to the first and last 4096 bytes of the device.
     std::string blob = SerializeGeometry(metadata.geometry);
-    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
         return false;
     }
@@ -219,7 +217,7 @@
         PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
         return false;
     }
-    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
         return false;
     }
diff --git a/init/README.md b/init/README.md
index 02a65d5..c56d295 100644
--- a/init/README.md
+++ b/init/README.md
@@ -213,7 +213,8 @@
 `interface <interface name> <instance name>`
 > Associates this service with a list of the HIDL services that it provides. The interface name
   must be a fully-qualified name and not a value name. This is used to allow hwservicemanager to
-  lazily start services.
+  lazily start services. When multiple interfaces are served, this tag should be used multiple
+  times.
   For example: interface vendor.foo.bar@1.0::IBaz default
 
 `ioprio <class> <priority>`
diff --git a/init/stable_properties.h b/init/stable_properties.h
index 4972d10..baef833 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -31,6 +31,7 @@
 static const std::set<std::string> kExportedActionableProperties = {
     "dev.bootcomplete",
     "init.svc.console",
+    "init.svc.dumpstatez",
     "init.svc.mediadrm",
     "init.svc.surfaceflinger",
     "init.svc.zygote",
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4723606..56a0679 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -194,26 +194,26 @@
   return true;
 }
 
-void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+bool Elf::GetInfo(Memory* memory, uint64_t* size) {
   if (!IsValidElf(memory)) {
-    *valid = false;
-    return;
+    return false;
   }
   *size = 0;
-  *valid = true;
 
-  // Now read the section header information.
   uint8_t class_type;
   if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
-    return;
+    return false;
   }
+
+  // Get the maximum size of the elf data from the header.
   if (class_type == ELFCLASS32) {
     ElfInterface32::GetMaxSize(memory, size);
   } else if (class_type == ELFCLASS64) {
     ElfInterface64::GetMaxSize(memory, size);
   } else {
-    *valid = false;
+    return false;
   }
+  return true;
 }
 
 bool Elf::IsValidPc(uint64_t pc) {
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 64005ae..52b7535 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -53,10 +53,8 @@
     return nullptr;
   }
 
-  bool valid;
   uint64_t max_size;
-  Elf::GetInfo(memory.get(), &valid, &max_size);
-  if (!valid) {
+  if (!Elf::GetInfo(memory.get(), &max_size)) {
     // Init as if the whole file is an elf.
     if (memory->Init(name, 0)) {
       elf_offset = offset;
@@ -109,11 +107,7 @@
   // first part of the elf file. This is done if the linker rosegment
   // option is used.
   std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
-  bool valid;
-  uint64_t max_size;
-  Elf::GetInfo(memory.get(), &valid, &max_size);
-  if (valid) {
-    // Valid elf, we are done.
+  if (Elf::IsValidElf(memory.get())) {
     return memory.release();
   }
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 099cc9e..ee1cd1a 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -190,6 +190,12 @@
         FillInDexFrame();
         // Clear the dex pc so that we don't repeat this frame later.
         regs_->set_dex_pc(0);
+
+        // Make sure there is enough room for the real frame.
+        if (frames_.size() == max_frames_) {
+          last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+          break;
+        }
       }
 
       FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index f4cdbda..9af859d 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -94,7 +94,7 @@
 
   static bool IsValidElf(Memory* memory);
 
-  static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+  static bool GetInfo(Memory* memory, uint64_t* size);
 
   static uint64_t GetLoadBias(Memory* memory);
 
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 4369030..831d3b5 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -946,6 +946,33 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, dex_pc_max_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+
+  Unwinder unwinder(1, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xa3400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa3000U, frame->map_start);
+  EXPECT_EQ(0xa4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
 // Verify format frame code.
 TEST_F(UnwinderTest, format_frame_static) {
   FrameData frame;
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 018b1a9..32d7901 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -103,7 +103,8 @@
   off64_t offset;
 };
 
-typedef void* ZipArchiveHandle;
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
 
 /*
  * Open a Zip archive, and sets handle to the value of the opaque
@@ -144,7 +145,7 @@
  * this handle for any further operations without an intervening
  * call to one of the OpenArchive variants.
  */
-void CloseArchive(ZipArchiveHandle handle);
+void CloseArchive(ZipArchiveHandle archive);
 
 /*
  * Find an entry in the Zip archive, by name. |entryName| must be a null
@@ -162,7 +163,7 @@
  * On non-Windows platforms this method does not modify internal state and
  * can be called concurrently.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
 
 /*
  * Start iterating over all entries of a zip file. The order of iteration
@@ -177,8 +178,8 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
-                       const ZipString* optional_suffix);
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+                       const ZipString* optional_prefix, const ZipString* optional_suffix);
 
 /*
  * Advance to the next element in the zipfile in iteration order.
@@ -203,7 +204,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd);
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd);
 
 /**
  * Uncompress a given zip entry to the memory region at |begin| and of
@@ -213,9 +214,9 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size);
 
-int GetFileDescriptor(const ZipArchiveHandle handle);
+int GetFileDescriptor(const ZipArchiveHandle archive);
 
 const char* ErrorCodeString(int32_t error_code);
 
@@ -226,7 +227,7 @@
  * Stream the uncompressed data through the supplied function,
  * passing cookie to it each time it gets called.
  */
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
                                 ProcessZipEntryFunction func, void* cookie);
 #endif
 
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 6a3db6b..4221ee7 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -517,8 +517,7 @@
 /*
  * Close a ZipArchive, closing the file and freeing the contents.
  */
-void CloseArchive(ZipArchiveHandle handle) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+void CloseArchive(ZipArchiveHandle archive) {
   ALOGV("Closing archive %p", archive);
   delete archive;
 }
@@ -745,10 +744,8 @@
   }
 };
 
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
-                       const ZipString* optional_suffix) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
-
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+                       const ZipString* optional_prefix, const ZipString* optional_suffix) {
   if (archive == NULL || archive->hash_table == NULL) {
     ALOGW("Zip: Invalid ZipArchiveHandle");
     return kInvalidHandle;
@@ -766,8 +763,7 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
-  const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
   if (entryName.name_length == 0) {
     ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
     return kInvalidEntryName;
@@ -1116,8 +1112,7 @@
   return 0;
 }
 
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) {
   const uint16_t method = entry->method;
 
   // this should default to kUnknownCompressionMethod.
@@ -1145,18 +1140,18 @@
   return return_value;
 }
 
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) {
   MemoryWriter writer(begin, size);
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) {
   auto writer = FileWriter::Create(fd, entry);
   if (!writer.IsValid()) {
     return kIoError;
   }
 
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
 const char* ErrorCodeString(int32_t error_code) {
@@ -1173,8 +1168,8 @@
   return "Unknown return code";
 }
 
-int GetFileDescriptor(const ZipArchiveHandle handle) {
-  return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
+int GetFileDescriptor(const ZipArchiveHandle archive) {
+  return archive->mapped_zip.GetFileDescriptor();
 }
 
 ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
@@ -1198,10 +1193,10 @@
   void* cookie_;
 };
 
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
                                 ProcessZipEntryFunction func, void* cookie) {
   ProcessWriter writer(func, cookie);
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
 #endif  //! defined(_WIN32)
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 1e2df2f..4d39dd9 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -57,7 +57,7 @@
 #define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
 #define LLK_BLACKLIST_UID_DEFAULT      ""
 #define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
-#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,/system/bin/keystore,ueventd"
+#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,/system/bin/keystore,ueventd,apexd"
 /* clang-format on */
 
 __END_DECLS
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index fe6364d..e8f51da 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -30,6 +30,7 @@
     LMK_TARGET = 0,  /* Associate minfree with oom_adj_score */
     LMK_PROCPRIO,    /* Register a process and set its oom_adj_score */
     LMK_PROCREMOVE,  /* Unregister a process */
+    LMK_PROCPURGE,   /* Purge all registered processes */
 };
 
 /*
@@ -142,6 +143,15 @@
     return 2 * sizeof(int);
 }
 
+/*
+ * Prepare LMK_PROCPURGE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+    packet[0] = htonl(LMK_PROCPURGE);
+    return sizeof(int);
+}
+
 __END_DECLS
 
 #endif /* _LMKD_H_ */
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 8b68dca..3a3d5db 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -612,9 +612,38 @@
     }
 
     lmkd_pack_get_procremove(packet, &params);
+    /*
+     * WARNING: After pid_remove() procp is freed and can't be used!
+     * Therefore placed at the end of the function.
+     */
     pid_remove(params.pid);
 }
 
+static void cmd_procpurge() {
+    int i;
+    struct proc *procp;
+    struct proc *next;
+
+    if (use_inkernel_interface) {
+        return;
+    }
+
+    for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
+        procadjslot_list[i].next = &procadjslot_list[i];
+        procadjslot_list[i].prev = &procadjslot_list[i];
+    }
+
+    for (i = 0; i < PIDHASH_SZ; i++) {
+        procp = pidhash[i];
+        while (procp) {
+            next = procp->pidhash_next;
+            free(procp);
+            procp = next;
+        }
+    }
+    memset(&pidhash[0], 0, sizeof(pidhash));
+}
+
 static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
     struct lmk_target target;
@@ -757,6 +786,11 @@
             goto wronglen;
         cmd_procremove(packet);
         break;
+    case LMK_PROCPURGE:
+        if (nargs != 0)
+            goto wronglen;
+        cmd_procpurge();
+        break;
     default:
         ALOGE("Received unknown command code %d", cmd);
         return;
@@ -1135,6 +1169,7 @@
     char *taskname;
     int tasksize;
     int r;
+    int result = -1;
 
 #ifdef LMKD_LOG_STATS
     struct memory_stat mem_st = {};
@@ -1143,14 +1178,12 @@
 
     taskname = proc_get_name(pid);
     if (!taskname) {
-        pid_remove(pid);
-        return -1;
+        goto out;
     }
 
     tasksize = proc_get_size(pid);
     if (tasksize <= 0) {
-        pid_remove(pid);
-        return -1;
+        goto out;
     }
 
 #ifdef LMKD_LOG_STATS
@@ -1169,13 +1202,12 @@
     r = kill(pid, SIGKILL);
     ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
         taskname, pid, uid, procp->oomadj, tasksize * page_k);
-    pid_remove(pid);
 
     TRACE_KILL_END();
 
     if (r) {
         ALOGE("kill(%d): errno=%d", pid, errno);
-        return -1;
+        goto out;
     } else {
 #ifdef LMKD_LOG_STATS
         if (memory_stat_parse_result == 0) {
@@ -1187,10 +1219,16 @@
                                           -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1);
         }
 #endif
-        return tasksize;
+        result = tasksize;
     }
 
-    return tasksize;
+out:
+    /*
+     * WARNING: After pid_remove() procp is freed and can't be used!
+     * Therefore placed at the end of the function.
+     */
+    pid_remove(pid);
+    return result;
 }
 
 /*
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index 658e079..bd17555 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -42,7 +42,7 @@
     LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
-        entry = (*it);
+        entry = it->get();
         if (entry->mClient == client) {
             if (!entry->isWatchingMultiple(mLogMask)) {
                 LogTimeEntry::unlock();
@@ -63,31 +63,12 @@
                 }
             }
             entry->triggerReader_Locked();
-            if (entry->runningReader_Locked()) {
-                LogTimeEntry::unlock();
-                return;
-            }
-            entry->incRef_Locked();
-            break;
+            LogTimeEntry::unlock();
+            return;
         }
         it++;
     }
 
-    if (it == times.end()) {
-        // Create LogTimeEntry in notifyNewLog() ?
-        if (mTail == (unsigned long)-1) {
-            LogTimeEntry::unlock();
-            return;
-        }
-        entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask,
-                                 mPid, mStart, mTimeout);
-        times.push_front(entry);
-    }
-
-    client->incRef();
-
-    // release client and entry reference counts once done
-    entry->startReader_Locked();
     LogTimeEntry::unlock();
 }
 
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 543dfc3..ceaf393 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -27,36 +27,11 @@
 
 class FlushCommand : public SocketClientCommand {
     LogReader& mReader;
-    bool mNonBlock;
-    unsigned long mTail;
     log_mask_t mLogMask;
-    pid_t mPid;
-    log_time mStart;
-    uint64_t mTimeout;
 
    public:
-    // for opening a reader
-    explicit FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
-                          log_mask_t logMask, pid_t pid, log_time start,
-                          uint64_t timeout)
-        : mReader(reader),
-          mNonBlock(nonBlock),
-          mTail(tail),
-          mLogMask(logMask),
-          mPid(pid),
-          mStart(start),
-          mTimeout((start != log_time::EPOCH) ? timeout : 0) {
-    }
-
-    // for notification of an update
     explicit FlushCommand(LogReader& reader, log_mask_t logMask)
-        : mReader(reader),
-          mNonBlock(false),
-          mTail(-1),
-          mLogMask(logMask),
-          mPid(0),
-          mStart(log_time::EPOCH),
-          mTimeout(0) {
+        : mReader(reader), mLogMask(logMask) {
     }
 
     virtual void runSocketCommand(SocketClient* client);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index fd1b8b2..fbdbf79 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -105,10 +105,8 @@
 
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = (*times);
-        if (entry->owned_Locked()) {
-            entry->triggerReader_Locked();
-        }
+        LogTimeEntry* entry = times->get();
+        entry->triggerReader_Locked();
         times++;
     }
 
@@ -409,17 +407,15 @@
 
         LastLogTimes::iterator times = mTimes.begin();
         while (times != mTimes.end()) {
-            LogTimeEntry* entry = (*times);
-            if (entry->owned_Locked()) {
-                if (!entry->mNonBlock) {
-                    end_always = true;
-                    break;
-                }
-                // it passing mEnd is blocked by the following checks.
-                if (!end_set || (end <= entry->mEnd)) {
-                    end = entry->mEnd;
-                    end_set = true;
-                }
+            LogTimeEntry* entry = times->get();
+            if (!entry->mNonBlock) {
+                end_always = true;
+                break;
+            }
+            // it passing mEnd is blocked by the following checks.
+            if (!end_set || (end <= entry->mEnd)) {
+                end = entry->mEnd;
+                end_set = true;
             }
             times++;
         }
@@ -710,8 +706,8 @@
     // Region locked?
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = (*times);
-        if (entry->owned_Locked() && entry->isWatching(id) &&
+        LogTimeEntry* entry = times->get();
+        if (entry->isWatching(id) &&
             (!oldest || (oldest->mStart > entry->mStart) ||
              ((oldest->mStart == entry->mStart) &&
               (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
@@ -1052,9 +1048,9 @@
                 LogTimeEntry::wrlock();
                 LastLogTimes::iterator times = mTimes.begin();
                 while (times != mTimes.end()) {
-                    LogTimeEntry* entry = (*times);
+                    LogTimeEntry* entry = times->get();
                     // Killer punch
-                    if (entry->owned_Locked() && entry->isWatching(id)) {
+                    if (entry->isWatching(id)) {
                         entry->release_Locked();
                     }
                     times++;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 2b6556d..13c7af3 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -41,6 +41,7 @@
     runOnEachSocket(&command);
 }
 
+// Note returning false will release the SocketClient instance.
 bool LogReader::onDataAvailable(SocketClient* cli) {
     static bool name_set;
     if (!name_set) {
@@ -57,6 +58,18 @@
     }
     buffer[len] = '\0';
 
+    // Clients are only allowed to send one command, disconnect them if they
+    // send another.
+    LogTimeEntry::wrlock();
+    for (const auto& entry : mLogbuf.mTimes) {
+        if (entry->mClient == cli) {
+            entry->release_Locked();
+            LogTimeEntry::unlock();
+            return false;
+        }
+    }
+    LogTimeEntry::unlock();
+
     unsigned long tail = 0;
     static const char _tail[] = " tail=";
     char* cp = strstr(buffer, _tail);
@@ -199,14 +212,25 @@
         cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
         logMask, (int)pid, sequence.nsec(), timeout);
 
-    FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+    LogTimeEntry::wrlock();
+    auto entry = std::make_unique<LogTimeEntry>(
+        *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
+    if (!entry->startReader_Locked()) {
+        LogTimeEntry::unlock();
+        return false;
+    }
+
+    // release client and entry reference counts once done
+    cli->incRef();
+    mLogbuf.mTimes.emplace_front(std::move(entry));
 
     // Set acceptable upper limit to wait for slow reader processing b/27242723
     struct timeval t = { LOGD_SNDTIMEO, 0 };
     setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
                sizeof(t));
 
-    command.runSocketCommand(cli);
+    LogTimeEntry::unlock();
+
     return true;
 }
 
@@ -215,9 +239,8 @@
     LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
-        LogTimeEntry* entry = (*it);
+        LogTimeEntry* entry = it->get();
         if (entry->mClient == cli) {
-            times.erase(it);
             entry->release_Locked();
             break;
         }
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 7a6f84b..1715501 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -30,11 +30,7 @@
 LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
                            bool nonBlock, unsigned long tail, log_mask_t logMask,
                            pid_t pid, log_time start, uint64_t timeout)
-    : mRefCount(1),
-      mRelease(false),
-      mError(false),
-      threadRunning(false),
-      leadingDropped(false),
+    : leadingDropped(false),
       mReader(reader),
       mLogMask(logMask),
       mPid(pid),
@@ -52,65 +48,21 @@
     cleanSkip_Locked();
 }
 
-void LogTimeEntry::startReader_Locked(void) {
+bool LogTimeEntry::startReader_Locked() {
     pthread_attr_t attr;
 
-    threadRunning = true;
-
     if (!pthread_attr_init(&attr)) {
         if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
             if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
                                 this)) {
                 pthread_attr_destroy(&attr);
-                return;
+                return true;
             }
         }
         pthread_attr_destroy(&attr);
     }
-    threadRunning = false;
-    if (mClient) {
-        mClient->decRef();
-    }
-    decRef_Locked();
-}
 
-void LogTimeEntry::threadStop(void* obj) {
-    LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
-    wrlock();
-
-    if (me->mNonBlock) {
-        me->error_Locked();
-    }
-
-    SocketClient* client = me->mClient;
-
-    if (me->isError_Locked()) {
-        LogReader& reader = me->mReader;
-        LastLogTimes& times = reader.logbuf().mTimes;
-
-        LastLogTimes::iterator it = times.begin();
-        while (it != times.end()) {
-            if (*it == me) {
-                times.erase(it);
-                me->release_nodelete_Locked();
-                break;
-            }
-            it++;
-        }
-
-        me->mClient = nullptr;
-        reader.release(client);
-    }
-
-    if (client) {
-        client->decRef();
-    }
-
-    me->threadRunning = false;
-    me->decRef_Locked();
-
-    unlock();
+    return false;
 }
 
 void* LogTimeEntry::threadStart(void* obj) {
@@ -118,13 +70,7 @@
 
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    pthread_cleanup_push(threadStop, obj);
-
     SocketClient* client = me->mClient;
-    if (!client) {
-        me->error();
-        return nullptr;
-    }
 
     LogBuffer& logbuf = me->mReader.logbuf();
 
@@ -137,14 +83,14 @@
 
     log_time start = me->mStart;
 
-    while (me->threadRunning && !me->isError_Locked()) {
+    while (!me->mRelease) {
         if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
             if (pthread_cond_timedwait(&me->threadTriggeredCondition,
                                        &timesLock, &me->mTimeout) == ETIMEDOUT) {
                 me->mTimeout.tv_sec = 0;
                 me->mTimeout.tv_nsec = 0;
             }
-            if (!me->threadRunning || me->isError_Locked()) {
+            if (me->mRelease) {
                 break;
             }
         }
@@ -162,13 +108,12 @@
         wrlock();
 
         if (start == LogBufferElement::FLUSH_ERROR) {
-            me->error_Locked();
             break;
         }
 
         me->mStart = start + log_time(0, 1);
 
-        if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
+        if (me->mNonBlock || me->mRelease) {
             break;
         }
 
@@ -179,9 +124,21 @@
         }
     }
 
-    unlock();
+    LogReader& reader = me->mReader;
+    reader.release(client);
 
-    pthread_cleanup_pop(true);
+    client->decRef();
+
+    LastLogTimes& times = reader.logbuf().mTimes;
+    auto it =
+        std::find_if(times.begin(), times.end(),
+                     [&me](const auto& other) { return other.get() == me; });
+
+    if (it != times.end()) {
+        times.erase(it);
+    }
+
+    unlock();
 
     return nullptr;
 }
@@ -247,10 +204,6 @@
         goto skip;
     }
 
-    if (me->isError_Locked()) {
-        goto stop;
-    }
-
     if (!me->mTail) {
         goto ok;
     }
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 76d016c..f4e165f 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -22,6 +22,7 @@
 #include <time.h>
 
 #include <list>
+#include <memory>
 
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
@@ -33,16 +34,12 @@
 
 class LogTimeEntry {
     static pthread_mutex_t timesLock;
-    unsigned int mRefCount;
-    bool mRelease;
-    bool mError;
-    bool threadRunning;
+    bool mRelease = false;
     bool leadingDropped;
     pthread_cond_t threadTriggeredCondition;
     pthread_t mThread;
     LogReader& mReader;
     static void* threadStart(void* me);
-    static void threadStop(void* me);
     const log_mask_t mLogMask;
     const pid_t mPid;
     unsigned int skipAhead[LOG_ID_MAX];
@@ -73,11 +70,8 @@
         pthread_mutex_unlock(&timesLock);
     }
 
-    void startReader_Locked(void);
+    bool startReader_Locked();
 
-    bool runningReader_Locked(void) const {
-        return threadRunning || mRelease || mError || mNonBlock;
-    }
     void triggerReader_Locked(void) {
         pthread_cond_signal(&threadTriggeredCondition);
     }
@@ -87,54 +81,11 @@
     }
     void cleanSkip_Locked(void);
 
-    // These called after LogTimeEntry removed from list, lock implicitly held
-    void release_nodelete_Locked(void) {
-        mRelease = true;
-        pthread_cond_signal(&threadTriggeredCondition);
-        // assumes caller code path will call decRef_Locked()
-    }
-
     void release_Locked(void) {
         mRelease = true;
         pthread_cond_signal(&threadTriggeredCondition);
-        if (mRefCount || threadRunning) {
-            return;
-        }
-        // No one else is holding a reference to this
-        delete this;
     }
 
-    // Called to mark socket in jeopardy
-    void error_Locked(void) {
-        mError = true;
-    }
-    void error(void) {
-        wrlock();
-        error_Locked();
-        unlock();
-    }
-
-    bool isError_Locked(void) const {
-        return mRelease || mError;
-    }
-
-    // Mark Used
-    //  Locking implied, grabbed when protection around loop iteration
-    void incRef_Locked(void) {
-        ++mRefCount;
-    }
-
-    bool owned_Locked(void) const {
-        return mRefCount != 0;
-    }
-
-    void decRef_Locked(void) {
-        if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
-            return;
-        }
-        // No one else is holding a reference to this
-        delete this;
-    }
     bool isWatching(log_id_t id) const {
         return mLogMask & (1 << id);
     }
@@ -146,6 +97,6 @@
     static int FilterSecondPass(const LogBufferElement* element, void* me);
 };
 
-typedef std::list<LogTimeEntry*> LastLogTimes;
+typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
 
 #endif  // _LOGD_LOG_TIMES_H__
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index a4c3955..e4e1650 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,13 @@
   LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
 endif
 
+EXPORT_GLOBAL_HWASAN_OPTIONS :=
+ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
+  ifneq ($(HWADDRESS_SANITIZER_GLOBAL_OPTIONS),)
+    EXPORT_GLOBAL_HWASAN_OPTIONS := export HWASAN_OPTIONS $(HWADDRESS_SANITIZER_GLOBAL_OPTIONS)
+  endif
+endif
+
 EXPORT_GLOBAL_GCOV_OPTIONS :=
 ifeq ($(NATIVE_COVERAGE),true)
   EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
@@ -151,6 +158,7 @@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
 
 bcp_md5 :=
 bcp_dep :=
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index 6029ae7..ca22eb8 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,3 +1,4 @@
+ccross@google.com
 jeffv@google.com
 jiyong@google.com
 smoreland@google.com
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 2e2ab74..4576776 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -11,3 +11,4 @@
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
+    %EXPORT_GLOBAL_HWASAN_OPTIONS%