Merge "Add benchmarks for symbol reading."
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 9765292..2814932 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -16,13 +16,13 @@
 
 #include "incremental.h"
 
-#include <android-base/endian.h>
+#include "incremental_utils.h"
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <openssl/base64.h>
 
 #include "adb_client.h"
-#include "adb_io.h"
 #include "adb_utils.h"
 #include "commandline.h"
 #include "sysdeps.h"
@@ -31,65 +31,8 @@
 
 namespace incremental {
 
-namespace {
-
-static constexpr auto IDSIG = ".idsig"sv;
-
 using android::base::StringPrintf;
 
-using Size = int64_t;
-
-static inline int32_t read_int32(borrowed_fd fd) {
-    int32_t result;
-    return ReadFdExactly(fd, &result, sizeof(result)) ? result : -1;
-}
-
-static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
-    int32_t le_val = read_int32(fd);
-    auto old_size = bytes->size();
-    bytes->resize(old_size + sizeof(le_val));
-    memcpy(bytes->data() + old_size, &le_val, sizeof(le_val));
-}
-
-static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
-    int32_t le_size = read_int32(fd);
-    if (le_size < 0) {
-        return;
-    }
-    int32_t size = int32_t(le32toh(le_size));
-    auto old_size = bytes->size();
-    bytes->resize(old_size + sizeof(le_size) + size);
-    memcpy(bytes->data() + old_size, &le_size, sizeof(le_size));
-    ReadFdExactly(fd, bytes->data() + old_size + sizeof(le_size), size);
-}
-
-static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
-    std::vector<char> result;
-    append_int(fd, &result);              // version
-    append_bytes_with_size(fd, &result);  // hashingInfo
-    append_bytes_with_size(fd, &result);  // signingInfo
-    auto le_tree_size = read_int32(fd);
-    auto tree_size = int32_t(le32toh(le_tree_size));  // size of the verity tree
-    return {std::move(result), tree_size};
-}
-
-static inline Size verity_tree_size_for_file(Size fileSize) {
-    constexpr int INCFS_DATA_FILE_BLOCK_SIZE = 4096;
-    constexpr int SHA256_DIGEST_SIZE = 32;
-    constexpr int digest_size = SHA256_DIGEST_SIZE;
-    constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
-
-    Size total_tree_block_count = 0;
-
-    auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
-    auto hash_block_count = block_count;
-    for (auto i = 0; hash_block_count > 1; i++) {
-        hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
-        total_tree_block_count += hash_block_count;
-    }
-    return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
-}
-
 // Read, verify and return the signature bytes. Keeping fd at the position of start of verity tree.
 static std::pair<unique_fd, std::vector<char>> read_signature(Size file_size,
                                                               std::string signature_file,
@@ -104,7 +47,7 @@
         return {};
     }
 
-    unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
+    unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY));
     if (fd < 0) {
         if (!silent) {
             fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
@@ -172,9 +115,8 @@
             return {};
         }
 
-        auto file_desc =
-                StringPrintf("%s:%lld:%s:%s", android::base::Basename(file).c_str(),
-                             (long long)st.st_size, std::to_string(i).c_str(), signature.c_str());
+        auto file_desc = StringPrintf("%s:%lld:%d:%s:1", android::base::Basename(file).c_str(),
+                                      (long long)st.st_size, i, signature.c_str());
         command_args.push_back(std::move(file_desc));
 
         signature_fds.push_back(std::move(signature_fd));
@@ -190,21 +132,9 @@
         return {};
     }
 
-    // Pushing verity trees for all installation files.
-    for (auto&& local_fd : signature_fds) {
-        if (!copy_to_file(local_fd.get(), connection_fd.get())) {
-            if (!silent) {
-                fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
-            }
-            return {};
-        }
-    }
-
     return connection_fd;
 }
 
-}  // namespace
-
 bool can_install(const Files& files) {
     for (const auto& file : files) {
         struct stat st;
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
index 4b87d0a..1a1361c 100644
--- a/adb/client/incremental_server.cpp
+++ b/adb/client/incremental_server.cpp
@@ -1,4 +1,4 @@
-/*
+/*
  * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,9 +44,10 @@
 
 namespace incremental {
 
-static constexpr int kBlockSize = 4096;
+static constexpr int kHashesPerBlock = kBlockSize / kDigestSize;
 static constexpr int kCompressedSizeMax = kBlockSize * 0.95;
 static constexpr int8_t kTypeData = 0;
+static constexpr int8_t kTypeHash = 1;
 static constexpr int8_t kCompressionNone = 0;
 static constexpr int8_t kCompressionLZ4 = 1;
 static constexpr int kCompressBound = std::max(kBlockSize, LZ4_COMPRESSBOUND(kBlockSize));
@@ -132,41 +133,64 @@
     CompressionType compression_type;  // 1 byte
     BlockIdx block_idx;                // 4 bytes
     BlockSize block_size;              // 2 bytes
+
+    static constexpr size_t responseSizeFor(size_t dataSize) {
+        return dataSize + sizeof(ResponseHeader);
+    }
+} __attribute__((packed));
+
+template <size_t Size = kBlockSize>
+struct BlockBuffer {
+    ResponseHeader header;
+    char data[Size];
 } __attribute__((packed));
 
 // Holds streaming state for a file
 class File {
   public:
     // Plain file
-    File(const char* filepath, FileId id, int64_t size, unique_fd fd) : File(filepath, id, size) {
+    File(const char* filepath, FileId id, int64_t size, unique_fd fd, int64_t tree_offset,
+         unique_fd tree_fd)
+        : File(filepath, id, size, tree_offset) {
         this->fd_ = std::move(fd);
+        this->tree_fd_ = std::move(tree_fd);
         priority_blocks_ = PriorityBlocksForFile(filepath, fd_.get(), size);
     }
-    int64_t ReadBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed,
-                      std::string* error) const {
-        char* buf_ptr = static_cast<char*>(buf);
+    int64_t ReadDataBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed) const {
         int64_t bytes_read = -1;
         const off64_t offsetStart = blockIndexToOffset(block_idx);
-        bytes_read = adb_pread(fd_, &buf_ptr[sizeof(ResponseHeader)], kBlockSize, offsetStart);
+        bytes_read = adb_pread(fd_, buf, kBlockSize, offsetStart);
+        return bytes_read;
+    }
+    int64_t ReadTreeBlock(BlockIdx block_idx, void* buf) const {
+        int64_t bytes_read = -1;
+        const off64_t offsetStart = tree_offset_ + blockIndexToOffset(block_idx);
+        bytes_read = adb_pread(tree_fd_, buf, kBlockSize, offsetStart);
         return bytes_read;
     }
 
-    const unique_fd& RawFd() const { return fd_; }
     const std::vector<BlockIdx>& PriorityBlocks() const { return priority_blocks_; }
 
     std::vector<bool> sentBlocks;
     NumBlocks sentBlocksCount = 0;
 
+    std::vector<bool> sentTreeBlocks;
+
     const char* const filepath;
     const FileId id;
     const int64_t size;
 
   private:
-    File(const char* filepath, FileId id, int64_t size) : filepath(filepath), id(id), size(size) {
+    File(const char* filepath, FileId id, int64_t size, int64_t tree_offset)
+        : filepath(filepath), id(id), size(size), tree_offset_(tree_offset) {
         sentBlocks.resize(numBytesToNumBlocks(size));
+        sentTreeBlocks.resize(verity_tree_blocks_for_file(size));
     }
     unique_fd fd_;
     std::vector<BlockIdx> priority_blocks_;
+
+    unique_fd tree_fd_;
+    const int64_t tree_offset_;
 };
 
 class IncrementalServer {
@@ -174,6 +198,8 @@
     IncrementalServer(unique_fd adb_fd, unique_fd output_fd, std::vector<File> files)
         : adb_fd_(std::move(adb_fd)), output_fd_(std::move(output_fd)), files_(std::move(files)) {
         buffer_.reserve(kReadBufferSize);
+        pendingBlocksBuffer_.resize(kChunkFlushSize + 2 * kBlockSize);
+        pendingBlocks_ = pendingBlocksBuffer_.data() + sizeof(ChunkHeader);
     }
 
     bool Serve();
@@ -208,7 +234,11 @@
     void erase_buffer_head(int count) { buffer_.erase(buffer_.begin(), buffer_.begin() + count); }
 
     enum class SendResult { Sent, Skipped, Error };
-    SendResult SendBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+    SendResult SendDataBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+
+    bool SendTreeBlock(FileId fileId, int32_t fileBlockIdx, BlockIdx blockIdx);
+    bool SendTreeBlocksForDataBlock(FileId fileId, BlockIdx blockIdx);
+
     bool SendDone();
     void RunPrefetching();
 
@@ -228,7 +258,10 @@
     int compressed_ = 0, uncompressed_ = 0;
     long long sentSize_ = 0;
 
-    std::vector<char> pendingBlocks_;
+    static constexpr auto kChunkFlushSize = 31 * kBlockSize;
+
+    std::vector<char> pendingBlocksBuffer_;
+    char* pendingBlocks_ = nullptr;
 
     // True when client notifies that all the data has been received
     bool servingComplete_;
@@ -250,7 +283,7 @@
 
         if (bcur > 0) {
             // output the rest.
-            WriteFdExactly(output_fd_, buffer_.data(), bcur);
+            (void)WriteFdExactly(output_fd_, buffer_.data(), bcur);
             erase_buffer_head(bcur);
         }
 
@@ -265,9 +298,10 @@
         auto res = adb_poll(&pfd, 1, blocking ? kPollTimeoutMillis : 0);
 
         if (res != 1) {
-            WriteFdExactly(output_fd_, buffer_.data(), buffer_.size());
+            auto err = errno;
+            (void)WriteFdExactly(output_fd_, buffer_.data(), buffer_.size());
             if (res < 0) {
-                D("Failed to poll: %s\n", strerror(errno));
+                D("Failed to poll: %s", strerror(err));
                 return false;
             }
             if (blocking) {
@@ -289,7 +323,7 @@
             continue;
         }
 
-        D("Failed to read from fd %d: %d. Exit\n", adb_fd_.get(), errno);
+        D("Failed to read from fd %d: %d. Exit", adb_fd_.get(), errno);
         break;
     }
     // socket is closed. print remaining messages
@@ -313,56 +347,113 @@
     return request;
 }
 
-auto IncrementalServer::SendBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
+bool IncrementalServer::SendTreeBlocksForDataBlock(const FileId fileId, const BlockIdx blockIdx) {
+    auto& file = files_[fileId];
+    const int32_t data_block_count = numBytesToNumBlocks(file.size);
+
+    const int32_t total_nodes_count(file.sentTreeBlocks.size());
+    const int32_t leaf_nodes_count = (data_block_count + kHashesPerBlock - 1) / kHashesPerBlock;
+
+    const int32_t leaf_nodes_offset = total_nodes_count - leaf_nodes_count;
+
+    // Leaf level, sending only 1 block.
+    const int32_t leaf_idx = leaf_nodes_offset + blockIdx / kHashesPerBlock;
+    if (file.sentTreeBlocks[leaf_idx]) {
+        return true;
+    }
+    if (!SendTreeBlock(fileId, blockIdx, leaf_idx)) {
+        return false;
+    }
+    file.sentTreeBlocks[leaf_idx] = true;
+
+    // Non-leaf, sending EVERYTHING. This should be done only once.
+    if (leaf_nodes_offset == 0 || file.sentTreeBlocks[0]) {
+        return true;
+    }
+
+    for (int32_t i = 0; i < leaf_nodes_offset; ++i) {
+        if (!SendTreeBlock(fileId, blockIdx, i)) {
+            return false;
+        }
+        file.sentTreeBlocks[i] = true;
+    }
+    return true;
+}
+
+bool IncrementalServer::SendTreeBlock(FileId fileId, int32_t fileBlockIdx, BlockIdx blockIdx) {
+    const auto& file = files_[fileId];
+
+    BlockBuffer buffer;
+    const int64_t bytesRead = file.ReadTreeBlock(blockIdx, buffer.data);
+    if (bytesRead <= 0) {
+        fprintf(stderr, "Failed to get data for %s.idsig at blockIdx=%d.\n", file.filepath,
+                blockIdx);
+        return false;
+    }
+
+    buffer.header.compression_type = kCompressionNone;
+    buffer.header.block_type = kTypeHash;
+    buffer.header.file_id = toBigEndian(fileId);
+    buffer.header.block_size = toBigEndian(int16_t(bytesRead));
+    buffer.header.block_idx = toBigEndian(blockIdx);
+
+    Send(&buffer, ResponseHeader::responseSizeFor(bytesRead), /*flush=*/false);
+
+    return true;
+}
+
+auto IncrementalServer::SendDataBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
     auto& file = files_[fileId];
     if (blockIdx >= static_cast<long>(file.sentBlocks.size())) {
-        fprintf(stderr, "Failed to read file %s at block %" PRId32 " (past end).\n", file.filepath,
-                blockIdx);
-        return SendResult::Error;
+        // may happen as we schedule some extra blocks for reported page misses
+        D("Skipped reading file %s at block %" PRId32 " (past end).", file.filepath, blockIdx);
+        return SendResult::Skipped;
     }
     if (file.sentBlocks[blockIdx]) {
         return SendResult::Skipped;
     }
-    std::string error;
-    char raw[sizeof(ResponseHeader) + kBlockSize];
-    bool isZipCompressed = false;
-    const int64_t bytesRead = file.ReadBlock(blockIdx, &raw, &isZipCompressed, &error);
-    if (bytesRead < 0) {
-        fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%s).\n", file.filepath, blockIdx,
-                error.c_str());
+
+    if (!SendTreeBlocksForDataBlock(fileId, blockIdx)) {
         return SendResult::Error;
     }
 
-    ResponseHeader* header = nullptr;
-    char data[sizeof(ResponseHeader) + kCompressBound];
-    char* compressed = data + sizeof(*header);
+    BlockBuffer raw;
+    bool isZipCompressed = false;
+    const int64_t bytesRead = file.ReadDataBlock(blockIdx, raw.data, &isZipCompressed);
+    if (bytesRead < 0) {
+        fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%d).\n", file.filepath, blockIdx,
+                errno);
+        return SendResult::Error;
+    }
+
+    BlockBuffer<kCompressBound> compressed;
     int16_t compressedSize = 0;
     if (!isZipCompressed) {
-        compressedSize =
-                LZ4_compress_default(raw + sizeof(*header), compressed, bytesRead, kCompressBound);
+        compressedSize = LZ4_compress_default(raw.data, compressed.data, bytesRead, kCompressBound);
     }
     int16_t blockSize;
+    ResponseHeader* header;
     if (compressedSize > 0 && compressedSize < kCompressedSizeMax) {
         ++compressed_;
         blockSize = compressedSize;
-        header = reinterpret_cast<ResponseHeader*>(data);
+        header = &compressed.header;
         header->compression_type = kCompressionLZ4;
     } else {
         ++uncompressed_;
         blockSize = bytesRead;
-        header = reinterpret_cast<ResponseHeader*>(raw);
+        header = &raw.header;
         header->compression_type = kCompressionNone;
     }
 
     header->block_type = kTypeData;
-
     header->file_id = toBigEndian(fileId);
     header->block_size = toBigEndian(blockSize);
     header->block_idx = toBigEndian(blockIdx);
 
     file.sentBlocks[blockIdx] = true;
     file.sentBlocksCount += 1;
-    Send(header, sizeof(*header) + blockSize, flush);
+    Send(header, ResponseHeader::responseSizeFor(blockSize), flush);
+
     return SendResult::Sent;
 }
 
@@ -388,7 +479,8 @@
         if (!priority_blocks.empty()) {
             for (auto& i = prefetch.priorityIndex;
                  blocksToSend > 0 && i < (BlockIdx)priority_blocks.size(); ++i) {
-                if (auto res = SendBlock(file.id, priority_blocks[i]); res == SendResult::Sent) {
+                if (auto res = SendDataBlock(file.id, priority_blocks[i]);
+                    res == SendResult::Sent) {
                     --blocksToSend;
                 } else if (res == SendResult::Error) {
                     fprintf(stderr, "Failed to send priority block %" PRId32 "\n", i);
@@ -396,7 +488,7 @@
             }
         }
         for (auto& i = prefetch.overallIndex; blocksToSend > 0 && i < prefetch.overallEnd; ++i) {
-            if (auto res = SendBlock(file.id, i); res == SendResult::Sent) {
+            if (auto res = SendDataBlock(file.id, i); res == SendResult::Sent) {
                 --blocksToSend;
             } else if (res == SendResult::Error) {
                 fprintf(stderr, "Failed to send block %" PRId32 "\n", i);
@@ -409,30 +501,25 @@
 }
 
 void IncrementalServer::Send(const void* data, size_t size, bool flush) {
-    constexpr auto kChunkFlushSize = 31 * kBlockSize;
-
-    if (pendingBlocks_.empty()) {
-        pendingBlocks_.resize(sizeof(ChunkHeader));
-    }
-    pendingBlocks_.insert(pendingBlocks_.end(), static_cast<const char*>(data),
-                          static_cast<const char*>(data) + size);
-    if (flush || pendingBlocks_.size() > kChunkFlushSize) {
+    pendingBlocks_ = std::copy_n(static_cast<const char*>(data), size, pendingBlocks_);
+    if (flush || pendingBlocks_ - pendingBlocksBuffer_.data() > kChunkFlushSize) {
         Flush();
     }
 }
 
 void IncrementalServer::Flush() {
-    if (pendingBlocks_.empty()) {
+    auto dataBytes = pendingBlocks_ - (pendingBlocksBuffer_.data() + sizeof(ChunkHeader));
+    if (dataBytes == 0) {
         return;
     }
 
-    *(ChunkHeader*)pendingBlocks_.data() =
-            toBigEndian<int32_t>(pendingBlocks_.size() - sizeof(ChunkHeader));
-    if (!WriteFdExactly(adb_fd_, pendingBlocks_.data(), pendingBlocks_.size())) {
-        fprintf(stderr, "Failed to write %d bytes\n", int(pendingBlocks_.size()));
+    *(ChunkHeader*)pendingBlocksBuffer_.data() = toBigEndian<int32_t>(dataBytes);
+    auto totalBytes = sizeof(ChunkHeader) + dataBytes;
+    if (!WriteFdExactly(adb_fd_, pendingBlocksBuffer_.data(), totalBytes)) {
+        fprintf(stderr, "Failed to write %d bytes\n", int(totalBytes));
     }
-    sentSize_ += pendingBlocks_.size();
-    pendingBlocks_.clear();
+    sentSize_ += totalBytes;
+    pendingBlocks_ = pendingBlocksBuffer_.data() + sizeof(ChunkHeader);
 }
 
 bool IncrementalServer::ServingComplete(std::optional<TimePoint> startTime, int missesCount,
@@ -443,7 +530,7 @@
     D("Streaming completed.\n"
       "Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
       "%d, mb: %.3f\n"
-      "Total time taken: %.3fms\n",
+      "Total time taken: %.3fms",
       missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
       duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() / 1000.0);
     return true;
@@ -510,9 +597,21 @@
                                 fileId, blockIdx);
                         break;
                     }
-                    // fprintf(stderr, "\treading file %d block %04d\n", (int)fileId,
-                    // (int)blockIdx);
-                    if (auto res = SendBlock(fileId, blockIdx, true); res == SendResult::Error) {
+
+                    if (VLOG_IS_ON(INCREMENTAL)) {
+                        auto& file = files_[fileId];
+                        auto posP = std::find(file.PriorityBlocks().begin(),
+                                              file.PriorityBlocks().end(), blockIdx);
+                        D("\tMISSING BLOCK: reading file %d block %04d (in priority: %d of %d)",
+                          (int)fileId, (int)blockIdx,
+                          posP == file.PriorityBlocks().end()
+                                  ? -1
+                                  : int(posP - file.PriorityBlocks().begin()),
+                          int(file.PriorityBlocks().size()));
+                    }
+
+                    if (auto res = SendDataBlock(fileId, blockIdx, true);
+                        res == SendResult::Error) {
                         fprintf(stderr, "Failed to send block %" PRId32 ".\n", blockIdx);
                     } else if (res == SendResult::Sent) {
                         ++missesSent;
@@ -536,7 +635,7 @@
                                 fileId);
                         break;
                     }
-                    D("Received prefetch request for file_id %" PRId16 ".\n", fileId);
+                    D("Received prefetch request for file_id %" PRId16 ".", fileId);
                     prefetches_.emplace_back(files_[fileId]);
                     break;
                 }
@@ -551,6 +650,43 @@
     }
 }
 
+static std::pair<unique_fd, int64_t> open_fd(const char* filepath) {
+    struct stat st;
+    if (stat(filepath, &st)) {
+        error_exit("inc-server: failed to stat input file '%s'.", filepath);
+    }
+
+    unique_fd fd(adb_open(filepath, O_RDONLY));
+    if (fd < 0) {
+        error_exit("inc-server: failed to open file '%s'.", filepath);
+    }
+
+    return {std::move(fd), st.st_size};
+}
+
+static std::pair<unique_fd, int64_t> open_signature(int64_t file_size, const char* filepath) {
+    std::string signature_file(filepath);
+    signature_file += IDSIG;
+
+    unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY));
+    if (fd < 0) {
+        error_exit("inc-server: failed to open file '%s'.", signature_file.c_str());
+    }
+
+    auto [tree_offset, tree_size] = skip_id_sig_headers(fd);
+    if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
+        error_exit("Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+                   signature_file.c_str(), (long long)tree_size, (long long)expected);
+    }
+
+    int32_t data_block_count = numBytesToNumBlocks(file_size);
+    int32_t leaf_nodes_count = (data_block_count + kHashesPerBlock - 1) / kHashesPerBlock;
+    D("Verity tree loaded: %s, tree size: %d (%d blocks, %d leafs)", signature_file.c_str(),
+      int(tree_size), int(numBytesToNumBlocks(tree_size)), int(leaf_nodes_count));
+
+    return {std::move(fd), tree_offset};
+}
+
 bool serve(int connection_fd, int output_fd, int argc, const char** argv) {
     auto connection_ufd = unique_fd(connection_fd);
     auto output_ufd = unique_fd(output_fd);
@@ -563,17 +699,11 @@
     for (int i = 0; i < argc; ++i) {
         auto filepath = argv[i];
 
-        struct stat st;
-        if (stat(filepath, &st)) {
-            fprintf(stderr, "Failed to stat input file %s. Abort.\n", filepath);
-            return {};
-        }
+        auto [file_fd, file_size] = open_fd(filepath);
+        auto [sign_fd, sign_offset] = open_signature(file_size, filepath);
 
-        unique_fd fd(adb_open(filepath, O_RDONLY));
-        if (fd < 0) {
-            error_exit("inc-server: failed to open file '%s'.", filepath);
-        }
-        files.emplace_back(filepath, i, st.st_size, std::move(fd));
+        files.emplace_back(filepath, i, file_size, std::move(file_fd), sign_offset,
+                           std::move(sign_fd));
     }
 
     IncrementalServer server(std::move(connection_ufd), std::move(output_ufd), std::move(files));
diff --git a/adb/client/incremental_utils.cpp b/adb/client/incremental_utils.cpp
index fa501e4..dd117d2 100644
--- a/adb/client/incremental_utils.cpp
+++ b/adb/client/incremental_utils.cpp
@@ -18,6 +18,7 @@
 
 #include "incremental_utils.h"
 
+#include <android-base/endian.h>
 #include <android-base/mapped_file.h>
 #include <android-base/strings.h>
 #include <ziparchive/zip_archive.h>
@@ -28,19 +29,98 @@
 #include <numeric>
 #include <unordered_set>
 
+#include "adb_io.h"
 #include "adb_trace.h"
 #include "sysdeps.h"
 
 using namespace std::literals;
 
-static constexpr int kBlockSize = 4096;
+namespace incremental {
 
 static constexpr inline int32_t offsetToBlockIndex(int64_t offset) {
     return (offset & ~(kBlockSize - 1)) >> 12;
 }
 
+Size verity_tree_blocks_for_file(Size fileSize) {
+    if (fileSize == 0) {
+        return 0;
+    }
+
+    constexpr int hash_per_block = kBlockSize / kDigestSize;
+
+    Size total_tree_block_count = 0;
+
+    auto block_count = 1 + (fileSize - 1) / kBlockSize;
+    auto hash_block_count = block_count;
+    for (auto i = 0; hash_block_count > 1; i++) {
+        hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+        total_tree_block_count += hash_block_count;
+    }
+    return total_tree_block_count;
+}
+
+Size verity_tree_size_for_file(Size fileSize) {
+    return verity_tree_blocks_for_file(fileSize) * kBlockSize;
+}
+
+static inline int32_t read_int32(borrowed_fd fd) {
+    int32_t result;
+    return ReadFdExactly(fd, &result, sizeof(result)) ? result : -1;
+}
+
+static inline int32_t skip_int(borrowed_fd fd) {
+    return adb_lseek(fd, 4, SEEK_CUR);
+}
+
+static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
+    int32_t le_val = read_int32(fd);
+    auto old_size = bytes->size();
+    bytes->resize(old_size + sizeof(le_val));
+    memcpy(bytes->data() + old_size, &le_val, sizeof(le_val));
+}
+
+static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
+    int32_t le_size = read_int32(fd);
+    if (le_size < 0) {
+        return;
+    }
+    int32_t size = int32_t(le32toh(le_size));
+    auto old_size = bytes->size();
+    bytes->resize(old_size + sizeof(le_size) + size);
+    memcpy(bytes->data() + old_size, &le_size, sizeof(le_size));
+    ReadFdExactly(fd, bytes->data() + old_size + sizeof(le_size), size);
+}
+
+static inline int32_t skip_bytes_with_size(borrowed_fd fd) {
+    int32_t le_size = read_int32(fd);
+    if (le_size < 0) {
+        return -1;
+    }
+    int32_t size = int32_t(le32toh(le_size));
+    return (int32_t)adb_lseek(fd, size, SEEK_CUR);
+}
+
+std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
+    std::vector<char> result;
+    append_int(fd, &result);              // version
+    append_bytes_with_size(fd, &result);  // hashingInfo
+    append_bytes_with_size(fd, &result);  // signingInfo
+    auto le_tree_size = read_int32(fd);
+    auto tree_size = int32_t(le32toh(le_tree_size));  // size of the verity tree
+    return {std::move(result), tree_size};
+}
+
+std::pair<off64_t, ssize_t> skip_id_sig_headers(borrowed_fd fd) {
+    skip_int(fd);                            // version
+    skip_bytes_with_size(fd);                // hashingInfo
+    auto offset = skip_bytes_with_size(fd);  // signingInfo
+    auto le_tree_size = read_int32(fd);
+    auto tree_size = int32_t(le32toh(le_tree_size));  // size of the verity tree
+    return {offset + sizeof(le_tree_size), tree_size};
+}
+
 template <class T>
-T valueAt(int fd, off64_t offset) {
+static T valueAt(borrowed_fd fd, off64_t offset) {
     T t;
     memset(&t, 0, sizeof(T));
     if (adb_pread(fd, &t, sizeof(T), offset) != sizeof(T)) {
@@ -68,7 +148,7 @@
             v.end());
 }
 
-static off64_t CentralDirOffset(int fd, int64_t fileSize) {
+static off64_t CentralDirOffset(borrowed_fd fd, Size fileSize) {
     static constexpr int kZipEocdRecMinSize = 22;
     static constexpr int32_t kZipEocdRecSig = 0x06054b50;
     static constexpr int kZipEocdCentralDirSizeFieldOffset = 12;
@@ -103,7 +183,7 @@
 }
 
 // Does not support APKs larger than 4GB
-static off64_t SignerBlockOffset(int fd, int64_t fileSize) {
+static off64_t SignerBlockOffset(borrowed_fd fd, Size fileSize) {
     static constexpr int kApkSigBlockMinSize = 32;
     static constexpr int kApkSigBlockFooterSize = 24;
     static constexpr int64_t APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42l;
@@ -132,7 +212,7 @@
     return signerBlockOffset;
 }
 
-static std::vector<int32_t> ZipPriorityBlocks(off64_t signerBlockOffset, int64_t fileSize) {
+static std::vector<int32_t> ZipPriorityBlocks(off64_t signerBlockOffset, Size fileSize) {
     int32_t signerBlockIndex = offsetToBlockIndex(signerBlockOffset);
     int32_t lastBlockIndex = offsetToBlockIndex(fileSize);
     const auto numPriorityBlocks = lastBlockIndex - signerBlockIndex + 1;
@@ -160,7 +240,7 @@
     return zipPriorityBlocks;
 }
 
-[[maybe_unused]] static ZipArchiveHandle openZipArchiveFd(int fd) {
+[[maybe_unused]] static ZipArchiveHandle openZipArchiveFd(borrowed_fd fd) {
     bool transferFdOwnership = false;
 #ifdef _WIN32
     //
@@ -179,20 +259,22 @@
         D("%s failed at DuplicateHandle: %d", __func__, (int)::GetLastError());
         return {};
     }
-    fd = _open_osfhandle((intptr_t)dupedHandle, _O_RDONLY | _O_BINARY);
-    if (fd < 0) {
+    int osfd = _open_osfhandle((intptr_t)dupedHandle, _O_RDONLY | _O_BINARY);
+    if (osfd < 0) {
         D("%s failed at _open_osfhandle: %d", __func__, errno);
         ::CloseHandle(handle);
         return {};
     }
     transferFdOwnership = true;
+#else
+    int osfd = fd.get();
 #endif
     ZipArchiveHandle zip;
-    if (OpenArchiveFd(fd, "apk_fd", &zip, transferFdOwnership) != 0) {
+    if (OpenArchiveFd(osfd, "apk_fd", &zip, transferFdOwnership) != 0) {
         D("%s failed at OpenArchiveFd: %d", __func__, errno);
 #ifdef _WIN32
         // "_close()" is a secret WinCRT name for the regular close() function.
-        _close(fd);
+        _close(osfd);
 #endif
         return {};
     }
@@ -200,7 +282,7 @@
 }
 
 static std::pair<ZipArchiveHandle, std::unique_ptr<android::base::MappedFile>> openZipArchive(
-        int fd, int64_t fileSize) {
+        borrowed_fd fd, Size fileSize) {
 #ifndef __LP64__
     if (fileSize >= INT_MAX) {
         return {openZipArchiveFd(fd), nullptr};
@@ -220,7 +302,7 @@
     return {zip, std::move(mapping)};
 }
 
-static std::vector<int32_t> InstallationPriorityBlocks(int fd, int64_t fileSize) {
+static std::vector<int32_t> InstallationPriorityBlocks(borrowed_fd fd, Size fileSize) {
     static constexpr std::array<std::string_view, 3> additional_matches = {
             "resources.arsc"sv, "AndroidManifest.xml"sv, "classes.dex"sv};
     auto [zip, _] = openZipArchive(fd, fileSize);
@@ -249,8 +331,9 @@
         if (entryName == "classes.dex"sv) {
             // Only the head is needed for installation
             int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
-            appendBlocks(startBlockIndex, 1, &installationPriorityBlocks);
-            D("\tadding to priority blocks: '%.*s' 1", (int)entryName.size(), entryName.data());
+            appendBlocks(startBlockIndex, 2, &installationPriorityBlocks);
+            D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
+              2);
         } else {
             // Full entries are needed for installation
             off64_t entryStartOffset = entry.offset;
@@ -273,9 +356,9 @@
     return installationPriorityBlocks;
 }
 
-namespace incremental {
-std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, int fd, int64_t fileSize) {
-    if (!android::base::EndsWithIgnoreCase(filepath, ".apk")) {
+std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, borrowed_fd fd,
+                                           Size fileSize) {
+    if (!android::base::EndsWithIgnoreCase(filepath, ".apk"sv)) {
         return {};
     }
     off64_t signerOffset = SignerBlockOffset(fd, fileSize);
@@ -291,4 +374,5 @@
     unduplicate(priorityBlocks);
     return priorityBlocks;
 }
+
 }  // namespace incremental
diff --git a/adb/client/incremental_utils.h b/adb/client/incremental_utils.h
index 8bcf6c0..fe2914d 100644
--- a/adb/client/incremental_utils.h
+++ b/adb/client/incremental_utils.h
@@ -16,11 +16,33 @@
 
 #pragma once
 
-#include <stdint.h>
+#include "adb_unique_fd.h"
 
 #include <string>
+#include <string_view>
+#include <utility>
 #include <vector>
 
+#include <stdint.h>
+
+#include <android-base/off64_t.h>
+
 namespace incremental {
-std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, int fd, int64_t fileSize);
-}  // namespace incremental
\ No newline at end of file
+
+using Size = int64_t;
+constexpr int kBlockSize = 4096;
+constexpr int kSha256DigestSize = 32;
+constexpr int kDigestSize = kSha256DigestSize;
+
+constexpr std::string_view IDSIG = ".idsig";
+
+std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, borrowed_fd fd,
+                                           Size fileSize);
+
+Size verity_tree_blocks_for_file(Size fileSize);
+Size verity_tree_size_for_file(Size fileSize);
+
+std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd);
+std::pair<off64_t, ssize_t> skip_id_sig_headers(borrowed_fd fd);
+
+}  // namespace incremental
diff --git a/adb/libs/adbconnection/Android.bp b/adb/libs/adbconnection/Android.bp
index ce2ab51..f7d2dc1 100644
--- a/adb/libs/adbconnection/Android.bp
+++ b/adb/libs/adbconnection/Android.bp
@@ -53,6 +53,7 @@
         linux: {
             version_script: "libadbconnection_client.map.txt",
         },
+        darwin: { enabled: false },
     },
     stubs: {
         symbol_file: "libadbconnection_client.map.txt",
diff --git a/adb/libs/adbconnection/adbconnection_client.cpp b/adb/libs/adbconnection/adbconnection_client.cpp
index c132342..b569421 100644
--- a/adb/libs/adbconnection/adbconnection_client.cpp
+++ b/adb/libs/adbconnection/adbconnection_client.cpp
@@ -122,7 +122,16 @@
     return nullptr;
   }
 
+#if defined(__APPLE__)
+  ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET, 0));
+  // TODO: find a better home for all these copies of the mac CLOEXEC workaround.
+  if (int fd = ctx->control_socket_.get(), flags = fcntl(fd, F_GETFD);
+      flags != -1 && (flags & FD_CLOEXEC) == 0) {
+    fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+  }
+#else
   ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0));
+#endif
   if (ctx->control_socket_ < 0) {
     PLOG(ERROR) << "failed to create Unix domain socket";
     return nullptr;
diff --git a/adb/pairing_connection/Android.bp b/adb/pairing_connection/Android.bp
index 707161b..9595511 100644
--- a/adb/pairing_connection/Android.bp
+++ b/adb/pairing_connection/Android.bp
@@ -84,7 +84,7 @@
     static_libs: [
         "libadb_protos",
         // Statically link libadb_tls_connection because it is not
-	// ABI-stable.
+        // ABI-stable.
         "libadb_tls_connection",
         "libprotobuf-cpp-lite",
     ],
@@ -133,7 +133,6 @@
         "//frameworks/base/services:__subpackages__",
     ],
 
-    host_supported: true,
     recovery_available: false,
 
     stl: "libc++_static",
diff --git a/fs_mgr/clean_scratch_files.rc b/fs_mgr/clean_scratch_files.rc
index 738d1aa..25a7e69 100644
--- a/fs_mgr/clean_scratch_files.rc
+++ b/fs_mgr/clean_scratch_files.rc
@@ -1,2 +1,2 @@
 on post-fs-data && property:ro.debuggable=1
-    exec_background - root root -- clean_scratch_files
+    exec_background - root root -- /system/bin/clean_scratch_files
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 7be024f..778c0c1 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -121,7 +121,7 @@
 }
 
 static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer,
-                       bool needs_projid, bool needs_casefold) {
+                       bool needs_projid, bool needs_casefold, bool fs_compress) {
     if (!dev_sz) {
         int rc = get_dev_sz(fs_blkdev, &dev_sz);
         if (rc) {
@@ -147,6 +147,12 @@
         args.push_back("-C");
         args.push_back("utf8");
     }
+    if (fs_compress) {
+        args.push_back("-O");
+        args.push_back("compression");
+        args.push_back("-O");
+        args.push_back("extra_attr");
+    }
     args.push_back(fs_blkdev.c_str());
     args.push_back(size_str.c_str());
 
@@ -166,7 +172,7 @@
 
     if (entry.fs_type == "f2fs") {
         return format_f2fs(entry.blk_device, entry.length, crypt_footer, needs_projid,
-                           needs_casefold);
+                           needs_casefold, entry.fs_mgr_flags.fs_compress);
     } else if (entry.fs_type == "ext4") {
         return format_ext4(entry.blk_device, entry.mount_point, crypt_footer, needs_projid,
                            entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index b218f21..0825a77 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -178,6 +178,7 @@
         CheckFlag("slotselect_other", slot_select_other);
         CheckFlag("fsverity", fs_verity);
         CheckFlag("metadata_csum", ext_meta_csum);
+        CheckFlag("fscompress", fs_compress);
 
 #undef CheckFlag
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 79d9402..7216402 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -84,6 +84,7 @@
         bool slot_select_other : 1;
         bool fs_verity : 1;
         bool ext_meta_csum : 1;
+        bool fs_compress : 1;
     } fs_mgr_flags = {};
 
     bool is_encryptable() const {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index a209ea6..4e3984c 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -226,3 +226,19 @@
         "libutils",
     ],
 }
+
+cc_test {
+    name: "snapshot_power_test",
+    srcs: [
+        "power_test.cpp",
+    ],
+    static_libs: [
+        "libsnapshot",
+    ],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr_binder",
+        "liblog",
+    ],
+    gtest: false,
+}
diff --git a/fs_mgr/libsnapshot/PowerTest.md b/fs_mgr/libsnapshot/PowerTest.md
new file mode 100644
index 0000000..0b0cb5d
--- /dev/null
+++ b/fs_mgr/libsnapshot/PowerTest.md
@@ -0,0 +1,40 @@
+snapshot\_power\_test
+---------------------
+
+snapshot\_power\_test is a standalone test to simulate power failures during a snapshot-merge operation.
+
+### Test Setup
+
+Start by creating two large files that will be used as the pre-merge and post-merge state. You can take two different partition images (for example, a product.img from two separate builds), or just create random data:
+
+	dd if=/dev/urandom of=pre-merge count=1024 bs=1048576
+	dd if=/dev/urandom of=post-merge count=1024 bs=1048576
+
+Next, push these files to an unencrypted directory on the device:
+
+	adb push pre-merge /data/local/unencrypted
+	adb push post-merge /data/local/unencrypted
+
+Next, run the test setup:
+
+	adb sync data
+	adb shell /data/nativetest64/snapshot_power_test/snapshot_power_test \
+		/data/local/unencrypted/pre-merge \
+		/data/local/unencrypted/post-merge
+
+This will create the necessary fiemap-based images.
+
+### Running
+The actual test can be run via `run_power_test.sh`. Its syntax is:
+
+	run_power_test.sh <POST_MERGE_FILE>
+
+`POST_MERGE_FILE` should be the path on the device of the image to validate the merge against. Example:
+
+	run_power_test.sh /data/local/unencrypted/post-merge
+
+The device will begin the merge with a 5% chance of injecting a kernel crash every 10ms. The device should be capable of rebooting normally without user intervention. Once the merge has completed, the test will run a final check command to validate the contents of the snapshot against the post-merge file. It will error if there are any incorrect blocks.
+
+Two environment variables can be passed to `run_power_test.sh`:
+1. `FAIL_RATE` - A fraction between 0 and 100 (inclusive) indicating the probability the device should inject a kernel crash every 10ms.
+2. `DEVICE_SERIAL` - If multiple devices are attached to adb, this argument is passed as the serial to select (to `adb -s`).
diff --git a/fs_mgr/libsnapshot/power_test.cpp b/fs_mgr/libsnapshot/power_test.cpp
new file mode 100644
index 0000000..4d2548a
--- /dev/null
+++ b/fs_mgr/libsnapshot/power_test.cpp
@@ -0,0 +1,559 @@
+//
+// Copyright (C) 2020 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 <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iostream>
+#include <random>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parsedouble.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::dm::DmTable;
+using android::dm::DmTargetSnapshot;
+using android::dm::SnapshotStorageMode;
+using android::fiemap::ImageManager;
+using android::fs_mgr::Fstab;
+
+namespace android {
+namespace snapshot {
+
+static void usage() {
+    std::cerr << "Usage:\n";
+    std::cerr << "  create <orig-payload> <new-payload>\n";
+    std::cerr << "\n";
+    std::cerr << "  Create a snapshot device containing the contents of\n";
+    std::cerr << "  orig-payload, and then write the contents of new-payload.\n";
+    std::cerr << "  The original files are not modified.\n";
+    std::cerr << "\n";
+    std::cerr << "  merge <fail-rate>\n";
+    std::cerr << "\n";
+    std::cerr << "  Merge the snapshot previously started by create, and wait\n";
+    std::cerr << "  for it to complete. Once done, it is compared to the\n";
+    std::cerr << "  new-payload for consistency. The original files are not \n";
+    std::cerr << "  modified. If a fail-rate is passed (as a fraction between 0\n";
+    std::cerr << "  and 100), every 10ms the device has that percent change of\n";
+    std::cerr << "  injecting a kernel crash.\n";
+    std::cerr << "\n";
+    std::cerr << "  check <new-payload>\n";
+    std::cerr << "  Verify that all artifacts are correct after a merge\n";
+    std::cerr << "  completes.\n";
+    std::cerr << "\n";
+    std::cerr << "  cleanup\n";
+    std::cerr << "  Remove all ImageManager artifacts from create/merge.\n";
+}
+
+class PowerTest final {
+  public:
+    PowerTest();
+    bool Run(int argc, char** argv);
+
+  private:
+    bool OpenImageManager();
+    bool Create(int argc, char** argv);
+    bool Merge(int argc, char** argv);
+    bool Check(int argc, char** argv);
+    bool Cleanup();
+    bool CleanupImage(const std::string& name);
+    bool SetupImages(const std::string& first_file, borrowed_fd second_fd);
+    bool MapImages();
+    bool MapSnapshot(SnapshotStorageMode mode);
+    bool GetMergeStatus(DmTargetSnapshot::Status* status);
+
+    static constexpr char kSnapshotName[] = "snapshot-power-test";
+    static constexpr char kSnapshotImageName[] = "snapshot-power-test-image";
+    static constexpr char kSnapshotCowName[] = "snapshot-power-test-cow";
+
+    DeviceMapper& dm_;
+    std::unique_ptr<ImageManager> images_;
+    std::string image_path_;
+    std::string cow_path_;
+    std::string snapshot_path_;
+};
+
+PowerTest::PowerTest() : dm_(DeviceMapper::Instance()) {}
+
+bool PowerTest::Run([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+    if (!OpenImageManager()) {
+        return false;
+    }
+
+    if (argc < 2) {
+        usage();
+        return false;
+    }
+    if (argv[1] == "create"s) {
+        return Create(argc, argv);
+    } else if (argv[1] == "merge"s) {
+        return Merge(argc, argv);
+    } else if (argv[1] == "check"s) {
+        return Check(argc, argv);
+    } else if (argv[1] == "cleanup"s) {
+        return Cleanup();
+    } else {
+        usage();
+        return false;
+    }
+}
+
+bool PowerTest::OpenImageManager() {
+    std::vector<std::string> dirs = {
+            "/data/gsi/test",
+            "/metadata/gsi/test",
+    };
+    for (const auto& dir : dirs) {
+        if (mkdir(dir.c_str(), 0700) && errno != EEXIST) {
+            std::cerr << "mkdir " << dir << ": " << strerror(errno) << "\n";
+            return false;
+        }
+    }
+
+    images_ = ImageManager::Open("/metadata/gsi/test", "/data/gsi/test");
+    if (!images_) {
+        std::cerr << "Could not open ImageManager\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::Create(int argc, char** argv) {
+    if (argc < 4) {
+        usage();
+        return false;
+    }
+
+    std::string first = argv[2];
+    std::string second = argv[3];
+
+    unique_fd second_fd(open(second.c_str(), O_RDONLY));
+    if (second_fd < 0) {
+        std::cerr << "open " << second << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    if (!Cleanup()) {
+        return false;
+    }
+    if (!SetupImages(first, second_fd)) {
+        return false;
+    }
+    if (!MapSnapshot(SnapshotStorageMode::Persistent)) {
+        return false;
+    }
+
+    struct stat s;
+    if (fstat(second_fd, &s)) {
+        std::cerr << "fstat " << second << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    unique_fd snap_fd(open(snapshot_path_.c_str(), O_WRONLY));
+    if (snap_fd < 0) {
+        std::cerr << "open " << snapshot_path_ << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    uint8_t chunk[4096];
+    uint64_t written = 0;
+    while (written < s.st_size) {
+        uint64_t remaining = s.st_size - written;
+        size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+        if (!android::base::ReadFully(second_fd, chunk, bytes)) {
+            std::cerr << "read " << second << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::WriteFully(snap_fd, chunk, bytes)) {
+            std::cerr << "write " << snapshot_path_ << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        written += bytes;
+    }
+    if (fsync(snap_fd)) {
+        std::cerr << "fsync: " << strerror(errno) << "\n";
+        return false;
+    }
+
+    sync();
+
+    snap_fd = {};
+    if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+        std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+        return false;
+    }
+    if (!images_->UnmapImageIfExists(kSnapshotImageName)) {
+        std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+        return false;
+    }
+    if (!images_->UnmapImageIfExists(kSnapshotCowName)) {
+        std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::Cleanup() {
+    if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+        std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+        return false;
+    }
+    if (!CleanupImage(kSnapshotImageName) || !CleanupImage(kSnapshotCowName)) {
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::CleanupImage(const std::string& name) {
+    if (!images_->UnmapImageIfExists(name)) {
+        std::cerr << "failed to unmap " << name << "\n";
+        return false;
+    }
+    if (images_->BackingImageExists(name) && !images_->DeleteBackingImage(name)) {
+        std::cerr << "failed to delete " << name << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::SetupImages(const std::string& first, borrowed_fd second_fd) {
+    unique_fd first_fd(open(first.c_str(), O_RDONLY));
+    if (first_fd < 0) {
+        std::cerr << "open " << first << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    struct stat s1, s2;
+    if (fstat(first_fd.get(), &s1)) {
+        std::cerr << "first stat: " << strerror(errno) << "\n";
+        return false;
+    }
+    if (fstat(second_fd.get(), &s2)) {
+        std::cerr << "second stat: " << strerror(errno) << "\n";
+        return false;
+    }
+
+    // Pick the bigger size of both images, rounding up to the nearest block.
+    uint64_t s1_size = (s1.st_size + 4095) & ~uint64_t(4095);
+    uint64_t s2_size = (s2.st_size + 4095) & ~uint64_t(4095);
+    uint64_t image_size = std::max(s1_size, s2_size) + (1024 * 1024 * 128);
+    if (!images_->CreateBackingImage(kSnapshotImageName, image_size, 0, nullptr)) {
+        std::cerr << "failed to create " << kSnapshotImageName << "\n";
+        return false;
+    }
+    // Use the same size for the cow.
+    if (!images_->CreateBackingImage(kSnapshotCowName, image_size, 0, nullptr)) {
+        std::cerr << "failed to create " << kSnapshotCowName << "\n";
+        return false;
+    }
+    if (!MapImages()) {
+        return false;
+    }
+
+    unique_fd image_fd(open(image_path_.c_str(), O_WRONLY));
+    if (image_fd < 0) {
+        std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    uint8_t chunk[4096];
+    uint64_t written = 0;
+    while (written < s1.st_size) {
+        uint64_t remaining = s1.st_size - written;
+        size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+        if (!android::base::ReadFully(first_fd, chunk, bytes)) {
+            std::cerr << "read: " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::WriteFully(image_fd, chunk, bytes)) {
+            std::cerr << "write: " << strerror(errno) << "\n";
+            return false;
+        }
+        written += bytes;
+    }
+    if (fsync(image_fd)) {
+        std::cerr << "fsync: " << strerror(errno) << "\n";
+        return false;
+    }
+
+    // Zero the first block of the COW.
+    unique_fd cow_fd(open(cow_path_.c_str(), O_WRONLY));
+    if (cow_fd < 0) {
+        std::cerr << "open: " << cow_path_ << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    memset(chunk, 0, sizeof(chunk));
+    if (!android::base::WriteFully(cow_fd, chunk, sizeof(chunk))) {
+        std::cerr << "read: " << strerror(errno) << "\n";
+        return false;
+    }
+    if (fsync(cow_fd)) {
+        std::cerr << "fsync: " << strerror(errno) << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::MapImages() {
+    if (!images_->MapImageDevice(kSnapshotImageName, 10s, &image_path_)) {
+        std::cerr << "failed to map " << kSnapshotImageName << "\n";
+        return false;
+    }
+    if (!images_->MapImageDevice(kSnapshotCowName, 10s, &cow_path_)) {
+        std::cerr << "failed to map " << kSnapshotCowName << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::MapSnapshot(SnapshotStorageMode mode) {
+    uint64_t sectors;
+    {
+        unique_fd fd(open(image_path_.c_str(), O_RDONLY));
+        if (fd < 0) {
+            std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        sectors = get_block_device_size(fd) / 512;
+    }
+
+    DmTable table;
+    table.Emplace<DmTargetSnapshot>(0, sectors, image_path_, cow_path_, mode, 8);
+    if (!dm_.CreateDevice(kSnapshotName, table, &snapshot_path_, 10s)) {
+        std::cerr << "failed to create snapshot device\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::GetMergeStatus(DmTargetSnapshot::Status* status) {
+    std::vector<DeviceMapper::TargetInfo> targets;
+    if (!dm_.GetTableStatus(kSnapshotName, &targets)) {
+        std::cerr << "failed to get merge status\n";
+        return false;
+    }
+    if (targets.size() != 1) {
+        std::cerr << "merge device has wrong number of targets\n";
+        return false;
+    }
+    if (!DmTargetSnapshot::ParseStatusText(targets[0].data, status)) {
+        std::cerr << "could not parse merge target status text\n";
+        return false;
+    }
+    return true;
+}
+
+static std::string GetUserdataBlockDeviceName() {
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        return {};
+    }
+
+    auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
+    if (!entry) {
+        return {};
+    }
+
+    auto prefix = "/dev/block/"s;
+    if (!android::base::StartsWith(entry->blk_device, prefix)) {
+        return {};
+    }
+    return entry->blk_device.substr(prefix.size());
+}
+
+bool PowerTest::Merge(int argc, char** argv) {
+    // Start an f2fs GC to really stress things. :TODO: figure out data device
+    auto userdata_dev = GetUserdataBlockDeviceName();
+    if (userdata_dev.empty()) {
+        std::cerr << "could not locate userdata block device\n";
+        return false;
+    }
+
+    auto cmd =
+            android::base::StringPrintf("echo 1 > /sys/fs/f2fs/%s/gc_urgent", userdata_dev.c_str());
+    system(cmd.c_str());
+
+    if (dm_.GetState(kSnapshotName) == DmDeviceState::INVALID) {
+        if (!MapImages()) {
+            return false;
+        }
+        if (!MapSnapshot(SnapshotStorageMode::Merge)) {
+            return false;
+        }
+    }
+
+    std::random_device r;
+    std::default_random_engine re(r());
+    std::uniform_real_distribution<double> dist(0.0, 100.0);
+
+    std::optional<double> failure_rate;
+    if (argc >= 3) {
+        double d;
+        if (!android::base::ParseDouble(argv[2], &d)) {
+            std::cerr << "Could not parse failure rate as double: " << argv[2] << "\n";
+            return false;
+        }
+        failure_rate = d;
+    }
+
+    while (true) {
+        DmTargetSnapshot::Status status;
+        if (!GetMergeStatus(&status)) {
+            return false;
+        }
+        if (!status.error.empty()) {
+            std::cerr << "merge reported error: " << status.error << "\n";
+            return false;
+        }
+        if (status.sectors_allocated == status.metadata_sectors) {
+            break;
+        }
+
+        std::cerr << status.sectors_allocated << " / " << status.metadata_sectors << "\n";
+
+        if (failure_rate && *failure_rate >= dist(re)) {
+            system("echo 1 > /proc/sys/kernel/sysrq");
+            system("echo c > /proc/sysrq-trigger");
+        }
+
+        std::this_thread::sleep_for(10ms);
+    }
+
+    std::cout << "Merge completed.\n";
+    return true;
+}
+
+bool PowerTest::Check([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+    if (argc < 3) {
+        std::cerr << "Expected argument: <new-image-path>\n";
+        return false;
+    }
+    std::string md_path, image_path;
+    std::string canonical_path = argv[2];
+
+    if (!dm_.GetDmDevicePathByName(kSnapshotName, &md_path)) {
+        std::cerr << "could not get dm-path for merge device\n";
+        return false;
+    }
+    if (!images_->GetMappedImageDevice(kSnapshotImageName, &image_path)) {
+        std::cerr << "could not get image path\n";
+        return false;
+    }
+
+    unique_fd md_fd(open(md_path.c_str(), O_RDONLY));
+    if (md_fd < 0) {
+        std::cerr << "open: " << md_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+    unique_fd image_fd(open(image_path.c_str(), O_RDONLY));
+    if (image_fd < 0) {
+        std::cerr << "open: " << image_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+    unique_fd canonical_fd(open(canonical_path.c_str(), O_RDONLY));
+    if (canonical_fd < 0) {
+        std::cerr << "open: " << canonical_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    struct stat s;
+    if (fstat(canonical_fd, &s)) {
+        std::cerr << "fstat: " << canonical_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+    uint64_t canonical_size = s.st_size;
+    uint64_t md_size = get_block_device_size(md_fd);
+    uint64_t image_size = get_block_device_size(image_fd);
+    if (image_size != md_size) {
+        std::cerr << "image size does not match merge device size\n";
+        return false;
+    }
+    if (canonical_size > image_size) {
+        std::cerr << "canonical size " << canonical_size << " is greater than image size "
+                  << image_size << "\n";
+        return false;
+    }
+
+    constexpr size_t kBlockSize = 4096;
+    uint8_t canonical_buffer[kBlockSize];
+    uint8_t image_buffer[kBlockSize];
+    uint8_t md_buffer[kBlockSize];
+
+    uint64_t remaining = canonical_size;
+    uint64_t blockno = 0;
+    while (remaining) {
+        size_t bytes = (size_t)std::min((uint64_t)kBlockSize, remaining);
+        if (!android::base::ReadFully(canonical_fd, canonical_buffer, bytes)) {
+            std::cerr << "read: " << canonical_buffer << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::ReadFully(image_fd, image_buffer, bytes)) {
+            std::cerr << "read: " << image_buffer << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::ReadFully(md_fd, md_buffer, bytes)) {
+            std::cerr << "read: " << md_buffer << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (memcmp(canonical_buffer, image_buffer, bytes)) {
+            std::cerr << "canonical and image differ at block " << blockno << "\n";
+            return false;
+        }
+        if (memcmp(canonical_buffer, md_buffer, bytes)) {
+            std::cerr << "canonical and image differ at block " << blockno << "\n";
+            return false;
+        }
+
+        remaining -= bytes;
+        blockno++;
+    }
+
+    std::cout << "Images all match.\n";
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::snapshot::PowerTest test;
+
+    if (!test.Run(argc, argv)) {
+        std::cerr << "Unexpected error running test." << std::endl;
+        return 1;
+    }
+    fflush(stdout);
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/run_power_test.sh b/fs_mgr/libsnapshot/run_power_test.sh
new file mode 100755
index 0000000..dc03dc9
--- /dev/null
+++ b/fs_mgr/libsnapshot/run_power_test.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -e
+
+if [ -z "$FAIL_RATE" ]; then
+    FAIL_RATE=5.0
+fi
+if [ ! -z "$ANDROID_SERIAL" ]; then
+    DEVICE_ARGS=-s $ANDROID_SERIAL
+else
+    DEVICE_ARGS=
+fi
+
+TEST_BIN=/data/nativetest64/snapshot_power_test/snapshot_power_test
+
+while :
+do
+    adb $DEVICE_ARGS wait-for-device
+    adb $DEVICE_ARGS root
+    adb $DEVICE_ARGS shell rm $TEST_BIN
+    adb $DEVICE_ARGS sync data
+    set +e
+    output=$(adb $DEVICE_ARGS shell $TEST_BIN merge $FAIL_RATE 2>&1)
+    set -e
+    if [[ "$output" == *"Merge completed"* ]]; then
+        echo "Merge completed."
+        break
+    fi
+    if [[ "$output" == *"Unexpected error"* ]]; then
+        echo "Unexpected error."
+        exit 1
+    fi
+done
+
+adb $DEVICE_ARGS shell $TEST_BIN check $1
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
index 0ff8995..de835b3 100644
--- a/fs_mgr/tests/AndroidTest.xml
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -21,6 +21,9 @@
         <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
         <option name="append-bitness" value="true" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+        <option name="throw-on-error" value="false" />
+    </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="CtsFsMgrTestCases" />
diff --git a/init/Android.bp b/init/Android.bp
index 1b3aa18..827a829 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -41,6 +41,7 @@
     "builtins.cpp",
     "devices.cpp",
     "firmware_handler.cpp",
+    "first_stage_console.cpp",
     "first_stage_init.cpp",
     "first_stage_mount.cpp",
     "fscrypt_init_extensions.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index b49fb3b..416b732 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -50,6 +50,7 @@
 LOCAL_SRC_FILES := \
     block_dev_initializer.cpp \
     devices.cpp \
+    first_stage_console.cpp \
     first_stage_init.cpp \
     first_stage_main.cpp \
     first_stage_mount.cpp \
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 920dc6c..17f509a 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -24,6 +24,9 @@
         <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
         <option name="append-bitness" value="true" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+        <option name="throw-on-error" value="false" />
+    </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="CtsInitTestCases" />
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
new file mode 100644
index 0000000..cae53f4
--- /dev/null
+++ b/init/first_stage_console.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 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 "first_stage_console.h"
+
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+static void RunScript() {
+    LOG(INFO) << "Attempting to run /first_stage.sh...";
+    pid_t pid = fork();
+    if (pid != 0) {
+        int status;
+        waitpid(pid, &status, 0);
+        LOG(INFO) << "/first_stage.sh exited with status " << status;
+        return;
+    }
+    const char* path = "/system/bin/sh";
+    const char* args[] = {path, "/first_stage.sh", nullptr};
+    int rv = execv(path, const_cast<char**>(args));
+    LOG(ERROR) << "unable to execv /first_stage.sh, returned " << rv << " errno " << errno;
+}
+
+namespace android {
+namespace init {
+
+void StartConsole() {
+    if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+        PLOG(ERROR) << "unable to create /dev/console";
+        return;
+    }
+    pid_t pid = fork();
+    if (pid != 0) {
+        int status;
+        waitpid(pid, &status, 0);
+        LOG(ERROR) << "console shell exited with status " << status;
+        return;
+    }
+    int fd = -1;
+    int tries = 50; // should timeout after 5s
+    // The device driver for console may not be ready yet so retry for a while in case of failure.
+    while (tries--) {
+        fd = open("/dev/console", O_RDWR);
+        if (fd != -1) {
+            break;
+        }
+        std::this_thread::sleep_for(100ms);
+    }
+    if (fd == -1) {
+        LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+        _exit(127);
+    }
+    ioctl(fd, TIOCSCTTY, 0);
+    dup2(fd, STDIN_FILENO);
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
+    close(fd);
+
+    RunScript();
+    const char* path = "/system/bin/sh";
+    const char* args[] = {path, nullptr};
+    int rv = execv(path, const_cast<char**>(args));
+    LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+    _exit(127);
+}
+
+bool FirstStageConsole(const std::string& cmdline) {
+    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
new file mode 100644
index 0000000..7485339
--- /dev/null
+++ b/init/first_stage_console.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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 <string>
+
+namespace android {
+namespace init {
+
+void StartConsole();
+bool FirstStageConsole(const std::string& cmdline);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ef8ffbe..5eca644 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -24,12 +24,10 @@
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <filesystem>
 #include <string>
-#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -39,6 +37,7 @@
 #include <private/android_filesystem_config.h>
 
 #include "debug_ramdisk.h"
+#include "first_stage_console.h"
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
 #include "switch_root.h"
@@ -94,49 +93,6 @@
     }
 }
 
-void StartConsole() {
-    if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
-        PLOG(ERROR) << "unable to create /dev/console";
-        return;
-    }
-    pid_t pid = fork();
-    if (pid != 0) {
-        int status;
-        waitpid(pid, &status, 0);
-        LOG(ERROR) << "console shell exited with status " << status;
-        return;
-    }
-    int fd = -1;
-    int tries = 10;
-    // The device driver for console may not be ready yet so retry for a while in case of failure.
-    while (tries--) {
-        fd = open("/dev/console", O_RDWR);
-        if (fd != -1) {
-            break;
-        }
-        std::this_thread::sleep_for(100ms);
-    }
-    if (fd == -1) {
-        LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
-        _exit(127);
-    }
-    ioctl(fd, TIOCSCTTY, 0);
-    dup2(fd, STDIN_FILENO);
-    dup2(fd, STDOUT_FILENO);
-    dup2(fd, STDERR_FILENO);
-    close(fd);
-
-    const char* path = "/system/bin/sh";
-    const char* args[] = {path, nullptr};
-    int rv = execv(path, const_cast<char**>(args));
-    LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
-    _exit(127);
-}
-
-bool FirstStageConsole(const std::string& cmdline) {
-    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
-}
-
 bool ForceNormalBoot(const std::string& cmdline) {
     return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index d2dc6d3..2f91663 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -838,20 +838,25 @@
 }
 
 static void UserspaceRebootWatchdogThread() {
-    if (!WaitForProperty("sys.init.userspace_reboot.in_progress", "1", 20s)) {
-        // TODO(b/135984674): should we reboot instead?
-        LOG(WARNING) << "Userspace reboot didn't start in 20 seconds. Stopping watchdog";
-        return;
+    auto started_timeout = GetMillisProperty("init.userspace_reboot.started.timeoutmillis", 10s);
+    if (!WaitForProperty("sys.init.userspace_reboot.in_progress", "1", started_timeout)) {
+        LOG(ERROR) << "Userspace reboot didn't start in " << started_timeout.count()
+                   << "ms. Switching to full reboot";
+        // Init might be wedged, don't try to write reboot reason into a persistent property and do
+        // a dirty reboot.
+        PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_start", false);
+        RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_start");
     }
     LOG(INFO) << "Starting userspace reboot watchdog";
-    auto timeout = GetMillisProperty("init.userspace_reboot.watchdog.timeoutmillis", 5min);
-    LOG(INFO) << "UserspaceRebootWatchdog timeout: " << timeout.count() << "ms";
-    if (!WaitForProperty("sys.boot_completed", "1", timeout)) {
-        LOG(ERROR) << "Failed to boot in " << timeout.count() << "ms. Switching to full reboot";
+    auto watchdog_timeout = GetMillisProperty("init.userspace_reboot.watchdog.timeoutmillis", 5min);
+    LOG(INFO) << "UserspaceRebootWatchdog timeout: " << watchdog_timeout.count() << "ms";
+    if (!WaitForProperty("sys.boot_completed", "1", watchdog_timeout)) {
+        LOG(ERROR) << "Failed to boot in " << watchdog_timeout.count()
+                   << "ms. Switching to full reboot";
         // In this case device is in a boot loop. Only way to recover is to do dirty reboot.
         // Since init might be wedged, don't try to write reboot reason into a persistent property.
-        PersistRebootReason("userspace_failed,watchdog_triggered", false);
-        RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered");
+        PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_boot", false);
+        RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_boot");
     }
     LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
 }
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index b9a6bc9..f3be0b8 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -79,32 +79,9 @@
     struct logger_entry entry;
   } __attribute__((aligned(4)));
 #ifdef __cplusplus
-  /* Matching log_time operators */
-  bool operator==(const log_msg& T) const {
-    return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
-  }
-  bool operator!=(const log_msg& T) const {
-    return !(*this == T);
-  }
-  bool operator<(const log_msg& T) const {
-    return (entry.sec < T.entry.sec) ||
-           ((entry.sec == T.entry.sec) && (entry.nsec < T.entry.nsec));
-  }
-  bool operator>=(const log_msg& T) const {
-    return !(*this < T);
-  }
-  bool operator>(const log_msg& T) const {
-    return (entry.sec > T.entry.sec) ||
-           ((entry.sec == T.entry.sec) && (entry.nsec > T.entry.nsec));
-  }
-  bool operator<=(const log_msg& T) const {
-    return !(*this > T);
-  }
   uint64_t nsec() const {
     return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
   }
-
-  /* packet methods */
   log_id_t id() {
     return static_cast<log_id_t>(entry.lid);
   }
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 19f95d4..7bf2120 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -137,6 +137,19 @@
   uint64_t cd_start_offset;
 };
 
+// Reads |T| at |readPtr| and increments |readPtr|. Returns std::nullopt if the boundary check
+// fails.
+template <typename T>
+static std::optional<T> TryConsumeUnaligned(uint8_t** readPtr, const uint8_t* bufStart,
+                                            size_t bufSize) {
+  if (bufSize < sizeof(T) || *readPtr - bufStart > bufSize - sizeof(T)) {
+    ALOGW("Zip: %zu byte read exceeds the boundary of allocated buf, offset %zu, bufSize %zu",
+          sizeof(T), *readPtr - bufStart, bufSize);
+    return std::nullopt;
+  }
+  return ConsumeUnaligned<T>(readPtr);
+}
+
 static ZipError FindCentralDirectoryInfoForZip64(const char* debugFileName, ZipArchive* archive,
                                                  off64_t eocdOffset, CentralDirectoryInfo* cdInfo) {
   if (eocdOffset <= sizeof(Zip64EocdLocator)) {
@@ -379,13 +392,19 @@
     std::optional<uint64_t> compressedFileSize;
     std::optional<uint64_t> localHeaderOffset;
     if (zip32UncompressedSize == UINT32_MAX) {
-      uncompressedFileSize = ConsumeUnaligned<uint64_t>(&readPtr);
+      uncompressedFileSize =
+          TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
+      if (!uncompressedFileSize.has_value()) return kInvalidOffset;
     }
     if (zip32CompressedSize == UINT32_MAX) {
-      compressedFileSize = ConsumeUnaligned<uint64_t>(&readPtr);
+      compressedFileSize =
+          TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
+      if (!compressedFileSize.has_value()) return kInvalidOffset;
     }
     if (zip32LocalFileHeaderOffset == UINT32_MAX) {
-      localHeaderOffset = ConsumeUnaligned<uint64_t>(&readPtr);
+      localHeaderOffset =
+          TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
+      if (!localHeaderOffset.has_value()) return kInvalidOffset;
     }
 
     // calculate how many bytes we read after the data size field.
@@ -606,6 +625,10 @@
 
 static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, const ZipEntry64* entry) {
   // Maximum possible size for data descriptor: 2 * 4 + 2 * 8 = 24 bytes
+  // The zip format doesn't specify the size of data descriptor. But we won't read OOB here even
+  // if the descriptor isn't present. Because the size cd + eocd in the end of the zipfile is
+  // larger than 24 bytes. And if the descriptor contains invalid data, we'll abort due to
+  // kInconsistentInformation.
   uint8_t ddBuf[24];
   off64_t offset = entry->offset;
   if (entry->method != kCompressStored) {
@@ -1499,8 +1522,9 @@
       return false;
     }
   } else {
-    if (off < 0 || off > data_length_) {
-      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
+    if (off < 0 || data_length_ < len || off > data_length_ - len) {
+      ALOGE("Zip: invalid offset: %" PRId64 ", read length: %zu, data length: %" PRId64, off, len,
+            data_length_);
       return false;
     }
     memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 3a1d36f..56a670a 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -145,6 +145,8 @@
 # libcore failure logging
 90100 exp_det_cert_pin_failure (certs|4)
 
+# 150000 - 160000 reserved for Android Automotive builds
+
 1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
 
 # for events that go to stats log buffer
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
index aa30707..65eb854 100644
--- a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
@@ -2,10 +2,6 @@
     <hal format="hidl">
         <name>android.hardware.keymaster</name>
         <transport>hwbinder</transport>
-        <version>4.0</version>
-        <interface>
-        <name>IKeymasterDevice</name>
-            <instance>default</instance>
-        </interface>
+        <fqname>@4.0::IKeymasterDevice/default</fqname>
     </hal>
 </manifest>