Merge "Add new directory for stats metadata" into rvc-dev
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 14ccfbe..2199fd3 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1423,13 +1423,13 @@
 #endif
 }
 
-static bool _is_valid_fd(int fd) {
+static bool _is_valid_os_fd(int fd) {
     // Disallow invalid FDs and stdin/out/err as well.
     if (fd < 3) {
         return false;
     }
 #ifdef _WIN32
-    HANDLE handle = adb_get_os_handle(fd);
+    auto handle = (HANDLE)fd;
     DWORD info = 0;
     if (GetHandleInformation(handle, &info) == 0) {
         return false;
@@ -2005,7 +2005,7 @@
 #endif
         }
         int connection_fd = atoi(argv[1]);
-        if (!_is_valid_fd(connection_fd)) {
+        if (!_is_valid_os_fd(connection_fd)) {
             error_exit("Invalid connection_fd number given: %d", connection_fd);
         }
 
@@ -2013,7 +2013,7 @@
         close_on_exec(connection_fd);
 
         int output_fd = atoi(argv[2]);
-        if (!_is_valid_fd(output_fd)) {
+        if (!_is_valid_os_fd(output_fd)) {
             error_exit("Invalid output_fd number given: %d", output_fd);
         }
         output_fd = adb_register_socket(output_fd);
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index cc38926..94bd8f5 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -112,6 +112,7 @@
     uint64_t bytes_transferred;
     uint64_t bytes_expected;
     bool expect_multiple_files;
+    std::string last_progress_str;
 
     TransferLedger() {
         Reset();
@@ -127,6 +128,7 @@
     }
 
     void Reset() {
+        last_progress_str.clear();
         start_time = std::chrono::steady_clock::now();
         files_transferred = 0;
         files_skipped = 0;
@@ -181,7 +183,10 @@
                     android::base::StringPrintf("[%4s] %s", overall_percentage_str, file.c_str());
             }
         }
-        lp.Print(output, LinePrinter::LineType::INFO);
+        if (output != last_progress_str) {
+            lp.Print(output, LinePrinter::LineType::INFO);
+            last_progress_str = std::move(output);
+        }
     }
 
     void ReportTransferRate(LinePrinter& lp, const std::string& name, TransferDirection direction) {
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index fd608cc..a9e65dc 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -41,8 +41,7 @@
 
 static inline int32_t read_int32(borrowed_fd fd) {
     int32_t result;
-    ReadFully(fd, &result, sizeof(result));
-    return result;
+    return ReadFdExactly(fd, &result, sizeof(result)) ? result : -1;
 }
 
 static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
@@ -54,11 +53,14 @@
 
 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));
-    ReadFully(fd, bytes->data() + old_size + sizeof(le_size), 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) {
@@ -200,7 +202,7 @@
         return {};
     }
     auto [pipe_read_fd, pipe_write_fd] = print_fds;
-    auto pipe_write_fd_param = std::to_string(pipe_write_fd);
+    auto pipe_write_fd_param = std::to_string(intptr_t(adb_get_os_handle(pipe_write_fd)));
     close_on_exec(pipe_read_fd);
 
     std::vector<std::string> args(std::move(files));
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
index 1f47de8..737563c 100644
--- a/adb/client/incremental_server.cpp
+++ b/adb/client/incremental_server.cpp
@@ -228,7 +228,7 @@
     std::vector<char> pendingBlocks_;
 
     // True when client notifies that all the data has been received
-    bool servingComplete_;
+    bool servingComplete_ = false;
 };
 
 bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
diff --git a/adb/client/incremental_utils.cpp b/adb/client/incremental_utils.cpp
index 316480c..caadb26 100644
--- a/adb/client/incremental_utils.cpp
+++ b/adb/client/incremental_utils.cpp
@@ -18,6 +18,7 @@
 
 #include "incremental_utils.h"
 
+#include <android-base/mapped_file.h>
 #include <android-base/strings.h>
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_writer.h>
@@ -26,6 +27,7 @@
 #include <numeric>
 #include <unordered_set>
 
+#include "adb_trace.h"
 #include "sysdeps.h"
 
 static constexpr int kBlockSize = 4096;
@@ -155,18 +157,81 @@
     return zipPriorityBlocks;
 }
 
-// TODO(b/151676293): avoid using OpenArchiveFd that reads local file headers
+[[maybe_unused]] static ZipArchiveHandle openZipArchiveFd(int fd) {
+    bool transferFdOwnership = false;
+#ifdef _WIN32
+    //
+    // Need to create a special CRT FD here as the current one is not compatible with
+    // normal read()/write() calls that libziparchive uses.
+    // To make this work we have to create a copy of the file handle, as CRT doesn't care
+    // and closes it together with the new descriptor.
+    //
+    // Note: don't move this into a helper function, it's better to be hard to reuse because
+    //       the code is ugly and won't work unless it's a last resort.
+    //
+    auto handle = adb_get_os_handle(fd);
+    HANDLE dupedHandle;
+    if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), &dupedHandle, 0,
+                           false, DUPLICATE_SAME_ACCESS)) {
+        D("%s failed at DuplicateHandle: %d", __func__, (int)::GetLastError());
+        return {};
+    }
+    fd = _open_osfhandle((intptr_t)dupedHandle, _O_RDONLY | _O_BINARY);
+    if (fd < 0) {
+        D("%s failed at _open_osfhandle: %d", __func__, errno);
+        ::CloseHandle(handle);
+        return {};
+    }
+    transferFdOwnership = true;
+#endif
+    ZipArchiveHandle zip;
+    if (OpenArchiveFd(fd, "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);
+#endif
+        return {};
+    }
+    return zip;
+}
+
+static std::pair<ZipArchiveHandle, std::unique_ptr<android::base::MappedFile>> openZipArchive(
+        int fd, int64_t fileSize) {
+#ifndef __LP64__
+    if (fileSize >= INT_MAX) {
+        return {openZipArchiveFd(fd), nullptr};
+    }
+#endif
+    auto mapping =
+            android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), 0, fileSize, PROT_READ);
+    if (!mapping) {
+        D("%s failed at FromOsHandle: %d", __func__, errno);
+        return {};
+    }
+    ZipArchiveHandle zip;
+    if (OpenArchiveFromMemory(mapping->data(), mapping->size(), "apk_mapping", &zip) != 0) {
+        D("%s failed at OpenArchiveFromMemory: %d", __func__, errno);
+        return {};
+    }
+    return {zip, std::move(mapping)};
+}
+
+// TODO(b/151676293): avoid using libziparchive as it reads local file headers
 // which causes additional performance cost. Instead, only read from central directory.
 static std::vector<int32_t> InstallationPriorityBlocks(int fd, int64_t fileSize) {
-    std::vector<int32_t> installationPriorityBlocks;
-    ZipArchiveHandle zip;
-    if (OpenArchiveFd(fd, "", &zip, false) != 0) {
+    auto [zip, _] = openZipArchive(fd, fileSize);
+    if (!zip) {
         return {};
     }
+
     void* cookie = nullptr;
     if (StartIteration(zip, &cookie) != 0) {
+        D("%s failed at StartIteration: %d", __func__, errno);
         return {};
     }
+
+    std::vector<int32_t> installationPriorityBlocks;
     ZipEntry entry;
     std::string_view entryName;
     while (Next(cookie, &entry, &entryName) == 0) {
@@ -183,6 +248,7 @@
             int32_t endBlockIndex = offsetToBlockIndex(entryEndOffset);
             int32_t numNewBlocks = endBlockIndex - startBlockIndex + 1;
             appendBlocks(startBlockIndex, numNewBlocks, &installationPriorityBlocks);
+            D("\tadding to priority blocks: '%.*s'", (int)entryName.size(), entryName.data());
         } else if (entryName == "classes.dex") {
             // Only the head is needed for installation
             int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
@@ -213,4 +279,4 @@
     unduplicate(priorityBlocks);
     return priorityBlocks;
 }
-}  // namespace incremental
\ No newline at end of file
+}  // namespace incremental
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
index 562f587..fd55020 100644
--- a/adb/fdevent/fdevent.cpp
+++ b/adb/fdevent/fdevent.cpp
@@ -63,7 +63,10 @@
 
     int fd_num = fd.get();
 
-    fdevent* fde = new fdevent();
+    auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fdevent{});
+    CHECK(inserted);
+
+    fdevent* fde = &it->second;
     fde->id = fdevent_id_++;
     fde->state = 0;
     fde->fd = std::move(fd);
@@ -76,10 +79,6 @@
         LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
     }
 
-    auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde);
-    CHECK(inserted);
-    UNUSED(it);
-
     this->Register(fde);
     return fde;
 }
@@ -92,12 +91,12 @@
 
     this->Unregister(fde);
 
-    auto erased = this->installed_fdevents_.erase(fde->fd.get());
+    unique_fd fd = std::move(fde->fd);
+
+    auto erased = this->installed_fdevents_.erase(fd.get());
     CHECK_EQ(1UL, erased);
 
-    unique_fd result = std::move(fde->fd);
-    delete fde;
-    return result;
+    return fd;
 }
 
 void fdevent_context::Add(fdevent* fde, unsigned events) {
@@ -123,9 +122,9 @@
 
     for (const auto& [fd, fde] : this->installed_fdevents_) {
         UNUSED(fd);
-        auto timeout_opt = fde->timeout;
+        auto timeout_opt = fde.timeout;
         if (timeout_opt) {
-            auto deadline = fde->last_active + *timeout_opt;
+            auto deadline = fde.last_active + *timeout_opt;
             auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
             if (time_left < 0ms) {
                 time_left = 0ms;
@@ -194,11 +193,13 @@
 #endif
 }
 
-static auto& g_ambient_fdevent_context =
-        *new std::unique_ptr<fdevent_context>(fdevent_create_context());
+static auto& g_ambient_fdevent_context() {
+    static auto context = fdevent_create_context().release();
+    return context;
+}
 
 static fdevent_context* fdevent_get_ambient() {
-    return g_ambient_fdevent_context.get();
+    return g_ambient_fdevent_context();
 }
 
 fdevent* fdevent_create(int fd, fd_func func, void* arg) {
@@ -256,5 +257,6 @@
 }
 
 void fdevent_reset() {
-    g_ambient_fdevent_context = fdevent_create_context();
+    auto old = std::exchange(g_ambient_fdevent_context(), fdevent_create_context().release());
+    delete old;
 }
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 86814d7..9fc3b2c 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -52,6 +52,20 @@
     unsigned events;
 };
 
+struct fdevent final {
+    uint64_t id;
+
+    unique_fd fd;
+    int force_eof = 0;
+
+    uint16_t state = 0;
+    std::optional<std::chrono::milliseconds> timeout;
+    std::chrono::steady_clock::time_point last_active;
+
+    std::variant<fd_func, fd_func2> func;
+    void* arg = nullptr;
+};
+
 struct fdevent_context {
   public:
     virtual ~fdevent_context() = default;
@@ -113,7 +127,7 @@
     std::atomic<bool> terminate_loop_ = false;
 
   protected:
-    std::unordered_map<int, fdevent*> installed_fdevents_;
+    std::unordered_map<int, fdevent> installed_fdevents_;
 
   private:
     uint64_t fdevent_id_ = 0;
@@ -121,20 +135,6 @@
     std::deque<std::function<void()>> run_queue_ GUARDED_BY(run_queue_mutex_);
 };
 
-struct fdevent {
-    uint64_t id;
-
-    unique_fd fd;
-    int force_eof = 0;
-
-    uint16_t state = 0;
-    std::optional<std::chrono::milliseconds> timeout;
-    std::chrono::steady_clock::time_point last_active;
-
-    std::variant<fd_func, fd_func2> func;
-    void* arg = nullptr;
-};
-
 // Backwards compatibility shims that forward to the global fdevent_context.
 fdevent* fdevent_create(int fd, fd_func func, void* arg);
 fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
diff --git a/adb/fdevent/fdevent_epoll.cpp b/adb/fdevent/fdevent_epoll.cpp
index e3d1674..4ef41d1 100644
--- a/adb/fdevent/fdevent_epoll.cpp
+++ b/adb/fdevent/fdevent_epoll.cpp
@@ -155,15 +155,15 @@
             event_map[fde] = events;
         }
 
-        for (const auto& [fd, fde] : installed_fdevents_) {
+        for (auto& [fd, fde] : installed_fdevents_) {
             unsigned events = 0;
-            if (auto it = event_map.find(fde); it != event_map.end()) {
+            if (auto it = event_map.find(&fde); it != event_map.end()) {
                 events = it->second;
             }
 
             if (events == 0) {
-                if (fde->timeout) {
-                    auto deadline = fde->last_active + *fde->timeout;
+                if (fde.timeout) {
+                    auto deadline = fde.last_active + *fde.timeout;
                     if (deadline < post_poll) {
                         events |= FDE_TIMEOUT;
                     }
@@ -171,13 +171,13 @@
             }
 
             if (events != 0) {
-                LOG(DEBUG) << dump_fde(fde) << " got events " << std::hex << std::showbase
+                LOG(DEBUG) << dump_fde(&fde) << " got events " << std::hex << std::showbase
                            << events;
-                fde_events.push_back({fde, events});
-                fde->last_active = post_poll;
+                fde_events.push_back({&fde, events});
+                fde.last_active = post_poll;
             }
         }
-        this->HandleEvents(std::move(fde_events));
+        this->HandleEvents(fde_events);
         fde_events.clear();
     }
 
diff --git a/adb/fdevent/fdevent_epoll.h b/adb/fdevent/fdevent_epoll.h
index 684fa32..6214d2e 100644
--- a/adb/fdevent/fdevent_epoll.h
+++ b/adb/fdevent/fdevent_epoll.h
@@ -47,12 +47,7 @@
   protected:
     virtual void Interrupt() final;
 
-  public:
-    // All operations to fdevent should happen only in the main thread.
-    // That's why we don't need a lock for fdevent.
-    std::unordered_map<int, fdevent*> epoll_node_map_;
-    std::list<fdevent*> pending_list_;
-
+  private:
     unique_fd epoll_fd_;
     unique_fd interrupt_fd_;
     fdevent* interrupt_fde_ = nullptr;
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index cc4a7a1..ac86c08 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -103,24 +103,27 @@
 void fdevent_context_poll::Loop() {
     main_thread_id_ = android::base::GetThreadId();
 
+    std::vector<adb_pollfd> pollfds;
+    std::vector<fdevent_event> poll_events;
+
     while (true) {
         if (terminate_loop_) {
             break;
         }
 
         D("--- --- waiting for events");
-        std::vector<adb_pollfd> pollfds;
+        pollfds.clear();
         for (const auto& [fd, fde] : this->installed_fdevents_) {
             adb_pollfd pfd;
             pfd.fd = fd;
             pfd.events = 0;
-            if (fde->state & FDE_READ) {
+            if (fde.state & FDE_READ) {
                 pfd.events |= POLLIN;
             }
-            if (fde->state & FDE_WRITE) {
+            if (fde.state & FDE_WRITE) {
                 pfd.events |= POLLOUT;
             }
-            if (fde->state & FDE_ERROR) {
+            if (fde.state & FDE_ERROR) {
                 pfd.events |= POLLERR;
             }
 #if defined(__linux__)
@@ -147,7 +150,6 @@
         }
 
         auto post_poll = std::chrono::steady_clock::now();
-        std::vector<fdevent_event> poll_events;
 
         for (const auto& pollfd : pollfds) {
             unsigned events = 0;
@@ -170,7 +172,7 @@
 
             auto it = this->installed_fdevents_.find(pollfd.fd);
             CHECK(it != this->installed_fdevents_.end());
-            fdevent* fde = it->second;
+            fdevent* fde = &it->second;
 
             if (events == 0) {
                 if (fde->timeout) {
@@ -187,7 +189,8 @@
                 fde->last_active = post_poll;
             }
         }
-        this->HandleEvents(std::move(poll_events));
+        this->HandleEvents(poll_events);
+        poll_events.clear();
     }
 
     main_thread_id_.reset();
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 4efbc02..3e781b8 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -276,6 +276,7 @@
 class Process {
   public:
     constexpr explicit Process(HANDLE h = nullptr) : h_(h) {}
+    constexpr Process(Process&& other) : h_(std::exchange(other.h_, nullptr)) {}
     ~Process() { close(); }
     constexpr explicit operator bool() const { return h_ != nullptr; }
 
@@ -292,6 +293,8 @@
     }
 
   private:
+    DISALLOW_COPY_AND_ASSIGN(Process);
+
     void close() {
         if (*this) {
             ::CloseHandle(h_);
@@ -666,6 +669,8 @@
 class Process {
   public:
     constexpr explicit Process(pid_t pid) : pid_(pid) {}
+    constexpr Process(Process&& other) : pid_(std::exchange(other.pid_, -1)) {}
+
     constexpr explicit operator bool() const { return pid_ >= 0; }
 
     void wait() {
@@ -682,6 +687,8 @@
     }
 
   private:
+    DISALLOW_COPY_AND_ASSIGN(Process);
+
     pid_t pid_;
 };
 
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 46d4bd3..2c0989e 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -34,14 +34,54 @@
 #include <stdlib.h>
 #include <string.h>
 
-void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {
+static void bootimg_set_cmdline_v3(boot_img_hdr_v3* h, const std::string& cmdline) {
     if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
     strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
+void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {
+    if (h->header_version == 3) {
+        return bootimg_set_cmdline_v3(reinterpret_cast<boot_img_hdr_v3*>(h), cmdline);
+    }
+    if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
+    strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
+}
+
+static boot_img_hdr_v3* mkbootimg_v3(const std::vector<char>& kernel,
+                                     const std::vector<char>& ramdisk, const boot_img_hdr_v2& src,
+                                     std::vector<char>* out) {
+#define V3_PAGE_SIZE 4096
+    const size_t page_mask = V3_PAGE_SIZE - 1;
+    int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
+    int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
+
+    int64_t bootimg_size = V3_PAGE_SIZE + kernel_actual + ramdisk_actual;
+    out->resize(bootimg_size);
+
+    boot_img_hdr_v3* hdr = reinterpret_cast<boot_img_hdr_v3*>(out->data());
+
+    memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+    hdr->kernel_size = kernel.size();
+    hdr->ramdisk_size = ramdisk.size();
+    hdr->os_version = src.os_version;
+    hdr->header_size = sizeof(boot_img_hdr_v3);
+    hdr->header_version = 3;
+
+    memcpy(hdr->magic + V3_PAGE_SIZE, kernel.data(), kernel.size());
+    memcpy(hdr->magic + V3_PAGE_SIZE + kernel_actual, ramdisk.data(), ramdisk.size());
+
+    return hdr;
+}
+
 boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
                            const std::vector<char>& second, const std::vector<char>& dtb,
                            size_t base, const boot_img_hdr_v2& src, std::vector<char>* out) {
+    if (src.header_version == 3) {
+        if (!second.empty() || !dtb.empty()) {
+            die("Second stage bootloader and dtb not supported in v3 boot image\n");
+        }
+        return reinterpret_cast<boot_img_hdr_v2*>(mkbootimg_v3(kernel, ramdisk, src, out));
+    }
     const size_t page_mask = src.page_size - 1;
 
     int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7fdc28b..7f6e723 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -464,7 +464,7 @@
     }
 
     // Is this actually a boot image?
-    if (kernel_data.size() < sizeof(boot_img_hdr_v2)) {
+    if (kernel_data.size() < sizeof(boot_img_hdr_v3)) {
         die("cannot load '%s': too short", kernel.c_str());
     }
     if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
@@ -493,7 +493,7 @@
 
     std::vector<char> dtb_data;
     if (!g_dtb_path.empty()) {
-        if (g_boot_img_hdr.header_version < 2) {
+        if (g_boot_img_hdr.header_version != 2) {
                     die("Argument dtb not supported for boot image header version %d\n",
                         g_boot_img_hdr.header_version);
         }
diff --git a/libstats/pull/include/stats_pull_atom_callback.h b/libstats/pull/include/stats_pull_atom_callback.h
index ad9b04e..0b0df2b 100644
--- a/libstats/pull/include/stats_pull_atom_callback.h
+++ b/libstats/pull/include/stats_pull_atom_callback.h
@@ -112,6 +112,8 @@
  * invoke the callback when the stats service determines that this atom needs to be
  * pulled.
  *
+ * Requires the REGISTER_STATS_PULL_ATOM permission.
+ *
  * \param atom_tag          The tag of the atom for this pull atom callback.
  * \param metadata          Optional metadata specifying the timeout, cool down time, and
  *                          additive fields for mapping isolated to host uids.
@@ -128,6 +130,8 @@
  * Unregisters a callback for an atom when that atom is to be pulled. Note that any ongoing
  * pulls will still occur.
  *
+ * Requires the REGISTER_STATS_PULL_ATOM permission.
+ *
  * \param atomTag           The tag of the atom of which to unregister
  */
 void AStatsManager_unregisterPullAtomCallback(int32_t atom_tag);
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 047af90..098a9cb 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -126,6 +126,9 @@
 int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
                       bool assume_ownership = true);
 
+int32_t OpenArchiveFdRange(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+                           off64_t length, off64_t offset, bool assume_ownership = true);
+
 int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
                               ZipArchiveHandle* handle);
 /*
@@ -222,6 +225,12 @@
 
 int GetFileDescriptor(const ZipArchiveHandle archive);
 
+/**
+ * Returns the offset of the zip archive in the backing file descriptor, or 0 if the zip archive is
+ * not backed by a file descriptor.
+ */
+off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive);
+
 const char* ErrorCodeString(int32_t error_code);
 
 #if !defined(_WIN32)
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 68837cc..958c34b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -162,8 +162,8 @@
 }
 #endif
 
-ZipArchive::ZipArchive(const int fd, bool assume_ownership)
-    : mapped_zip(fd),
+ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
+    : mapped_zip(map),
       close_file(assume_ownership),
       directory_offset(0),
       central_directory(),
@@ -173,7 +173,8 @@
       hash_table(nullptr) {
 #if defined(__BIONIC__)
   if (assume_ownership) {
-    android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
+    CHECK(mapped_zip.HasFd());
+    android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
   }
 #endif
 }
@@ -442,14 +443,32 @@
 
 int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
                       bool assume_ownership) {
-  ZipArchive* archive = new ZipArchive(fd, assume_ownership);
+  ZipArchive* archive = new ZipArchive(MappedZipFile(fd), assume_ownership);
   *handle = archive;
   return OpenArchiveInternal(archive, debug_file_name);
 }
 
+int32_t OpenArchiveFdRange(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+                           off64_t length, off64_t offset, bool assume_ownership) {
+  ZipArchive* archive = new ZipArchive(MappedZipFile(fd, length, offset), assume_ownership);
+  *handle = archive;
+
+  if (length < 0) {
+    ALOGW("Invalid zip length %" PRId64, length);
+    return kIoError;
+  }
+
+  if (offset < 0) {
+    ALOGW("Invalid zip offset %" PRId64, offset);
+    return kIoError;
+  }
+
+  return OpenArchiveInternal(archive, debug_file_name);
+}
+
 int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
   const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
-  ZipArchive* archive = new ZipArchive(fd, true);
+  ZipArchive* archive = new ZipArchive(MappedZipFile(fd), true);
   *handle = archive;
 
   if (fd < 0) {
@@ -1126,6 +1145,10 @@
   return archive->mapped_zip.GetFileDescriptor();
 }
 
+off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive) {
+  return archive->mapped_zip.GetFileOffset();
+}
+
 #if !defined(_WIN32)
 class ProcessWriter : public zip_archive::Writer {
  public:
@@ -1165,31 +1188,65 @@
   return base_ptr_;
 }
 
+off64_t MappedZipFile::GetFileOffset() const {
+  return fd_offset_;
+}
+
 off64_t MappedZipFile::GetFileLength() const {
   if (has_fd_) {
-    off64_t result = lseek64(fd_, 0, SEEK_END);
-    if (result == -1) {
+    if (data_length_ != -1) {
+      return data_length_;
+    }
+    data_length_ = lseek64(fd_, 0, SEEK_END);
+    if (data_length_ == -1) {
       ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
     }
-    return result;
+    return data_length_;
   } else {
     if (base_ptr_ == nullptr) {
       ALOGE("Zip: invalid file map");
       return -1;
     }
-    return static_cast<off64_t>(data_length_);
+    return data_length_;
   }
 }
 
 // Attempts to read |len| bytes into |buf| at offset |off|.
 bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
   if (has_fd_) {
-    if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
+    if (off < 0) {
+      ALOGE("Zip: invalid offset %" PRId64, off);
+      return false;
+    }
+
+    off64_t read_offset;
+    if (__builtin_add_overflow(fd_offset_, off, &read_offset)) {
+      ALOGE("Zip: invalid read offset %" PRId64 " overflows, fd offset %" PRId64, off, fd_offset_);
+      return false;
+    }
+
+    if (data_length_ != -1) {
+      off64_t read_end;
+      if (len > std::numeric_limits<off64_t>::max() ||
+          __builtin_add_overflow(off, static_cast<off64_t>(len), &read_end)) {
+        ALOGE("Zip: invalid read length %" PRId64 " overflows, offset %" PRId64,
+              static_cast<off64_t>(len), off);
+        return false;
+      }
+
+      if (read_end > data_length_) {
+        ALOGE("Zip: invalid read length %" PRId64 " exceeds data length %" PRId64 ", offset %"
+              PRId64, static_cast<off64_t>(len), data_length_, off);
+        return false;
+      }
+    }
+
+    if (!android::base::ReadFullyAtOffset(fd_, buf, len, read_offset)) {
       ALOGE("Zip: failed to read at offset %" PRId64, off);
       return false;
     }
   } else {
-    if (off < 0 || off > static_cast<off64_t>(data_length_)) {
+    if (off < 0 || off > data_length_) {
       ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
       return false;
     }
@@ -1207,7 +1264,8 @@
 bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
   if (mapped_zip.HasFd()) {
     directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
-                                                      cd_start_offset, cd_size, PROT_READ);
+                                                      mapped_zip.GetFileOffset() + cd_start_offset,
+                                                      cd_size, PROT_READ);
     if (!directory_map) {
       ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
             cd_start_offset, cd_size, strerror(errno));
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 1d05fc7..3625038 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -97,10 +97,14 @@
 class MappedZipFile {
  public:
   explicit MappedZipFile(const int fd)
-      : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
+      : has_fd_(true), fd_(fd), fd_offset_(0), base_ptr_(nullptr), data_length_(-1) {}
+
+  explicit MappedZipFile(const int fd, off64_t length, off64_t offset)
+      : has_fd_(true), fd_(fd), fd_offset_(offset), base_ptr_(nullptr), data_length_(length) {}
 
   explicit MappedZipFile(const void* address, size_t length)
-      : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
+      : has_fd_(false), fd_(-1), fd_offset_(0), base_ptr_(address),
+        data_length_(static_cast<off64_t>(length)) {}
 
   bool HasFd() const { return has_fd_; }
 
@@ -108,6 +112,8 @@
 
   const void* GetBasePtr() const;
 
+  off64_t GetFileOffset() const;
+
   off64_t GetFileLength() const;
 
   bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
@@ -120,9 +126,10 @@
   const bool has_fd_;
 
   const int fd_;
+  const off64_t fd_offset_;
 
   const void* const base_ptr_;
-  const off64_t data_length_;
+  mutable off64_t data_length_;
 };
 
 class CentralDirectory {
@@ -180,7 +187,7 @@
   uint32_t hash_table_size;
   ZipStringOffset* hash_table;
 
-  ZipArchive(const int fd, bool assume_ownership);
+  ZipArchive(MappedZipFile&& map, bool assume_ownership);
   ZipArchive(const void* address, size_t length);
   ~ZipArchive();
 
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 0916304..35fb3fe 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -108,6 +108,32 @@
   close(fd);
 }
 
+TEST(ziparchive, OpenAssumeFdRangeOwnership) {
+  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+  ASSERT_NE(-1, fd);
+  const off64_t length = lseek64(fd, 0, SEEK_END);
+  ASSERT_NE(-1, length);
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
+                                  static_cast<size_t>(length), 0));
+  CloseArchive(handle);
+  ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
+  ASSERT_EQ(EBADF, errno);
+}
+
+TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
+  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+  ASSERT_NE(-1, fd);
+  const off64_t length = lseek(fd, 0, SEEK_END);
+  ASSERT_NE(-1, length);
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
+                                  static_cast<size_t>(length), 0, false));
+  CloseArchive(handle);
+  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
+  close(fd);
+}
+
 TEST(ziparchive, Iteration_std_string_view) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -254,6 +280,48 @@
   CloseArchive(handle);
 }
 
+TEST(ziparchive, OpenArchiveFdRange) {
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+
+  const std::string leading_garbage(21, 'x');
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
+                                        leading_garbage.size()));
+
+  std::string valid_content;
+  ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
+
+  const std::string ending_garbage(42, 'x');
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
+                                        ending_garbage.size()));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
+                                  valid_content.size(),
+                                  static_cast<off64_t>(leading_garbage.size())));
+
+  // An entry that's deflated.
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+  const uint32_t a_size = data.uncompressed_length;
+  ASSERT_EQ(a_size, kATxtContents.size());
+  auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
+  ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
+
+  // An entry that's stored.
+  ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
+  const uint32_t b_size = data.uncompressed_length;
+  ASSERT_EQ(b_size, kBTxtContents.size());
+  buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
+  ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
+
+  CloseArchive(handle);
+}
+
 TEST(ziparchive, ExtractToMemory) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));