Merge "libsnapshot_test: Fix running on DSUs."
diff --git a/adb/Android.bp b/adb/Android.bp
index 432770c..9db151d 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -218,6 +218,7 @@
         "client/usb_dispatch.cpp",
         "client/transport_local.cpp",
         "client/transport_mdns.cpp",
+        "client/mdns_utils.cpp",
         "client/transport_usb.cpp",
         "client/pairing/pairing_client.cpp",
     ],
@@ -264,7 +265,10 @@
 cc_test_host {
     name: "adb_test",
     defaults: ["adb_defaults"],
-    srcs: libadb_test_srcs,
+    srcs: libadb_test_srcs + [
+        "client/mdns_utils_test.cpp",
+    ],
+
     static_libs: [
         "libadb_crypto_static",
         "libadb_host",
diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h
index 3a6b0b1..42f414b 100644
--- a/adb/adb_wifi.h
+++ b/adb/adb_wifi.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <optional>
 #include <string>
 
 #include "adb.h"
@@ -30,6 +31,19 @@
 std::string mdns_check();
 std::string mdns_list_discovered_services();
 
+struct MdnsInfo {
+    std::string service_name;
+    std::string service_type;
+    std::string addr;
+    uint16_t port = 0;
+
+    MdnsInfo(std::string_view name, std::string_view type, std::string_view addr, uint16_t port)
+        : service_name(name), service_type(type), addr(addr), port(port) {}
+};
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name);
+std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name);
+
 #else  // !ADB_HOST
 
 struct AdbdAuthContext;
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 31c938c..a308732 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -417,17 +417,19 @@
 }
 
 const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
-    static std::string cached_error [[clang::no_destroy]];
-    static const std::optional<FeatureSet> features
-            [[clang::no_destroy]] ([]() -> std::optional<FeatureSet> {
-                std::string result;
-                if (adb_query(format_host_command("features"), &result, &cached_error)) {
-                    return StringToFeatureSet(result);
-                }
-                return std::nullopt;
-            }());
-    if (!features && error) {
-        *error = cached_error;
+    static std::mutex feature_mutex [[clang::no_destroy]];
+    static std::optional<FeatureSet> features [[clang::no_destroy]] GUARDED_BY(feature_mutex);
+    std::lock_guard<std::mutex> lock(feature_mutex);
+    if (!features) {
+        std::string result;
+        std::string err;
+        if (adb_query(format_host_command("features"), &result, &err)) {
+            features = StringToFeatureSet(result);
+        } else {
+            if (error) {
+                *error = err;
+            }
+        }
     }
     return features;
 }
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index d66d400..d6f536e 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -301,7 +301,7 @@
 }
 
 template <class TimePoint>
-static int msBetween(TimePoint start, TimePoint end) {
+static int ms_between(TimePoint start, TimePoint end) {
     return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
 }
 
@@ -349,7 +349,7 @@
     }
 
     const auto end = clock::now();
-    printf("Install command complete in %d ms\n", msBetween(start, end));
+    printf("Install command complete in %d ms\n", ms_between(start, end));
 
     if (wait) {
         (*server_process).wait();
@@ -358,9 +358,9 @@
     return 0;
 }
 
-static std::pair<InstallMode, std::optional<InstallMode>> calculateInstallMode(
-        InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incrementalRequest) {
-    if (incrementalRequest == CmdlineOption::Enable) {
+static std::pair<InstallMode, std::optional<InstallMode>> calculate_install_mode(
+        InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incremental_request) {
+    if (incremental_request == CmdlineOption::Enable) {
         if (fastdeploy) {
             error_exit(
                     "--incremental and --fast-deploy options are incompatible. "
@@ -369,30 +369,30 @@
     }
 
     if (modeFromArgs != INSTALL_DEFAULT) {
-        if (incrementalRequest == CmdlineOption::Enable) {
+        if (incremental_request == CmdlineOption::Enable) {
             error_exit("--incremental is not compatible with other installation modes");
         }
         return {modeFromArgs, std::nullopt};
     }
 
-    if (incrementalRequest != CmdlineOption::Disable && !is_abb_exec_supported()) {
-        if (incrementalRequest == CmdlineOption::None) {
-            incrementalRequest = CmdlineOption::Disable;
+    if (incremental_request != CmdlineOption::Disable && !is_abb_exec_supported()) {
+        if (incremental_request == CmdlineOption::None) {
+            incremental_request = CmdlineOption::Disable;
         } else {
             error_exit("Device doesn't support incremental installations");
         }
     }
-    if (incrementalRequest == CmdlineOption::None) {
+    if (incremental_request == CmdlineOption::None) {
         // check if the host is ok with incremental by default
         if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) {
             using namespace android::base;
             auto val = ParseBool(incrementalFromEnv);
             if (val == ParseBoolResult::kFalse) {
-                incrementalRequest = CmdlineOption::Disable;
+                incremental_request = CmdlineOption::Disable;
             }
         }
     }
-    if (incrementalRequest == CmdlineOption::None) {
+    if (incremental_request == CmdlineOption::None) {
         // still ok: let's see if the device allows using incremental by default
         // it starts feeling like we're looking for an excuse to not to use incremental...
         std::string error;
@@ -408,17 +408,17 @@
             using namespace android::base;
             auto val = ParseBool(buf);
             if (val == ParseBoolResult::kFalse) {
-                incrementalRequest = CmdlineOption::Disable;
+                incremental_request = CmdlineOption::Disable;
             }
         }
     }
 
-    if (incrementalRequest == CmdlineOption::Enable) {
+    if (incremental_request == CmdlineOption::Enable) {
         // explicitly requested - no fallback
         return {INSTALL_INCREMENTAL, std::nullopt};
     }
     const auto bestMode = best_install_mode();
-    if (incrementalRequest == CmdlineOption::None) {
+    if (incremental_request == CmdlineOption::None) {
         // no opinion - use incremental, fallback to regular on a failure.
         return {INSTALL_INCREMENTAL, bestMode};
     }
@@ -426,57 +426,75 @@
     return {bestMode, std::nullopt};
 }
 
-int install_app(int argc, const char** argv) {
-    std::vector<int> processedArgIndices;
-    InstallMode installMode = INSTALL_DEFAULT;
-    bool use_fastdeploy = false;
-    bool is_reinstall = false;
-    bool wait = false;
-    auto incremental_request = CmdlineOption::None;
-    FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+static std::vector<const char*> parse_install_mode(std::vector<const char*> argv,
+                                                   InstallMode* install_mode,
+                                                   CmdlineOption* incremental_request,
+                                                   bool* incremental_wait) {
+    *install_mode = INSTALL_DEFAULT;
+    *incremental_request = CmdlineOption::None;
+    *incremental_wait = false;
 
-    for (int i = 1; i < argc; i++) {
-        if (argv[i] == "--streaming"sv) {
-            processedArgIndices.push_back(i);
-            installMode = INSTALL_STREAM;
-        } else if (argv[i] == "--no-streaming"sv) {
-            processedArgIndices.push_back(i);
-            installMode = INSTALL_PUSH;
-        } else if (argv[i] == "-r"sv) {
-            // Note that this argument is not added to processedArgIndices because it
-            // must be passed through to pm
-            is_reinstall = true;
-        } else if (argv[i] == "--fastdeploy"sv) {
-            processedArgIndices.push_back(i);
-            use_fastdeploy = true;
-        } else if (argv[i] == "--no-fastdeploy"sv) {
-            processedArgIndices.push_back(i);
-            use_fastdeploy = false;
-        } else if (argv[i] == "--force-agent"sv) {
-            processedArgIndices.push_back(i);
-            agent_update_strategy = FastDeploy_AgentUpdateAlways;
-        } else if (argv[i] == "--date-check-agent"sv) {
-            processedArgIndices.push_back(i);
-            agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
-        } else if (argv[i] == "--version-check-agent"sv) {
-            processedArgIndices.push_back(i);
-            agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
-        } else if (strlen(argv[i]) >= "--incr"sv.size() && "--incremental"sv.starts_with(argv[i])) {
-            processedArgIndices.push_back(i);
-            incremental_request = CmdlineOption::Enable;
-        } else if (strlen(argv[i]) >= "--no-incr"sv.size() &&
-                   "--no-incremental"sv.starts_with(argv[i])) {
-            processedArgIndices.push_back(i);
-            incremental_request = CmdlineOption::Disable;
-        } else if (argv[i] == "--wait"sv) {
-            processedArgIndices.push_back(i);
-            wait = true;
+    std::vector<const char*> passthrough;
+    for (auto&& arg : argv) {
+        if (arg == "--streaming"sv) {
+            *install_mode = INSTALL_STREAM;
+        } else if (arg == "--no-streaming"sv) {
+            *install_mode = INSTALL_PUSH;
+        } else if (strlen(arg) >= "--incr"sv.size() && "--incremental"sv.starts_with(arg)) {
+            *incremental_request = CmdlineOption::Enable;
+        } else if (strlen(arg) >= "--no-incr"sv.size() && "--no-incremental"sv.starts_with(arg)) {
+            *incremental_request = CmdlineOption::Disable;
+        } else if (arg == "--wait"sv) {
+            *incremental_wait = true;
+        } else {
+            passthrough.push_back(arg);
         }
     }
+    return passthrough;
+}
 
-    auto [primaryMode, fallbackMode] =
-            calculateInstallMode(installMode, use_fastdeploy, incremental_request);
-    if ((primaryMode == INSTALL_STREAM || fallbackMode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+static std::vector<const char*> parse_fast_deploy_mode(
+        std::vector<const char*> argv, bool* use_fastdeploy,
+        FastDeploy_AgentUpdateStrategy* agent_update_strategy) {
+    *use_fastdeploy = false;
+    *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+    std::vector<const char*> passthrough;
+    for (auto&& arg : argv) {
+        if (arg == "--fastdeploy"sv) {
+            *use_fastdeploy = true;
+        } else if (arg == "--no-fastdeploy"sv) {
+            *use_fastdeploy = false;
+        } else if (arg == "--force-agent"sv) {
+            *agent_update_strategy = FastDeploy_AgentUpdateAlways;
+        } else if (arg == "--date-check-agent"sv) {
+            *agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+        } else if (arg == "--version-check-agent"sv) {
+            *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+        } else {
+            passthrough.push_back(arg);
+        }
+    }
+    return passthrough;
+}
+
+int install_app(int argc, const char** argv) {
+    InstallMode install_mode = INSTALL_DEFAULT;
+    auto incremental_request = CmdlineOption::None;
+    bool incremental_wait = false;
+
+    bool use_fastdeploy = false;
+    FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+    auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request,
+                                          &incremental_wait);
+    auto passthrough_argv =
+            parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy);
+
+    auto [primary_mode, fallback_mode] =
+            calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+    if ((primary_mode == INSTALL_STREAM ||
+         fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
         best_install_mode() == INSTALL_PUSH) {
         error_exit("Attempting to use streaming install on unsupported device");
     }
@@ -490,19 +508,12 @@
     }
     fastdeploy_set_agent_update_strategy(agent_update_strategy);
 
-    std::vector<const char*> passthrough_argv;
-    for (int i = 0; i < argc; i++) {
-        if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
-            processedArgIndices.end()) {
-            passthrough_argv.push_back(argv[i]);
-        }
-    }
     if (passthrough_argv.size() < 2) {
         error_exit("install requires an apk argument");
     }
 
-    auto runInstallMode = [&](InstallMode installMode, bool silent) {
-        switch (installMode) {
+    auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+        switch (install_mode) {
             case INSTALL_PUSH:
                 return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
                                           use_fastdeploy);
@@ -511,20 +522,20 @@
                                             use_fastdeploy);
             case INSTALL_INCREMENTAL:
                 return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
-                                               wait, silent);
+                                               incremental_wait, silent);
             case INSTALL_DEFAULT:
             default:
-                return 1;
+                error_exit("invalid install mode");
         }
     };
-    auto res = runInstallMode(primaryMode, fallbackMode.has_value());
-    if (res && fallbackMode.value_or(primaryMode) != primaryMode) {
-        res = runInstallMode(*fallbackMode, false);
+    auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+    if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+        res = run_install_mode(*fallback_mode, false);
     }
     return res;
 }
 
-int install_multiple_app(int argc, const char** argv) {
+static int install_multiple_app_streamed(int argc, const char** argv) {
     // Find all APK arguments starting at end.
     // All other arguments passed through verbatim.
     int first_apk = -1;
@@ -550,7 +561,6 @@
     if (first_apk == -1) error_exit("need APK file on command line");
 
     const bool use_abb_exec = is_abb_exec_supported();
-
     const std::string install_cmd =
             use_abb_exec ? "package"
                          : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
@@ -673,6 +683,44 @@
     return EXIT_SUCCESS;
 }
 
+int install_multiple_app(int argc, const char** argv) {
+    InstallMode install_mode = INSTALL_DEFAULT;
+    auto incremental_request = CmdlineOption::None;
+    bool incremental_wait = false;
+    bool use_fastdeploy = false;
+
+    auto passthrough_argv = parse_install_mode({argv + 1, argv + argc}, &install_mode,
+                                               &incremental_request, &incremental_wait);
+
+    auto [primary_mode, fallback_mode] =
+            calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+    if ((primary_mode == INSTALL_STREAM ||
+         fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+        best_install_mode() == INSTALL_PUSH) {
+        error_exit("Attempting to use streaming install on unsupported device");
+    }
+
+    auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+        switch (install_mode) {
+            case INSTALL_PUSH:
+            case INSTALL_STREAM:
+                return install_multiple_app_streamed(passthrough_argv.size(),
+                                                     passthrough_argv.data());
+            case INSTALL_INCREMENTAL:
+                return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
+                                               incremental_wait, silent);
+            case INSTALL_DEFAULT:
+            default:
+                error_exit("invalid install mode");
+        }
+    };
+    auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+    if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+        res = run_install_mode(*fallback_mode, false);
+    }
+    return res;
+}
+
 int install_multi_package(int argc, const char** argv) {
     // Find all APK arguments starting at end.
     // All other arguments passed through verbatim.
@@ -697,23 +745,31 @@
         fprintf(stderr, "adb: multi-package install is not supported on this device\n");
         return EXIT_FAILURE;
     }
-    std::string install_cmd = "exec:cmd package";
 
-    std::string multi_package_cmd =
-            android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+    const bool use_abb_exec = is_abb_exec_supported();
+    const std::string install_cmd = use_abb_exec ? "package" : "exec:cmd package";
+
+    std::vector<std::string> multi_package_cmd_args = {install_cmd, "install-create",
+                                                       "--multi-package"};
+
+    multi_package_cmd_args.reserve(first_package + 4);
     for (int i = 1; i < first_package; i++) {
-        multi_package_cmd += " " + escape_arg(argv[i]);
+        if (use_abb_exec) {
+            multi_package_cmd_args.push_back(argv[i]);
+        } else {
+            multi_package_cmd_args.push_back(escape_arg(argv[i]));
+        }
     }
 
     if (apex_found) {
-        multi_package_cmd += " --staged";
+        multi_package_cmd_args.emplace_back("--staged");
     }
 
     // Create multi-package install session
     std::string error;
     char buf[BUFSIZ];
     {
-        unique_fd fd(adb_connect(multi_package_cmd, &error));
+        unique_fd fd = send_command(multi_package_cmd_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
             return EXIT_FAILURE;
@@ -735,6 +791,7 @@
         fputs(buf, stderr);
         return EXIT_FAILURE;
     }
+    const auto parent_session_id_str = std::to_string(parent_session_id);
 
     fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
 
@@ -742,17 +799,30 @@
 
     // Valid session, now create the individual sessions and stream the APKs
     int success = EXIT_FAILURE;
-    std::string individual_cmd =
-            android::base::StringPrintf("%s install-create", install_cmd.c_str());
-    std::string all_session_ids = "";
+    std::vector<std::string> individual_cmd_args = {install_cmd, "install-create"};
     for (int i = 1; i < first_package; i++) {
-        individual_cmd += " " + escape_arg(argv[i]);
+        if (use_abb_exec) {
+            individual_cmd_args.push_back(argv[i]);
+        } else {
+            individual_cmd_args.push_back(escape_arg(argv[i]));
+        }
     }
     if (apex_found) {
-        individual_cmd += " --staged";
+        individual_cmd_args.emplace_back("--staged");
     }
-    std::string individual_apex_cmd = individual_cmd + " --apex";
-    std::string cmd = "";
+
+    std::vector<std::string> individual_apex_cmd_args;
+    if (apex_found) {
+        individual_apex_cmd_args = individual_cmd_args;
+        individual_apex_cmd_args.emplace_back("--apex");
+    }
+
+    std::vector<std::string> add_session_cmd_args = {
+            install_cmd,
+            "install-add-session",
+            parent_session_id_str,
+    };
+
     for (int i = first_package; i < argc; i++) {
         const char* file = argv[i];
         char buf[BUFSIZ];
@@ -760,9 +830,9 @@
             unique_fd fd;
             // Create individual install session
             if (android::base::EndsWithIgnoreCase(file, ".apex")) {
-                fd.reset(adb_connect(individual_apex_cmd, &error));
+                fd = send_command(individual_apex_cmd_args, &error);
             } else {
-                fd.reset(adb_connect(individual_cmd, &error));
+                fd = send_command(individual_cmd_args, &error);
             }
             if (fd < 0) {
                 fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
@@ -785,6 +855,7 @@
             fputs(buf, stderr);
             goto finalize_multi_package_session;
         }
+        const auto session_id_str = std::to_string(session_id);
 
         fprintf(stdout, "Created child session ID %d.\n", session_id);
         session_ids.push_back(session_id);
@@ -799,10 +870,15 @@
                 goto finalize_multi_package_session;
             }
 
-            std::string cmd = android::base::StringPrintf(
-                    "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
-                    static_cast<uint64_t>(sb.st_size), session_id, i,
-                    android::base::Basename(split).c_str());
+            std::vector<std::string> cmd_args = {
+                    install_cmd,
+                    "install-write",
+                    "-S",
+                    std::to_string(sb.st_size),
+                    session_id_str,
+                    android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+                    "-",
+            };
 
             unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
             if (local_fd < 0) {
@@ -811,7 +887,7 @@
             }
 
             std::string error;
-            unique_fd remote_fd(adb_connect(cmd, &error));
+            unique_fd remote_fd = send_command(cmd_args, &error);
             if (remote_fd < 0) {
                 fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
                 goto finalize_multi_package_session;
@@ -830,13 +906,11 @@
                 goto finalize_multi_package_session;
             }
         }
-        all_session_ids += android::base::StringPrintf(" %d", session_id);
+        add_session_cmd_args.push_back(std::to_string(session_id));
     }
 
-    cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
-                                      parent_session_id, all_session_ids.c_str());
     {
-        unique_fd fd(adb_connect(cmd, &error));
+        unique_fd fd = send_command(add_session_cmd_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
             goto finalize_multi_package_session;
@@ -845,7 +919,8 @@
     }
 
     if (strncmp("Success", buf, 7)) {
-        fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+        fprintf(stderr, "adb: failed to link sessions (%s)\n",
+                android::base::Join(add_session_cmd_args, " ").c_str());
         fputs(buf, stderr);
         goto finalize_multi_package_session;
     }
@@ -855,11 +930,14 @@
 
 finalize_multi_package_session:
     // Commit session if we streamed everything okay; otherwise abandon
-    std::string service =
-            android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
-                                        success == 0 ? "commit" : "abandon", parent_session_id);
+    std::vector<std::string> service_args = {
+            install_cmd,
+            success == 0 ? "install-commit" : "install-abandon",
+            parent_session_id_str,
+    };
+
     {
-        unique_fd fd(adb_connect(service, &error));
+        unique_fd fd = send_command(service_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
             return EXIT_FAILURE;
@@ -880,10 +958,13 @@
     session_ids.push_back(parent_session_id);
     // try to abandon all remaining sessions
     for (std::size_t i = 0; i < session_ids.size(); i++) {
-        service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
-                                              session_ids[i]);
+        std::vector<std::string> service_args = {
+                install_cmd,
+                "install-abandon",
+                std::to_string(session_ids[i]),
+        };
         fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
-        unique_fd fd(adb_connect(service, &error));
+        unique_fd fd = send_command(service_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
             continue;
diff --git a/adb/client/adb_wifi.cpp b/adb/client/adb_wifi.cpp
index fa71028..61a9a48 100644
--- a/adb/client/adb_wifi.cpp
+++ b/adb/client/adb_wifi.cpp
@@ -179,17 +179,21 @@
 
 void adb_wifi_pair_device(const std::string& host, const std::string& password,
                           std::string& response) {
-    // Check the address for a valid address and port.
-    std::string parsed_host;
-    std::string err;
-    int port = -1;
-    if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
-        response = "Failed to parse address for pairing: " + err;
-        return;
-    }
-    if (port <= 0 || port > 65535) {
-        response = "Invalid port while parsing address [" + host + "]";
-        return;
+    auto mdns_info = mdns_get_pairing_service_info(host);
+
+    if (!mdns_info.has_value()) {
+        // Check the address for a valid address and port.
+        std::string parsed_host;
+        std::string err;
+        int port = -1;
+        if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
+            response = "Failed to parse address for pairing: " + err;
+            return;
+        }
+        if (port <= 0 || port > 65535) {
+            response = "Invalid port while parsing address [" + host + "]";
+            return;
+        }
     }
 
     auto priv_key = adb_auth_get_user_privkey();
@@ -220,7 +224,11 @@
 
     PairingResultWaiter waiter;
     std::unique_lock<std::mutex> lock(waiter.mutex_);
-    if (!client->Start(host, waiter.OnResult, &waiter)) {
+    if (!client->Start(mdns_info.has_value()
+                               ? android::base::StringPrintf("%s:%d", mdns_info->addr.c_str(),
+                                                             mdns_info->port)
+                               : host,
+                       waiter.OnResult, &waiter)) {
         response = "Failed: Unable to start pairing client.";
         return;
     }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index efb6c2f..eaa32e5 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -104,7 +104,8 @@
         " connect HOST[:PORT]      connect to a device via TCP/IP [default port=5555]\n"
         " disconnect [HOST[:PORT]]\n"
         "     disconnect from given TCP/IP device [default port=5555], or all\n"
-        " pair HOST[:PORT]         pair with a device for secure TCP/IP communication\n"
+        " pair HOST[:PORT] [PAIRING CODE]\n"
+        "     pair with a device for secure TCP/IP communication\n"
         " forward --list           list all forward socket connections\n"
         " forward [--no-rebind] LOCAL REMOTE\n"
         "     forward socket connection using:\n"
@@ -1735,13 +1736,17 @@
     } else if (!strcmp(argv[0], "abb")) {
         return adb_abb(argc, argv);
     } else if (!strcmp(argv[0], "pair")) {
-        if (argc != 2) error_exit("usage: adb pair <host>[:<port>]");
+        if (argc < 2 || argc > 3) error_exit("usage: adb pair HOST[:PORT] [PAIRING CODE]");
 
         std::string password;
-        printf("Enter pairing code: ");
-        fflush(stdout);
-        if (!std::getline(std::cin, password) || password.empty()) {
-            error_exit("No pairing code provided");
+        if (argc == 2) {
+            printf("Enter pairing code: ");
+            fflush(stdout);
+            if (!std::getline(std::cin, password) || password.empty()) {
+                error_exit("No pairing code provided");
+            }
+        } else {
+            password = argv[2];
         }
         std::string query =
                 android::base::StringPrintf("host:pair:%s:%s", password.c_str(), argv[1]);
diff --git a/adb/client/mdns_utils.cpp b/adb/client/mdns_utils.cpp
new file mode 100644
index 0000000..8666b18
--- /dev/null
+++ b/adb/client/mdns_utils.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "client/mdns_utils.h"
+
+#include <android-base/strings.h>
+
+namespace mdns {
+
+// <Instance>.<Service>.<Domain>
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name) {
+    CHECK(!name.empty());
+
+    // Return the whole name if it doesn't fall under <Instance>.<Service>.<Domain> or
+    // <Instance>.<Service>
+    bool has_local_suffix = false;
+    // Strip the local suffix, if any
+    {
+        std::string local_suffix = ".local";
+        local_suffix += android::base::EndsWith(name, ".") ? "." : "";
+
+        if (android::base::ConsumeSuffix(&name, local_suffix)) {
+            if (name.empty()) {
+                return std::nullopt;
+            }
+            has_local_suffix = true;
+        }
+    }
+
+    std::string transport;
+    // Strip the transport suffix, if any
+    {
+        std::string add_dot = (!has_local_suffix && android::base::EndsWith(name, ".")) ? "." : "";
+        std::array<std::string, 2> transport_suffixes{"._tcp", "._udp"};
+
+        for (const auto& t : transport_suffixes) {
+            if (android::base::ConsumeSuffix(&name, t + add_dot)) {
+                if (name.empty()) {
+                    return std::nullopt;
+                }
+                transport = t.substr(1);
+                break;
+            }
+        }
+
+        if (has_local_suffix && transport.empty()) {
+            return std::nullopt;
+        }
+    }
+
+    if (!has_local_suffix && transport.empty()) {
+        return std::make_optional<MdnsInstance>(name, "", "");
+    }
+
+    // Split the service name from the instance name
+    auto pos = name.rfind(".");
+    if (pos == 0 || pos == std::string::npos || pos == name.size() - 1) {
+        return std::nullopt;
+    }
+
+    return std::make_optional<MdnsInstance>(name.substr(0, pos), name.substr(pos + 1), transport);
+}
+
+}  // namespace mdns
diff --git a/adb/client/mdns_utils.h b/adb/client/mdns_utils.h
new file mode 100644
index 0000000..40d095d
--- /dev/null
+++ b/adb/client/mdns_utils.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <optional>
+#include <string_view>
+
+#include "adb_wifi.h"
+
+namespace mdns {
+
+struct MdnsInstance {
+    std::string instance_name;   // "my name"
+    std::string service_name;    // "_adb-tls-connect"
+    std::string transport_type;  // either "_tcp" or "_udp"
+
+    MdnsInstance(std::string_view inst, std::string_view serv, std::string_view trans)
+        : instance_name(inst), service_name(serv), transport_type(trans) {}
+};
+
+// This parser is based on https://tools.ietf.org/html/rfc6763#section-4.1 for
+// structured service instance names, where the whole name is in the format
+// <Instance>.<Service>.<Domain>.
+//
+// In our case, we ignore <Domain> portion of the name, which
+// we always assume to be ".local", or link-local mDNS.
+//
+// The string can be in one of the following forms:
+//   - <Instance>.<Service>.<Domain>.?
+//     - e.g. "instance._service._tcp.local" (or "...local.")
+//   - <Instance>.<Service>.? (must contain either "_tcp" or "_udp" at the end)
+//     - e.g. "instance._service._tcp" (or "..._tcp.)
+//   - <Instance> (can contain dots '.')
+//     - e.g. "myname", "name.", "my.name."
+//
+// Returns an MdnsInstance with the appropriate fields filled in (instance name is never empty),
+// otherwise returns std::nullopt.
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name);
+
+}  // namespace mdns
diff --git a/adb/client/mdns_utils_test.cpp b/adb/client/mdns_utils_test.cpp
new file mode 100644
index 0000000..ec71529
--- /dev/null
+++ b/adb/client/mdns_utils_test.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 "client/mdns_utils.h"
+
+#include <gtest/gtest.h>
+
+namespace mdns {
+
+TEST(mdns_utils, mdns_parse_instance_name) {
+    // Just the instance name
+    {
+        std::string str = ".";
+        auto res = mdns_parse_instance_name(str);
+        ASSERT_TRUE(res.has_value());
+        EXPECT_EQ(str, res->instance_name);
+        EXPECT_TRUE(res->service_name.empty());
+        EXPECT_TRUE(res->transport_type.empty());
+    }
+    {
+        std::string str = "my.name";
+        auto res = mdns_parse_instance_name(str);
+        ASSERT_TRUE(res.has_value());
+        EXPECT_EQ(str, res->instance_name);
+        EXPECT_TRUE(res->service_name.empty());
+        EXPECT_TRUE(res->transport_type.empty());
+    }
+    {
+        std::string str = "my.name.";
+        auto res = mdns_parse_instance_name(str);
+        ASSERT_TRUE(res.has_value());
+        EXPECT_EQ(str, res->instance_name);
+        EXPECT_TRUE(res->service_name.empty());
+        EXPECT_TRUE(res->transport_type.empty());
+    }
+
+    // With "_tcp", "_udp" transport type
+    for (const std::string_view transport : {"._tcp", "._udp"}) {
+        {
+            std::string str = android::base::StringPrintf("%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf(".service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("service.%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("my.service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("my.service%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("my..service%s", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my.");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("my.name.service%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "my.name");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str = android::base::StringPrintf("name.service.%s.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+
+        // With ".local" domain
+        {
+            std::string str = ".local";
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = ".local.";
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = "name.local";
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("%s.local", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("service%s.local", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str = android::base::StringPrintf("name.service%s.local", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "name");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str =
+                    android::base::StringPrintf("name.service%s.local.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            ASSERT_TRUE(res.has_value());
+            EXPECT_EQ(res->instance_name, "name");
+            EXPECT_EQ(res->service_name, "service");
+            EXPECT_EQ(res->transport_type, transport.substr(1));
+        }
+        {
+            std::string str =
+                    android::base::StringPrintf("name.service%s..local.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+        {
+            std::string str =
+                    android::base::StringPrintf("name.service.%s.local.", transport.data());
+            auto res = mdns_parse_instance_name(str);
+            EXPECT_FALSE(res.has_value());
+        }
+    }
+}
+
+}  // namespace mdns
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index c9993b7..9db2453 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -38,6 +38,7 @@
 #include "adb_trace.h"
 #include "adb_utils.h"
 #include "adb_wifi.h"
+#include "client/mdns_utils.h"
 #include "fdevent/fdevent.h"
 #include "sysdeps.h"
 
@@ -221,7 +222,7 @@
         }
 
         std::string response;
-        connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+        connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(), regType_.c_str()),
                        &response);
         D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
           ip_addr_, port_, response.c_str());
@@ -248,26 +249,8 @@
             return false;
         }
 
-        // adb secure service needs to do something different from just
-        // connecting here.
-        if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
-            std::string response;
-            D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(),
-              regType_.c_str(), ip_addr_, port_);
-            int index = adb_DNSServiceIndexByName(regType_.c_str());
-            if (index == kADBSecureConnectServiceRefIndex) {
-                ConnectSecureWifiDevice();
-            } else {
-                connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
-                               &response);
-                D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
-                  ip_addr_, port_, response.c_str());
-            }
-        } else {
-            D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
-              serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
-        }
-
+        // Add to the service registry before trying to auto-connect, since socket_spec_connect will
+        // check these registries for the ip address when connecting via mdns instance name.
         int adbSecureServiceType = serviceIndex();
         ServiceRegistry* services = nullptr;
         switch (adbSecureServiceType) {
@@ -294,6 +277,25 @@
         }
         services->push_back(std::unique_ptr<ResolvedService>(this));
 
+        if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
+            std::string response;
+            D("Attempting to connect serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
+              serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
+            int index = adb_DNSServiceIndexByName(regType_.c_str());
+            if (index == kADBSecureConnectServiceRefIndex) {
+                ConnectSecureWifiDevice();
+            } else {
+                connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(),
+                                                           regType_.c_str()),
+                               &response);
+                D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
+                  ip_addr_, port_, response.c_str());
+            }
+        } else {
+            D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
+              serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
+        }
+
         return true;
     }
 
@@ -319,7 +321,7 @@
 
     static void initAdbServiceRegistries();
 
-    static void forEachService(const ServiceRegistry& services, const std::string& hostname,
+    static void forEachService(const ServiceRegistry& services, std::string_view hostname,
                                adb_secure_foreach_service_callback cb);
 
     static bool connectByServiceName(const ServiceRegistry& services,
@@ -362,7 +364,7 @@
 
 // static
 void ResolvedService::forEachService(const ServiceRegistry& services,
-                                     const std::string& wanted_service_name,
+                                     std::string_view wanted_service_name,
                                      adb_secure_foreach_service_callback cb) {
     initAdbServiceRegistries();
 
@@ -372,7 +374,7 @@
         auto ip = service->ipAddress();
         auto port = service->port();
 
-        if (wanted_service_name == "") {
+        if (wanted_service_name.empty()) {
             cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
         } else if (service_name == wanted_service_name) {
             cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
@@ -396,14 +398,12 @@
 
 void adb_secure_foreach_pairing_service(const char* service_name,
                                         adb_secure_foreach_service_callback cb) {
-    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
-                                    service_name ? service_name : "", cb);
+    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
 }
 
 void adb_secure_foreach_connect_service(const char* service_name,
                                         adb_secure_foreach_service_callback cb) {
-    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
-                                    service_name ? service_name : "", cb);
+    ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, service_name, cb);
 }
 
 bool adb_secure_connect_by_service_name(const char* service_name) {
@@ -676,3 +676,79 @@
     ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
     return result;
 }
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name) {
+    CHECK(!name.empty());
+
+    auto mdns_instance = mdns::mdns_parse_instance_name(name);
+    if (!mdns_instance.has_value()) {
+        D("Failed to parse mDNS name [%s]", name.data());
+        return std::nullopt;
+    }
+
+    std::optional<MdnsInfo> info;
+    auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+                  uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+    std::string reg_type;
+    if (!mdns_instance->service_name.empty()) {
+        reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
+                                               mdns_instance->transport_type.data());
+        int index = adb_DNSServiceIndexByName(reg_type);
+        switch (index) {
+            case kADBTransportServiceRefIndex:
+                ResolvedService::forEachService(*ResolvedService::sAdbTransportServices,
+                                                mdns_instance->instance_name, cb);
+                break;
+            case kADBSecureConnectServiceRefIndex:
+                ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
+                                                mdns_instance->instance_name, cb);
+                break;
+            default:
+                D("Unknown reg_type [%s]", reg_type.data());
+                return std::nullopt;
+        }
+        return info;
+    }
+
+    for (const auto& service :
+         {ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) {
+        ResolvedService::forEachService(*service, name, cb);
+        if (info.has_value()) {
+            return info;
+        }
+    }
+
+    return std::nullopt;
+}
+
+std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name) {
+    CHECK(!name.empty());
+
+    auto mdns_instance = mdns::mdns_parse_instance_name(name);
+    if (!mdns_instance.has_value()) {
+        D("Failed to parse mDNS pairing name [%s]", name.data());
+        return std::nullopt;
+    }
+
+    std::optional<MdnsInfo> info;
+    auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+                  uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+    // Verify it's a pairing service if user explicitly inputs it.
+    if (!mdns_instance->service_name.empty()) {
+        auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
+                                                    mdns_instance->transport_type.data());
+        int index = adb_DNSServiceIndexByName(reg_type);
+        switch (index) {
+            case kADBSecurePairingServiceRefIndex:
+                break;
+            default:
+                D("Not an adb pairing reg_type [%s]", reg_type.data());
+                return std::nullopt;
+        }
+    }
+
+    ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, name, cb);
+    return info;
+}
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index b7b25fa..5cad70d 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -30,6 +30,7 @@
 
 #include "adb.h"
 #include "adb_utils.h"
+#include "adb_wifi.h"
 #include "sysdeps.h"
 
 using namespace std::string_literals;
@@ -195,7 +196,24 @@
             fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
         } else {
 #if ADB_HOST
-            fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+            // Check if the address is an mdns service we can connect to.
+            if (auto mdns_info = mdns_get_connect_service_info(address.substr(4));
+                mdns_info != std::nullopt) {
+                fd->reset(network_connect(mdns_info->addr, mdns_info->port, SOCK_STREAM, 0, error));
+                if (fd->get() != -1) {
+                    // TODO(joshuaduong): We still show the ip address for the serial. Change it to
+                    // use the mdns instance name, so we can adjust to address changes on
+                    // reconnects.
+                    port_value = mdns_info->port;
+                    if (serial) {
+                        *serial = android::base::StringPrintf("%s.%s",
+                                                              mdns_info->service_name.c_str(),
+                                                              mdns_info->service_type.c_str());
+                    }
+                }
+            } else {
+                fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+            }
 #else
             // Disallow arbitrary connections in adbd.
             *error = "adbd does not support arbitrary tcp connections";
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 9912f11..4b99411 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -25,6 +25,7 @@
 import random
 import select
 import socket
+import string
 import struct
 import subprocess
 import sys
@@ -628,21 +629,49 @@
 class MdnsTest(unittest.TestCase):
     """Tests for adb mdns."""
 
+    @staticmethod
+    def _mdns_services(port):
+        output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+        return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+    @staticmethod
+    def _devices(port):
+        output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+        return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+    @contextlib.contextmanager
+    def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect):
+        """Context manager for an ADB connection.
+
+        This automatically disconnects when done with the connection.
+        """
+
+        output = subprocess.check_output(["adb", "-P", str(server_port), "connect", mdns_instance])
+        if should_connect:
+            self.assertEqual(output.strip(), "connected to {}".format(serial).encode("utf8"))
+        else:
+            self.assertTrue(output.startswith("failed to resolve host: '{}'"
+                .format(mdns_instance).encode("utf8")))
+
+        try:
+            yield
+        finally:
+            # Perform best-effort disconnection. Discard the output.
+            subprocess.Popen(["adb", "disconnect", serial],
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE).communicate()
+
+
     @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
     def test_mdns_services_register_unregister(self):
         """Ensure that `adb mdns services` correctly adds and removes a service
         """
         from zeroconf import IPVersion, ServiceInfo
- 
-        def _mdns_services(port):
-            output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
-            return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
 
         with adb_server() as server_port:
             output = subprocess.check_output(["adb", "-P", str(server_port),
                                               "mdns", "services"]).strip()
             self.assertTrue(output.startswith(b"List of discovered mdns services"))
-            print(f"services={_mdns_services(server_port)}")
 
             """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
             """Register/Unregister a service"""
@@ -656,20 +685,52 @@
                         name=serv_instance + "." + serv_type + "local.",
                         addresses=[serv_ipaddr],
                         port=serv_port)
-                print(f"Registering {serv_instance}.{serv_type} ...")
                 with zeroconf_register_service(zc, service_info) as info:
                     """Give adb some time to register the service"""
                     time.sleep(1)
-                    print(f"services={_mdns_services(server_port)}")
                     self.assertTrue(any((serv_instance in line and serv_type in line)
-                        for line in _mdns_services(server_port)))
+                        for line in MdnsTest._mdns_services(server_port)))
 
                 """Give adb some time to unregister the service"""
-                print("Unregistering mdns service...")
                 time.sleep(1)
-                print(f"services={_mdns_services(server_port)}")
                 self.assertFalse(any((serv_instance in line and serv_type in line)
-                    for line in _mdns_services(server_port)))
+                    for line in MdnsTest._mdns_services(server_port)))
+
+    @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+    def test_mdns_connect(self):
+        """Ensure that `adb connect` by mdns instance name works (for non-pairing services)
+        """
+        from zeroconf import IPVersion, ServiceInfo
+
+        with adb_server() as server_port:
+            with zeroconf_context(IPVersion.V4Only) as zc:
+                serv_instance = "fakeadbd-" + ''.join(
+                        random.choice(string.ascii_letters) for i in range(4))
+                serv_type = "_" + self.service_name + "._tcp."
+                serv_ipaddr = socket.inet_aton("127.0.0.1")
+                should_connect = self.service_name != "adb-tls-pairing"
+                with fake_adbd() as (port, _):
+                    service_info = ServiceInfo(
+                            serv_type + "local.",
+                            name=serv_instance + "." + serv_type + "local.",
+                            addresses=[serv_ipaddr],
+                            port=port)
+                    with zeroconf_register_service(zc, service_info) as info:
+                        """Give adb some time to register the service"""
+                        time.sleep(1)
+                        self.assertTrue(any((serv_instance in line and serv_type in line)
+                            for line in MdnsTest._mdns_services(server_port)))
+                        full_name = '.'.join([serv_instance, serv_type])
+                        with self._adb_mdns_connect(server_port, serv_instance, full_name,
+                                should_connect):
+                            if should_connect:
+                                self.assertEqual(MdnsTest._devices(server_port),
+                                        [[full_name, "device"]])
+
+                    """Give adb some time to unregister the service"""
+                    time.sleep(1)
+                    self.assertFalse(any((serv_instance in line and serv_type in line)
+                        for line in MdnsTest._mdns_services(server_port)))
 
 def main():
     """Main entrypoint."""
diff --git a/base/Android.bp b/base/Android.bp
index 61fbc3d..6d8544a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -58,7 +58,6 @@
     name: "libbase_defaults",
     defaults: ["libbase_cflags_defaults"],
     srcs: [
-        "abi_compatibility.cpp",
         "chrono_utils.cpp",
         "cmsg.cpp",
         "file.cpp",
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
deleted file mode 100644
index 06a7801..0000000
--- a/base/abi_compatibility.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 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 <memory>
-
-#include "android-base/cmsg.h"
-#include "android-base/file.h"
-#include "android-base/mapped_file.h"
-#include "android-base/unique_fd.h"
-
-namespace android {
-namespace base {
-
-// These ABI-compatibility shims are in a separate file for two reasons:
-//   1. If they were in the file with the actual functions, it prevents calls to
-//      those functions by other functions in the file, due to ambiguity.
-//   2. We will hopefully be able to delete these quickly.
-
-#if !defined(_WIN32)
-ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
-                                 const std::vector<int>& fds) {
-  return SendFileDescriptorVector(borrowed_fd(sockfd), data, len, fds);
-}
-
-ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
-                                    std::vector<unique_fd>* fds) {
-  return ReceiveFileDescriptorVector(borrowed_fd(sockfd), data, len, max_fds, fds);
-}
-#endif
-
-bool ReadFdToString(int fd, std::string* content) {
-  return ReadFdToString(borrowed_fd(fd), content);
-}
-
-bool WriteStringToFd(const std::string& content, int fd) {
-  return WriteStringToFd(content, borrowed_fd(fd));
-}
-
-bool ReadFully(int fd, void* data, size_t byte_count) {
-  return ReadFully(borrowed_fd(fd), data, byte_count);
-}
-
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
-  return ReadFullyAtOffset(borrowed_fd(fd), data, byte_count, offset);
-}
-
-bool WriteFully(int fd, const void* data, size_t byte_count) {
-  return WriteFully(borrowed_fd(fd), data, byte_count);
-}
-
-#if defined(__LP64__)
-#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
-#else
-#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEixmi
-#endif
-
-#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
-extern "C" std::unique_ptr<MappedFile> MAPPEDFILE_FROMFD(int fd, off64_t offset, size_t length,
-                                                         int prot) {
-  return MappedFile::FromFd(fd, offset, length, prot);
-}
-
-}  // namespace base
-}  // namespace android
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 676f446..6cd0430 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -14,6 +14,9 @@
     },
     {
       "name": "vts_libsnapshot_test"
+    },
+    {
+      "name": "libsnapshot_fuzzer_test"
     }
   ]
 }
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index c191102..2783e4d 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -246,8 +246,8 @@
     gtest: false,
 }
 
-cc_fuzz {
-    name: "libsnapshot_fuzzer",
+cc_defaults {
+    name: "libsnapshot_fuzzer_defaults",
 
     // TODO(b/154633114): make host supported.
     // host_supported: true,
@@ -289,6 +289,11 @@
         canonical_path_from_root: false,
         local_include_dirs: ["."],
     },
+}
+
+cc_fuzz {
+    name: "libsnapshot_fuzzer",
+    defaults: ["libsnapshot_fuzzer_defaults"],
     corpus: ["corpus/*"],
     fuzz_config: {
         cc: ["android-virtual-ab+bugs@google.com"],
@@ -298,3 +303,14 @@
         fuzz_on_haiku_device: true,
     },
 }
+
+cc_test {
+    name: "libsnapshot_fuzzer_test",
+    defaults: ["libsnapshot_fuzzer_defaults"],
+    data: ["corpus/*"],
+    test_suites: [
+        "device-tests",
+    ],
+    auto_gen_config: true,
+    require_root: true,
+}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
index 4dc6cdc..20b13b2 100644
--- a/fs_mgr/libsnapshot/fuzz_utils.h
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -68,17 +68,25 @@
     return 0;
 }
 
+// Get the field descriptor for the oneof field in the action message. If no oneof field is set,
+// return nullptr.
 template <typename Action>
-void ExecuteActionProto(typename Action::Class* module,
-                        const typename Action::Proto& action_proto) {
+const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
+        const typename Action::Proto& action_proto) {
     static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
 
     auto* action_refl = Action::Proto::GetReflection();
     if (!action_refl->HasOneof(action_proto, action_value_desc)) {
-        return;
+        return nullptr;
     }
+    return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+}
 
-    const auto* field_desc = action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+template <typename Action>
+void ExecuteActionProto(typename Action::ClassType* module,
+                        const typename Action::Proto& action_proto) {
+    const auto* field_desc = GetValueFieldDescriptor<Action>(action_proto);
+    if (field_desc == nullptr) return;
     auto number = field_desc->number();
     const auto& map = *Action::GetFunctionMap();
     auto it = map.find(number);
@@ -89,7 +97,7 @@
 
 template <typename Action>
 void ExecuteAllActionProtos(
-        typename Action::Class* module,
+        typename Action::ClassType* module,
         const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
     for (const auto& proto : action_protos) {
         ExecuteActionProto<Action>(module, proto);
@@ -134,53 +142,57 @@
 // ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
 // with these arguments.
 template <typename FuzzFunction, typename Signature, typename Enabled = void>
-struct ActionPerfomer;  // undefined
+struct ActionPerformerImpl;  // undefined
 
 template <typename FuzzFunction, typename MessageProto>
-struct ActionPerfomer<
+struct ActionPerformerImpl<
         FuzzFunction, void(const MessageProto&),
         typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
-    static void Invoke(typename FuzzFunction::Class* module,
-                       const google::protobuf::Message& action_proto,
-                       const google::protobuf::FieldDescriptor* field_desc) {
+    static typename FuzzFunction::ReturnType Invoke(
+            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+            const google::protobuf::FieldDescriptor* field_desc) {
         const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
                 action_proto.GetReflection()->GetMessage(action_proto, field_desc));
-        FuzzFunction::ImplBody(module, arg);
+        return FuzzFunction::ImplBody(module, arg);
     }
 };
 
 template <typename FuzzFunction, typename Primitive>
-struct ActionPerfomer<FuzzFunction, void(Primitive),
-                      typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
-    static void Invoke(typename FuzzFunction::Class* module,
-                       const google::protobuf::Message& action_proto,
-                       const google::protobuf::FieldDescriptor* field_desc) {
+struct ActionPerformerImpl<FuzzFunction, void(Primitive),
+                           typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
+    static typename FuzzFunction::ReturnType Invoke(
+            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+            const google::protobuf::FieldDescriptor* field_desc) {
         Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
                                     action_proto, field_desc);
-        FuzzFunction::ImplBody(module, arg);
+        return FuzzFunction::ImplBody(module, arg);
     }
 };
 
 template <typename FuzzFunction>
-struct ActionPerfomer<FuzzFunction, void()> {
-    static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&,
-                       const google::protobuf::FieldDescriptor*) {
-        FuzzFunction::ImplBody(module);
+struct ActionPerformerImpl<FuzzFunction, void()> {
+    static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
+                                                    const google::protobuf::Message&,
+                                                    const google::protobuf::FieldDescriptor*) {
+        return FuzzFunction::ImplBody(module);
     }
 };
 
 template <typename FuzzFunction>
-struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
-    static void Invoke(typename FuzzFunction::Class* module,
-                       const google::protobuf::Message& action_proto,
-                       const google::protobuf::FieldDescriptor* field_desc) {
+struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
+    static typename FuzzFunction::ReturnType Invoke(
+            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+            const google::protobuf::FieldDescriptor* field_desc) {
         std::string scratch;
         const std::string& arg = action_proto.GetReflection()->GetStringReference(
                 action_proto, field_desc, &scratch);
-        FuzzFunction::ImplBody(module, arg);
+        return FuzzFunction::ImplBody(module, arg);
     }
 };
 
+template <typename FuzzFunction>
+struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
+
 }  // namespace android::fuzz
 
 // Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
@@ -197,11 +209,11 @@
 //   FUZZ_CLASS(Foo, FooAction)
 // After linking functions of Foo to FooAction, execute all actions by:
 //   FooAction::ExecuteAll(foo_object, action_protos)
-#define FUZZ_CLASS(ClassType, Action)                                                            \
+#define FUZZ_CLASS(Class, Action)                                                                \
     class Action {                                                                               \
       public:                                                                                    \
         using Proto = Action##Proto;                                                             \
-        using Class = ClassType;                                                                 \
+        using ClassType = Class;                                                                 \
         using FunctionMap = android::fuzz::FunctionMap<Class>;                                   \
         static FunctionMap* GetFunctionMap() {                                                   \
             static Action::FunctionMap map;                                                      \
@@ -225,29 +237,33 @@
 // }
 // class Foo { public: void DoAwesomeFoo(bool arg); };
 // FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoFoo, module, bool arg) {
+// FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
 //   module->DoAwesomeFoo(arg);
 // }
 // The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
-#define FUZZ_FUNCTION(Action, FunctionName, module, ...)                                         \
-    class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) {                                       \
-      public:                                                                                    \
-        using Class = Action::Class;                                                             \
-        static void ImplBody(Action::Class*, ##__VA_ARGS__);                                     \
-                                                                                                 \
-      private:                                                                                   \
-        static bool registered_;                                                                 \
-    };                                                                                           \
-    auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] {                     \
-        auto tag = Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName);               \
-        auto func =                                                                              \
-                &::android::fuzz::ActionPerfomer<FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName), \
-                                                 void(__VA_ARGS__)>::Invoke;                     \
-        Action::GetFunctionMap()->CheckEmplace(tag, func);                                       \
-        return true;                                                                             \
-    })();                                                                                        \
-    void FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(Action::Class* module,         \
-                                                                  ##__VA_ARGS__)
+#define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...)             \
+    class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) {                      \
+      public:                                                                   \
+        using ActionType = Action;                                              \
+        using ClassType = Action::ClassType;                                    \
+        using ReturnType = Return;                                              \
+        using Signature = void(__VA_ARGS__);                                    \
+        static constexpr const char name[] = #FunctionName;                     \
+        static constexpr const auto tag =                                       \
+                Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
+        static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__);                   \
+                                                                                \
+      private:                                                                  \
+        static bool registered_;                                                \
+    };                                                                          \
+    auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] {    \
+        auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag;         \
+        auto func = &::android::fuzz::ActionPerformer<FUZZ_FUNCTION_CLASS_NAME( \
+                Action, FunctionName)>::Invoke;                                 \
+        Action::GetFunctionMap()->CheckEmplace(tag, func);                      \
+        return true;                                                            \
+    })();                                                                       \
+    Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__)
 
 // Implement a simple action by linking it to the function with the same name. Example:
 // message FooActionProto {
@@ -261,5 +277,9 @@
 // FUZZ_FUNCTION(FooAction, DoBar);
 // The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
 // also the name of the function of Foo.
-#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
-    FUZZ_FUNCTION(Action, FunctionName, module) { (void)module->FunctionName(); }
+#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName)                            \
+    FUZZ_FUNCTION(Action, FunctionName,                                       \
+                  decltype(std::declval<Action::ClassType>().FunctionName()), \
+                  Action::ClassType* module) {                                \
+        return module->FunctionName();                                        \
+    }
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 1e90ace..5b145c3 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -21,14 +21,21 @@
 #include <tuple>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/result.h>
+#include <gtest/gtest.h>
 #include <src/libfuzzer/libfuzzer_macro.h>
 #include <storage_literals/storage_literals.h>
 
 #include "fuzz_utils.h"
 #include "snapshot_fuzz_utils.h"
 
+using android::base::Error;
+using android::base::GetBoolProperty;
 using android::base::LogId;
 using android::base::LogSeverity;
+using android::base::ReadFileToString;
+using android::base::Result;
 using android::base::SetLogger;
 using android::base::StderrLogger;
 using android::base::StdioLogger;
@@ -37,6 +44,8 @@
 using android::snapshot::SnapshotFuzzData;
 using android::snapshot::SnapshotFuzzEnv;
 using chromeos_update_engine::DeltaArchiveManifest;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::Message;
 using google::protobuf::RepeatedPtrField;
 
 // Avoid linking to libgsi since it needs disk I/O.
@@ -74,48 +83,49 @@
 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
 
-#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \
-    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__)
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...)                                  \
+    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
+                  ##__VA_ARGS__)
 
-SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) {
-    (void)snapshot->FinishedSnapshotWrites(wipe);
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
+    return snapshot->FinishedSnapshotWrites(wipe);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) {
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
     std::function<bool()> before_cancel;
     if (args.has_before_cancel()) {
         before_cancel = [&]() { return args.fail_before_cancel(); };
     }
-    (void)snapshot->ProcessUpdateState({}, before_cancel);
+    return snapshot->ProcessUpdateState({}, before_cancel);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) {
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
     double progress;
-    (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+    return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) {
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
     std::function<void()> callback;
     if (has_callback) {
         callback = []() {};
     }
-    (void)snapshot->HandleImminentDataWipe(callback);
+    return snapshot->HandleImminentDataWipe(callback);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(Dump) {
+SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
     std::stringstream ss;
-    (void)snapshot->Dump(ss);
+    return snapshot->Dump(ss);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) {
-    (void)snapshot->CreateUpdateSnapshots(manifest);
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
+    return snapshot->CreateUpdateSnapshots(manifest);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) {
-    (void)snapshot->UnmapUpdateSnapshot(name);
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
+    return snapshot->UnmapUpdateSnapshot(name);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
                        const CreateLogicalAndSnapshotPartitionsArgs& args) {
     const std::string* super;
     if (args.use_correct_super()) {
@@ -123,20 +133,21 @@
     } else {
         super = &args.super();
     }
-    (void)snapshot->CreateLogicalAndSnapshotPartitions(
+    return snapshot->CreateLogicalAndSnapshotPartitions(
             *super, std::chrono::milliseconds(args.timeout_millis()));
 }
 
-SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata,
+SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
                        const RecoveryCreateSnapshotDevicesArgs& args) {
     std::unique_ptr<AutoDevice> device;
     if (args.has_metadata_device_object()) {
         device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
     }
-    (void)snapshot->RecoveryCreateSnapshotDevices(device);
+    return snapshot->RecoveryCreateSnapshotDevices(device);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) {
+SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
+                       const CreateLogicalPartitionParamsProto& params_proto) {
     auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
     CreateLogicalPartitionParams params;
     if (params_proto.use_correct_super()) {
@@ -153,10 +164,10 @@
     params.device_name = params_proto.device_name();
     params.partition_opener = partition_opener.get();
     std::string path;
-    (void)snapshot->MapUpdateSnapshot(params, &path);
+    return snapshot->MapUpdateSnapshot(params, &path);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(SwitchSlot) {
+SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
     (void)snapshot;
     CHECK(current_module != nullptr);
     CHECK(current_module->device_info != nullptr);
@@ -194,7 +205,8 @@
 }
 // Stop logging (except fatal messages) after global initialization. This is only done once.
 int StopLoggingAfterGlobalInit() {
-    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer;
+    (void)GetSnapshotFuzzEnv();
+    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
     SetLogger(&FatalOnlyLogger);
     return 0;
 }
@@ -202,15 +214,10 @@
 SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
     [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
     static SnapshotFuzzEnv env;
-    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
     return &env;
 }
 
-}  // namespace android::snapshot
-
-DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
-    using namespace android::snapshot;
-
+SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
     current_data = &snapshot_fuzz_data;
 
     auto env = GetSnapshotFuzzEnv();
@@ -219,9 +226,127 @@
     auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
     current_module = &test_module;
     CHECK(test_module.snapshot);
+    return test_module;
+}
 
-    SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
-
+void TearDownTest() {
     current_module = nullptr;
     current_data = nullptr;
 }
+
+}  // namespace android::snapshot
+
+DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
+    using namespace android::snapshot;
+
+    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+    auto test_module = SetUpTest(snapshot_fuzz_data);
+    SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
+    TearDownTest();
+}
+
+namespace android::snapshot {
+
+// Work-around to cast a 'void' value to Result<void>.
+template <typename T>
+struct GoodResult {
+    template <typename F>
+    static Result<T> Cast(F&& f) {
+        return f();
+    }
+};
+
+template <>
+struct GoodResult<void> {
+    template <typename F>
+    static Result<void> Cast(F&& f) {
+        f();
+        return {};
+    }
+};
+
+class LibsnapshotFuzzerTest : public ::testing::Test {
+  protected:
+    static void SetUpTestCase() {
+        // Do initialization once.
+        (void)GetSnapshotFuzzEnv();
+    }
+    void SetUp() override {
+        bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false);
+        if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices.";
+    }
+    void SetUpFuzzData(const std::string& fn) {
+        auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn;
+        std::string proto_text;
+        ASSERT_TRUE(ReadFileToString(path, &proto_text));
+        snapshot_fuzz_data_ = std::make_unique<SnapshotFuzzData>();
+        ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text,
+                                                                  snapshot_fuzz_data_.get()));
+        test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_);
+    }
+    void TearDown() override { android::snapshot::TearDownTest(); }
+    template <typename FuzzFunction>
+    Result<typename FuzzFunction::ReturnType> Execute(int action_index) {
+        if (action_index >= snapshot_fuzz_data_->actions_size()) {
+            return Error() << "Index " << action_index << " is out of bounds ("
+                           << snapshot_fuzz_data_->actions_size() << " actions in corpus";
+        }
+        const auto& action_proto = snapshot_fuzz_data_->actions(action_index);
+        const auto* field_desc =
+                android::fuzz::GetValueFieldDescriptor<typename FuzzFunction::ActionType>(
+                        action_proto);
+        if (field_desc == nullptr) {
+            return Error() << "Action at index " << action_index << " has no value defined.";
+        }
+        if (FuzzFunction::tag != field_desc->number()) {
+            return Error() << "Action at index " << action_index << " is expected to be "
+                           << FuzzFunction::name << ", but it is " << field_desc->name()
+                           << " in corpus.";
+        }
+        return GoodResult<typename FuzzFunction::ReturnType>::Cast([&]() {
+            return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
+                                                                        action_proto, field_desc);
+        });
+    }
+
+    std::unique_ptr<SnapshotFuzzData> snapshot_fuzz_data_;
+    SnapshotTestModule test_module_;
+};
+
+#define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name)
+
+MATCHER_P(ResultIs, expected, "") {
+    if (!arg.ok()) {
+        *result_listener << arg.error();
+        return false;
+    }
+    *result_listener << "expected: " << expected;
+    return arg.value() == expected;
+}
+
+#define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true))
+
+// Check that launch_device.txt is executed correctly.
+TEST_F(LibsnapshotFuzzerTest, LaunchDevice) {
+    SetUpFuzzData("launch_device.txt");
+
+    int i = 0;
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
+    ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
+    ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
+    ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
+}
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index c9f1ab0..8926535 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -27,7 +27,6 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/properties.h>
 #include <fs_mgr.h>
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/snapshot.h>
@@ -165,14 +164,6 @@
     reinterpret_cast<PropertyList*>(cookie)->insert(key);
 }
 
-void CheckUnsetGsidProps() {
-    PropertyList list;
-    property_list(&InsertProperty, reinterpret_cast<void*>(&list));
-    for (const auto& key : list) {
-        SetProperty(key, "");
-    }
-}
-
 // Attempt to delete all devices that is based on dev_name, including itself.
 void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false,
                                  uint64_t depth = 100) {
@@ -344,7 +335,6 @@
 };
 
 SnapshotFuzzEnv::SnapshotFuzzEnv() {
-    CheckUnsetGsidProps();
     CheckCleanupDeviceMapperDevices();
     CheckDetachLoopDevices();
     CheckUmountAll();
@@ -368,7 +358,6 @@
 }
 
 SnapshotFuzzEnv::~SnapshotFuzzEnv() {
-    CheckUnsetGsidProps();
     CheckCleanupDeviceMapperDevices();
     mounted_data_ = nullptr;
     auto_delete_data_mount_point_ = nullptr;
@@ -396,7 +385,7 @@
         const std::string& metadata_dir, const std::string& data_dir) {
     PCHECK(Mkdir(metadata_dir));
     PCHECK(Mkdir(data_dir));
-    return ImageManager::Open(metadata_dir, data_dir);
+    return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
 }
 
 // Helper to create a loop device for a file.
@@ -507,4 +496,21 @@
     return std::make_unique<AutoUnmount>(mount_point);
 }
 
+SnapshotFuzzImageManager::~SnapshotFuzzImageManager() {
+    // Remove relevant gsid.mapped_images.* props.
+    for (const auto& name : mapped_) {
+        CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name;
+    }
+}
+
+bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name,
+                                              const std::chrono::milliseconds& timeout_ms,
+                                              std::string* path) {
+    if (impl_->MapImageDevice(name, timeout_ms, path)) {
+        mapped_.insert(name);
+        return true;
+    }
+    return false;
+}
+
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 2405088..fa327b8 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
+#include <set>
 #include <string>
 
 #include <android-base/file.h>
@@ -134,4 +136,68 @@
     bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
 };
 
+// A spy class on ImageManager implementation. Upon destruction, unmaps all images
+// map through this object.
+class SnapshotFuzzImageManager : public android::fiemap::IImageManager {
+  public:
+    static std::unique_ptr<SnapshotFuzzImageManager> Open(const std::string& metadata_dir,
+                                                          const std::string& data_dir) {
+        auto impl = android::fiemap::ImageManager::Open(metadata_dir, data_dir);
+        if (impl == nullptr) return nullptr;
+        return std::unique_ptr<SnapshotFuzzImageManager>(
+                new SnapshotFuzzImageManager(std::move(impl)));
+    }
+
+    ~SnapshotFuzzImageManager();
+
+    // Spied APIs.
+    bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
+                        std::string* path) override;
+
+    // Other functions call through.
+    android::fiemap::FiemapStatus CreateBackingImage(
+            const std::string& name, uint64_t size, int flags,
+            std::function<bool(uint64_t, uint64_t)>&& on_progress) override {
+        return impl_->CreateBackingImage(name, size, flags, std::move(on_progress));
+    }
+    bool DeleteBackingImage(const std::string& name) override {
+        return impl_->DeleteBackingImage(name);
+    }
+    bool UnmapImageDevice(const std::string& name) override {
+        return impl_->UnmapImageDevice(name);
+    }
+    bool BackingImageExists(const std::string& name) override {
+        return impl_->BackingImageExists(name);
+    }
+    bool IsImageMapped(const std::string& name) override { return impl_->IsImageMapped(name); }
+    bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
+                                  std::string* dev) override {
+        return impl_->MapImageWithDeviceMapper(opener, name, dev);
+    }
+    bool GetMappedImageDevice(const std::string& name, std::string* device) override {
+        return impl_->GetMappedImageDevice(name, device);
+    }
+    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override {
+        return impl_->MapAllImages(init);
+    }
+    bool DisableImage(const std::string& name) override { return impl_->DisableImage(name); }
+    bool RemoveDisabledImages() override { return impl_->RemoveDisabledImages(); }
+    std::vector<std::string> GetAllBackingImages() override { return impl_->GetAllBackingImages(); }
+    android::fiemap::FiemapStatus ZeroFillNewImage(const std::string& name,
+                                                   uint64_t bytes) override {
+        return impl_->ZeroFillNewImage(name, bytes);
+    }
+    bool RemoveAllImages() override { return impl_->RemoveAllImages(); }
+    bool UnmapImageIfExists(const std::string& name) override {
+        return impl_->UnmapImageIfExists(name);
+    }
+
+  private:
+    std::unique_ptr<android::fiemap::IImageManager> impl_;
+    std::set<std::string> mapped_;
+
+    SnapshotFuzzImageManager(std::unique_ptr<android::fiemap::IImageManager>&& impl)
+        : impl_(std::move(impl)) {}
+};
+
 }  // namespace android::snapshot
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 6b4458c..f50764d 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -37,9 +37,7 @@
   uint32_t tv_sec = 0; /* good to Feb 5 2106 */
   uint32_t tv_nsec = 0;
 
-  static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
-  static const uint32_t tv_nsec_max = 999999999UL;
-  static const timespec EPOCH;
+  static constexpr timespec EPOCH = {0, 0};
 
   log_time() {}
   explicit log_time(const timespec& T)
@@ -55,16 +53,6 @@
     tv_nsec = static_cast<uint32_t>(T.tv_nsec);
   }
 #endif
-  explicit log_time(const char* T) {
-    const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
-    tv_sec = c[0] | (static_cast<uint32_t>(c[1]) << 8) |
-             (static_cast<uint32_t>(c[2]) << 16) |
-             (static_cast<uint32_t>(c[3]) << 24);
-    tv_nsec = c[4] | (static_cast<uint32_t>(c[5]) << 8) |
-              (static_cast<uint32_t>(c[6]) << 16) |
-              (static_cast<uint32_t>(c[7]) << 24);
-  }
-
   /* timespec */
   bool operator==(const timespec& T) const {
     return (tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
@@ -90,17 +78,6 @@
     return !(*this > T);
   }
 
-  log_time operator-=(const timespec& T);
-  log_time operator-(const timespec& T) const {
-    log_time local(*this);
-    return local -= T;
-  }
-  log_time operator+=(const timespec& T);
-  log_time operator+(const timespec& T) const {
-    log_time local(*this);
-    return local += T;
-  }
-
   /* log_time */
   bool operator==(const log_time& T) const {
     return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
@@ -123,12 +100,36 @@
     return !(*this > T);
   }
 
-  log_time operator-=(const log_time& T);
+  log_time operator-=(const log_time& T) {
+    // No concept of negative time, clamp to EPOCH
+    if (*this <= T) {
+      return *this = log_time(EPOCH);
+    }
+
+    if (this->tv_nsec < T.tv_nsec) {
+      --this->tv_sec;
+      this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+    } else {
+      this->tv_nsec -= T.tv_nsec;
+    }
+    this->tv_sec -= T.tv_sec;
+
+    return *this;
+  }
   log_time operator-(const log_time& T) const {
     log_time local(*this);
     return local -= T;
   }
-  log_time operator+=(const log_time& T);
+  log_time operator+=(const log_time& T) {
+    this->tv_nsec += T.tv_nsec;
+    if (this->tv_nsec >= NS_PER_SEC) {
+      this->tv_nsec -= NS_PER_SEC;
+      ++this->tv_sec;
+    }
+    this->tv_sec += T.tv_sec;
+
+    return *this;
+  }
   log_time operator+(const log_time& T) const {
     log_time local(*this);
     return local += T;
@@ -146,10 +147,8 @@
            tv_nsec / (NS_PER_SEC / MS_PER_SEC);
   }
 
-  static const char default_format[];
-
   /* Add %#q for the fraction of a second to the standard library functions */
-  char* strptime(const char* s, const char* format = default_format);
+  char* strptime(const char* s, const char* format);
 } __attribute__((__packed__));
 }
 
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 3fbe1cb..14c408c 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -21,11 +21,7 @@
 
 #include <private/android_logger.h>
 
-const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-const timespec log_time::EPOCH = {0, 0};
-
 // Add %#q for fractional seconds to standard strptime function
-
 char* log_time::strptime(const char* s, const char* format) {
   time_t now;
 #ifdef __linux__
@@ -131,59 +127,3 @@
 #endif
   return ret;
 }
-
-log_time log_time::operator-=(const timespec& T) {
-  // No concept of negative time, clamp to EPOCH
-  if (*this <= T) {
-    return *this = log_time(EPOCH);
-  }
-
-  if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
-    --this->tv_sec;
-    this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
-  } else {
-    this->tv_nsec -= T.tv_nsec;
-  }
-  this->tv_sec -= T.tv_sec;
-
-  return *this;
-}
-
-log_time log_time::operator+=(const timespec& T) {
-  this->tv_nsec += (unsigned long int)T.tv_nsec;
-  if (this->tv_nsec >= NS_PER_SEC) {
-    this->tv_nsec -= NS_PER_SEC;
-    ++this->tv_sec;
-  }
-  this->tv_sec += T.tv_sec;
-
-  return *this;
-}
-
-log_time log_time::operator-=(const log_time& T) {
-  // No concept of negative time, clamp to EPOCH
-  if (*this <= T) {
-    return *this = log_time(EPOCH);
-  }
-
-  if (this->tv_nsec < T.tv_nsec) {
-    --this->tv_sec;
-    this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
-  } else {
-    this->tv_nsec -= T.tv_nsec;
-  }
-  this->tv_sec -= T.tv_sec;
-
-  return *this;
-}
-
-log_time log_time::operator+=(const log_time& T) {
-  this->tv_nsec += T.tv_nsec;
-  if (this->tv_nsec >= NS_PER_SEC) {
-    this->tv_nsec -= NS_PER_SEC;
-    ++this->tv_sec;
-  }
-  this->tv_sec += T.tv_sec;
-
-  return *this;
-}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index a4e4def..3bd5cf2 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -684,8 +684,8 @@
       if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
         continue;
       }
-      log_time tx(eventData + 4 + 1);
-      if (ts != tx) {
+      log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+      if (ts != *tx) {
         if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) {
           state.SkipWithError("signal");
           break;
@@ -757,8 +757,8 @@
       if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
         continue;
       }
-      log_time tx(eventData + 4 + 1);
-      if (ts != tx) {
+      log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+      if (ts != *tx) {
         if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) {
           state.SkipWithError("signal");
           break;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index bbc985a..fbc3d7a 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -270,10 +270,10 @@
       return;
     }
 
-    log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
-    if (ts == tx) {
+    log_time* tx = reinterpret_cast<log_time*>(&eventData->payload.data);
+    if (ts == *tx) {
       ++count;
-    } else if (ts1 == tx) {
+    } else if (ts1 == *tx) {
       ++second_count;
     }
 
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index e35cb0d..b883c13 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -35,8 +35,9 @@
 #include "sparse_crc32.h"
 #include "sparse_format.h"
 
+#include <android-base/mapped_file.h>
+
 #ifndef _WIN32
-#include <sys/mman.h>
 #define O_BINARY 0
 #else
 #define ftruncate64 ftruncate
@@ -45,7 +46,6 @@
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
 #define ftruncate64 ftruncate
-#define mmap64 mmap
 #define off64_t off_t
 #endif
 
@@ -649,52 +649,10 @@
 }
 
 int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
-  int ret;
-  int64_t aligned_offset;
-  int aligned_diff;
-  uint64_t buffer_size;
-  char* ptr;
+  auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
+  if (!m) return -errno;
 
-  aligned_offset = offset & ~(4096 - 1);
-  aligned_diff = offset - aligned_offset;
-  buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
-  if (buffer_size > SIZE_MAX) return -E2BIG;
-  char* data =
-      reinterpret_cast<char*>(mmap64(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
-  if (data == MAP_FAILED) {
-    return -errno;
-  }
-  ptr = data + aligned_diff;
-#else
-  off64_t pos;
-  char* data = reinterpret_cast<char*>(malloc(len));
-  if (!data) {
-    return -errno;
-  }
-  pos = lseek64(fd, offset, SEEK_SET);
-  if (pos < 0) {
-    free(data);
-    return -errno;
-  }
-  ret = read_all(fd, data, len);
-  if (ret < 0) {
-    free(data);
-    return ret;
-  }
-  ptr = data;
-#endif
-
-  ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
-  munmap(data, buffer_size);
-#else
-  free(data);
-#endif
-
-  return ret;
+  return out->sparse_ops->write_data_chunk(out, m->size(), m->data());
 }
 
 /* Write a contiguous region of data blocks from a file */
diff --git a/libziparchive/.clang-format b/libziparchive/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/libziparchive/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
deleted file mode 100644
index c5a968a..0000000
--- a/libziparchive/Android.bp
+++ /dev/null
@@ -1,237 +0,0 @@
-//
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_defaults {
-    name: "libziparchive_flags",
-    cflags: [
-        // ZLIB_CONST turns on const for input buffers, which is pretty standard.
-        "-DZLIB_CONST",
-        "-Werror",
-        "-Wall",
-        "-D_FILE_OFFSET_BITS=64",
-    ],
-    cppflags: [
-        // Incorrectly warns when C++11 empty brace {} initializer is used.
-        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
-        "-Wno-missing-field-initializers",
-        "-Wconversion",
-        "-Wno-sign-conversion",
-    ],
-
-    // Enable -Wold-style-cast only for non-Windows targets.  _islower_l,
-    // _isupper_l etc. in MinGW locale_win32.h (included from
-    // libcxx/include/__locale) has an old-style-cast.
-    target: {
-        not_windows: {
-            cppflags: [
-                "-Wold-style-cast",
-            ],
-        },
-    },
-    sanitize: {
-        misc_undefined: [
-            "signed-integer-overflow",
-            "unsigned-integer-overflow",
-            "shift",
-            "integer-divide-by-zero",
-            "implicit-signed-integer-truncation",
-            // TODO: Fix crash when we enable this option
-            // "implicit-unsigned-integer-truncation",
-            // TODO: not tested yet.
-            // "implicit-integer-sign-change",
-        ],
-    },
-}
-
-cc_defaults {
-    name: "libziparchive_defaults",
-    srcs: [
-        "zip_archive.cc",
-        "zip_archive_stream_entry.cc",
-        "zip_cd_entry_map.cc",
-        "zip_error.cpp",
-        "zip_writer.cc",
-    ],
-
-    target: {
-        windows: {
-            cflags: ["-mno-ms-bitfields"],
-
-            enabled: true,
-        },
-    },
-
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-
-    // for FRIEND_TEST
-    static_libs: ["libgtest_prod"],
-    export_static_lib_headers: ["libgtest_prod"],
-
-    export_include_dirs: ["include"],
-}
-
-cc_library {
-    name: "libziparchive",
-    host_supported: true,
-    vendor_available: true,
-    recovery_available: true,
-    native_bridge_supported: true,
-    vndk: {
-        enabled: true,
-    },
-    double_loadable: true,
-    export_shared_lib_headers: ["libbase"],
-
-    defaults: [
-        "libziparchive_defaults",
-        "libziparchive_flags",
-    ],
-    shared_libs: [
-        "liblog",
-        "libbase",
-        "libz",
-    ],
-    target: {
-        linux_bionic: {
-            enabled: true,
-        },
-    },
-
-    apex_available: [
-        "//apex_available:platform",
-        "com.android.art.debug",
-        "com.android.art.release",
-    ],
-}
-
-// Tests.
-cc_test {
-    name: "ziparchive-tests",
-    host_supported: true,
-    defaults: ["libziparchive_flags"],
-
-    data: [
-        "testdata/**/*",
-    ],
-
-    srcs: [
-        "entry_name_utils_test.cc",
-        "zip_archive_test.cc",
-        "zip_writer_test.cc",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-
-    static_libs: [
-        "libziparchive",
-        "libz",
-        "libutils",
-    ],
-
-    target: {
-        host: {
-            cppflags: ["-Wno-unnamed-type-template-args"],
-        },
-        windows: {
-            enabled: true,
-        },
-    },
-    test_suites: ["device-tests"],
-}
-
-// Performance benchmarks.
-cc_benchmark {
-    name: "ziparchive-benchmarks",
-    defaults: ["libziparchive_flags"],
-
-    srcs: [
-        "zip_archive_benchmark.cpp",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-
-    static_libs: [
-        "libziparchive",
-        "libz",
-        "libutils",
-    ],
-
-    target: {
-        host: {
-            cppflags: ["-Wno-unnamed-type-template-args"],
-        },
-    },
-}
-
-cc_binary {
-    name: "ziptool",
-    defaults: ["libziparchive_flags"],
-    srcs: ["ziptool.cpp"],
-    shared_libs: [
-        "libbase",
-        "libziparchive",
-    ],
-    recovery_available: true,
-    host_supported: true,
-    target: {
-        android: {
-            symlinks: ["unzip", "zipinfo"],
-        },
-    },
-}
-
-cc_fuzz {
-    name: "libziparchive_fuzzer",
-    srcs: ["libziparchive_fuzzer.cpp"],
-    static_libs: ["libziparchive", "libbase", "libz", "liblog"],
-    host_supported: true,
-    corpus: ["testdata/*"],
-}
-
-sh_test {
-    name: "ziptool-tests",
-    src: "run-ziptool-tests-on-android.sh",
-    filename: "run-ziptool-tests-on-android.sh",
-    test_suites: ["general-tests"],
-    host_supported: true,
-    device_supported: false,
-    test_config: "ziptool-tests.xml",
-    data: ["cli-tests/**/*"],
-    target_required: ["cli-test", "ziptool"],
-}
-
-python_test_host {
-    name: "ziparchive_tests_large",
-    srcs: ["test_ziparchive_large.py"],
-    main: "test_ziparchive_large.py",
-    version: {
-        py2: {
-            enabled: true,
-            embedded_launcher: false,
-        },
-        py3: {
-            enabled: false,
-            embedded_launcher: false,
-        },
-    },
-    test_suites: ["general-tests"],
-}
diff --git a/libziparchive/OWNERS b/libziparchive/OWNERS
deleted file mode 100644
index fcc567a..0000000
--- a/libziparchive/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-narayan@google.com
diff --git a/libziparchive/cli-tests/files/example.zip b/libziparchive/cli-tests/files/example.zip
deleted file mode 100644
index c3292e9..0000000
--- a/libziparchive/cli-tests/files/example.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/cli-tests/unzip.test b/libziparchive/cli-tests/unzip.test
deleted file mode 100755
index 6e5cbf2..0000000
--- a/libziparchive/cli-tests/unzip.test
+++ /dev/null
@@ -1,148 +0,0 @@
-# unzip tests.
-
-# Note: since "master key", Android uses libziparchive for all zip file
-# handling, and that scans the whole central directory immediately. Not only
-# lookups by name but also iteration is implemented using the resulting hash
-# table, meaning that any test that makes assumptions about iteration order
-# will fail on Android.
-
-name: unzip -l
-command: unzip -l $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/x.txt ]
-expected-stdout:
-	Archive:  $FILES/example.zip
-	  Length      Date    Time    Name
-	---------  ---------- -----   ----
-	     1024  2017-06-04 08:45   d1/d2/x.txt
-	---------                     -------
-	     1024                     1 file
----
-
-name: unzip -lq
-command: unzip -lq $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/x.txt ]
-expected-stdout:
-	  Length      Date    Time    Name
-	---------  ---------- -----   ----
-	     1024  2017-06-04 08:45   d1/d2/x.txt
-	---------                     -------
-	     1024                     1 file
----
-
-name: unzip -lv
-command: unzip -lv $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/file ]
-expected-stdout:
-	Archive:  $FILES/example.zip
-	 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
-	--------  ------  ------- ---- ---------- ----- --------  ----
-	    1024  Defl:N       11  99% 2017-06-04 08:45 48d7f063  d1/d2/x.txt
-	--------          -------  ---                            -------
-	    1024               11  99%                            1 file
----
-
-name: unzip -v
-command: unzip -v $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/file ]
-expected-stdout:
-	Archive:  $FILES/example.zip
-	 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
-	--------  ------  ------- ---- ---------- ----- --------  ----
-	    1024  Defl:N       11  99% 2017-06-04 08:45 48d7f063  d1/d2/x.txt
-	--------          -------  ---                            -------
-	    1024               11  99%                            1 file
----
-
-name: unzip one file
-command: unzip -q $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-after: [ ! -f d1/d2/b.txt ]
-expected-stdout:
-	a
----
-
-name: unzip all files
-command: unzip -q $FILES/example.zip
-after: [ -f d1/d2/a.txt ]
-after: [ -f d1/d2/b.txt ]
-after: [ -f d1/d2/c.txt ]
-after: [ -f d1/d2/empty.txt ]
-after: [ -f d1/d2/x.txt ]
-after: [ -d d1/d2/dir ]
-expected-stdout:
----
-
-name: unzip -o
-before: mkdir -p d1/d2
-before: echo b > d1/d2/a.txt
-command: unzip -q -o $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-expected-stdout:
-	a
----
-
-name: unzip -n
-before: mkdir -p d1/d2
-before: echo b > d1/d2/a.txt
-command: unzip -q -n $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-expected-stdout:
-	b
----
-
-# The reference implementation will create *one* level of missing directories,
-# so this succeeds.
-name: unzip -d shallow non-existent
-command: unzip -q -d will-be-created $FILES/example.zip d1/d2/a.txt
-after: [ -d will-be-created ]
-after: [ -f will-be-created/d1/d2/a.txt ]
----
-
-# The reference implementation will *only* create one level of missing
-# directories, so this fails.
-name: unzip -d deep non-existent
-command: unzip -q -d oh-no/will-not-be-created $FILES/example.zip d1/d2/a.txt 2> stderr ; echo $? > status
-after: [ ! -d oh-no ]
-after: [ ! -d oh-no/will-not-be-created ]
-after: [ ! -f oh-no/will-not-be-created/d1/d2/a.txt ]
-after: grep -q "oh-no/will-not-be-created" stderr
-after: grep -q "No such file or directory" stderr
-# The reference implementation has *lots* of non-zero exit values, but we stick to 0 and 1.
-after: [ $(cat status) -gt 0 ]
----
-
-name: unzip -d exists
-before: mkdir dir
-command: unzip -q -d dir $FILES/example.zip d1/d2/a.txt && cat dir/d1/d2/a.txt
-after: [ ! -f d1/d2/a.txt ]
-expected-stdout:
-	a
----
-
-name: unzip -p
-command: unzip -p $FILES/example.zip d1/d2/a.txt
-after: [ ! -f d1/d2/a.txt ]
-expected-stdout:
-	a
----
-
-name: unzip -x FILE...
-# Note: the RI ignores -x DIR for some reason, but it's not obvious we should.
-command: unzip -q $FILES/example.zip -x d1/d2/a.txt d1/d2/b.txt d1/d2/empty.txt d1/d2/x.txt && cat d1/d2/c.txt
-after: [ ! -f d1/d2/a.txt ]
-after: [ ! -f d1/d2/b.txt ]
-after: [ ! -f d1/d2/empty.txt ]
-after: [ ! -f d1/d2/x.txt ]
-after: [ -d d1/d2/dir ]
-expected-stdout:
-	ccc
----
-
-name: unzip FILE -x FILE...
-command: unzip -q $FILES/example.zip d1/d2/a.txt d1/d2/b.txt -x d1/d2/a.txt && cat d1/d2/b.txt
-after: [ ! -f d1/d2/a.txt ]
-after: [ -f d1/d2/b.txt ]
-after: [ ! -f d1/d2/c.txt ]
-after: [ ! -f d1/d2/empty.txt ]
-after: [ ! -f d1/d2/x.txt ]
-after: [ ! -d d1/d2/dir ]
-expected-stdout:
-	bb
----
diff --git a/libziparchive/cli-tests/zipinfo.test b/libziparchive/cli-tests/zipinfo.test
deleted file mode 100755
index d5bce1c..0000000
--- a/libziparchive/cli-tests/zipinfo.test
+++ /dev/null
@@ -1,53 +0,0 @@
-# zipinfo tests.
-
-# Note: since "master key", Android uses libziparchive for all zip file
-# handling, and that scans the whole central directory immediately. Not only
-# lookups by name but also iteration is implemented using the resulting hash
-# table, meaning that any test that makes assumptions about iteration order
-# will fail on Android.
-
-name: zipinfo -1
-command: zipinfo -1 $FILES/example.zip | sort
-expected-stdout:
-	d1/
-	d1/d2/a.txt
-	d1/d2/b.txt
-	d1/d2/c.txt
-	d1/d2/dir/
-	d1/d2/empty.txt
-	d1/d2/x.txt
----
-
-name: zipinfo header
-command: zipinfo $FILES/example.zip | head -2
-expected-stdout:
-	Archive:  $FILES/example.zip
-	Zip file size: 1082 bytes, number of entries: 7
----
-
-name: zipinfo footer
-command: zipinfo $FILES/example.zip | tail -1
-expected-stdout:
-	7 files, 1033 bytes uncompressed, 20 bytes compressed:  98.1%
----
-
-name: zipinfo directory
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/ | sed s/17-Jun-/2017-06-/
-expected-stdout:
-	drwxr-x---  3.0 unx        0 bx stor 2017-06-04 08:40 d1/
----
-
-name: zipinfo stored
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/d2/empty.txt | sed s/17-Jun-/2017-06-/
-expected-stdout:
-	-rw-r-----  3.0 unx        0 bx stor 2017-06-04 08:43 d1/d2/empty.txt
----
-
-name: zipinfo deflated
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/d2/x.txt | sed s/17-Jun-/2017-06-/
-expected-stdout:
-	-rw-r-----  3.0 unx     1024 tx defN 2017-06-04 08:45 d1/d2/x.txt
----
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
deleted file mode 100644
index 10311b5..0000000
--- a/libziparchive/entry_name_utils-inl.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
-#define LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <limits>
-
-// Check if |length| bytes at |entry_name| constitute a valid entry name.
-// Entry names must be valid UTF-8 and must not contain '0'. They also must
-// fit into the central directory record.
-inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
-  if (length > std::numeric_limits<uint16_t>::max()) {
-    return false;
-  }
-  for (size_t i = 0; i < length; ++i) {
-    const uint8_t byte = entry_name[i];
-    if (byte == 0) {
-      return false;
-    } else if ((byte & 0x80) == 0) {
-      // Single byte sequence.
-      continue;
-    } else if ((byte & 0xc0) == 0x80 || (byte & 0xfe) == 0xfe) {
-      // Invalid sequence.
-      return false;
-    } else {
-      // 2-5 byte sequences.
-      for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
-           first = static_cast<uint8_t>((first & 0x7f) << 1)) {
-        ++i;
-
-        // Missing continuation byte..
-        if (i == length) {
-          return false;
-        }
-
-        // Invalid continuation byte.
-        const uint8_t continuation_byte = entry_name[i];
-        if ((continuation_byte & 0xc0) != 0x80) {
-          return false;
-        }
-      }
-    }
-  }
-
-  return true;
-}
-
-#endif  // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
deleted file mode 100644
index d83d854..0000000
--- a/libziparchive/entry_name_utils_test.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entry_name_utils-inl.h"
-
-#include <gtest/gtest.h>
-
-TEST(entry_name_utils, NullChars) {
-  // 'A', 'R', '\0', 'S', 'E'
-  const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
-  ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
-
-  const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
-  ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
-}
-
-TEST(entry_name_utils, InvalidSequence) {
-  // 0xfe is an invalid start byte
-  const uint8_t invalid[] = {0x41, 0xfe};
-  ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
-
-  // 0x91 is an invalid start byte (it's a valid continuation byte).
-  const uint8_t invalid2[] = {0x41, 0x91};
-  ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
-}
-
-TEST(entry_name_utils, TruncatedContinuation) {
-  // Malayalam script with truncated bytes. There should be 2 bytes
-  // after 0xe0
-  const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
-  ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
-
-  // 0xc2 is the start of a 2 byte sequence that we've subsequently
-  // dropped.
-  const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
-  ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
-}
-
-TEST(entry_name_utils, BadContinuation) {
-  // 0x41 is an invalid continuation char, since it's MSBs
-  // aren't "10..." (are 01).
-  const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
-  ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
-
-  // 0x41 is an invalid continuation char, since it's MSBs
-  // aren't "10..." (are 11).
-  const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
-  ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
-}
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
deleted file mode 100644
index 005d697..0000000
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2013 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
-
-/*
- * Read-only access to Zip archives, with minimal heap allocation.
- */
-
-#include <stdint.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include <functional>
-#include <string>
-#include <string_view>
-
-#include "android-base/off64_t.h"
-
-/* Zip compression methods we support */
-enum {
-  kCompressStored = 0,    // no compression
-  kCompressDeflated = 8,  // standard deflate
-};
-
-// This struct holds the common information of a zip entry other than the
-// the entry size. The compressed and uncompressed length will be handled
-// separately in the derived class.
-struct ZipEntryCommon {
-  // Compression method. One of kCompressStored or kCompressDeflated.
-  // See also `gpbf` for deflate subtypes.
-  uint16_t method;
-
-  // Modification time. The zipfile format specifies
-  // that the first two little endian bytes contain the time
-  // and the last two little endian bytes contain the date.
-  // See `GetModificationTime`. Use signed integer to avoid the
-  // sub-overflow.
-  // TODO: should be overridden by extra time field, if present.
-  int32_t mod_time;
-
-  // Returns `mod_time` as a broken-down struct tm.
-  struct tm GetModificationTime() const;
-
-  // Suggested Unix mode for this entry, from the zip archive if created on
-  // Unix, or a default otherwise. See also `external_file_attributes`.
-  mode_t unix_mode;
-
-  // 1 if this entry contains a data descriptor segment, 0
-  // otherwise.
-  uint8_t has_data_descriptor;
-
-  // Crc32 value of this ZipEntry. This information might
-  // either be stored in the local file header or in a special
-  // Data descriptor footer at the end of the file entry.
-  uint32_t crc32;
-
-  // If the value of uncompressed length and compressed length are stored in
-  // the zip64 extended info of the extra field.
-  bool zip64_format_size{false};
-
-  // The offset to the start of data for this ZipEntry.
-  off64_t offset;
-
-  // The version of zip and the host file system this came from (for zipinfo).
-  uint16_t version_made_by;
-
-  // The raw attributes, whose interpretation depends on the host
-  // file system in `version_made_by` (for zipinfo). See also `unix_mode`.
-  uint32_t external_file_attributes;
-
-  // Specifics about the deflation (for zipinfo).
-  uint16_t gpbf;
-  // Whether this entry is believed to be text or binary (for zipinfo).
-  bool is_text;
-};
-
-struct ZipEntry64;
-// Many users of the library assume the entry size is capped at UNIT32_MAX. So we keep
-// the interface for the old ZipEntry here; and we could switch them over to the new
-// ZipEntry64 later.
-struct ZipEntry : public ZipEntryCommon {
-  // Compressed length of this ZipEntry. The maximum value is UNIT32_MAX.
-  // Might be present either in the local file header or in the data
-  // descriptor footer.
-  uint32_t compressed_length{0};
-
-  // Uncompressed length of this ZipEntry. The maximum value is UNIT32_MAX.
-  // Might be present either in the local file header or in the data
-  // descriptor footer.
-  uint32_t uncompressed_length{0};
-
-  // Copies the contents of a ZipEntry64 object to a 32 bits ZipEntry. Returns 0 if the
-  // size of the entry fits into uint32_t, returns a negative error code
-  // (kUnsupportedEntrySize) otherwise.
-  static int32_t CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src);
-
- private:
-  ZipEntry& operator=(const ZipEntryCommon& other) {
-    ZipEntryCommon::operator=(other);
-    return *this;
-  }
-};
-
-// Represents information about a zip entry in a zip file.
-struct ZipEntry64 : public ZipEntryCommon {
-  // Compressed length of this ZipEntry. The maximum value is UNIT64_MAX.
-  // Might be present either in the local file header, the zip64 extended field,
-  // or in the data descriptor footer.
-  uint64_t compressed_length{0};
-
-  // Uncompressed length of this ZipEntry. The maximum value is UNIT64_MAX.
-  // Might be present either in the local file header, the zip64 extended field,
-  // or in the data descriptor footer.
-  uint64_t uncompressed_length{0};
-
-  explicit ZipEntry64() = default;
-  explicit ZipEntry64(const ZipEntry& zip_entry) : ZipEntryCommon(zip_entry) {
-    compressed_length = zip_entry.compressed_length;
-    uncompressed_length = zip_entry.uncompressed_length;
-  }
-};
-
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
-/*
- * Open a Zip archive, and sets handle to the value of the opaque
- * handle for the file. This handle must be released by calling
- * CloseArchive with this handle.
- *
- * Returns 0 on success, and negative values on failure.
- */
-int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
-
-/*
- * Like OpenArchive, but takes a file descriptor open for reading
- * at the start of the file.  The descriptor must be mappable (this does
- * not allow access to a stream).
- *
- * Sets handle to the value of the opaque handle for this file descriptor.
- * This handle must be released by calling CloseArchive with this handle.
- *
- * If assume_ownership parameter is 'true' calling CloseArchive will close
- * the file.
- *
- * This function maps and scans the central directory and builds a table
- * of entries for future lookups.
- *
- * "debugFileName" will appear in error messages, but is not otherwise used.
- *
- * Returns 0 on success, and negative values on failure.
- */
-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);
-/*
- * Close archive, releasing resources associated with it. This will
- * unmap the central directory of the zipfile and free all internal
- * data structures associated with the file. It is an error to use
- * this handle for any further operations without an intervening
- * call to one of the OpenArchive variants.
- */
-void CloseArchive(ZipArchiveHandle archive);
-
-/** See GetArchiveInfo(). */
-struct ZipArchiveInfo {
-  /** The size in bytes of the archive itself. Used by zipinfo. */
-  off64_t archive_size;
-  /** The number of entries in the archive. */
-  uint64_t entry_count;
-};
-
-/**
- * Returns information about the given archive.
- */
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
-
-/*
- * Find an entry in the Zip archive, by name. |data| must be non-null.
- *
- * Returns 0 if an entry is found, and populates |data| with information
- * about this entry. Returns negative values otherwise.
- *
- * It's important to note that |data->crc32|, |data->compLen| and
- * |data->uncompLen| might be set to values from the central directory
- * if this file entry contains a data descriptor footer. To verify crc32s
- * and length, a call to VerifyCrcAndLengths must be made after entry data
- * has been processed.
- *
- * On non-Windows platforms this method does not modify internal state and
- * can be called concurrently.
- */
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
-                  ZipEntry64* data);
-
-/*
- * Start iterating over all entries of a zip file. The order of iteration
- * is not guaranteed to be the same as the order of elements
- * in the central directory but is stable for a given zip file. |cookie| will
- * contain the value of an opaque cookie which can be used to make one or more
- * calls to Next. All calls to StartIteration must be matched by a call to
- * EndIteration to free any allocated memory.
- *
- * This method also accepts optional prefix and suffix to restrict iteration to
- * entry names that start with |optional_prefix| or end with |optional_suffix|.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
-                       const std::string_view optional_prefix = "",
-                       const std::string_view optional_suffix = "");
-
-/*
- * Start iterating over all entries of a zip file. Use the matcher functor to
- * restrict iteration to entry names that make the functor return true.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
-                       std::function<bool(std::string_view entry_name)> matcher);
-
-/*
- * Advance to the next element in the zipfile in iteration order.
- *
- * Returns 0 on success, -1 if there are no more elements in this
- * archive and lower negative values on failure.
- */
-int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name);
-int32_t Next(void* cookie, ZipEntry64* data, std::string* name);
-
-/*
- * End iteration over all entries of a zip file and frees the memory allocated
- * in StartIteration.
- */
-void EndIteration(void* cookie);
-
-/*
- * Uncompress and write an entry to an open file identified by |fd|.
- * |entry->uncompressed_length| bytes will be written to the file at
- * its current offset, and the file will be truncated at the end of
- * the uncompressed data (no truncation if |fd| references a block
- * device).
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd);
-
-/**
- * Uncompress a given zip entry to the memory region at |begin| and of
- * size |size|. This size is expected to be the same as the *declared*
- * uncompressed length of the zip entry. It is an error if the *actual*
- * number of uncompressed bytes differs from this number.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin,
-                        size_t size);
-
-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);
-
-// Many users of libziparchive assume the entry size to be 32 bits long. So we keep these
-// interfaces that use 32 bit ZipEntry to make old code work. TODO(xunchang) Remove the 32 bit
-// wrapper functions once we switch all users to recognize ZipEntry64.
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
-int32_t Next(void* cookie, ZipEntry* data, std::string* name);
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd);
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin,
-                        size_t size);
-
-#if !defined(_WIN32)
-typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie);
-
-/*
- * Stream the uncompressed data through the supplied function,
- * passing cookie to it each time it gets called.
- */
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry,
-                                ProcessZipEntryFunction func, void* cookie);
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry,
-                                ProcessZipEntryFunction func, void* cookie);
-#endif
-
-namespace zip_archive {
-
-class Writer {
- public:
-  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
-  virtual ~Writer();
-
- protected:
-  Writer() = default;
-
- private:
-  Writer(const Writer&) = delete;
-  void operator=(const Writer&) = delete;
-};
-
-class Reader {
- public:
-  virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const = 0;
-  virtual ~Reader();
-
- protected:
-  Reader() = default;
-
- private:
-  Reader(const Reader&) = delete;
-  void operator=(const Reader&) = delete;
-};
-
-/*
- * Inflates the first |compressed_length| bytes of |reader| to a given |writer|.
- * |crc_out| is set to the CRC32 checksum of the uncompressed data.
- *
- * Returns 0 on success and negative values on failure, for example if |reader|
- * cannot supply the right amount of data, or if the number of bytes written to
- * data does not match |uncompressed_length|.
- *
- * If |crc_out| is not nullptr, it is set to the crc32 checksum of the
- * uncompressed data.
- */
-int32_t Inflate(const Reader& reader, const uint64_t compressed_length,
-                const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out);
-}  // namespace zip_archive
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
deleted file mode 100644
index 8c6ca79..0000000
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Read-only stream access to Zip archives entries.
-#pragma once
-
-#include <ziparchive/zip_archive.h>
-
-#include <vector>
-
-#include "android-base/off64_t.h"
-
-class ZipArchiveStreamEntry {
- public:
-  virtual ~ZipArchiveStreamEntry() {}
-
-  virtual const std::vector<uint8_t>* Read() = 0;
-
-  virtual bool Verify() = 0;
-
-  static ZipArchiveStreamEntry* Create(ZipArchiveHandle handle, const ZipEntry& entry);
-  static ZipArchiveStreamEntry* CreateRaw(ZipArchiveHandle handle, const ZipEntry& entry);
-
- protected:
-  ZipArchiveStreamEntry(ZipArchiveHandle handle) : handle_(handle) {}
-
-  virtual bool Init(const ZipEntry& entry);
-
-  ZipArchiveHandle handle_;
-
-  off64_t offset_ = 0;
-  uint32_t crc32_ = 0u;
-};
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
deleted file mode 100644
index d68683d..0000000
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdio>
-#include <ctime>
-
-#include <gtest/gtest_prod.h>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-
-struct z_stream_s;
-typedef struct z_stream_s z_stream;
-
-/**
- * Writes a Zip file via a stateful interface.
- *
- * Example:
- *
- *   FILE* file = fopen("path/to/zip.zip", "wb");
- *
- *   ZipWriter writer(file);
- *
- *   writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
- *   writer.WriteBytes(buffer, bufferLen);
- *   writer.WriteBytes(buffer2, bufferLen2);
- *   writer.FinishEntry();
- *
- *   writer.StartEntry("empty.txt", 0);
- *   writer.FinishEntry();
- *
- *   writer.Finish();
- *
- *   fclose(file);
- */
-class ZipWriter {
- public:
-  enum {
-    /**
-     * Flag to compress the zip entry using deflate.
-     */
-    kCompress = 0x01,
-
-    /**
-     * Flag to align the zip entry data on a 32bit boundary. Useful for
-     * mmapping the data at runtime.
-     */
-    kAlign32 = 0x02,
-  };
-
-  /**
-   * A struct representing a zip file entry.
-   */
-  struct FileEntry {
-    std::string path;
-    uint16_t compression_method;
-    uint32_t crc32;
-    uint32_t compressed_size;
-    uint32_t uncompressed_size;
-    uint16_t last_mod_time;
-    uint16_t last_mod_date;
-    uint16_t padding_length;
-    off64_t local_file_header_offset;
-  };
-
-  static const char* ErrorCodeString(int32_t error_code);
-
-  /**
-   * Create a ZipWriter that will write into a FILE stream. The file should be opened with
-   * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
-   * caller is responsible for closing the file.
-   */
-  explicit ZipWriter(FILE* f);
-
-  // Move constructor.
-  ZipWriter(ZipWriter&& zipWriter) noexcept;
-
-  // Move assignment.
-  ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
-
-  /**
-   * Starts a new zip entry with the given path and flags.
-   * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
-   * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
-   * Returns 0 on success, and an error value < 0 on failure.
-   */
-  int32_t StartEntry(std::string_view path, size_t flags);
-
-  /**
-   * Starts a new zip entry with the given path and flags, where the
-   * entry will be aligned to the given alignment.
-   * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
-   * will result in an error.
-   * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
-   * Returns 0 on success, and an error value < 0 on failure.
-   */
-  int32_t StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment);
-
-  /**
-   * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
-   */
-  int32_t StartEntryWithTime(std::string_view path, size_t flags, time_t time);
-
-  /**
-   * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
-   */
-  int32_t StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time, uint32_t alignment);
-
-  /**
-   * Writes bytes to the zip file for the previously started zip entry.
-   * Returns 0 on success, and an error value < 0 on failure.
-   */
-  int32_t WriteBytes(const void* data, size_t len);
-
-  /**
-   * Finish a zip entry started with StartEntry(const char*, size_t) or
-   * StartEntryWithTime(const char*, size_t, time_t). This must be called before
-   * any new zip entries are started, or before Finish() is called.
-   * Returns 0 on success, and an error value < 0 on failure.
-   */
-  int32_t FinishEntry();
-
-  /**
-   * Discards the last-written entry. Can only be called after an entry has been written using
-   * FinishEntry().
-   * Returns 0 on success, and an error value < 0 on failure.
-   */
-  int32_t DiscardLastEntry();
-
-  /**
-   * Sets `out_entry` to the last entry written after a call to FinishEntry().
-   * Returns 0 on success, and an error value < 0 if no entries have been written.
-   */
-  int32_t GetLastEntry(FileEntry* out_entry);
-
-  /**
-   * Writes the Central Directory Headers and flushes the zip file stream.
-   * Returns 0 on success, and an error value < 0 on failure.
-   */
-  int32_t Finish();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ZipWriter);
-
-  int32_t HandleError(int32_t error_code);
-  int32_t PrepareDeflate();
-  int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
-  int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
-  int32_t FlushCompressedBytes(FileEntry* file);
-  bool ShouldUseDataDescriptor() const;
-
-  enum class State {
-    kWritingZip,
-    kWritingEntry,
-    kDone,
-    kError,
-  };
-
-  FILE* file_;
-  bool seekable_;
-  off64_t current_offset_;
-  State state_;
-  std::vector<FileEntry> files_;
-  FileEntry current_file_entry_;
-
-  std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
-  std::vector<uint8_t> buffer_;
-
-  FRIEND_TEST(zipwriter, WriteToUnseekableFile);
-};
diff --git a/libziparchive/libziparchive_fuzzer.cpp b/libziparchive/libziparchive_fuzzer.cpp
deleted file mode 100644
index 75e7939..0000000
--- a/libziparchive/libziparchive_fuzzer.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <ziparchive/zip_archive.h>
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  ZipArchiveHandle handle = nullptr;
-  OpenArchiveFromMemory(data, size, "fuzz", &handle);
-  CloseArchive(handle);
-  return 0;
-}
diff --git a/libziparchive/run-ziptool-tests-on-android.sh b/libziparchive/run-ziptool-tests-on-android.sh
deleted file mode 100755
index 3c23d43..0000000
--- a/libziparchive/run-ziptool-tests-on-android.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-# Copy the tests across.
-adb shell rm -rf /data/local/tmp/ziptool-tests/
-adb shell mkdir /data/local/tmp/ziptool-tests/
-adb push cli-tests/ /data/local/tmp/ziptool-tests/
-#adb push cli-test /data/local/tmp/ziptool-tests/
-
-if tty -s; then
-  dash_t="-t"
-else
-  dash_t=""
-fi
-
-exec adb shell $dash_t cli-test /data/local/tmp/ziptool-tests/cli-tests/*.test
diff --git a/libziparchive/test_ziparchive_large.py b/libziparchive/test_ziparchive_large.py
deleted file mode 100644
index 46d02aa..0000000
--- a/libziparchive/test_ziparchive_large.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-#
-
-"""Unittests for parsing files in zip64 format"""
-
-import os
-import subprocess
-import tempfile
-import unittest
-import zipfile
-import time
-
-class Zip64Test(unittest.TestCase):
-  @staticmethod
-  def _WriteFile(path, size_in_kib):
-    contents = os.path.basename(path)[0] * 1024
-    with open(path, 'w') as f:
-      for it in range(0, size_in_kib):
-        f.write(contents)
-
-  @staticmethod
-  def _AddEntriesToZip(output_zip, entries_dict=None):
-    for name, size in entries_dict.items():
-      file_path = tempfile.NamedTemporaryFile()
-      Zip64Test._WriteFile(file_path.name, size)
-      output_zip.write(file_path.name, arcname = name)
-
-  def _getEntryNames(self, zip_name):
-    cmd = ['ziptool', 'zipinfo', '-1', zip_name]
-    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    output, _ = proc.communicate()
-    self.assertEquals(0, proc.returncode)
-    self.assertNotEqual(None, output)
-    return output.split()
-
-  def _ExtractEntries(self, zip_name):
-    temp_dir = tempfile.mkdtemp()
-    cmd = ['ziptool', 'unzip', '-d', temp_dir, zip_name]
-    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    proc.communicate()
-    self.assertEquals(0, proc.returncode)
-
-  def test_entriesSmallerThan2G(self):
-    zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
-    # Add a few entries with each of them smaller than 2GiB. But the entire zip file is larger
-    # than 4GiB in size.
-    with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip:
-      entry_dict = {'a.txt': 1025 * 1024, 'b.txt': 1025 * 1024, 'c.txt': 1025 * 1024,
-                    'd.txt': 1025 * 1024, 'e.txt': 1024}
-      self._AddEntriesToZip(output_zip, entry_dict)
-
-    read_names = self._getEntryNames(zip_path.name)
-    self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
-    self._ExtractEntries(zip_path.name)
-
-
-  def test_largeNumberOfEntries(self):
-    zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
-    entry_dict = {}
-    # Add 100k entries (more than 65535|UINT16_MAX).
-    for num in range(0, 100 * 1024):
-      entry_dict[str(num)] = 50
-
-    with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip:
-      self._AddEntriesToZip(output_zip, entry_dict)
-
-    read_names = self._getEntryNames(zip_path.name)
-    self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
-    self._ExtractEntries(zip_path.name)
-
-
-  def test_largeCompressedEntriesSmallerThan4G(self):
-    zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
-    with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED,
-                         allowZip64=True) as output_zip:
-      # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
-      # sizes in the extra field. Test if our ziptool should be able to parse it.
-      entry_dict = {'e.txt': 4095 * 1024, 'f.txt': 4095 * 1024}
-      self._AddEntriesToZip(output_zip, entry_dict)
-
-    read_names = self._getEntryNames(zip_path.name)
-    self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
-    self._ExtractEntries(zip_path.name)
-
-
-  def test_forceDataDescriptor(self):
-    file_path = tempfile.NamedTemporaryFile(suffix='.txt')
-    self._WriteFile(file_path.name, 5000 * 1024)
-
-    zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
-    with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip:
-      pass
-    # The fd option force writes a data descriptor
-    cmd = ['zip', '-fd', zip_path.name, file_path.name]
-    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    proc.communicate()
-    read_names = self._getEntryNames(zip_path.name)
-    self.assertEquals([file_path.name[1:]], read_names)
-    self._ExtractEntries(zip_path.name)
-
-
-  def test_largeUncompressedEntriesLargerThan4G(self):
-    zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
-    with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_STORED,
-                         allowZip64=True) as output_zip:
-      # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
-      # sizes in the extra field. Test if our ziptool should be able to parse it.
-      entry_dict = {'g.txt': 5000 * 1024, 'h.txt': 6000 * 1024}
-      self._AddEntriesToZip(output_zip, entry_dict)
-
-    read_names = self._getEntryNames(zip_path.name)
-    self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
-    self._ExtractEntries(zip_path.name)
-
-
-  def test_largeCompressedEntriesLargerThan4G(self):
-    zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
-    with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED,
-                         allowZip64=True) as output_zip:
-      # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
-      # sizes in the extra field. Test if our ziptool should be able to parse it.
-      entry_dict = {'i.txt': 4096 * 1024, 'j.txt': 7000 * 1024}
-      self._AddEntriesToZip(output_zip, entry_dict)
-
-    read_names = self._getEntryNames(zip_path.name)
-    self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
-    self._ExtractEntries(zip_path.name)
-
-
-if __name__ == '__main__':
-  testsuite = unittest.TestLoader().discover(
-      os.path.dirname(os.path.realpath(__file__)))
-  unittest.TextTestRunner(verbosity=2).run(testsuite)
diff --git a/libziparchive/testdata/bad_crc.zip b/libziparchive/testdata/bad_crc.zip
deleted file mode 100644
index e12ba07..0000000
--- a/libziparchive/testdata/bad_crc.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/bad_filename.zip b/libziparchive/testdata/bad_filename.zip
deleted file mode 100644
index 294eaf5..0000000
--- a/libziparchive/testdata/bad_filename.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/crash.apk b/libziparchive/testdata/crash.apk
deleted file mode 100644
index d6dd52d..0000000
--- a/libziparchive/testdata/crash.apk
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/declaredlength.zip b/libziparchive/testdata/declaredlength.zip
deleted file mode 100644
index 773380c..0000000
--- a/libziparchive/testdata/declaredlength.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/dummy-update.zip b/libziparchive/testdata/dummy-update.zip
deleted file mode 100644
index 6976bf1..0000000
--- a/libziparchive/testdata/dummy-update.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/empty.zip b/libziparchive/testdata/empty.zip
deleted file mode 100644
index 15cb0ec..0000000
--- a/libziparchive/testdata/empty.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/large.zip b/libziparchive/testdata/large.zip
deleted file mode 100644
index 49659c8..0000000
--- a/libziparchive/testdata/large.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/valid.zip b/libziparchive/testdata/valid.zip
deleted file mode 100644
index 9e7cb78..0000000
--- a/libziparchive/testdata/valid.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/zero-size-cd.zip b/libziparchive/testdata/zero-size-cd.zip
deleted file mode 100644
index b6c8cbe..0000000
--- a/libziparchive/testdata/zero-size-cd.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/zip64.zip b/libziparchive/testdata/zip64.zip
deleted file mode 100644
index 3f25a4c..0000000
--- a/libziparchive/testdata/zip64.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
deleted file mode 100644
index 014f881..0000000
--- a/libziparchive/zip_archive.cc
+++ /dev/null
@@ -1,1586 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-/*
- * Read-only access to Zip archives, with minimal heap allocation.
- */
-
-#define LOG_TAG "ziparchive"
-
-#include "ziparchive/zip_archive.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <memory>
-#include <optional>
-#include <vector>
-
-#if defined(__APPLE__)
-#define lseek64 lseek
-#endif
-
-#if defined(__BIONIC__)
-#include <android/fdsan.h>
-#endif
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/macros.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
-#include <android-base/mapped_file.h>
-#include <android-base/memory.h>
-#include <android-base/strings.h>
-#include <android-base/utf8.h>
-#include <log/log.h>
-#include "zlib.h"
-
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
-#include "zip_archive_private.h"
-
-// Used to turn on crc checks - verify that the content CRC matches the values
-// specified in the local file header and the central directory.
-static constexpr bool kCrcChecksEnabled = false;
-
-// The maximum number of bytes to scan backwards for the EOCD start.
-static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
-
-// Set a reasonable cap (256 GiB) for the zip file size. So the data is always valid when
-// we parse the fields in cd or local headers as 64 bits signed integers.
-static constexpr uint64_t kMaxFileLength = 256 * static_cast<uint64_t>(1u << 30u);
-
-/*
- * A Read-only Zip archive.
- *
- * We want "open" and "find entry by name" to be fast operations, and
- * we want to use as little memory as possible.  We memory-map the zip
- * central directory, and load a hash table with pointers to the filenames
- * (which aren't null-terminated).  The other fields are at a fixed offset
- * from the filename, so we don't need to extract those (but we do need
- * to byte-read and endian-swap them every time we want them).
- *
- * It's possible that somebody has handed us a massive (~1GB) zip archive,
- * so we can't expect to mmap the entire file.
- *
- * To speed comparisons when doing a lookup by name, we could make the mapping
- * "private" (copy-on-write) and null-terminate the filenames after verifying
- * the record structure.  However, this requires a private mapping of
- * every page that the Central Directory touches.  Easier to tuck a copy
- * of the string length into the hash table entry.
- */
-
-#if defined(__BIONIC__)
-uint64_t GetOwnerTag(const ZipArchive* archive) {
-  return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
-                                        reinterpret_cast<uint64_t>(archive));
-}
-#endif
-
-ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
-    : mapped_zip(map),
-      close_file(assume_ownership),
-      directory_offset(0),
-      central_directory(),
-      directory_map(),
-      num_entries(0) {
-#if defined(__BIONIC__)
-  if (assume_ownership) {
-    CHECK(mapped_zip.HasFd());
-    android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
-  }
-#endif
-}
-
-ZipArchive::ZipArchive(const void* address, size_t length)
-    : mapped_zip(address, length),
-      close_file(false),
-      directory_offset(0),
-      central_directory(),
-      directory_map(),
-      num_entries(0) {}
-
-ZipArchive::~ZipArchive() {
-  if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
-#if defined(__BIONIC__)
-    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
-#else
-    close(mapped_zip.GetFileDescriptor());
-#endif
-  }
-}
-
-struct CentralDirectoryInfo {
-  uint64_t num_records;
-  // The size of the central directory (in bytes).
-  uint64_t cd_size;
-  // The offset of the start of the central directory, relative
-  // to the start of the file.
-  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)) {
-    ALOGW("Zip: %s: Not enough space for zip64 eocd locator", debugFileName);
-    return kInvalidFile;
-  }
-  // We expect to find the zip64 eocd locator immediately before the zip eocd.
-  const int64_t locatorOffset = eocdOffset - sizeof(Zip64EocdLocator);
-  Zip64EocdLocator zip64EocdLocator{};
-  if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>((&zip64EocdLocator)),
-                                        sizeof(Zip64EocdLocator), locatorOffset)) {
-    ALOGW("Zip: %s: Read %zu from offset %" PRId64 " failed %s", debugFileName,
-          sizeof(Zip64EocdLocator), locatorOffset, debugFileName);
-    return kIoError;
-  }
-
-  if (zip64EocdLocator.locator_signature != Zip64EocdLocator::kSignature) {
-    ALOGW("Zip: %s: Zip64 eocd locator signature not found at offset %" PRId64, debugFileName,
-          locatorOffset);
-    return kInvalidFile;
-  }
-
-  const int64_t zip64EocdOffset = zip64EocdLocator.zip64_eocd_offset;
-  if (locatorOffset <= sizeof(Zip64EocdRecord) ||
-      zip64EocdOffset > locatorOffset - sizeof(Zip64EocdRecord)) {
-    ALOGW("Zip: %s: Bad zip64 eocd offset %" PRId64 ", eocd locator offset %" PRId64, debugFileName,
-          zip64EocdOffset, locatorOffset);
-    return kInvalidOffset;
-  }
-
-  Zip64EocdRecord zip64EocdRecord{};
-  if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&zip64EocdRecord),
-                                        sizeof(Zip64EocdRecord), zip64EocdOffset)) {
-    ALOGW("Zip: %s: read %zu from offset %" PRId64 " failed %s", debugFileName,
-          sizeof(Zip64EocdLocator), zip64EocdOffset, debugFileName);
-    return kIoError;
-  }
-
-  if (zip64EocdRecord.record_signature != Zip64EocdRecord::kSignature) {
-    ALOGW("Zip: %s: Zip64 eocd record signature not found at offset %" PRId64, debugFileName,
-          zip64EocdOffset);
-    return kInvalidFile;
-  }
-
-  if (zip64EocdOffset <= zip64EocdRecord.cd_size ||
-      zip64EocdRecord.cd_start_offset > zip64EocdOffset - zip64EocdRecord.cd_size) {
-    ALOGW("Zip: %s: Bad offset for zip64 central directory. cd offset %" PRIu64 ", cd size %" PRIu64
-          ", zip64 eocd offset %" PRIu64,
-          debugFileName, zip64EocdRecord.cd_start_offset, zip64EocdRecord.cd_size, zip64EocdOffset);
-    return kInvalidOffset;
-  }
-
-  *cdInfo = {.num_records = zip64EocdRecord.num_records,
-             .cd_size = zip64EocdRecord.cd_size,
-             .cd_start_offset = zip64EocdRecord.cd_start_offset};
-
-  return kSuccess;
-}
-
-static ZipError FindCentralDirectoryInfo(const char* debug_file_name, ZipArchive* archive,
-                                         off64_t file_length, uint32_t read_amount,
-                                         CentralDirectoryInfo* cdInfo) {
-  std::vector<uint8_t> scan_buffer(read_amount);
-  const off64_t search_start = file_length - read_amount;
-
-  if (!archive->mapped_zip.ReadAtOffset(scan_buffer.data(), read_amount, search_start)) {
-    ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
-          static_cast<int64_t>(search_start));
-    return kIoError;
-  }
-
-  /*
-   * Scan backward for the EOCD magic.  In an archive without a trailing
-   * comment, we'll find it on the first try.  (We may want to consider
-   * doing an initial minimal read; if we don't find it, retry with a
-   * second read as above.)
-   */
-  CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
-  int32_t i = read_amount - sizeof(EocdRecord);
-  for (; i >= 0; i--) {
-    if (scan_buffer[i] == 0x50) {
-      uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
-      if (android::base::get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
-        ALOGV("+++ Found EOCD at buf+%d", i);
-        break;
-      }
-    }
-  }
-  if (i < 0) {
-    ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
-    return kInvalidFile;
-  }
-
-  const off64_t eocd_offset = search_start + i;
-  auto eocd = reinterpret_cast<const EocdRecord*>(scan_buffer.data() + i);
-  /*
-   * Verify that there's no trailing space at the end of the central directory
-   * and its comment.
-   */
-  const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
-  if (calculated_length != file_length) {
-    ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
-          static_cast<int64_t>(file_length - calculated_length));
-    return kInvalidFile;
-  }
-
-  // One of the field is 0xFFFFFFFF, look for the zip64 EOCD instead.
-  if (eocd->cd_size == UINT32_MAX || eocd->cd_start_offset == UINT32_MAX) {
-    ALOGV("Looking for the zip64 EOCD, cd_size: %" PRIu32 "cd_start_offset: %" PRId32,
-          eocd->cd_size, eocd->cd_start_offset);
-    return FindCentralDirectoryInfoForZip64(debug_file_name, archive, eocd_offset, cdInfo);
-  }
-
-  /*
-   * Grab the CD offset and size, and the number of entries in the
-   * archive and verify that they look reasonable.
-   */
-  if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
-    ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
-          eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
-    return kInvalidOffset;
-  }
-
-  *cdInfo = {.num_records = eocd->num_records,
-             .cd_size = eocd->cd_size,
-             .cd_start_offset = eocd->cd_start_offset};
-  return kSuccess;
-}
-
-/*
- * Find the zip Central Directory and memory-map it.
- *
- * On success, returns kSuccess after populating fields from the EOCD area:
- *   directory_offset
- *   directory_ptr
- *   num_entries
- */
-static ZipError MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
-  // Test file length. We use lseek64 to make sure the file is small enough to be a zip file.
-  off64_t file_length = archive->mapped_zip.GetFileLength();
-  if (file_length == -1) {
-    return kInvalidFile;
-  }
-
-  if (file_length > kMaxFileLength) {
-    ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
-    return kInvalidFile;
-  }
-
-  if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
-    ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
-    return kInvalidFile;
-  }
-
-  /*
-   * Perform the traditional EOCD snipe hunt.
-   *
-   * We're searching for the End of Central Directory magic number,
-   * which appears at the start of the EOCD block.  It's followed by
-   * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
-   * need to read the last part of the file into a buffer, dig through
-   * it to find the magic number, parse some values out, and use those
-   * to determine the extent of the CD.
-   *
-   * We start by pulling in the last part of the file.
-   */
-  uint32_t read_amount = kMaxEOCDSearch;
-  if (file_length < read_amount) {
-    read_amount = static_cast<uint32_t>(file_length);
-  }
-
-  CentralDirectoryInfo cdInfo = {};
-  if (auto result =
-          FindCentralDirectoryInfo(debug_file_name, archive, file_length, read_amount, &cdInfo);
-      result != kSuccess) {
-    return result;
-  }
-
-  if (cdInfo.num_records == 0) {
-#if defined(__ANDROID__)
-    ALOGW("Zip: empty archive?");
-#endif
-    return kEmptyArchive;
-  }
-
-  if (cdInfo.cd_size >= SIZE_MAX) {
-    ALOGW("Zip: The size of central directory doesn't fit in range of size_t: %" PRIu64,
-          cdInfo.cd_size);
-    return kInvalidFile;
-  }
-
-  ALOGV("+++ num_entries=%" PRIu64 " dir_size=%" PRIu64 " dir_offset=%" PRIu64, cdInfo.num_records,
-        cdInfo.cd_size, cdInfo.cd_start_offset);
-
-  // It all looks good.  Create a mapping for the CD, and set the fields in archive.
-  if (!archive->InitializeCentralDirectory(static_cast<off64_t>(cdInfo.cd_start_offset),
-                                           static_cast<size_t>(cdInfo.cd_size))) {
-    return kMmapFailed;
-  }
-
-  archive->num_entries = cdInfo.num_records;
-  archive->directory_offset = cdInfo.cd_start_offset;
-
-  return kSuccess;
-}
-
-static ZipError ParseZip64ExtendedInfoInExtraField(
-    const uint8_t* extraFieldStart, uint16_t extraFieldLength, uint32_t zip32UncompressedSize,
-    uint32_t zip32CompressedSize, std::optional<uint32_t> zip32LocalFileHeaderOffset,
-    Zip64ExtendedInfo* zip64Info) {
-  if (extraFieldLength <= 4) {
-    ALOGW("Zip: Extra field isn't large enough to hold zip64 info, size %" PRIu16,
-          extraFieldLength);
-    return kInvalidFile;
-  }
-
-  // Each header MUST consist of:
-  // Header ID - 2 bytes
-  // Data Size - 2 bytes
-  uint16_t offset = 0;
-  while (offset < extraFieldLength - 4) {
-    auto readPtr = const_cast<uint8_t*>(extraFieldStart + offset);
-    auto headerId = ConsumeUnaligned<uint16_t>(&readPtr);
-    auto dataSize = ConsumeUnaligned<uint16_t>(&readPtr);
-
-    offset += 4;
-    if (dataSize > extraFieldLength - offset) {
-      ALOGW("Zip: Data size exceeds the boundary of extra field, data size %" PRIu16, dataSize);
-      return kInvalidOffset;
-    }
-
-    // Skip the other types of extensible data fields. Details in
-    // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT section 4.5
-    if (headerId != Zip64ExtendedInfo::kHeaderId) {
-      offset += dataSize;
-      continue;
-    }
-
-    std::optional<uint64_t> uncompressedFileSize;
-    std::optional<uint64_t> compressedFileSize;
-    std::optional<uint64_t> localHeaderOffset;
-    if (zip32UncompressedSize == UINT32_MAX) {
-      uncompressedFileSize =
-          TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
-      if (!uncompressedFileSize.has_value()) return kInvalidOffset;
-    }
-    if (zip32CompressedSize == UINT32_MAX) {
-      compressedFileSize =
-          TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
-      if (!compressedFileSize.has_value()) return kInvalidOffset;
-    }
-    if (zip32LocalFileHeaderOffset == UINT32_MAX) {
-      localHeaderOffset =
-          TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
-      if (!localHeaderOffset.has_value()) return kInvalidOffset;
-    }
-
-    // calculate how many bytes we read after the data size field.
-    size_t bytesRead = readPtr - (extraFieldStart + offset);
-    if (bytesRead == 0) {
-      ALOGW("Zip: Data size should not be 0 in zip64 extended field");
-      return kInvalidFile;
-    }
-
-    if (dataSize != bytesRead) {
-      auto localOffsetString = zip32LocalFileHeaderOffset.has_value()
-                                   ? std::to_string(zip32LocalFileHeaderOffset.value())
-                                   : "missing";
-      ALOGW("Zip: Invalid data size in zip64 extended field, expect %zu , get %" PRIu16
-            ", uncompressed size %" PRIu32 ", compressed size %" PRIu32 ", local header offset %s",
-            bytesRead, dataSize, zip32UncompressedSize, zip32CompressedSize,
-            localOffsetString.c_str());
-      return kInvalidFile;
-    }
-
-    zip64Info->uncompressed_file_size = uncompressedFileSize;
-    zip64Info->compressed_file_size = compressedFileSize;
-    zip64Info->local_header_offset = localHeaderOffset;
-    return kSuccess;
-  }
-
-  ALOGW("Zip: zip64 extended info isn't found in the extra field.");
-  return kInvalidFile;
-}
-
-/*
- * Parses the Zip archive's Central Directory.  Allocates and populates the
- * hash table.
- *
- * Returns 0 on success.
- */
-static ZipError ParseZipArchive(ZipArchive* archive) {
-  const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
-  const size_t cd_length = archive->central_directory.GetMapLength();
-  const uint64_t num_entries = archive->num_entries;
-
-  if (num_entries <= UINT16_MAX) {
-    archive->cd_entry_map = CdEntryMapZip32::Create(static_cast<uint16_t>(num_entries));
-  } else {
-    archive->cd_entry_map = CdEntryMapZip64::Create();
-  }
-  if (archive->cd_entry_map == nullptr) {
-    return kAllocationFailed;
-  }
-
-  /*
-   * Walk through the central directory, adding entries to the hash
-   * table and verifying values.
-   */
-  const uint8_t* const cd_end = cd_ptr + cd_length;
-  const uint8_t* ptr = cd_ptr;
-  for (uint64_t i = 0; i < num_entries; i++) {
-    if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
-      ALOGW("Zip: ran off the end (item #%" PRIu64 ", %zu bytes of central directory)", i,
-            cd_length);
-#if defined(__ANDROID__)
-      android_errorWriteLog(0x534e4554, "36392138");
-#endif
-      return kInvalidFile;
-    }
-
-    auto cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
-    if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
-      ALOGW("Zip: missed a central dir sig (at %" PRIu64 ")", i);
-      return kInvalidFile;
-    }
-
-    const uint16_t file_name_length = cdr->file_name_length;
-    const uint16_t extra_length = cdr->extra_field_length;
-    const uint16_t comment_length = cdr->comment_length;
-    const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
-
-    if (file_name_length >= cd_length || file_name > cd_end - file_name_length) {
-      ALOGW("Zip: file name for entry %" PRIu64
-            " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
-            i, file_name_length, cd_length);
-      return kInvalidEntryName;
-    }
-
-    const uint8_t* extra_field = file_name + file_name_length;
-    if (extra_length >= cd_length || extra_field > cd_end - extra_length) {
-      ALOGW("Zip: extra field for entry %" PRIu64
-            " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
-            i, extra_length, cd_length);
-      return kInvalidFile;
-    }
-
-    off64_t local_header_offset = cdr->local_file_header_offset;
-    if (local_header_offset == UINT32_MAX) {
-      Zip64ExtendedInfo zip64_info{};
-      if (auto status = ParseZip64ExtendedInfoInExtraField(
-              extra_field, extra_length, cdr->uncompressed_size, cdr->compressed_size,
-              cdr->local_file_header_offset, &zip64_info);
-          status != kSuccess) {
-        return status;
-      }
-      CHECK(zip64_info.local_header_offset.has_value());
-      local_header_offset = zip64_info.local_header_offset.value();
-    }
-
-    if (local_header_offset >= archive->directory_offset) {
-      ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu64,
-            static_cast<int64_t>(local_header_offset), i);
-      return kInvalidFile;
-    }
-
-    // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
-    if (!IsValidEntryName(file_name, file_name_length)) {
-      ALOGW("Zip: invalid file name at entry %" PRIu64, i);
-      return kInvalidEntryName;
-    }
-
-    // Add the CDE filename to the hash table.
-    std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
-    if (auto add_result =
-            archive->cd_entry_map->AddToMap(entry_name, archive->central_directory.GetBasePtr());
-        add_result != 0) {
-      ALOGW("Zip: Error adding entry to hash table %d", add_result);
-      return add_result;
-    }
-
-    ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
-    if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
-      ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu64, ptr - cd_ptr, cd_length, i);
-      return kInvalidFile;
-    }
-  }
-
-  uint32_t lfh_start_bytes;
-  if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
-                                        sizeof(uint32_t), 0)) {
-    ALOGW("Zip: Unable to read header for entry at offset == 0.");
-    return kInvalidFile;
-  }
-
-  if (lfh_start_bytes != LocalFileHeader::kSignature) {
-    ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
-#if defined(__ANDROID__)
-    android_errorWriteLog(0x534e4554, "64211847");
-#endif
-    return kInvalidFile;
-  }
-
-  ALOGV("+++ zip good scan %" PRIu64 " entries", num_entries);
-
-  return kSuccess;
-}
-
-static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
-  int32_t result = MapCentralDirectory(debug_file_name, archive);
-  return result != kSuccess ? result : ParseZipArchive(archive);
-}
-
-int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
-                      bool 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(MappedZipFile(fd), true);
-  *handle = archive;
-
-  if (fd < 0) {
-    ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
-    return kIoError;
-  }
-
-  return OpenArchiveInternal(archive, fileName);
-}
-
-int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
-                              ZipArchiveHandle* handle) {
-  ZipArchive* archive = new ZipArchive(address, length);
-  *handle = archive;
-  return OpenArchiveInternal(archive, debug_file_name);
-}
-
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
-  ZipArchiveInfo result;
-  result.archive_size = archive->mapped_zip.GetFileLength();
-  result.entry_count = archive->num_entries;
-  return result;
-}
-
-/*
- * Close a ZipArchive, closing the file and freeing the contents.
- */
-void CloseArchive(ZipArchiveHandle archive) {
-  ALOGV("Closing archive %p", archive);
-  delete archive;
-}
-
-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) {
-    offset += entry->compressed_length;
-  } else {
-    offset += entry->uncompressed_length;
-  }
-
-  if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
-    return kIoError;
-  }
-
-  const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
-  uint8_t* ddReadPtr = (ddSignature == DataDescriptor::kOptSignature) ? ddBuf + 4 : ddBuf;
-  DataDescriptor descriptor{};
-  descriptor.crc32 = ConsumeUnaligned<uint32_t>(&ddReadPtr);
-  if (entry->zip64_format_size) {
-    descriptor.compressed_size = ConsumeUnaligned<uint64_t>(&ddReadPtr);
-    descriptor.uncompressed_size = ConsumeUnaligned<uint64_t>(&ddReadPtr);
-  } else {
-    descriptor.compressed_size = ConsumeUnaligned<uint32_t>(&ddReadPtr);
-    descriptor.uncompressed_size = ConsumeUnaligned<uint32_t>(&ddReadPtr);
-  }
-
-  // Validate that the values in the data descriptor match those in the central
-  // directory.
-  if (entry->compressed_length != descriptor.compressed_size ||
-      entry->uncompressed_length != descriptor.uncompressed_size ||
-      entry->crc32 != descriptor.crc32) {
-    ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
-          "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
-          entry->compressed_length, entry->uncompressed_length, entry->crc32,
-          descriptor.compressed_size, descriptor.uncompressed_size, descriptor.crc32);
-    return kInconsistentInformation;
-  }
-
-  return 0;
-}
-
-static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName,
-                         const uint64_t nameOffset, ZipEntry64* data) {
-  // Recover the start of the central directory entry from the filename
-  // pointer.  The filename is the first entry past the fixed-size data,
-  // so we can just subtract back from that.
-  const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
-  const uint8_t* ptr = base_ptr + nameOffset;
-  ptr -= sizeof(CentralDirectoryRecord);
-
-  // This is the base of our mmapped region, we have to sanity check that
-  // the name that's in the hash table is a pointer to a location within
-  // this mapped region.
-  if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
-    ALOGW("Zip: Invalid entry pointer");
-    return kInvalidOffset;
-  }
-
-  auto cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
-
-  // The offset of the start of the central directory in the zipfile.
-  // We keep this lying around so that we can sanity check all our lengths
-  // and our per-file structures.
-  const off64_t cd_offset = archive->directory_offset;
-
-  // Fill out the compression method, modification time, crc32
-  // and other interesting attributes from the central directory. These
-  // will later be compared against values from the local file header.
-  data->method = cdr->compression_method;
-  data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
-  data->crc32 = cdr->crc32;
-  data->compressed_length = cdr->compressed_size;
-  data->uncompressed_length = cdr->uncompressed_size;
-
-  // Figure out the local header offset from the central directory. The
-  // actual file data will begin after the local header and the name /
-  // extra comments.
-  off64_t local_header_offset = cdr->local_file_header_offset;
-  // One of the info field is UINT32_MAX, try to parse the real value in the zip64 extended info in
-  // the extra field.
-  if (cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX ||
-      cdr->local_file_header_offset == UINT32_MAX) {
-    const uint8_t* extra_field = ptr + sizeof(CentralDirectoryRecord) + cdr->file_name_length;
-    Zip64ExtendedInfo zip64_info{};
-    if (auto status = ParseZip64ExtendedInfoInExtraField(
-            extra_field, cdr->extra_field_length, cdr->uncompressed_size, cdr->compressed_size,
-            cdr->local_file_header_offset, &zip64_info);
-        status != kSuccess) {
-      return status;
-    }
-
-    data->uncompressed_length = zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size);
-    data->compressed_length = zip64_info.compressed_file_size.value_or(cdr->compressed_size);
-    local_header_offset = zip64_info.local_header_offset.value_or(local_header_offset);
-    data->zip64_format_size =
-        cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX;
-  }
-
-  if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
-    ALOGW("Zip: bad local hdr offset in zip");
-    return kInvalidOffset;
-  }
-
-  uint8_t lfh_buf[sizeof(LocalFileHeader)];
-  if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
-    ALOGW("Zip: failed reading lfh name from offset %" PRId64,
-          static_cast<int64_t>(local_header_offset));
-    return kIoError;
-  }
-
-  auto lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
-  if (lfh->lfh_signature != LocalFileHeader::kSignature) {
-    ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
-          static_cast<int64_t>(local_header_offset));
-    return kInvalidOffset;
-  }
-
-  // Check that the local file header name matches the declared name in the central directory.
-  CHECK_LE(entryName.size(), UINT16_MAX);
-  auto nameLen = static_cast<uint16_t>(entryName.size());
-  if (lfh->file_name_length != nameLen) {
-    ALOGW("Zip: lfh name length did not match central directory for %s: %" PRIu16 " %" PRIu16,
-          std::string(entryName).c_str(), lfh->file_name_length, nameLen);
-    return kInconsistentInformation;
-  }
-  const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
-  if (name_offset > cd_offset - lfh->file_name_length) {
-    ALOGW("Zip: lfh name has invalid declared length");
-    return kInvalidOffset;
-  }
-
-  std::vector<uint8_t> name_buf(nameLen);
-  if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
-    ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
-    return kIoError;
-  }
-  if (memcmp(entryName.data(), name_buf.data(), nameLen) != 0) {
-    ALOGW("Zip: lfh name did not match central directory");
-    return kInconsistentInformation;
-  }
-
-  uint64_t lfh_uncompressed_size = lfh->uncompressed_size;
-  uint64_t lfh_compressed_size = lfh->compressed_size;
-  if (lfh_uncompressed_size == UINT32_MAX || lfh_compressed_size == UINT32_MAX) {
-    if (lfh_uncompressed_size != UINT32_MAX || lfh_compressed_size != UINT32_MAX) {
-      ALOGW(
-          "Zip: The zip64 extended field in the local header MUST include BOTH original and "
-          "compressed file size fields.");
-      return kInvalidFile;
-    }
-
-    const off64_t lfh_extra_field_offset = name_offset + lfh->file_name_length;
-    const uint16_t lfh_extra_field_size = lfh->extra_field_length;
-    if (lfh_extra_field_offset > cd_offset - lfh_extra_field_size) {
-      ALOGW("Zip: extra field has a bad size for entry %s", std::string(entryName).c_str());
-      return kInvalidOffset;
-    }
-
-    std::vector<uint8_t> local_extra_field(lfh_extra_field_size);
-    if (!archive->mapped_zip.ReadAtOffset(local_extra_field.data(), lfh_extra_field_size,
-                                          lfh_extra_field_offset)) {
-      ALOGW("Zip: failed reading lfh extra field from offset %" PRId64, lfh_extra_field_offset);
-      return kIoError;
-    }
-
-    Zip64ExtendedInfo zip64_info{};
-    if (auto status = ParseZip64ExtendedInfoInExtraField(
-            local_extra_field.data(), lfh_extra_field_size, lfh->uncompressed_size,
-            lfh->compressed_size, std::nullopt, &zip64_info);
-        status != kSuccess) {
-      return status;
-    }
-
-    CHECK(zip64_info.uncompressed_file_size.has_value());
-    CHECK(zip64_info.compressed_file_size.has_value());
-    lfh_uncompressed_size = zip64_info.uncompressed_file_size.value();
-    lfh_compressed_size = zip64_info.compressed_file_size.value();
-  }
-
-  // Paranoia: Match the values specified in the local file header
-  // to those specified in the central directory.
-
-  // Warn if central directory and local file header don't agree on the use
-  // of a trailing Data Descriptor. The reference implementation is inconsistent
-  // and appears to use the LFH value during extraction (unzip) but the CD value
-  // while displayng information about archives (zipinfo). The spec remains
-  // silent on this inconsistency as well.
-  //
-  // For now, always use the version from the LFH but make sure that the values
-  // specified in the central directory match those in the data descriptor.
-  //
-  // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
-  // bit 11 (EFS: The language encoding flag, marking that filename and comment are
-  // encoded using UTF-8). This implementation does not check for the presence of
-  // that flag and always enforces that entry names are valid UTF-8.
-  if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
-    ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
-          cdr->gpb_flags, lfh->gpb_flags);
-  }
-
-  // If there is no trailing data descriptor, verify that the central directory and local file
-  // header agree on the crc, compressed, and uncompressed sizes of the entry.
-  if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
-    data->has_data_descriptor = 0;
-    if (data->compressed_length != lfh_compressed_size ||
-        data->uncompressed_length != lfh_uncompressed_size || data->crc32 != lfh->crc32) {
-      ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
-            "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
-            data->compressed_length, data->uncompressed_length, data->crc32, lfh_compressed_size,
-            lfh_uncompressed_size, lfh->crc32);
-      return kInconsistentInformation;
-    }
-  } else {
-    data->has_data_descriptor = 1;
-  }
-
-  // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
-  data->version_made_by = cdr->version_made_by;
-  data->external_file_attributes = cdr->external_file_attributes;
-  if ((data->version_made_by >> 8) == 3) {
-    data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
-  } else {
-    data->unix_mode = 0777;
-  }
-
-  // 4.4.4: general purpose bit flags.
-  data->gpbf = lfh->gpb_flags;
-
-  // 4.4.14: the lowest bit of the internal file attributes field indicates text.
-  // Currently only needed to implement zipinfo.
-  data->is_text = (cdr->internal_file_attributes & 1);
-
-  const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
-                              lfh->file_name_length + lfh->extra_field_length;
-  if (data_offset > cd_offset) {
-    ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
-    return kInvalidOffset;
-  }
-
-  if (data->compressed_length > cd_offset - data_offset) {
-    ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
-          static_cast<int64_t>(data_offset), data->compressed_length,
-          static_cast<int64_t>(cd_offset));
-    return kInvalidOffset;
-  }
-
-  if (data->method == kCompressStored && data->uncompressed_length > cd_offset - data_offset) {
-    ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
-          static_cast<int64_t>(data_offset), data->uncompressed_length,
-          static_cast<int64_t>(cd_offset));
-    return kInvalidOffset;
-  }
-
-  data->offset = data_offset;
-  return 0;
-}
-
-struct IterationHandle {
-  ZipArchive* archive;
-
-  std::function<bool(std::string_view)> matcher;
-
-  uint32_t position = 0;
-
-  IterationHandle(ZipArchive* archive, std::function<bool(std::string_view)> in_matcher)
-      : archive(archive), matcher(std::move(in_matcher)) {}
-
-  bool Match(std::string_view entry_name) const { return matcher(entry_name); }
-};
-
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
-                       const std::string_view optional_prefix,
-                       const std::string_view optional_suffix) {
-  if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
-      optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
-    ALOGW("Zip: prefix/suffix too long");
-    return kInvalidEntryName;
-  }
-  auto matcher = [prefix = std::string(optional_prefix),
-                  suffix = std::string(optional_suffix)](std::string_view name) mutable {
-    return android::base::StartsWith(name, prefix) && android::base::EndsWith(name, suffix);
-  };
-  return StartIteration(archive, cookie_ptr, std::move(matcher));
-}
-
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
-                       std::function<bool(std::string_view)> matcher) {
-  if (archive == nullptr || archive->cd_entry_map == nullptr) {
-    ALOGW("Zip: Invalid ZipArchiveHandle");
-    return kInvalidHandle;
-  }
-
-  archive->cd_entry_map->ResetIteration();
-  *cookie_ptr = new IterationHandle(archive, matcher);
-  return 0;
-}
-
-void EndIteration(void* cookie) {
-  delete reinterpret_cast<IterationHandle*>(cookie);
-}
-
-int32_t ZipEntry::CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src) {
-  if (src->compressed_length > UINT32_MAX || src->uncompressed_length > UINT32_MAX) {
-    ALOGW(
-        "Zip: the entry size is too large to fit into the 32 bits ZipEntry, uncompressed "
-        "length %" PRIu64 ", compressed length %" PRIu64,
-        src->uncompressed_length, src->compressed_length);
-    return kUnsupportedEntrySize;
-  }
-
-  *dst = *src;
-  dst->uncompressed_length = static_cast<uint32_t>(src->uncompressed_length);
-  dst->compressed_length = static_cast<uint32_t>(src->compressed_length);
-  return kSuccess;
-}
-
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
-                  ZipEntry* data) {
-  ZipEntry64 entry64;
-  if (auto status = FindEntry(archive, entryName, &entry64); status != kSuccess) {
-    return status;
-  }
-
-  return ZipEntry::CopyFromZipEntry64(data, &entry64);
-}
-
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
-                  ZipEntry64* data) {
-  if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
-    ALOGW("Zip: Invalid filename of length %zu", entryName.size());
-    return kInvalidEntryName;
-  }
-
-  const auto [result, offset] =
-      archive->cd_entry_map->GetCdEntryOffset(entryName, archive->central_directory.GetBasePtr());
-  if (result != 0) {
-    ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
-    return static_cast<int32_t>(result);  // kEntryNotFound is safe to truncate.
-  }
-  // We know there are at most hash_table_size entries, safe to truncate.
-  return FindEntry(archive, entryName, offset, data);
-}
-
-int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
-  ZipEntry64 entry64;
-  if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
-    return status;
-  }
-
-  return ZipEntry::CopyFromZipEntry64(data, &entry64);
-}
-
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
-  ZipEntry64 entry64;
-  if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
-    return status;
-  }
-
-  return ZipEntry::CopyFromZipEntry64(data, &entry64);
-}
-
-int32_t Next(void* cookie, ZipEntry64* data, std::string* name) {
-  std::string_view sv;
-  int32_t result = Next(cookie, data, &sv);
-  if (result == 0 && name) {
-    *name = std::string(sv);
-  }
-  return result;
-}
-
-int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name) {
-  IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
-  if (handle == nullptr) {
-    ALOGW("Zip: Null ZipArchiveHandle");
-    return kInvalidHandle;
-  }
-
-  ZipArchive* archive = handle->archive;
-  if (archive == nullptr || archive->cd_entry_map == nullptr) {
-    ALOGW("Zip: Invalid ZipArchiveHandle");
-    return kInvalidHandle;
-  }
-
-  auto entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr());
-  while (entry != std::pair<std::string_view, uint64_t>()) {
-    const auto [entry_name, offset] = entry;
-    if (handle->Match(entry_name)) {
-      const int error = FindEntry(archive, entry_name, offset, data);
-      if (!error && name) {
-        *name = entry_name;
-      }
-      return error;
-    }
-    entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr());
-  }
-
-  archive->cd_entry_map->ResetIteration();
-  return kIterationEnd;
-}
-
-// A Writer that writes data to a fixed size memory region.
-// The size of the memory region must be equal to the total size of
-// the data appended to it.
-class MemoryWriter : public zip_archive::Writer {
- public:
-  static std::unique_ptr<MemoryWriter> Create(uint8_t* buf, size_t size, const ZipEntry64* entry) {
-    const uint64_t declared_length = entry->uncompressed_length;
-    if (declared_length > size) {
-      ALOGW("Zip: file size %" PRIu64 " is larger than the buffer size %zu.", declared_length,
-            size);
-      return nullptr;
-    }
-
-    return std::unique_ptr<MemoryWriter>(new MemoryWriter(buf, size));
-  }
-
-  virtual bool Append(uint8_t* buf, size_t buf_size) override {
-    if (size_ < buf_size || bytes_written_ > size_ - buf_size) {
-      ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
-            bytes_written_ + buf_size);
-      return false;
-    }
-
-    memcpy(buf_ + bytes_written_, buf, buf_size);
-    bytes_written_ += buf_size;
-    return true;
-  }
-
- private:
-  MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
-
-  uint8_t* const buf_{nullptr};
-  const size_t size_;
-  size_t bytes_written_;
-};
-
-// A Writer that appends data to a file |fd| at its current position.
-// The file will be truncated to the end of the written data.
-class FileWriter : public zip_archive::Writer {
- public:
-  // Creates a FileWriter for |fd| and prepare to write |entry| to it,
-  // guaranteeing that the file descriptor is valid and that there's enough
-  // space on the volume to write out the entry completely and that the file
-  // is truncated to the correct length (no truncation if |fd| references a
-  // block device).
-  //
-  // Returns a valid FileWriter on success, |nullptr| if an error occurred.
-  static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry64* entry) {
-    const uint64_t declared_length = entry->uncompressed_length;
-    const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
-    if (current_offset == -1) {
-      ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
-      return nullptr;
-    }
-
-    if (declared_length > SIZE_MAX || declared_length > INT64_MAX) {
-      ALOGW("Zip: file size %" PRIu64 " is too large to extract.", declared_length);
-      return nullptr;
-    }
-
-#if defined(__linux__)
-    if (declared_length > 0) {
-      // Make sure we have enough space on the volume to extract the compressed
-      // entry. Note that the call to ftruncate below will change the file size but
-      // will not allocate space on disk and this call to fallocate will not
-      // change the file size.
-      // Note: fallocate is only supported by the following filesystems -
-      // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
-      // EOPNOTSUPP error when issued in other filesystems.
-      // Hence, check for the return error code before concluding that the
-      // disk does not have enough space.
-      long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
-      if (result == -1 && errno == ENOSPC) {
-        ALOGW("Zip: unable to allocate %" PRIu64 " bytes at offset %" PRId64 ": %s",
-              declared_length, static_cast<int64_t>(current_offset), strerror(errno));
-        return nullptr;
-      }
-    }
-#endif  // __linux__
-
-    struct stat sb;
-    if (fstat(fd, &sb) == -1) {
-      ALOGW("Zip: unable to fstat file: %s", strerror(errno));
-      return nullptr;
-    }
-
-    // Block device doesn't support ftruncate(2).
-    if (!S_ISBLK(sb.st_mode)) {
-      long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
-      if (result == -1) {
-        ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
-              static_cast<int64_t>(declared_length + current_offset), strerror(errno));
-        return nullptr;
-      }
-    }
-
-    return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
-  }
-
-  FileWriter(FileWriter&& other) noexcept
-      : fd_(other.fd_),
-        declared_length_(other.declared_length_),
-        total_bytes_written_(other.total_bytes_written_) {
-    other.fd_ = -1;
-  }
-
-  virtual bool Append(uint8_t* buf, size_t buf_size) override {
-    if (declared_length_ < buf_size || total_bytes_written_ > declared_length_ - buf_size) {
-      ALOGW("Zip: Unexpected size %zu  (declared) vs %zu (actual)", declared_length_,
-            total_bytes_written_ + buf_size);
-      return false;
-    }
-
-    const bool result = android::base::WriteFully(fd_, buf, buf_size);
-    if (result) {
-      total_bytes_written_ += buf_size;
-    } else {
-      ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
-    }
-
-    return result;
-  }
-
- private:
-  explicit FileWriter(const int fd = -1, const uint64_t declared_length = 0)
-      : Writer(),
-        fd_(fd),
-        declared_length_(static_cast<size_t>(declared_length)),
-        total_bytes_written_(0) {
-    CHECK_LE(declared_length, SIZE_MAX);
-  }
-
-  int fd_;
-  const size_t declared_length_;
-  size_t total_bytes_written_;
-};
-
-class EntryReader : public zip_archive::Reader {
- public:
-  EntryReader(const MappedZipFile& zip_file, const ZipEntry64* entry)
-      : Reader(), zip_file_(zip_file), entry_(entry) {}
-
-  virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
-    return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
-  }
-
-  virtual ~EntryReader() {}
-
- private:
-  const MappedZipFile& zip_file_;
-  const ZipEntry64* entry_;
-};
-
-// This method is using libz macros with old-style-casts
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
-  return inflateInit2(stream, window_bits);
-}
-#pragma GCC diagnostic pop
-
-namespace zip_archive {
-
-// Moved out of line to avoid -Wweak-vtables.
-Reader::~Reader() {}
-Writer::~Writer() {}
-
-int32_t Inflate(const Reader& reader, const uint64_t compressed_length,
-                const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
-  const size_t kBufSize = 32768;
-  std::vector<uint8_t> read_buf(kBufSize);
-  std::vector<uint8_t> write_buf(kBufSize);
-  z_stream zstream;
-  int zerr;
-
-  /*
-   * Initialize the zlib stream struct.
-   */
-  memset(&zstream, 0, sizeof(zstream));
-  zstream.zalloc = Z_NULL;
-  zstream.zfree = Z_NULL;
-  zstream.opaque = Z_NULL;
-  zstream.next_in = NULL;
-  zstream.avail_in = 0;
-  zstream.next_out = &write_buf[0];
-  zstream.avail_out = kBufSize;
-  zstream.data_type = Z_UNKNOWN;
-
-  /*
-   * Use the undocumented "negative window bits" feature to tell zlib
-   * that there's no zlib header waiting for it.
-   */
-  zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
-  if (zerr != Z_OK) {
-    if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
-    } else {
-      ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
-    }
-
-    return kZlibError;
-  }
-
-  auto zstream_deleter = [](z_stream* stream) {
-    inflateEnd(stream); /* free up any allocated structures */
-  };
-
-  std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
-
-  const bool compute_crc = (crc_out != nullptr);
-  uLong crc = 0;
-  uint64_t remaining_bytes = compressed_length;
-  uint64_t total_output = 0;
-  do {
-    /* read as much as we can */
-    if (zstream.avail_in == 0) {
-      const uint32_t read_size =
-          (remaining_bytes > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining_bytes);
-      const off64_t offset = (compressed_length - remaining_bytes);
-      // Make sure to read at offset to ensure concurrent access to the fd.
-      if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
-        ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
-        return kIoError;
-      }
-
-      remaining_bytes -= read_size;
-
-      zstream.next_in = &read_buf[0];
-      zstream.avail_in = read_size;
-    }
-
-    /* uncompress the data */
-    zerr = inflate(&zstream, Z_NO_FLUSH);
-    if (zerr != Z_OK && zerr != Z_STREAM_END) {
-      ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
-            zstream.avail_in, zstream.next_out, zstream.avail_out);
-      return kZlibError;
-    }
-
-    /* write when we're full or when we're done */
-    if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
-      const size_t write_size = zstream.next_out - &write_buf[0];
-      if (!writer->Append(&write_buf[0], write_size)) {
-        return kIoError;
-      } else if (compute_crc) {
-        DCHECK_LE(write_size, kBufSize);
-        crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
-      }
-
-      total_output += kBufSize - zstream.avail_out;
-      zstream.next_out = &write_buf[0];
-      zstream.avail_out = kBufSize;
-    }
-  } while (zerr == Z_OK);
-
-  CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
-
-  // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
-  // "feature" of zlib to tell it there won't be a zlib file header. zlib
-  // doesn't bother calculating the checksum in that scenario. We just do
-  // it ourselves above because there are no additional gains to be made by
-  // having zlib calculate it for us, since they do it by calling crc32 in
-  // the same manner that we have above.
-  if (compute_crc) {
-    *crc_out = crc;
-  }
-  if (total_output != uncompressed_length || remaining_bytes != 0) {
-    ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu64 ")", zstream.total_out,
-          uncompressed_length);
-    return kInconsistentInformation;
-  }
-
-  return 0;
-}
-}  // namespace zip_archive
-
-static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
-                                    zip_archive::Writer* writer, uint64_t* crc_out) {
-  const EntryReader reader(mapped_zip, entry);
-
-  return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
-                              crc_out);
-}
-
-static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
-                                 zip_archive::Writer* writer, uint64_t* crc_out) {
-  static const uint32_t kBufSize = 32768;
-  std::vector<uint8_t> buf(kBufSize);
-
-  const uint64_t length = entry->uncompressed_length;
-  uint64_t count = 0;
-  uLong crc = 0;
-  while (count < length) {
-    uint64_t remaining = length - count;
-    off64_t offset = entry->offset + count;
-
-    // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
-    const uint32_t block_size =
-        (remaining > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining);
-
-    // Make sure to read at offset to ensure concurrent access to the fd.
-    if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
-      ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
-            block_size, static_cast<int64_t>(offset), strerror(errno));
-      return kIoError;
-    }
-
-    if (!writer->Append(&buf[0], block_size)) {
-      return kIoError;
-    }
-    if (crc_out) {
-      crc = crc32(crc, &buf[0], block_size);
-    }
-    count += block_size;
-  }
-
-  if (crc_out) {
-    *crc_out = crc;
-  }
-
-  return 0;
-}
-
-int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry,
-                        zip_archive::Writer* writer) {
-  const uint16_t method = entry->method;
-
-  // this should default to kUnknownCompressionMethod.
-  int32_t return_value = -1;
-  uint64_t crc = 0;
-  if (method == kCompressStored) {
-    return_value =
-        CopyEntryToWriter(handle->mapped_zip, entry, writer, kCrcChecksEnabled ? &crc : nullptr);
-  } else if (method == kCompressDeflated) {
-    return_value =
-        InflateEntryToWriter(handle->mapped_zip, entry, writer, kCrcChecksEnabled ? &crc : nullptr);
-  }
-
-  if (!return_value && entry->has_data_descriptor) {
-    return_value = ValidateDataDescriptor(handle->mapped_zip, entry);
-    if (return_value) {
-      return return_value;
-    }
-  }
-
-  // Validate that the CRC matches the calculated value.
-  if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
-    ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
-    return kInconsistentInformation;
-  }
-
-  return return_value;
-}
-
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin,
-                        size_t size) {
-  ZipEntry64 entry64(*entry);
-  return ExtractToMemory(archive, &entry64, begin, size);
-}
-
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin,
-                        size_t size) {
-  auto writer = MemoryWriter::Create(begin, size, entry);
-  if (!writer) {
-    return kIoError;
-  }
-
-  return ExtractToWriter(archive, entry, writer.get());
-}
-
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd) {
-  ZipEntry64 entry64(*entry);
-  return ExtractEntryToFile(archive, &entry64, fd);
-}
-
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd) {
-  auto writer = FileWriter::Create(fd, entry);
-  if (!writer) {
-    return kIoError;
-  }
-
-  return ExtractToWriter(archive, entry, writer.get());
-}
-
-int GetFileDescriptor(const ZipArchiveHandle archive) {
-  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:
-  ProcessWriter(ProcessZipEntryFunction func, void* cookie)
-      : Writer(), proc_function_(func), cookie_(cookie) {}
-
-  virtual bool Append(uint8_t* buf, size_t buf_size) override {
-    return proc_function_(buf, buf_size, cookie_);
-  }
-
- private:
-  ProcessZipEntryFunction proc_function_;
-  void* cookie_;
-};
-
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry,
-                                ProcessZipEntryFunction func, void* cookie) {
-  ZipEntry64 entry64(*entry);
-  return ProcessZipEntryContents(archive, &entry64, func, cookie);
-}
-
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry,
-                                ProcessZipEntryFunction func, void* cookie) {
-  ProcessWriter writer(func, cookie);
-  return ExtractToWriter(archive, entry, &writer);
-}
-
-#endif  //! defined(_WIN32)
-
-int MappedZipFile::GetFileDescriptor() const {
-  if (!has_fd_) {
-    ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
-    return -1;
-  }
-  return fd_;
-}
-
-const void* MappedZipFile::GetBasePtr() const {
-  if (has_fd_) {
-    ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
-    return nullptr;
-  }
-  return base_ptr_;
-}
-
-off64_t MappedZipFile::GetFileOffset() const {
-  return fd_offset_;
-}
-
-off64_t MappedZipFile::GetFileLength() const {
-  if (has_fd_) {
-    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 data_length_;
-  } else {
-    if (base_ptr_ == nullptr) {
-      ALOGE("Zip: invalid file map");
-      return -1;
-    }
-    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 (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 || 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);
-  }
-  return true;
-}
-
-void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_offset,
-                                  size_t cd_size) {
-  base_ptr_ = static_cast<const uint8_t*>(map_base_ptr) + cd_start_offset;
-  length_ = cd_size;
-}
-
-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(),
-                                                      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));
-      return false;
-    }
-
-    CHECK_EQ(directory_map->size(), cd_size);
-    central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
-  } else {
-    if (mapped_zip.GetBasePtr() == nullptr) {
-      ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer");
-      return false;
-    }
-    if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
-        mapped_zip.GetFileLength()) {
-      ALOGE(
-          "Zip: Failed to map central directory, offset exceeds mapped memory region ("
-          "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
-          static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
-      return false;
-    }
-
-    central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
-  }
-  return true;
-}
-
-// This function returns the embedded timestamp as is; and doesn't perform validations.
-tm ZipEntryCommon::GetModificationTime() const {
-  tm t = {};
-
-  t.tm_hour = (mod_time >> 11) & 0x1f;
-  t.tm_min = (mod_time >> 5) & 0x3f;
-  t.tm_sec = (mod_time & 0x1f) << 1;
-
-  t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
-  t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
-  t.tm_mday = (mod_time >> 16) & 0x1f;
-
-  return t;
-}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
deleted file mode 100644
index cfa5912..0000000
--- a/libziparchive/zip_archive_benchmark.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2017 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 <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <string>
-#include <tuple>
-#include <vector>
-
-#include <android-base/test_utils.h>
-#include <benchmark/benchmark.h>
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-#include <ziparchive/zip_writer.h>
-
-static std::unique_ptr<TemporaryFile> CreateZip(int size = 4, int count = 1000) {
-  auto result = std::make_unique<TemporaryFile>();
-  FILE* fp = fdopen(result->fd, "w");
-
-  ZipWriter writer(fp);
-  std::string lastName = "file";
-  for (size_t i = 0; i < count; i++) {
-    // Make file names longer and longer.
-    lastName = lastName + std::to_string(i);
-    writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
-    while (size > 0) {
-      writer.WriteBytes("helo", 4);
-      size -= 4;
-    }
-    writer.FinishEntry();
-  }
-  writer.Finish();
-  fclose(fp);
-
-  return result;
-}
-
-static void FindEntry_no_match(benchmark::State& state) {
-  // Create a temporary zip archive.
-  std::unique_ptr<TemporaryFile> temp_file(CreateZip());
-  ZipArchiveHandle handle;
-  ZipEntry data;
-
-  // In order to walk through all file names in the archive, look for a name
-  // that does not exist in the archive.
-  std::string_view name("thisFileNameDoesNotExist");
-
-  // Start the benchmark.
-  for (auto _ : state) {
-    OpenArchive(temp_file->path, &handle);
-    FindEntry(handle, name, &data);
-    CloseArchive(handle);
-  }
-}
-BENCHMARK(FindEntry_no_match);
-
-static void Iterate_all_files(benchmark::State& state) {
-  std::unique_ptr<TemporaryFile> temp_file(CreateZip());
-  ZipArchiveHandle handle;
-  void* iteration_cookie;
-  ZipEntry data;
-  std::string name;
-
-  for (auto _ : state) {
-    OpenArchive(temp_file->path, &handle);
-    StartIteration(handle, &iteration_cookie);
-    while (Next(iteration_cookie, &data, &name) == 0) {
-    }
-    EndIteration(iteration_cookie);
-    CloseArchive(handle);
-  }
-}
-BENCHMARK(Iterate_all_files);
-
-static void StartAlignedEntry(benchmark::State& state) {
-  TemporaryFile file;
-  FILE* fp = fdopen(file.fd, "w");
-
-  ZipWriter writer(fp);
-
-  auto alignment = uint32_t(state.range(0));
-  std::string name = "name";
-  int counter = 0;
-  for (auto _ : state) {
-    writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
-    state.PauseTiming();
-    writer.WriteBytes("hola", 4);
-    writer.FinishEntry();
-    state.ResumeTiming();
-  }
-
-  writer.Finish();
-  fclose(fp);
-}
-BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
-
-static void ExtractEntry(benchmark::State& state) {
-  std::unique_ptr<TemporaryFile> temp_file(CreateZip(1024 * 1024, 1));
-
-  ZipArchiveHandle handle;
-  ZipEntry data;
-  if (OpenArchive(temp_file->path, &handle)) {
-    state.SkipWithError("Failed to open archive");
-  }
-  if (FindEntry(handle, "file0", &data)) {
-    state.SkipWithError("Failed to find archive entry");
-  }
-
-  std::vector<uint8_t> buffer(1024 * 1024);
-  for (auto _ : state) {
-    if (ExtractToMemory(handle, &data, buffer.data(), uint32_t(buffer.size()))) {
-      state.SkipWithError("Failed to extract archive entry");
-      break;
-    }
-  }
-  CloseArchive(handle);
-}
-
-BENCHMARK(ExtractEntry)->Arg(2)->Arg(16)->Arg(1024);
-
-BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
deleted file mode 100644
index d461856..0000000
--- a/libziparchive/zip_archive_common.h
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
-#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
-
-#include "android-base/macros.h"
-
-#include <inttypes.h>
-
-#include <optional>
-
-// The "end of central directory" (EOCD) record. Each archive
-// contains exactly once such record which appears at the end of
-// the archive. It contains archive wide information like the
-// number of entries in the archive and the offset to the central
-// directory of the offset.
-struct EocdRecord {
-  static const uint32_t kSignature = 0x06054b50;
-
-  // End of central directory signature, should always be
-  // |kSignature|.
-  uint32_t eocd_signature;
-  // The number of the current "disk", i.e, the "disk" that this
-  // central directory is on.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that disk_num == 1.
-  uint16_t disk_num;
-  // The disk where the central directory starts.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that cd_start_disk == 1.
-  uint16_t cd_start_disk;
-  // The number of central directory records on this disk.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that num_records_on_disk == num_records.
-  uint16_t num_records_on_disk;
-  // The total number of central directory records.
-  uint16_t num_records;
-  // The size of the central directory (in bytes).
-  uint32_t cd_size;
-  // The offset of the start of the central directory, relative
-  // to the start of the file.
-  uint32_t cd_start_offset;
-  // Length of the central directory comment.
-  uint16_t comment_length;
-
- private:
-  EocdRecord() = default;
-  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
-} __attribute__((packed));
-
-// A structure representing the fixed length fields for a single
-// record in the central directory of the archive. In addition to
-// the fixed length fields listed here, each central directory
-// record contains a variable length "file_name" and "extra_field"
-// whose lengths are given by |file_name_length| and |extra_field_length|
-// respectively.
-struct CentralDirectoryRecord {
-  static const uint32_t kSignature = 0x02014b50;
-
-  // The start of record signature. Must be |kSignature|.
-  uint32_t record_signature;
-  // Source tool version. Top byte gives source OS.
-  uint16_t version_made_by;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_needed;
-  // The "general purpose bit flags" for this entry. The only
-  // flag value that we currently check for is the "data descriptor"
-  // flag.
-  uint16_t gpb_flags;
-  // The compression method for this entry, one of |kCompressStored|
-  // and |kCompressDeflated|.
-  uint16_t compression_method;
-  // The file modification time and date for this entry.
-  uint16_t last_mod_time;
-  uint16_t last_mod_date;
-  // The CRC-32 checksum for this entry.
-  uint32_t crc32;
-  // The compressed size (in bytes) of this entry.
-  uint32_t compressed_size;
-  // The uncompressed size (in bytes) of this entry.
-  uint32_t uncompressed_size;
-  // The length of the entry file name in bytes. The file name
-  // will appear immediately after this record.
-  uint16_t file_name_length;
-  // The length of the extra field info (in bytes). This data
-  // will appear immediately after the entry file name.
-  uint16_t extra_field_length;
-  // The length of the entry comment (in bytes). This data will
-  // appear immediately after the extra field.
-  uint16_t comment_length;
-  // The start disk for this entry. Ignored by this implementation).
-  uint16_t file_start_disk;
-  // File attributes. Ignored by this implementation.
-  uint16_t internal_file_attributes;
-  // File attributes. For archives created on Unix, the top bits are the mode.
-  uint32_t external_file_attributes;
-  // The offset to the local file header for this entry, from the
-  // beginning of this archive.
-  uint32_t local_file_header_offset;
-
- private:
-  CentralDirectoryRecord() = default;
-  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-} __attribute__((packed));
-
-// The local file header for a given entry. This duplicates information
-// present in the central directory of the archive. It is an error for
-// the information here to be different from the central directory
-// information for a given entry.
-struct LocalFileHeader {
-  static const uint32_t kSignature = 0x04034b50;
-
-  // The local file header signature, must be |kSignature|.
-  uint32_t lfh_signature;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_needed;
-  // The "general purpose bit flags" for this entry. The only
-  // flag value that we currently check for is the "data descriptor"
-  // flag.
-  uint16_t gpb_flags;
-  // The compression method for this entry, one of |kCompressStored|
-  // and |kCompressDeflated|.
-  uint16_t compression_method;
-  // The file modification time and date for this entry.
-  uint16_t last_mod_time;
-  uint16_t last_mod_date;
-  // The CRC-32 checksum for this entry.
-  uint32_t crc32;
-  // The compressed size (in bytes) of this entry.
-  uint32_t compressed_size;
-  // The uncompressed size (in bytes) of this entry.
-  uint32_t uncompressed_size;
-  // The length of the entry file name in bytes. The file name
-  // will appear immediately after this record.
-  uint16_t file_name_length;
-  // The length of the extra field info (in bytes). This data
-  // will appear immediately after the entry file name.
-  uint16_t extra_field_length;
-
- private:
-  LocalFileHeader() = default;
-  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-} __attribute__((packed));
-
-struct DataDescriptor {
-  // The *optional* data descriptor start signature.
-  static const uint32_t kOptSignature = 0x08074b50;
-
-  // CRC-32 checksum of the entry.
-  uint32_t crc32;
-
-  // For ZIP64 format archives, the compressed and uncompressed sizes are 8
-  // bytes each. Also, the ZIP64 format MAY be used regardless of the size
-  // of a file.  When extracting, if the zip64 extended information extra field
-  // is present for the file the compressed and uncompressed sizes will be 8
-  // byte values.
-
-  // Compressed size of the entry, the field can be either 4 bytes or 8 bytes
-  // in the zip file.
-  uint64_t compressed_size;
-  // Uncompressed size of the entry, the field can be either 4 bytes or 8 bytes
-  // in the zip file.
-  uint64_t uncompressed_size;
-
- private:
-  DataDescriptor() = default;
-  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-};
-
-// The zip64 end of central directory locator helps to find the zip64 EOCD.
-struct Zip64EocdLocator {
-  static constexpr uint32_t kSignature = 0x07064b50;
-
-  // The signature of zip64 eocd locator, must be |kSignature|
-  uint32_t locator_signature;
-  // The start disk of the zip64 eocd. This implementation assumes that each
-  // archive spans a single disk only.
-  uint32_t eocd_start_disk;
-  // The offset offset of the zip64 end of central directory record.
-  uint64_t zip64_eocd_offset;
-  // The total number of disks. This implementation assumes that each archive
-  // spans a single disk only.
-  uint32_t num_of_disks;
-
- private:
-  Zip64EocdLocator() = default;
-  DISALLOW_COPY_AND_ASSIGN(Zip64EocdLocator);
-} __attribute__((packed));
-
-// The optional zip64 EOCD. If one of the fields in the end of central directory
-// record is too small to hold required data, the field SHOULD be  set to -1
-// (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record SHOULD be created.
-struct Zip64EocdRecord {
-  static constexpr uint32_t kSignature = 0x06064b50;
-
-  // The signature of zip64 eocd record, must be |kSignature|
-  uint32_t record_signature;
-  // Size of zip64 end of central directory record. It SHOULD be the size of the
-  // remaining record and SHOULD NOT include the leading 12 bytes.
-  uint64_t record_size;
-  // The version of the tool that make this archive.
-  uint16_t version_made_by;
-  // Tool version needed to extract this archive.
-  uint16_t version_needed;
-  // Number of this disk.
-  uint32_t disk_num;
-  // Number of the disk with the start of the central directory.
-  uint32_t cd_start_disk;
-  // Total number of entries in the central directory on this disk.
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that num_records_on_disk == num_records.
-  uint64_t num_records_on_disk;
-  // The total number of central directory records.
-  uint64_t num_records;
-  // The size of the central directory in bytes.
-  uint64_t cd_size;
-  // The offset of the start of the central directory, relative to the start of
-  // the file.
-  uint64_t cd_start_offset;
-
- private:
-  Zip64EocdRecord() = default;
-  DISALLOW_COPY_AND_ASSIGN(Zip64EocdRecord);
-} __attribute__((packed));
-
-// The possible contents of the Zip64 Extended Information Extra Field. It may appear in
-// the 'extra' field of a central directory record or local file header. The order of
-// the fields in the zip64 extended information record is fixed, but the fields MUST
-// only appear if the corresponding local or central directory record field is set to
-// 0xFFFF or 0xFFFFFFFF. And this entry in the Local header MUST include BOTH original
-// and compressed file size fields.
-struct Zip64ExtendedInfo {
-  static constexpr uint16_t kHeaderId = 0x0001;
-  // The header tag for this 'extra' block, should be |kHeaderId|.
-  uint16_t header_id;
-  // The size in bytes of the remaining data (excluding the top 4 bytes).
-  uint16_t data_size;
-  // Size in bytes of the uncompressed file.
-  std::optional<uint64_t> uncompressed_file_size;
-  // Size in bytes of the compressed file.
-  std::optional<uint64_t> compressed_file_size;
-  // Local file header offset relative to the start of the zip file.
-  std::optional<uint64_t> local_header_offset;
-
-  // This implementation assumes that each archive spans a single disk only. So
-  // the disk_number is not used.
-  // uint32_t disk_num;
- private:
-  Zip64ExtendedInfo() = default;
-  DISALLOW_COPY_AND_ASSIGN(Zip64ExtendedInfo);
-};
-
-// mask value that signifies that the entry has a DD
-static const uint32_t kGPBDDFlagMask = 0x0008;
-
-// The maximum size of a central directory or a file
-// comment in bytes.
-static const uint32_t kMaxCommentLen = 65535;
-
-#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
deleted file mode 100644
index 4b43cba..0000000
--- a/libziparchive/zip_archive_private.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2008 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 <ziparchive/zip_archive.h>
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "android-base/macros.h"
-#include "android-base/mapped_file.h"
-#include "android-base/memory.h"
-#include "zip_cd_entry_map.h"
-#include "zip_error.h"
-
-class MappedZipFile {
- public:
-  explicit MappedZipFile(const int fd)
-      : 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), fd_offset_(0), base_ptr_(address),
-        data_length_(static_cast<off64_t>(length)) {}
-
-  bool HasFd() const { return has_fd_; }
-
-  int GetFileDescriptor() const;
-
-  const void* GetBasePtr() const;
-
-  off64_t GetFileOffset() const;
-
-  off64_t GetFileLength() const;
-
-  bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
-
- private:
-  // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
-  // from the file. Otherwise, we're opening the archive from a memory mapped
-  // file. In that case, base_ptr_ points to the start of the memory region and
-  // data_length_ defines the file length.
-  const bool has_fd_;
-
-  const int fd_;
-  const off64_t fd_offset_;
-
-  const void* const base_ptr_;
-  mutable off64_t data_length_;
-};
-
-class CentralDirectory {
- public:
-  CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
-
-  const uint8_t* GetBasePtr() const { return base_ptr_; }
-
-  size_t GetMapLength() const { return length_; }
-
-  void Initialize(const void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
-
- private:
-  const uint8_t* base_ptr_;
-  size_t length_;
-};
-
-struct ZipArchive {
-  // open Zip archive
-  mutable MappedZipFile mapped_zip;
-  const bool close_file;
-
-  // mapped central directory area
-  off64_t directory_offset;
-  CentralDirectory central_directory;
-  std::unique_ptr<android::base::MappedFile> directory_map;
-
-  // number of entries in the Zip archive
-  uint64_t num_entries;
-  std::unique_ptr<CdEntryMapInterface> cd_entry_map;
-
-  ZipArchive(MappedZipFile&& map, bool assume_ownership);
-  ZipArchive(const void* address, size_t length);
-  ~ZipArchive();
-
-  bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
-};
-
-int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry,
-                        zip_archive::Writer* writer);
-
-// Reads the unaligned data of type |T| and auto increment the offset.
-template <typename T>
-static T ConsumeUnaligned(uint8_t** address) {
-  auto ret = android::base::get_unaligned<T>(*address);
-  *address += sizeof(T);
-  return ret;
-}
-
-// Writes the unaligned data of type |T| and auto increment the offset.
-template <typename T>
-void EmitUnaligned(uint8_t** address, T data) {
-  android::base::put_unaligned<T>(*address, data);
-  *address += sizeof(T);
-}
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
deleted file mode 100644
index 1ec95b6..0000000
--- a/libziparchive/zip_archive_stream_entry.cc
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ZIPARCHIVE"
-
-// Read-only stream access to Zip Archive entries.
-#include <errno.h>
-#include <inttypes.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <log/log.h>
-
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-#include <zlib.h>
-
-#include "zip_archive_private.h"
-
-static constexpr size_t kBufSize = 65535;
-
-bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
-  crc32_ = entry.crc32;
-  offset_ = entry.offset;
-  return true;
-}
-
-class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
- public:
-  explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
-      : ZipArchiveStreamEntry(handle) {}
-  virtual ~ZipArchiveStreamEntryUncompressed() {}
-
-  const std::vector<uint8_t>* Read() override;
-
-  bool Verify() override;
-
- protected:
-  bool Init(const ZipEntry& entry) override;
-
-  uint32_t length_ = 0u;
-
- private:
-  std::vector<uint8_t> data_;
-  uint32_t computed_crc32_ = 0u;
-};
-
-bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
-  if (!ZipArchiveStreamEntry::Init(entry)) {
-    return false;
-  }
-
-  length_ = entry.uncompressed_length;
-
-  data_.resize(kBufSize);
-  computed_crc32_ = 0;
-
-  return true;
-}
-
-const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
-  // Simple sanity check. The vector should *only* be handled by this code. A caller
-  // should not const-cast and modify the capacity. This may invalidate next_out.
-  //
-  // Note: it would be better to store the results of data() across Read calls.
-  CHECK_EQ(data_.capacity(), kBufSize);
-
-  if (length_ == 0) {
-    return nullptr;
-  }
-
-  size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
-  errno = 0;
-  if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
-    if (errno != 0) {
-      ALOGE("Error reading from archive fd: %s", strerror(errno));
-    } else {
-      ALOGE("Short read of zip file, possibly corrupted zip?");
-    }
-    length_ = 0;
-    return nullptr;
-  }
-
-  if (bytes < data_.size()) {
-    data_.resize(bytes);
-  }
-  computed_crc32_ = static_cast<uint32_t>(
-      crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
-  length_ -= bytes;
-  offset_ += bytes;
-  return &data_;
-}
-
-bool ZipArchiveStreamEntryUncompressed::Verify() {
-  return length_ == 0 && crc32_ == computed_crc32_;
-}
-
-class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
- public:
-  explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
-      : ZipArchiveStreamEntry(handle) {}
-  virtual ~ZipArchiveStreamEntryCompressed();
-
-  const std::vector<uint8_t>* Read() override;
-
-  bool Verify() override;
-
- protected:
-  bool Init(const ZipEntry& entry) override;
-
- private:
-  bool z_stream_init_ = false;
-  z_stream z_stream_;
-  std::vector<uint8_t> in_;
-  std::vector<uint8_t> out_;
-  uint32_t uncompressed_length_ = 0u;
-  uint32_t compressed_length_ = 0u;
-  uint32_t computed_crc32_ = 0u;
-};
-
-// This method is using libz macros with old-style-casts
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
-  return inflateInit2(stream, window_bits);
-}
-#pragma GCC diagnostic pop
-
-bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
-  if (!ZipArchiveStreamEntry::Init(entry)) {
-    return false;
-  }
-
-  // Initialize the zlib stream struct.
-  memset(&z_stream_, 0, sizeof(z_stream_));
-  z_stream_.zalloc = Z_NULL;
-  z_stream_.zfree = Z_NULL;
-  z_stream_.opaque = Z_NULL;
-  z_stream_.next_in = nullptr;
-  z_stream_.avail_in = 0;
-  z_stream_.avail_out = 0;
-  z_stream_.data_type = Z_UNKNOWN;
-
-  // Use the undocumented "negative window bits" feature to tell zlib
-  // that there's no zlib header waiting for it.
-  int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
-  if (zerr != Z_OK) {
-    if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
-    } else {
-      ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
-    }
-
-    return false;
-  }
-
-  z_stream_init_ = true;
-
-  uncompressed_length_ = entry.uncompressed_length;
-  compressed_length_ = entry.compressed_length;
-
-  out_.resize(kBufSize);
-  in_.resize(kBufSize);
-
-  computed_crc32_ = 0;
-
-  return true;
-}
-
-ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
-  if (z_stream_init_) {
-    inflateEnd(&z_stream_);
-    z_stream_init_ = false;
-  }
-}
-
-bool ZipArchiveStreamEntryCompressed::Verify() {
-  return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
-         crc32_ == computed_crc32_;
-}
-
-const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
-  // Simple sanity check. The vector should *only* be handled by this code. A caller
-  // should not const-cast and modify the capacity. This may invalidate next_out.
-  //
-  // Note: it would be better to store the results of data() across Read calls.
-  CHECK_EQ(out_.capacity(), kBufSize);
-
-  if (z_stream_.avail_out == 0) {
-    z_stream_.next_out = out_.data();
-    z_stream_.avail_out = static_cast<uint32_t>(out_.size());
-    ;
-  }
-
-  while (true) {
-    if (z_stream_.avail_in == 0) {
-      if (compressed_length_ == 0) {
-        return nullptr;
-      }
-      DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max());  // Should be buf size = 64k.
-      uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
-                                                         : compressed_length_;
-      ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
-      errno = 0;
-      if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
-        if (errno != 0) {
-          ALOGE("Error reading from archive fd: %s", strerror(errno));
-        } else {
-          ALOGE("Short read of zip file, possibly corrupted zip?");
-        }
-        return nullptr;
-      }
-
-      compressed_length_ -= bytes;
-      offset_ += bytes;
-      z_stream_.next_in = in_.data();
-      z_stream_.avail_in = bytes;
-    }
-
-    int zerr = inflate(&z_stream_, Z_NO_FLUSH);
-    if (zerr != Z_OK && zerr != Z_STREAM_END) {
-      ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
-            z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
-      return nullptr;
-    }
-
-    if (z_stream_.avail_out == 0) {
-      uncompressed_length_ -= out_.size();
-      computed_crc32_ = static_cast<uint32_t>(
-          crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
-      return &out_;
-    }
-    if (zerr == Z_STREAM_END) {
-      if (z_stream_.avail_out != 0) {
-        // Resize the vector down to the actual size of the data.
-        out_.resize(out_.size() - z_stream_.avail_out);
-        computed_crc32_ = static_cast<uint32_t>(
-            crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
-        uncompressed_length_ -= out_.size();
-        return &out_;
-      }
-      return nullptr;
-    }
-  }
-  return nullptr;
-}
-
-class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
- public:
-  explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
-      : ZipArchiveStreamEntryUncompressed(handle) {}
-  virtual ~ZipArchiveStreamEntryRawCompressed() {}
-
-  bool Verify() override;
-
- protected:
-  bool Init(const ZipEntry& entry) override;
-};
-
-bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
-  if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
-    return false;
-  }
-  length_ = entry.compressed_length;
-
-  return true;
-}
-
-bool ZipArchiveStreamEntryRawCompressed::Verify() {
-  return length_ == 0;
-}
-
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
-                                                     const ZipEntry& entry) {
-  ZipArchiveStreamEntry* stream = nullptr;
-  if (entry.method != kCompressStored) {
-    stream = new ZipArchiveStreamEntryCompressed(handle);
-  } else {
-    stream = new ZipArchiveStreamEntryUncompressed(handle);
-  }
-  if (stream && !stream->Init(entry)) {
-    delete stream;
-    stream = nullptr;
-  }
-
-  return stream;
-}
-
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
-                                                        const ZipEntry& entry) {
-  ZipArchiveStreamEntry* stream = nullptr;
-  if (entry.method == kCompressStored) {
-    // Not compressed, don't need to do anything special.
-    stream = new ZipArchiveStreamEntryUncompressed(handle);
-  } else {
-    stream = new ZipArchiveStreamEntryRawCompressed(handle);
-  }
-  if (stream && !stream->Init(entry)) {
-    delete stream;
-    stream = nullptr;
-  }
-  return stream;
-}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
deleted file mode 100644
index f5429be..0000000
--- a/libziparchive/zip_archive_test.cc
+++ /dev/null
@@ -1,1327 +0,0 @@
-/*
- * Copyright (C) 2013 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 <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string_view>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/mapped_file.h>
-#include <android-base/memory.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-
-#include "zip_archive_common.h"
-#include "zip_archive_private.h"
-
-static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
-
-static const std::string kValidZip = "valid.zip";
-static const std::string kLargeZip = "large.zip";
-static const std::string kBadCrcZip = "bad_crc.zip";
-
-static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
-                                                'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-
-static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I',  'M', 'K',
-                                                          207, 'H', 132, 210, '\\', '\0'};
-
-static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-
-static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
-  const std::string abs_path = test_data_dir + "/" + name;
-  return OpenArchive(abs_path.c_str(), handle);
-}
-
-class CdEntryMapTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    names_ = {
-        "a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt",
-    };
-    separator_ = "separator";
-    header_ = "metadata";
-    joined_names_ = header_ + android::base::Join(names_, separator_);
-    base_ptr_ = reinterpret_cast<uint8_t*>(&joined_names_[0]);
-
-    entry_maps_.emplace_back(CdEntryMapZip32::Create(static_cast<uint16_t>(names_.size())));
-    entry_maps_.emplace_back(CdEntryMapZip64::Create());
-    for (auto& cd_map : entry_maps_) {
-      ASSERT_NE(nullptr, cd_map);
-      size_t offset = header_.size();
-      for (const auto& name : names_) {
-        auto status = cd_map->AddToMap(
-            std::string_view{joined_names_.c_str() + offset, name.size()}, base_ptr_);
-        ASSERT_EQ(0, status);
-        offset += name.size() + separator_.size();
-      }
-    }
-  }
-
-  std::vector<std::string> names_;
-  // A continuous region of memory serves as a mock of the central directory.
-  std::string joined_names_;
-  // We expect some metadata at the beginning of the central directory and between filenames.
-  std::string header_;
-  std::string separator_;
-
-  std::vector<std::unique_ptr<CdEntryMapInterface>> entry_maps_;
-  uint8_t* base_ptr_{nullptr};  // Points to the start of the central directory.
-};
-
-TEST_F(CdEntryMapTest, AddDuplicatedEntry) {
-  for (auto& cd_map : entry_maps_) {
-    std::string_view name = "b.txt";
-    ASSERT_NE(0, cd_map->AddToMap(name, base_ptr_));
-  }
-}
-
-TEST_F(CdEntryMapTest, FindEntry) {
-  for (auto& cd_map : entry_maps_) {
-    uint64_t expected_offset = header_.size();
-    for (const auto& name : names_) {
-      auto [status, offset] = cd_map->GetCdEntryOffset(name, base_ptr_);
-      ASSERT_EQ(status, kSuccess);
-      ASSERT_EQ(offset, expected_offset);
-      expected_offset += name.size() + separator_.size();
-    }
-  }
-}
-
-TEST_F(CdEntryMapTest, Iteration) {
-  std::set<std::string_view> expected(names_.begin(), names_.end());
-  for (auto& cd_map : entry_maps_) {
-    cd_map->ResetIteration();
-    std::set<std::string_view> entry_set;
-    auto ret = cd_map->Next(base_ptr_);
-    while (ret != std::pair<std::string_view, uint64_t>{}) {
-      auto [it, insert_status] = entry_set.insert(ret.first);
-      ASSERT_TRUE(insert_status);
-      ret = cd_map->Next(base_ptr_);
-    }
-    ASSERT_EQ(expected, entry_set);
-  }
-}
-
-TEST(ziparchive, Open) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-  CloseArchive(handle);
-
-  ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, OutOfBound) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, EmptyArchive) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, ZeroSizeCentralDirectory) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, OpenMissing) {
-  ZipArchiveHandle handle;
-  ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
-
-  // Confirm the file descriptor is not going to be mistaken for a valid one.
-  ASSERT_EQ(-1, GetFileDescriptor(handle));
-}
-
-TEST(ziparchive, OpenAssumeFdOwnership) {
-  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
-  ASSERT_NE(-1, fd);
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
-  CloseArchive(handle);
-  ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
-  ASSERT_EQ(EBADF, errno);
-}
-
-TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
-  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
-  ASSERT_NE(-1, fd);
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
-  CloseArchive(handle);
-  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
-  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));
-
-  void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
-
-  ZipEntry64 data;
-  std::vector<std::string_view> names;
-  std::string_view name;
-  while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
-
-  // Assert that the names are as expected.
-  std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
-  std::sort(names.begin(), names.end());
-  ASSERT_EQ(expected_names, names);
-
-  CloseArchive(handle);
-}
-
-static void AssertIterationNames(void* iteration_cookie,
-                                 const std::vector<std::string>& expected_names_sorted) {
-  ZipEntry64 data;
-  std::vector<std::string> names;
-  std::string_view name;
-  for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
-    ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-    names.push_back(std::string(name));
-  }
-  // End of iteration.
-  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-  // Assert that the names are as expected.
-  std::sort(names.begin(), names.end());
-  ASSERT_EQ(expected_names_sorted, names);
-}
-
-static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
-                                 const std::vector<std::string>& expected_names_sorted) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
-  AssertIterationNames(iteration_cookie, expected_names_sorted);
-  CloseArchive(handle);
-}
-
-static void AssertIterationOrderWithMatcher(std::function<bool(std::string_view)> matcher,
-                                            const std::vector<std::string>& expected_names_sorted) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, matcher));
-  AssertIterationNames(iteration_cookie, expected_names_sorted);
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, Iteration) {
-  static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
-                                                                  "b/d.txt"};
-
-  AssertIterationOrder("", "", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithPrefix) {
-  static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
-
-  AssertIterationOrder("b/", "", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithSuffix) {
-  static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
-                                                                  "b/d.txt"};
-
-  AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithPrefixAndSuffix) {
-  static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
-
-  AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithAdditionalMatchesExactly) {
-  static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt"};
-  auto matcher = [](std::string_view name) { return name == "a.txt"; };
-  AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithAdditionalMatchesWithSuffix) {
-  static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
-                                                                  "b/d.txt"};
-  auto matcher = [](std::string_view name) {
-    return name == "a.txt" || android::base::EndsWith(name, ".txt");
-  };
-  AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithAdditionalMatchesWithPrefixAndSuffix) {
-  static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b/c.txt", "b/d.txt"};
-  auto matcher = [](std::string_view name) {
-    return name == "a.txt" ||
-           (android::base::EndsWith(name, ".txt") && android::base::StartsWith(name, "b/"));
-  };
-  AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
-
-  ZipEntry64 data;
-  std::string_view name;
-
-  // End of iteration.
-  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  ZipEntry64 data;
-  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
-
-  // Known facts about a.txt, from zipinfo -v.
-  ASSERT_EQ(63, data.offset);
-  ASSERT_EQ(kCompressDeflated, data.method);
-  ASSERT_EQ(17u, data.uncompressed_length);
-  ASSERT_EQ(13u, data.compressed_length);
-  ASSERT_EQ(0x950821c5, data.crc32);
-  ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
-
-  // An entry that doesn't exist. Should be a negative return code.
-  ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
-
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry_empty) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  ZipEntry64 data;
-  ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
-
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry_too_long) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  std::string very_long_name(65536, 'x');
-  ZipEntry64 data;
-  ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
-
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, TestInvalidDeclaredLength) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
-
-  void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
-
-  std::string_view name;
-  ZipEntry64 data;
-
-  ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
-  ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
-
-  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.
-  ZipEntry64 data;
-  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
-  const auto a_size = static_cast<size_t>(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 auto b_size = static_cast<size_t>(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));
-
-  // An entry that's deflated.
-  ZipEntry64 data;
-  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
-  const auto a_size = static_cast<size_t>(data.uncompressed_length);
-  ASSERT_EQ(a_size, kATxtContents.size());
-  uint8_t* buffer = new uint8_t[a_size];
-  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
-  ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
-  delete[] buffer;
-
-  // An entry that's stored.
-  ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
-  const auto b_size = static_cast<size_t>(data.uncompressed_length);
-  ASSERT_EQ(b_size, kBTxtContents.size());
-  buffer = new uint8_t[b_size];
-  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
-  ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
-  delete[] buffer;
-
-  CloseArchive(handle);
-}
-
-static const uint32_t kEmptyEntriesZip[] = {
-    0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
-    0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
-    0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
-    0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
-    0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
-    0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
-
-// This is a zip file containing a single entry (ab.txt) that contains
-// 90072 repetitions of the string "ab\n" and has an uncompressed length
-// of 270216 bytes.
-static const uint16_t kAbZip[] = {
-    0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
-    0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
-    0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
-    0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-    0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
-    0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
-    0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
-    0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
-    0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
-
-static const std::string kAbTxtName("ab.txt");
-static const size_t kAbUncompressedSize = 270216;
-
-TEST(ziparchive, EmptyEntries) {
-  TemporaryFile tmp_file;
-  ASSERT_NE(-1, tmp_file.fd);
-  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
-
-  ZipEntry64 entry;
-  ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
-  ASSERT_EQ(0u, entry.uncompressed_length);
-  // Extraction to a 1 byte buffer should succeed.
-  uint8_t buffer[1];
-  ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
-  // Extraction to an empty buffer should succeed.
-  ASSERT_EQ(0, ExtractToMemory(handle, &entry, nullptr, 0));
-
-  TemporaryFile tmp_output_file;
-  ASSERT_NE(-1, tmp_output_file.fd);
-  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
-
-  struct stat stat_buf;
-  ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
-  ASSERT_EQ(0, stat_buf.st_size);
-}
-
-TEST(ziparchive, EntryLargerThan32K) {
-  TemporaryFile tmp_file;
-  ASSERT_NE(-1, tmp_file.fd);
-  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
-                                        sizeof(kAbZip) - 1));
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
-
-  ZipEntry64 entry;
-  ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
-  ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
-
-  // Extract the entry to memory.
-  std::vector<uint8_t> buffer(kAbUncompressedSize);
-  ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
-
-  // Extract the entry to a file.
-  TemporaryFile tmp_output_file;
-  ASSERT_NE(-1, tmp_output_file.fd);
-  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
-
-  // Make sure the extracted file size is as expected.
-  struct stat stat_buf;
-  ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
-  ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
-
-  // Read the file back to a buffer and make sure the contents are
-  // the same as the memory buffer we extracted directly to.
-  std::vector<uint8_t> file_contents(kAbUncompressedSize);
-  ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
-  ASSERT_EQ(file_contents, buffer);
-
-  for (int i = 0; i < 90072; ++i) {
-    const uint8_t* line = &file_contents[0] + (3 * i);
-    ASSERT_EQ('a', line[0]);
-    ASSERT_EQ('b', line[1]);
-    ASSERT_EQ('\n', line[2]);
-  }
-}
-
-TEST(ziparchive, TrailerAfterEOCD) {
-  TemporaryFile tmp_file;
-  ASSERT_NE(-1, tmp_file.fd);
-
-  // Create a file with 8 bytes of random garbage.
-  static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
-  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
-  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
-
-  ZipArchiveHandle handle;
-  ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
-}
-
-TEST(ziparchive, ExtractToFile) {
-  TemporaryFile tmp_file;
-  ASSERT_NE(-1, tmp_file.fd);
-  const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
-  const size_t data_size = sizeof(data);
-
-  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  ZipEntry64 entry;
-  ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
-  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
-
-  // Assert that the first 8 bytes of the file haven't been clobbered.
-  uint8_t read_buffer[data_size];
-  ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
-  ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
-
-  // Assert that the remainder of the file contains the incompressed data.
-  std::vector<uint8_t> uncompressed_data(static_cast<size_t>(entry.uncompressed_length));
-  ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
-                                       static_cast<size_t>(entry.uncompressed_length)));
-  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
-
-  // Assert that the total length of the file is sane
-  ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
-            lseek(tmp_file.fd, 0, SEEK_END));
-}
-
-#if !defined(_WIN32)
-TEST(ziparchive, OpenFromMemory) {
-  const std::string zip_path = test_data_dir + "/dummy-update.zip";
-  android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
-  ASSERT_NE(-1, fd);
-  struct stat sb;
-  ASSERT_EQ(0, fstat(fd, &sb));
-
-  // Memory map the file first and open the archive from the memory region.
-  auto file_map{
-      android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0,
-            OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
-
-  // Assert one entry can be found and extracted correctly.
-  ZipEntry64 binary_entry;
-  ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
-  TemporaryFile tmp_binary;
-  ASSERT_NE(-1, tmp_binary.fd);
-  ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
-}
-#endif
-
-static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
-                                 bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
-  ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
-  std::unique_ptr<ZipArchiveStreamEntry> stream;
-  if (raw) {
-    stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
-    if (entry->method == kCompressStored) {
-      read_data->resize(static_cast<size_t>(entry->uncompressed_length));
-    } else {
-      read_data->resize(static_cast<size_t>(entry->compressed_length));
-    }
-  } else {
-    stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
-    read_data->resize(static_cast<size_t>(entry->uncompressed_length));
-  }
-  uint8_t* read_data_ptr = read_data->data();
-  ASSERT_TRUE(stream.get() != nullptr);
-  const std::vector<uint8_t>* data;
-  uint64_t total_size = 0;
-  while ((data = stream->Read()) != nullptr) {
-    total_size += data->size();
-    memcpy(read_data_ptr, data->data(), data->size());
-    read_data_ptr += data->size();
-  }
-  ASSERT_EQ(verified, stream->Verify());
-  ASSERT_EQ(total_size, read_data->size());
-}
-
-static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
-                                              const std::string& entry_name,
-                                              const std::vector<uint8_t>& contents, bool raw) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
-
-  ZipEntry entry;
-  std::vector<uint8_t> read_data;
-  ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
-
-  ASSERT_EQ(contents.size(), read_data.size());
-  ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
-
-  CloseArchive(handle);
-}
-
-static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
-                                            const std::string& entry_name) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
-
-  ZipEntry entry;
-  std::vector<uint8_t> read_data;
-  ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
-
-  std::vector<uint8_t> cmp_data(static_cast<size_t>(entry.uncompressed_length));
-  ASSERT_EQ(entry.uncompressed_length, read_data.size());
-  ASSERT_EQ(
-      0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
-  ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
-
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, StreamCompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
-}
-
-TEST(ziparchive, StreamUncompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
-}
-
-TEST(ziparchive, StreamRawCompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
-}
-
-TEST(ziparchive, StreamRawUncompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
-}
-
-TEST(ziparchive, StreamLargeCompressed) {
-  ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
-}
-
-TEST(ziparchive, StreamLargeUncompressed) {
-  ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
-}
-
-TEST(ziparchive, StreamCompressedBadCrc) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
-
-  ZipEntry entry;
-  std::vector<uint8_t> read_data;
-  ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
-
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, StreamUncompressedBadCrc) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
-
-  ZipEntry entry;
-  std::vector<uint8_t> read_data;
-  ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
-
-  CloseArchive(handle);
-}
-
-// Generated using the following Java program:
-//     public static void main(String[] foo) throws Exception {
-//       FileOutputStream fos = new
-//       FileOutputStream("/tmp/data_descriptor.zip");
-//       ZipOutputStream zos = new ZipOutputStream(fos);
-//       ZipEntry64 ze = new ZipEntry64("name");
-//       ze.setMethod(ZipEntry64.DEFLATED);
-//       zos.putNextEntry(ze);
-//       zos.write("abdcdefghijk".getBytes());
-//       zos.closeEntry();
-//       zos.close();
-//     }
-//
-// cat /tmp/data_descriptor.zip | xxd -i
-//
-static const std::vector<uint8_t> kDataDescriptorZipFile{
-    0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
-    0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
-    //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
-    0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
-    0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
-    0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
-    0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
-    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-// The offsets of the data descriptor in this file, so we can mess with
-// them later in the test.
-static constexpr uint32_t kDataDescriptorOffset = 48;
-static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
-static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
-
-static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
-                                 std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
-  TemporaryFile tmp_file;
-  ASSERT_NE(-1, tmp_file.fd);
-  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
-
-  // This function expects a variant of kDataDescriptorZipFile, for look for
-  // an entry whose name is "name" and whose size is 12 (contents =
-  // "abdcdefghijk").
-  ZipEntry64 entry;
-  ASSERT_EQ(0, FindEntry(handle, "name", &entry));
-  ASSERT_EQ(12u, entry.uncompressed_length);
-
-  entry_out->resize(12);
-  (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
-
-  CloseArchive(handle);
-}
-
-TEST(ziparchive, ValidDataDescriptors) {
-  std::vector<uint8_t> entry;
-  int32_t error_code = 0;
-  ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
-
-  ASSERT_EQ(0, error_code);
-  ASSERT_EQ(12u, entry.size());
-  ASSERT_EQ('a', entry[0]);
-  ASSERT_EQ('k', entry[11]);
-}
-
-TEST(ziparchive, InvalidDataDescriptors_csize) {
-  std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
-  invalid_csize[kCSizeOffset] = 0xfe;
-
-  std::vector<uint8_t> entry;
-  int32_t error_code = 0;
-  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
-
-  ASSERT_EQ(kInconsistentInformation, error_code);
-}
-
-TEST(ziparchive, InvalidDataDescriptors_size) {
-  std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
-  invalid_size[kSizeOffset] = 0xfe;
-
-  std::vector<uint8_t> entry;
-  int32_t error_code = 0;
-  ExtractEntryToMemory(invalid_size, &entry, &error_code);
-
-  ASSERT_EQ(kInconsistentInformation, error_code);
-}
-
-TEST(ziparchive, ErrorCodeString) {
-  ASSERT_STREQ("Success", ErrorCodeString(0));
-
-  // Out of bounds.
-  ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
-  ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
-  ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
-
-  ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
-}
-
-// A zip file whose local file header at offset zero is corrupted.
-//
-// ---------------
-// cat foo > a.txt
-// zip a.zip a.txt
-// cat a.zip | xxd -i
-//
-// Manual changes :
-// [2] = 0xff  // Corrupt the LFH signature of entry 0.
-// [3] = 0xff  // Corrupt the LFH signature of entry 0.
-static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
-    //[lfh-sig-----------], [lfh contents---------------------------------
-    0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
-    //--------------------------------------------------------------------
-    0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
-    //-------------------------------]  [file-name-----------------], [---
-    0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
-    // entry-contents------------------------------------------------------
-    0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
-    //--------------------------------------------------------------------
-    0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
-    //-------------------------------------], [cd-record-sig-------], [---
-    0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
-    // cd-record-----------------------------------------------------------
-    0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
-    //--------------------------------------------------------------------
-    0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
-    //--------------------------------------------------------------------
-    0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
-    //-]  [lfh-file-header-off-], [file-name-----------------], [extra----
-    0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
-    //--------------------------------------------------------------------
-    0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
-    //-------------------------------------------------------], [eocd-sig-
-    0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
-    //-------], [---------------------------------------------------------
-    0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
-    //-------------------------------------------]
-    0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-TEST(ziparchive, BrokenLfhSignature) {
-  TemporaryFile tmp_file;
-  ASSERT_NE(-1, tmp_file.fd);
-  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
-                                        kZipFileWithBrokenLfhSignature.size()));
-  ZipArchiveHandle handle;
-  ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
-}
-
-class VectorReader : public zip_archive::Reader {
- public:
-  VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
-
-  bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
-    if ((offset + len) < input_.size()) {
-      return false;
-    }
-
-    memcpy(buf, &input_[static_cast<size_t>(offset)], len);
-    return true;
-  }
-
- private:
-  const std::vector<uint8_t>& input_;
-};
-
-class VectorWriter : public zip_archive::Writer {
- public:
-  VectorWriter() : Writer() {}
-
-  bool Append(uint8_t* buf, size_t size) {
-    output_.insert(output_.end(), buf, buf + size);
-    return true;
-  }
-
-  std::vector<uint8_t>& GetOutput() { return output_; }
-
- private:
-  std::vector<uint8_t> output_;
-};
-
-class BadReader : public zip_archive::Reader {
- public:
-  BadReader() : Reader() {}
-
-  bool ReadAtOffset(uint8_t*, size_t, off64_t) const { return false; }
-};
-
-class BadWriter : public zip_archive::Writer {
- public:
-  BadWriter() : Writer() {}
-
-  bool Append(uint8_t*, size_t) { return false; }
-};
-
-TEST(ziparchive, Inflate) {
-  const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
-  const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
-
-  const VectorReader reader(kATxtContentsCompressed);
-  {
-    VectorWriter writer;
-    uint64_t crc_out = 0;
-
-    int32_t ret =
-        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
-    ASSERT_EQ(0, ret);
-    ASSERT_EQ(kATxtContents, writer.GetOutput());
-    ASSERT_EQ(0x950821C5u, crc_out);
-  }
-
-  {
-    VectorWriter writer;
-    int32_t ret =
-        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
-    ASSERT_EQ(0, ret);
-    ASSERT_EQ(kATxtContents, writer.GetOutput());
-  }
-
-  {
-    BadWriter writer;
-    int32_t ret =
-        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
-    ASSERT_EQ(kIoError, ret);
-  }
-
-  {
-    BadReader reader;
-    VectorWriter writer;
-    int32_t ret =
-        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
-    ASSERT_EQ(kIoError, ret);
-    ASSERT_EQ(0u, writer.GetOutput().size());
-  }
-}
-
-// The class constructs a zipfile with zip64 format, and test the parsing logic.
-class Zip64ParseTest : public ::testing::Test {
- protected:
-  struct LocalFileEntry {
-    std::vector<uint8_t> local_file_header;
-    std::string file_name;
-    std::vector<uint8_t> extended_field;
-    // Fake data to mimic the compressed bytes in the zipfile.
-    std::vector<uint8_t> compressed_bytes;
-    std::vector<uint8_t> data_descriptor;
-
-    size_t GetSize() const {
-      return local_file_header.size() + file_name.size() + extended_field.size() +
-             compressed_bytes.size() + data_descriptor.size();
-    }
-
-    void CopyToOutput(std::vector<uint8_t>* output) const {
-      std::copy(local_file_header.begin(), local_file_header.end(), std::back_inserter(*output));
-      std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
-      std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
-      std::copy(compressed_bytes.begin(), compressed_bytes.end(), std::back_inserter(*output));
-      std::copy(data_descriptor.begin(), data_descriptor.end(), std::back_inserter(*output));
-    }
-  };
-
-  struct CdRecordEntry {
-    std::vector<uint8_t> central_directory_record;
-    std::string file_name;
-    std::vector<uint8_t> extended_field;
-
-    size_t GetSize() const {
-      return central_directory_record.size() + file_name.size() + extended_field.size();
-    }
-
-    void CopyToOutput(std::vector<uint8_t>* output) const {
-      std::copy(central_directory_record.begin(), central_directory_record.end(),
-                std::back_inserter(*output));
-      std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
-      std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
-    }
-  };
-
-  static void ConstructLocalFileHeader(const std::string& name, std::vector<uint8_t>* output,
-                                       uint32_t uncompressed_size, uint32_t compressed_size) {
-    LocalFileHeader lfh = {};
-    lfh.lfh_signature = LocalFileHeader::kSignature;
-    lfh.compressed_size = compressed_size;
-    lfh.uncompressed_size = uncompressed_size;
-    lfh.file_name_length = static_cast<uint16_t>(name.size());
-    lfh.extra_field_length = 20;
-    *output = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&lfh),
-                                   reinterpret_cast<uint8_t*>(&lfh) + sizeof(LocalFileHeader));
-  }
-
-  // Put one zip64 extended info in the extended field.
-  static void ConstructExtendedField(const std::vector<uint64_t>& zip64_fields,
-                                     std::vector<uint8_t>* output) {
-    ASSERT_FALSE(zip64_fields.empty());
-    uint16_t data_size = 8 * static_cast<uint16_t>(zip64_fields.size());
-    std::vector<uint8_t> extended_field(data_size + 4);
-    android::base::put_unaligned(extended_field.data(), Zip64ExtendedInfo::kHeaderId);
-    android::base::put_unaligned(extended_field.data() + 2, data_size);
-    size_t offset = 4;
-    for (const auto& field : zip64_fields) {
-      android::base::put_unaligned(extended_field.data() + offset, field);
-      offset += 8;
-    }
-
-    *output = std::move(extended_field);
-  }
-
-  static void ConstructCentralDirectoryRecord(const std::string& name, uint32_t uncompressed_size,
-                                              uint32_t compressed_size, uint32_t local_offset,
-                                              std::vector<uint8_t>* output) {
-    CentralDirectoryRecord cdr = {};
-    cdr.record_signature = CentralDirectoryRecord::kSignature;
-    cdr.compressed_size = uncompressed_size;
-    cdr.uncompressed_size = compressed_size;
-    cdr.file_name_length = static_cast<uint16_t>(name.size());
-    cdr.extra_field_length = local_offset == UINT32_MAX ? 28 : 20;
-    cdr.local_file_header_offset = local_offset;
-    *output =
-        std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&cdr),
-                             reinterpret_cast<uint8_t*>(&cdr) + sizeof(CentralDirectoryRecord));
-  }
-
-  // Add an entry to the zipfile, construct the corresponding local header and cd entry.
-  void AddEntry(const std::string& name, const std::vector<uint8_t>& content,
-                bool uncompressed_size_in_extended, bool compressed_size_in_extended,
-                bool local_offset_in_extended, bool include_data_descriptor = false) {
-    auto uncompressed_size = static_cast<uint32_t>(content.size());
-    auto compressed_size = static_cast<uint32_t>(content.size());
-    uint32_t local_file_header_offset = 0;
-    std::for_each(file_entries_.begin(), file_entries_.end(),
-                  [&local_file_header_offset](const LocalFileEntry& file_entry) {
-                    local_file_header_offset += file_entry.GetSize();
-                  });
-
-    std::vector<uint64_t> zip64_fields;
-    if (uncompressed_size_in_extended) {
-      zip64_fields.push_back(uncompressed_size);
-      uncompressed_size = UINT32_MAX;
-    }
-    if (compressed_size_in_extended) {
-      zip64_fields.push_back(compressed_size);
-      compressed_size = UINT32_MAX;
-    }
-    LocalFileEntry local_entry = {
-        .local_file_header = {},
-        .file_name = name,
-        .extended_field = {},
-        .compressed_bytes = content,
-    };
-    ConstructLocalFileHeader(name, &local_entry.local_file_header, uncompressed_size,
-                             compressed_size);
-    ConstructExtendedField(zip64_fields, &local_entry.extended_field);
-    if (include_data_descriptor) {
-      size_t descriptor_size = compressed_size_in_extended ? 24 : 16;
-      local_entry.data_descriptor.resize(descriptor_size);
-      uint8_t* write_ptr = local_entry.data_descriptor.data();
-      EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
-      EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
-      if (compressed_size_in_extended) {
-        EmitUnaligned<uint64_t>(&write_ptr, compressed_size_in_extended);
-        EmitUnaligned<uint64_t>(&write_ptr, uncompressed_size_in_extended);
-      } else {
-        EmitUnaligned<uint32_t>(&write_ptr, compressed_size_in_extended);
-        EmitUnaligned<uint32_t>(&write_ptr, uncompressed_size_in_extended);
-      }
-    }
-
-    file_entries_.push_back(std::move(local_entry));
-
-    if (local_offset_in_extended) {
-      zip64_fields.push_back(local_file_header_offset);
-      local_file_header_offset = UINT32_MAX;
-    }
-    CdRecordEntry cd_entry = {
-        .central_directory_record = {},
-        .file_name = name,
-        .extended_field = {},
-    };
-    ConstructCentralDirectoryRecord(name, uncompressed_size, compressed_size,
-                                    local_file_header_offset, &cd_entry.central_directory_record);
-    ConstructExtendedField(zip64_fields, &cd_entry.extended_field);
-    cd_entries_.push_back(std::move(cd_entry));
-  }
-
-  void ConstructEocd() {
-    ASSERT_EQ(file_entries_.size(), cd_entries_.size());
-    Zip64EocdRecord zip64_eocd = {};
-    zip64_eocd.record_signature = Zip64EocdRecord::kSignature;
-    zip64_eocd.num_records = file_entries_.size();
-    zip64_eocd.cd_size = 0;
-    std::for_each(
-        cd_entries_.begin(), cd_entries_.end(),
-        [&zip64_eocd](const CdRecordEntry& cd_entry) { zip64_eocd.cd_size += cd_entry.GetSize(); });
-    zip64_eocd.cd_start_offset = 0;
-    std::for_each(file_entries_.begin(), file_entries_.end(),
-                  [&zip64_eocd](const LocalFileEntry& file_entry) {
-                    zip64_eocd.cd_start_offset += file_entry.GetSize();
-                  });
-    zip64_eocd_record_ =
-        std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_eocd),
-                             reinterpret_cast<uint8_t*>(&zip64_eocd) + sizeof(Zip64EocdRecord));
-
-    Zip64EocdLocator zip64_locator = {};
-    zip64_locator.locator_signature = Zip64EocdLocator::kSignature;
-    zip64_locator.zip64_eocd_offset = zip64_eocd.cd_start_offset + zip64_eocd.cd_size;
-    zip64_eocd_locator_ =
-        std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_locator),
-                             reinterpret_cast<uint8_t*>(&zip64_locator) + sizeof(Zip64EocdLocator));
-
-    EocdRecord eocd = {};
-    eocd.eocd_signature = EocdRecord::kSignature,
-    eocd.num_records = file_entries_.size() > UINT16_MAX
-                           ? UINT16_MAX
-                           : static_cast<uint16_t>(file_entries_.size());
-    eocd.cd_size = UINT32_MAX;
-    eocd.cd_start_offset = UINT32_MAX;
-    eocd_record_ = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&eocd),
-                                        reinterpret_cast<uint8_t*>(&eocd) + sizeof(EocdRecord));
-  }
-
-  // Concatenate all the local file entries, cd entries, and eocd metadata.
-  void ConstructZipFile() {
-    for (const auto& file_entry : file_entries_) {
-      file_entry.CopyToOutput(&zip_content_);
-    }
-    for (const auto& cd_entry : cd_entries_) {
-      cd_entry.CopyToOutput(&zip_content_);
-    }
-    std::copy(zip64_eocd_record_.begin(), zip64_eocd_record_.end(),
-              std::back_inserter(zip_content_));
-    std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
-              std::back_inserter(zip_content_));
-    std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
-  }
-
-  std::vector<uint8_t> zip_content_;
-
-  std::vector<LocalFileEntry> file_entries_;
-  std::vector<CdRecordEntry> cd_entries_;
-  std::vector<uint8_t> zip64_eocd_record_;
-  std::vector<uint8_t> zip64_eocd_locator_;
-  std::vector<uint8_t> eocd_record_;
-};
-
-TEST_F(Zip64ParseTest, openFile) {
-  AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, openFilelocalOffsetInExtendedField) {
-  AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, true);
-  AddEntry("b.txt", std::vector<uint8_t>(200, 'b'), true, true, true);
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, openFileCompressedNotInExtendedField) {
-  AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, false, false);
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  // Zip64 extended fields must include both uncompressed and compressed size.
-  ASSERT_NE(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, findEntry) {
-  AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
-  AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, false);
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  ZipEntry64 entry;
-  ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
-  ASSERT_EQ(200, entry.uncompressed_length);
-  ASSERT_EQ(200, entry.compressed_length);
-
-  ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
-  ASSERT_EQ(300, entry.uncompressed_length);
-  ASSERT_EQ(300, entry.compressed_length);
-  CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, openFileIncorrectDataSizeInLocalExtendedField) {
-  AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
-  ASSERT_EQ(1, file_entries_.size());
-  auto& extended_field = file_entries_[0].extended_field;
-  // data size exceeds the extended field size in local header.
-  android::base::put_unaligned<uint16_t>(extended_field.data() + 2, 30);
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  ZipEntry64 entry;
-  ASSERT_NE(0, FindEntry(handle, "a.txt", &entry));
-
-  CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, iterates) {
-  std::set<std::string_view> names{"a.txt", "b.txt", "c.txt", "d.txt", "e.txt"};
-  for (const auto& name : names) {
-    AddEntry(std::string(name), std::vector<uint8_t>(100, name[0]), true, true, true);
-  }
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-
-  void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
-  std::set<std::string_view> result;
-  std::string_view name;
-  ZipEntry64 entry;
-  while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name);
-  ASSERT_EQ(names, result);
-
-  CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, zip64EocdWrongLocatorOffset) {
-  AddEntry("a.txt", std::vector<uint8_t>(1, 'a'), true, true, true);
-  ConstructEocd();
-  zip_content_.resize(20, 'a');
-  std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
-            std::back_inserter(zip_content_));
-  std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
-
-  ZipArchiveHandle handle;
-  ASSERT_NE(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, extract) {
-  std::vector<uint8_t> content(200, 'a');
-  AddEntry("a.txt", content, true, true, true);
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  ZipEntry64 entry;
-  ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
-
-  VectorWriter writer;
-  ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
-  ASSERT_EQ(content, writer.GetOutput());
-}
-
-TEST_F(Zip64ParseTest, extractWithDataDescriptor) {
-  std::vector<uint8_t> content(300, 'b');
-  AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
-  AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
-  ConstructEocd();
-  ConstructZipFile();
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(
-      0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-  ZipEntry64 entry;
-  ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
-
-  VectorWriter writer;
-  ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
-  ASSERT_EQ(content, writer.GetOutput());
-}
diff --git a/libziparchive/zip_cd_entry_map.cc b/libziparchive/zip_cd_entry_map.cc
deleted file mode 100644
index f187c06..0000000
--- a/libziparchive/zip_cd_entry_map.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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 "zip_cd_entry_map.h"
-
-#include <android-base/logging.h>
-#include <log/log.h>
-
-/*
- * Round up to the next highest power of 2.
- *
- * Found on http://graphics.stanford.edu/~seander/bithacks.html.
- */
-static uint32_t RoundUpPower2(uint32_t val) {
-  val--;
-  val |= val >> 1;
-  val |= val >> 2;
-  val |= val >> 4;
-  val |= val >> 8;
-  val |= val >> 16;
-  val++;
-
-  return val;
-}
-
-static uint32_t ComputeHash(std::string_view name) {
-  return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
-}
-
-// Convert a ZipEntry to a hash table index, verifying that it's in a valid range.
-std::pair<ZipError, uint64_t> CdEntryMapZip32::GetCdEntryOffset(std::string_view name,
-                                                                const uint8_t* start) const {
-  const uint32_t hash = ComputeHash(name);
-
-  // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
-  uint32_t ent = hash & (hash_table_size_ - 1);
-  while (hash_table_[ent].name_offset != 0) {
-    if (hash_table_[ent].ToStringView(start) == name) {
-      return {kSuccess, hash_table_[ent].name_offset};
-    }
-    ent = (ent + 1) & (hash_table_size_ - 1);
-  }
-
-  ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
-  return {kEntryNotFound, 0};
-}
-
-ZipError CdEntryMapZip32::AddToMap(std::string_view name, const uint8_t* start) {
-  const uint64_t hash = ComputeHash(name);
-  uint32_t ent = hash & (hash_table_size_ - 1);
-
-  /*
-   * We over-allocated the table, so we're guaranteed to find an empty slot.
-   * Further, we guarantee that the hashtable size is not 0.
-   */
-  while (hash_table_[ent].name_offset != 0) {
-    if (hash_table_[ent].ToStringView(start) == name) {
-      // We've found a duplicate entry. We don't accept duplicates.
-      ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
-      return kDuplicateEntry;
-    }
-    ent = (ent + 1) & (hash_table_size_ - 1);
-  }
-
-  // `name` has already been validated before entry.
-  const char* start_char = reinterpret_cast<const char*>(start);
-  hash_table_[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
-  hash_table_[ent].name_length = static_cast<uint16_t>(name.size());
-  return kSuccess;
-}
-
-void CdEntryMapZip32::ResetIteration() {
-  current_position_ = 0;
-}
-
-std::pair<std::string_view, uint64_t> CdEntryMapZip32::Next(const uint8_t* cd_start) {
-  while (current_position_ < hash_table_size_) {
-    const auto& entry = hash_table_[current_position_];
-    current_position_ += 1;
-
-    if (entry.name_offset != 0) {
-      return {entry.ToStringView(cd_start), entry.name_offset};
-    }
-  }
-  // We have reached the end of the hash table.
-  return {};
-}
-
-CdEntryMapZip32::CdEntryMapZip32(uint16_t num_entries) {
-  /*
-   * Create hash table.  We have a minimum 75% load factor, possibly as
-   * low as 50% after we round off to a power of 2.  There must be at
-   * least one unused entry to avoid an infinite loop during creation.
-   */
-  hash_table_size_ = RoundUpPower2(1 + (num_entries * 4) / 3);
-  hash_table_ = {
-      reinterpret_cast<ZipStringOffset*>(calloc(hash_table_size_, sizeof(ZipStringOffset))), free};
-}
-
-std::unique_ptr<CdEntryMapInterface> CdEntryMapZip32::Create(uint16_t num_entries) {
-  auto entry_map = new CdEntryMapZip32(num_entries);
-  CHECK(entry_map->hash_table_ != nullptr)
-      << "Zip: unable to allocate the " << entry_map->hash_table_size_
-      << " entry hash_table, entry size: " << sizeof(ZipStringOffset);
-  return std::unique_ptr<CdEntryMapInterface>(entry_map);
-}
-
-std::unique_ptr<CdEntryMapInterface> CdEntryMapZip64::Create() {
-  return std::unique_ptr<CdEntryMapInterface>(new CdEntryMapZip64());
-}
-
-ZipError CdEntryMapZip64::AddToMap(std::string_view name, const uint8_t* start) {
-  const auto [it, added] =
-      entry_table_.insert({name, name.data() - reinterpret_cast<const char*>(start)});
-  if (!added) {
-    ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
-    return kDuplicateEntry;
-  }
-  return kSuccess;
-}
-
-std::pair<ZipError, uint64_t> CdEntryMapZip64::GetCdEntryOffset(std::string_view name,
-                                                                const uint8_t* /*cd_start*/) const {
-  const auto it = entry_table_.find(name);
-  if (it == entry_table_.end()) {
-    ALOGV("Zip: Could not find entry %.*s", static_cast<int>(name.size()), name.data());
-    return {kEntryNotFound, 0};
-  }
-
-  return {kSuccess, it->second};
-}
-
-void CdEntryMapZip64::ResetIteration() {
-  iterator_ = entry_table_.begin();
-}
-
-std::pair<std::string_view, uint64_t> CdEntryMapZip64::Next(const uint8_t* /*cd_start*/) {
-  if (iterator_ == entry_table_.end()) {
-    return {};
-  }
-
-  return *iterator_++;
-}
diff --git a/libziparchive/zip_cd_entry_map.h b/libziparchive/zip_cd_entry_map.h
deleted file mode 100644
index 4957f75..0000000
--- a/libziparchive/zip_cd_entry_map.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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 <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string_view>
-#include <utility>
-
-#include "zip_error.h"
-
-// This class is the interface of the central directory entries map. The map
-// helps to locate a particular cd entry based on the filename.
-class CdEntryMapInterface {
- public:
-  virtual ~CdEntryMapInterface() = default;
-  // Adds an entry to the map. The |name| should internally points to the
-  // filename field of a cd entry. And |start| points to the beginning of the
-  // central directory. Returns 0 on success.
-  virtual ZipError AddToMap(std::string_view name, const uint8_t* start) = 0;
-  // For the zip entry |entryName|, finds the offset of its filename field in
-  // the central directory. Returns a pair of [status, offset]. The value of
-  // the status is 0 on success.
-  virtual std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
-                                                         const uint8_t* cd_start) const = 0;
-  // Resets the iterator to the beginning of the map.
-  virtual void ResetIteration() = 0;
-  // Returns the [name, cd offset] of the current element. Also increments the
-  // iterator to points to the next element. Returns an empty pair we have read
-  // past boundary.
-  virtual std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) = 0;
-};
-
-/**
- * More space efficient string representation of strings in an mmaped zipped
- * file than std::string_view. Using std::string_view as an entry in the
- * ZipArchive hash table wastes space. std::string_view stores a pointer to a
- * string (on 64 bit, 8 bytes) and the length to read from that pointer,
- * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
- * 6 bytes.
- *
- * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
- * mapped file instead of the entire address, consuming 8 bytes with alignment.
- */
-struct ZipStringOffset {
-  uint32_t name_offset;
-  uint16_t name_length;
-
-  const std::string_view ToStringView(const uint8_t* start) const {
-    return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
-  }
-};
-
-// This implementation of CdEntryMap uses an array hash table. It uses less
-// memory than std::map; and it's used as the default implementation for zip
-// archives without zip64 extension.
-class CdEntryMapZip32 : public CdEntryMapInterface {
- public:
-  static std::unique_ptr<CdEntryMapInterface> Create(uint16_t num_entries);
-
-  ZipError AddToMap(std::string_view name, const uint8_t* start) override;
-  std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
-                                                 const uint8_t* cd_start) const override;
-  void ResetIteration() override;
-  std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) override;
-
- private:
-  explicit CdEntryMapZip32(uint16_t num_entries);
-
-  // We know how many entries are in the Zip archive, so we can have a
-  // fixed-size hash table. We define a load factor of 0.75 and over
-  // allocate so the maximum number entries can never be higher than
-  // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
-  uint32_t hash_table_size_{0};
-  std::unique_ptr<ZipStringOffset[], decltype(&free)> hash_table_{nullptr, free};
-
-  // The position of element for the current iteration.
-  uint32_t current_position_{0};
-};
-
-// This implementation of CdEntryMap uses a std::map
-class CdEntryMapZip64 : public CdEntryMapInterface {
- public:
-  static std::unique_ptr<CdEntryMapInterface> Create();
-
-  ZipError AddToMap(std::string_view name, const uint8_t* start) override;
-  std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
-                                                 const uint8_t* cd_start) const override;
-  void ResetIteration() override;
-  std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) override;
-
- private:
-  CdEntryMapZip64() = default;
-
-  std::map<std::string_view, uint64_t> entry_table_;
-
-  std::map<std::string_view, uint64_t>::iterator iterator_;
-};
diff --git a/libziparchive/zip_error.cpp b/libziparchive/zip_error.cpp
deleted file mode 100644
index 14e49bb..0000000
--- a/libziparchive/zip_error.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 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 "zip_error.h"
-
-#include <android-base/macros.h>
-
-static const char* kErrorMessages[] = {
-    "Success",
-    "Iteration ended",
-    "Zlib error",
-    "Invalid file",
-    "Invalid handle",
-    "Duplicate entries in archive",
-    "Empty archive",
-    "Entry not found",
-    "Invalid offset",
-    "Inconsistent information",
-    "Invalid entry name",
-    "I/O error",
-    "File mapping failed",
-    "Allocation failed",
-    "Unsupported zip entry size",
-};
-
-const char* ErrorCodeString(int32_t error_code) {
-  // Make sure that the number of entries in kErrorMessages and the ZipError
-  // enum match.
-  static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
-                "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
-
-  const uint32_t idx = -error_code;
-  if (idx < arraysize(kErrorMessages)) {
-    return kErrorMessages[idx];
-  }
-
-  return "Unknown return code";
-}
diff --git a/libziparchive/zip_error.h b/libziparchive/zip_error.h
deleted file mode 100644
index 3d7285d..0000000
--- a/libziparchive/zip_error.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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 <stdint.h>
-
-enum ZipError : int32_t {
-  kSuccess = 0,
-
-  kIterationEnd = -1,
-
-  // We encountered a Zlib error when inflating a stream from this file.
-  // Usually indicates file corruption.
-  kZlibError = -2,
-
-  // The input file cannot be processed as a zip archive. Usually because
-  // it's too small, too large or does not have a valid signature.
-  kInvalidFile = -3,
-
-  // An invalid iteration / ziparchive handle was passed in as an input
-  // argument.
-  kInvalidHandle = -4,
-
-  // The zip archive contained two (or possibly more) entries with the same
-  // name.
-  kDuplicateEntry = -5,
-
-  // The zip archive contains no entries.
-  kEmptyArchive = -6,
-
-  // The specified entry was not found in the archive.
-  kEntryNotFound = -7,
-
-  // The zip archive contained an invalid local file header pointer.
-  kInvalidOffset = -8,
-
-  // The zip archive contained inconsistent entry information. This could
-  // be because the central directory & local file header did not agree, or
-  // if the actual uncompressed length or crc32 do not match their declared
-  // values.
-  kInconsistentInformation = -9,
-
-  // An invalid entry name was encountered.
-  kInvalidEntryName = -10,
-
-  // An I/O related system call (read, lseek, ftruncate, map) failed.
-  kIoError = -11,
-
-  // We were not able to mmap the central directory or entry contents.
-  kMmapFailed = -12,
-
-  // An allocation failed.
-  kAllocationFailed = -13,
-
-  // The compressed or uncompressed size is larger than UINT32_MAX and
-  // doesn't fit into the 32 bits zip entry.
-  kUnsupportedEntrySize = -14,
-
-  kLastErrorCode = kUnsupportedEntrySize,
-};
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
deleted file mode 100644
index 25b1da4..0000000
--- a/libziparchive/zip_writer.cc
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ziparchive/zip_writer.h"
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <zlib.h>
-#include <cstdio>
-#define DEF_MEM_LEVEL 8  // normally in zutil.h?
-
-#include <memory>
-#include <vector>
-
-#include "android-base/logging.h"
-
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
-
-#undef powerof2
-#define powerof2(x)                                               \
-  ({                                                              \
-    __typeof__(x) _x = (x);                                       \
-    __typeof__(x) _x2;                                            \
-    __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
-  })
-
-/* Zip compression methods we support */
-enum {
-  kCompressStored = 0,    // no compression
-  kCompressDeflated = 8,  // standard deflate
-};
-
-// Size of the output buffer used for compression.
-static const size_t kBufSize = 32768u;
-
-// No error, operation completed successfully.
-static const int32_t kNoError = 0;
-
-// The ZipWriter is in a bad state.
-static const int32_t kInvalidState = -1;
-
-// There was an IO error while writing to disk.
-static const int32_t kIoError = -2;
-
-// The zip entry name was invalid.
-static const int32_t kInvalidEntryName = -3;
-
-// An error occurred in zlib.
-static const int32_t kZlibError = -4;
-
-// The start aligned function was called with the aligned flag.
-static const int32_t kInvalidAlign32Flag = -5;
-
-// The alignment parameter is not a power of 2.
-static const int32_t kInvalidAlignment = -6;
-
-static const char* sErrorCodes[] = {
-    "Invalid state", "IO error", "Invalid entry name", "Zlib error",
-};
-
-const char* ZipWriter::ErrorCodeString(int32_t error_code) {
-  if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
-    return sErrorCodes[-error_code];
-  }
-  return nullptr;
-}
-
-static void DeleteZStream(z_stream* stream) {
-  deflateEnd(stream);
-  delete stream;
-}
-
-ZipWriter::ZipWriter(FILE* f)
-    : file_(f),
-      seekable_(false),
-      current_offset_(0),
-      state_(State::kWritingZip),
-      z_stream_(nullptr, DeleteZStream),
-      buffer_(kBufSize) {
-  // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
-  // will fail as well.
-  struct stat file_stats;
-  if (fstat(fileno(f), &file_stats) == 0) {
-    seekable_ = S_ISREG(file_stats.st_mode);
-  }
-}
-
-ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
-    : file_(writer.file_),
-      seekable_(writer.seekable_),
-      current_offset_(writer.current_offset_),
-      state_(writer.state_),
-      files_(std::move(writer.files_)),
-      z_stream_(std::move(writer.z_stream_)),
-      buffer_(std::move(writer.buffer_)) {
-  writer.file_ = nullptr;
-  writer.state_ = State::kError;
-}
-
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
-  file_ = writer.file_;
-  seekable_ = writer.seekable_;
-  current_offset_ = writer.current_offset_;
-  state_ = writer.state_;
-  files_ = std::move(writer.files_);
-  z_stream_ = std::move(writer.z_stream_);
-  buffer_ = std::move(writer.buffer_);
-  writer.file_ = nullptr;
-  writer.state_ = State::kError;
-  return *this;
-}
-
-int32_t ZipWriter::HandleError(int32_t error_code) {
-  state_ = State::kError;
-  z_stream_.reset();
-  return error_code;
-}
-
-int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
-  uint32_t alignment = 0;
-  if (flags & kAlign32) {
-    flags &= ~kAlign32;
-    alignment = 4;
-  }
-  return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
-}
-
-int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
-  return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
-}
-
-int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
-  uint32_t alignment = 0;
-  if (flags & kAlign32) {
-    flags &= ~kAlign32;
-    alignment = 4;
-  }
-  return StartAlignedEntryWithTime(path, flags, time, alignment);
-}
-
-static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
-  /* round up to an even number of seconds */
-  when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
-
-  struct tm* ptm;
-#if !defined(_WIN32)
-  struct tm tm_result;
-  ptm = localtime_r(&when, &tm_result);
-#else
-  ptm = localtime(&when);
-#endif
-
-  int year = ptm->tm_year;
-  if (year < 80) {
-    year = 80;
-  }
-
-  *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
-  *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
-}
-
-static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
-                              LocalFileHeader* dst) {
-  dst->lfh_signature = LocalFileHeader::kSignature;
-  if (use_data_descriptor) {
-    // Set this flag to denote that a DataDescriptor struct will appear after the data,
-    // containing the crc and size fields.
-    dst->gpb_flags |= kGPBDDFlagMask;
-
-    // The size and crc fields must be 0.
-    dst->compressed_size = 0u;
-    dst->uncompressed_size = 0u;
-    dst->crc32 = 0u;
-  } else {
-    dst->compressed_size = src.compressed_size;
-    dst->uncompressed_size = src.uncompressed_size;
-    dst->crc32 = src.crc32;
-  }
-  dst->compression_method = src.compression_method;
-  dst->last_mod_time = src.last_mod_time;
-  dst->last_mod_date = src.last_mod_date;
-  DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
-  dst->file_name_length = static_cast<uint16_t>(src.path.size());
-  dst->extra_field_length = src.padding_length;
-}
-
-int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
-                                             uint32_t alignment) {
-  if (state_ != State::kWritingZip) {
-    return kInvalidState;
-  }
-
-  // Can only have 16535 entries because of zip records.
-  if (files_.size() == std::numeric_limits<uint16_t>::max()) {
-    return HandleError(kIoError);
-  }
-
-  if (flags & kAlign32) {
-    return kInvalidAlign32Flag;
-  }
-
-  if (powerof2(alignment) == 0) {
-    return kInvalidAlignment;
-  }
-  if (alignment > std::numeric_limits<uint16_t>::max()) {
-    return kInvalidAlignment;
-  }
-
-  FileEntry file_entry = {};
-  file_entry.local_file_header_offset = current_offset_;
-  file_entry.path = path;
-  // No support for larger than 4GB files.
-  if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) {
-    return HandleError(kIoError);
-  }
-
-  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
-                        file_entry.path.size())) {
-    return kInvalidEntryName;
-  }
-
-  if (flags & ZipWriter::kCompress) {
-    file_entry.compression_method = kCompressDeflated;
-
-    int32_t result = PrepareDeflate();
-    if (result != kNoError) {
-      return result;
-    }
-  } else {
-    file_entry.compression_method = kCompressStored;
-  }
-
-  ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
-
-  off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
-  // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
-  static constexpr auto kPageSize = 4096;
-  static constexpr char kSmallZeroPadding[kPageSize] = {};
-  // use this buffer if our preallocated one is too small
-  std::vector<char> zero_padding_big;
-  const char* zero_padding = nullptr;
-
-  if (alignment != 0 && (offset & (alignment - 1))) {
-    // Pad the extra field so the data will be aligned.
-    uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
-    file_entry.padding_length = padding;
-    offset += padding;
-    if (padding <= std::size(kSmallZeroPadding)) {
-        zero_padding = kSmallZeroPadding;
-    } else {
-        zero_padding_big.resize(padding, 0);
-        zero_padding = zero_padding_big.data();
-    }
-  }
-
-  LocalFileHeader header = {};
-  // Always start expecting a data descriptor. When the data has finished being written,
-  // if it is possible to seek back, the GPB flag will reset and the sizes written.
-  CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
-
-  if (fwrite(&header, sizeof(header), 1, file_) != 1) {
-    return HandleError(kIoError);
-  }
-
-  if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
-    return HandleError(kIoError);
-  }
-
-  if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
-                                               file_) != file_entry.padding_length) {
-    return HandleError(kIoError);
-  }
-
-  current_file_entry_ = std::move(file_entry);
-  current_offset_ = offset;
-  state_ = State::kWritingEntry;
-  return kNoError;
-}
-
-int32_t ZipWriter::DiscardLastEntry() {
-  if (state_ != State::kWritingZip || files_.empty()) {
-    return kInvalidState;
-  }
-
-  FileEntry& last_entry = files_.back();
-  current_offset_ = last_entry.local_file_header_offset;
-  if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
-    return HandleError(kIoError);
-  }
-  files_.pop_back();
-  return kNoError;
-}
-
-int32_t ZipWriter::GetLastEntry(FileEntry* out_entry) {
-  CHECK(out_entry != nullptr);
-
-  if (files_.empty()) {
-    return kInvalidState;
-  }
-  *out_entry = files_.back();
-  return kNoError;
-}
-
-int32_t ZipWriter::PrepareDeflate() {
-  CHECK(state_ == State::kWritingZip);
-
-  // Initialize the z_stream for compression.
-  z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-  int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
-                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
-#pragma GCC diagnostic pop
-
-  if (zerr != Z_OK) {
-    if (zerr == Z_VERSION_ERROR) {
-      LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
-      return HandleError(kZlibError);
-    } else {
-      LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
-      return HandleError(kZlibError);
-    }
-  }
-
-  z_stream_->next_out = buffer_.data();
-  DCHECK_EQ(buffer_.size(), kBufSize);
-  z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
-  return kNoError;
-}
-
-int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
-  if (state_ != State::kWritingEntry) {
-    return HandleError(kInvalidState);
-  }
-  // Need to be able to mark down data correctly.
-  if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) >
-      std::numeric_limits<uint32_t>::max()) {
-    return HandleError(kIoError);
-  }
-  uint32_t len32 = static_cast<uint32_t>(len);
-
-  int32_t result = kNoError;
-  if (current_file_entry_.compression_method & kCompressDeflated) {
-    result = CompressBytes(&current_file_entry_, data, len32);
-  } else {
-    result = StoreBytes(&current_file_entry_, data, len32);
-  }
-
-  if (result != kNoError) {
-    return result;
-  }
-
-  current_file_entry_.crc32 = static_cast<uint32_t>(
-      crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32));
-  current_file_entry_.uncompressed_size += len32;
-  return kNoError;
-}
-
-int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
-  CHECK(state_ == State::kWritingEntry);
-
-  if (fwrite(data, 1, len, file_) != len) {
-    return HandleError(kIoError);
-  }
-  file->compressed_size += len;
-  current_offset_ += len;
-  return kNoError;
-}
-
-int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
-  CHECK(state_ == State::kWritingEntry);
-  CHECK(z_stream_);
-  CHECK(z_stream_->next_out != nullptr);
-  CHECK(z_stream_->avail_out != 0);
-
-  // Prepare the input.
-  z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
-  z_stream_->avail_in = len;
-
-  while (z_stream_->avail_in > 0) {
-    // We have more data to compress.
-    int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
-    if (zerr != Z_OK) {
-      return HandleError(kZlibError);
-    }
-
-    if (z_stream_->avail_out == 0) {
-      // The output is full, let's write it to disk.
-      size_t write_bytes = z_stream_->next_out - buffer_.data();
-      if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
-        return HandleError(kIoError);
-      }
-      file->compressed_size += write_bytes;
-      current_offset_ += write_bytes;
-
-      // Reset the output buffer for the next input.
-      z_stream_->next_out = buffer_.data();
-      DCHECK_EQ(buffer_.size(), kBufSize);
-      z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
-    }
-  }
-  return kNoError;
-}
-
-int32_t ZipWriter::FlushCompressedBytes(FileEntry* file) {
-  CHECK(state_ == State::kWritingEntry);
-  CHECK(z_stream_);
-  CHECK(z_stream_->next_out != nullptr);
-  CHECK(z_stream_->avail_out != 0);
-
-  // Keep deflating while there isn't enough space in the buffer to
-  // to complete the compress.
-  int zerr;
-  while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
-    CHECK(z_stream_->avail_out == 0);
-    size_t write_bytes = z_stream_->next_out - buffer_.data();
-    if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
-      return HandleError(kIoError);
-    }
-    file->compressed_size += write_bytes;
-    current_offset_ += write_bytes;
-
-    z_stream_->next_out = buffer_.data();
-    DCHECK_EQ(buffer_.size(), kBufSize);
-    z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
-  }
-  if (zerr != Z_STREAM_END) {
-    return HandleError(kZlibError);
-  }
-
-  size_t write_bytes = z_stream_->next_out - buffer_.data();
-  if (write_bytes != 0) {
-    if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
-      return HandleError(kIoError);
-    }
-    file->compressed_size += write_bytes;
-    current_offset_ += write_bytes;
-  }
-  z_stream_.reset();
-  return kNoError;
-}
-
-bool ZipWriter::ShouldUseDataDescriptor() const {
-  // Only use a trailing "data descriptor" if the output isn't seekable.
-  return !seekable_;
-}
-
-int32_t ZipWriter::FinishEntry() {
-  if (state_ != State::kWritingEntry) {
-    return kInvalidState;
-  }
-
-  if (current_file_entry_.compression_method & kCompressDeflated) {
-    int32_t result = FlushCompressedBytes(&current_file_entry_);
-    if (result != kNoError) {
-      return result;
-    }
-  }
-
-  if (ShouldUseDataDescriptor()) {
-    // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
-    // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
-    // We haven't supported zip64 format yet. Write both uncompressed size and compressed
-    // size as uint32_t.
-    std::vector<uint32_t> dataDescriptor = {
-        DataDescriptor::kOptSignature, current_file_entry_.crc32,
-        current_file_entry_.compressed_size, current_file_entry_.uncompressed_size};
-    if (fwrite(dataDescriptor.data(), dataDescriptor.size() * sizeof(uint32_t), 1, file_) != 1) {
-      return HandleError(kIoError);
-    }
-
-    current_offset_ += sizeof(uint32_t) * dataDescriptor.size();
-  } else {
-    // Seek back to the header and rewrite to include the size.
-    if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
-      return HandleError(kIoError);
-    }
-
-    LocalFileHeader header = {};
-    CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
-
-    if (fwrite(&header, sizeof(header), 1, file_) != 1) {
-      return HandleError(kIoError);
-    }
-
-    if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
-      return HandleError(kIoError);
-    }
-  }
-
-  files_.emplace_back(std::move(current_file_entry_));
-  state_ = State::kWritingZip;
-  return kNoError;
-}
-
-int32_t ZipWriter::Finish() {
-  if (state_ != State::kWritingZip) {
-    return kInvalidState;
-  }
-
-  off_t startOfCdr = current_offset_;
-  for (FileEntry& file : files_) {
-    CentralDirectoryRecord cdr = {};
-    cdr.record_signature = CentralDirectoryRecord::kSignature;
-    if (ShouldUseDataDescriptor()) {
-      cdr.gpb_flags |= kGPBDDFlagMask;
-    }
-    cdr.compression_method = file.compression_method;
-    cdr.last_mod_time = file.last_mod_time;
-    cdr.last_mod_date = file.last_mod_date;
-    cdr.crc32 = file.crc32;
-    cdr.compressed_size = file.compressed_size;
-    cdr.uncompressed_size = file.uncompressed_size;
-    // Checked in IsValidEntryName.
-    DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max());
-    cdr.file_name_length = static_cast<uint16_t>(file.path.size());
-    // Checked in StartAlignedEntryWithTime.
-    DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max());
-    cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
-    if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
-      return HandleError(kIoError);
-    }
-
-    if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
-      return HandleError(kIoError);
-    }
-
-    current_offset_ += sizeof(cdr) + file.path.size();
-  }
-
-  EocdRecord er = {};
-  er.eocd_signature = EocdRecord::kSignature;
-  er.disk_num = 0;
-  er.cd_start_disk = 0;
-  // Checked when adding entries.
-  DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max());
-  er.num_records_on_disk = static_cast<uint16_t>(files_.size());
-  er.num_records = static_cast<uint16_t>(files_.size());
-  if (current_offset_ > std::numeric_limits<uint32_t>::max()) {
-    return HandleError(kIoError);
-  }
-  er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr);
-  er.cd_start_offset = static_cast<uint32_t>(startOfCdr);
-
-  if (fwrite(&er, sizeof(er), 1, file_) != 1) {
-    return HandleError(kIoError);
-  }
-
-  current_offset_ += sizeof(er);
-
-  // Since we can BackUp() and potentially finish writing at an offset less than one we had
-  // already written at, we must truncate the file.
-
-  if (ftruncate(fileno(file_), current_offset_) != 0) {
-    return HandleError(kIoError);
-  }
-
-  if (fflush(file_) != 0) {
-    return HandleError(kIoError);
-  }
-
-  state_ = State::kDone;
-  return kNoError;
-}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
deleted file mode 100644
index d324d4b..0000000
--- a/libziparchive/zip_writer_test.cc
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ziparchive/zip_writer.h"
-#include "ziparchive/zip_archive.h"
-
-#include <android-base/test_utils.h>
-#include <gtest/gtest.h>
-#include <time.h>
-#include <memory>
-#include <vector>
-
-static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
-                                                            ZipArchiveHandle handle,
-                                                            ZipEntry* zip_entry);
-
-struct zipwriter : public ::testing::Test {
-  TemporaryFile* temp_file_;
-  int fd_;
-  FILE* file_;
-
-  void SetUp() override {
-    temp_file_ = new TemporaryFile();
-    fd_ = temp_file_->fd;
-    file_ = fdopen(fd_, "w");
-    ASSERT_NE(file_, nullptr);
-  }
-
-  void TearDown() override {
-    fclose(file_);
-    delete temp_file_;
-  }
-};
-
-TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
-  ZipWriter writer(file_);
-
-  const char* expected = "hello";
-
-  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes("he", 2));
-  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
-  EXPECT_EQ(kCompressStored, data.method);
-  EXPECT_EQ(0u, data.has_data_descriptor);
-  EXPECT_EQ(strlen(expected), data.compressed_length);
-  ASSERT_EQ(strlen(expected), data.uncompressed_length);
-  ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
-  ZipWriter writer(file_);
-
-  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes("he", 2));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
-  EXPECT_EQ(kCompressStored, data.method);
-  EXPECT_EQ(2u, data.compressed_length);
-  ASSERT_EQ(2u, data.uncompressed_length);
-  ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
-
-  ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
-  EXPECT_EQ(kCompressStored, data.method);
-  EXPECT_EQ(3u, data.compressed_length);
-  ASSERT_EQ(3u, data.uncompressed_length);
-  ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
-
-  ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
-  EXPECT_EQ(kCompressStored, data.method);
-  EXPECT_EQ(0u, data.compressed_length);
-  EXPECT_EQ(0u, data.uncompressed_length);
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
-  ZipWriter writer(file_);
-
-  ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
-  ASSERT_EQ(0, writer.WriteBytes("he", 2));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
-  EXPECT_EQ(0, data.offset & 0x03);
-
-  CloseArchive(handle);
-}
-
-static struct tm MakeTm() {
-  struct tm tm;
-  memset(&tm, 0, sizeof(struct tm));
-  tm.tm_year = 2001 - 1900;
-  tm.tm_mon = 1;
-  tm.tm_mday = 12;
-  tm.tm_hour = 18;
-  tm.tm_min = 30;
-  tm.tm_sec = 20;
-  return tm;
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
-  ZipWriter writer(file_);
-
-  struct tm tm = MakeTm();
-  time_t time = mktime(&tm);
-  ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
-  ASSERT_EQ(0, writer.WriteBytes("he", 2));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
-  EXPECT_EQ(0, data.offset & 0x03);
-
-  struct tm mod = data.GetModificationTime();
-  EXPECT_EQ(tm.tm_sec, mod.tm_sec);
-  EXPECT_EQ(tm.tm_min, mod.tm_min);
-  EXPECT_EQ(tm.tm_hour, mod.tm_hour);
-  EXPECT_EQ(tm.tm_mday, mod.tm_mday);
-  EXPECT_EQ(tm.tm_mon, mod.tm_mon);
-  EXPECT_EQ(tm.tm_year, mod.tm_year);
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
-  ZipWriter writer(file_);
-
-  ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
-  ASSERT_EQ(0, writer.WriteBytes("he", 2));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
-  EXPECT_EQ(0, data.offset & 0xfff);
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
-  ZipWriter writer(file_);
-
-  struct tm tm = MakeTm();
-  time_t time = mktime(&tm);
-  ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
-  ASSERT_EQ(0, writer.WriteBytes("he", 2));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
-  EXPECT_EQ(0, data.offset & 0xfff);
-
-  struct tm mod = data.GetModificationTime();
-  EXPECT_EQ(tm.tm_sec, mod.tm_sec);
-  EXPECT_EQ(tm.tm_min, mod.tm_min);
-  EXPECT_EQ(tm.tm_hour, mod.tm_hour);
-  EXPECT_EQ(tm.tm_mday, mod.tm_mday);
-  EXPECT_EQ(tm.tm_mon, mod.tm_mon);
-  EXPECT_EQ(tm.tm_year, mod.tm_year);
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
-  ZipWriter writer(file_);
-
-  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
-  ASSERT_EQ(0, writer.WriteBytes("helo", 4));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
-  EXPECT_EQ(kCompressDeflated, data.method);
-  EXPECT_EQ(0u, data.has_data_descriptor);
-  ASSERT_EQ(4u, data.uncompressed_length);
-  ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteCompressedZipFlushFull) {
-  // This exact data will cause the Finish() to require multiple calls
-  // to deflate() because the ZipWriter buffer isn't big enough to hold
-  // the entire compressed data buffer.
-  constexpr size_t kBufSize = 10000000;
-  std::vector<uint8_t> buffer(kBufSize);
-  size_t prev = 1;
-  for (size_t i = 0; i < kBufSize; i++) {
-    buffer[i] = static_cast<uint8_t>(i + prev);
-    prev = i;
-  }
-
-  ZipWriter writer(file_);
-  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
-  ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
-  EXPECT_EQ(kCompressDeflated, data.method);
-  EXPECT_EQ(kBufSize, data.uncompressed_length);
-
-  std::vector<uint8_t> decompress(kBufSize);
-  memset(decompress.data(), 0, kBufSize);
-  ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
-                               static_cast<uint32_t>(decompress.size())));
-  EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
-      << "Input buffer and output buffer are different.";
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, CheckStartEntryErrors) {
-  ZipWriter writer(file_);
-
-  ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
-  ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
-}
-
-TEST_F(zipwriter, BackupRemovesTheLastFile) {
-  ZipWriter writer(file_);
-
-  const char* kKeepThis = "keep this";
-  const char* kDropThis = "drop this";
-  const char* kReplaceWithThis = "replace with this";
-
-  ZipWriter::FileEntry entry;
-  EXPECT_LT(writer.GetLastEntry(&entry), 0);
-
-  ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  ASSERT_EQ(0, writer.GetLastEntry(&entry));
-  EXPECT_EQ("keep.txt", entry.path);
-
-  ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  ASSERT_EQ(0, writer.GetLastEntry(&entry));
-  EXPECT_EQ("drop.txt", entry.path);
-
-  ASSERT_EQ(0, writer.DiscardLastEntry());
-
-  ASSERT_EQ(0, writer.GetLastEntry(&entry));
-  EXPECT_EQ("keep.txt", entry.path);
-
-  ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  ASSERT_EQ(0, writer.GetLastEntry(&entry));
-  EXPECT_EQ("replace.txt", entry.path);
-
-  ASSERT_EQ(0, writer.Finish());
-
-  // Verify that "drop.txt" does not exist.
-
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
-  ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
-
-  ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
-
-  ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
-  ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
-
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteToUnseekableFile) {
-  const char* expected = "hello";
-  ZipWriter writer(file_);
-  writer.seekable_ = false;
-
-  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
-  EXPECT_EQ(kCompressStored, data.method);
-  EXPECT_EQ(1u, data.has_data_descriptor);
-  EXPECT_EQ(strlen(expected), data.compressed_length);
-  ASSERT_EQ(strlen(expected), data.uncompressed_length);
-  ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
-  CloseArchive(handle);
-}
-
-TEST_F(zipwriter, TruncateFileAfterBackup) {
-  ZipWriter writer(file_);
-
-  const char* kSmall = "small";
-
-  ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
-  std::vector<uint8_t> data;
-  data.resize(1024 * 1024, 0xef);
-  ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
-  ASSERT_EQ(0, writer.FinishEntry());
-
-  off_t before_len = ftello(file_);
-
-  ZipWriter::FileEntry entry;
-  ASSERT_EQ(0, writer.GetLastEntry(&entry));
-  ASSERT_EQ(0, writer.DiscardLastEntry());
-
-  ASSERT_EQ(0, writer.Finish());
-
-  off_t after_len = ftello(file_);
-
-  ASSERT_GT(before_len, after_len);
-}
-
-static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
-                                                            ZipArchiveHandle handle,
-                                                            ZipEntry* zip_entry) {
-  if (expected.size() != zip_entry->uncompressed_length) {
-    return ::testing::AssertionFailure()
-           << "uncompressed entry size " << zip_entry->uncompressed_length
-           << " does not match expected size " << expected.size();
-  }
-
-  std::string actual;
-  actual.resize(expected.size());
-
-  uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
-  if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
-    return ::testing::AssertionFailure() << "failed to extract entry";
-  }
-
-  if (expected != actual) {
-    return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
-                                         << "' does not match expected '" << expected << "'";
-  }
-  return ::testing::AssertionSuccess();
-}
diff --git a/libziparchive/ziptool-tests.xml b/libziparchive/ziptool-tests.xml
deleted file mode 100644
index 211119f..0000000
--- a/libziparchive/ziptool-tests.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 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.
--->
-<configuration description="Config for running ziptool-tests through Atest or in Infra">
-    <option name="test-suite-tag" value="ziptool-tests" />
-    <!-- This test requires a device, so it's not annotated with a null-device. -->
-    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
-        <option name="binary" value="run-ziptool-tests-on-android.sh" />
-        <!-- Test script assumes a relative path with the cli-tests/ folders. -->
-        <option name="relative-path-execution" value="true" />
-        <!-- Tests shouldn't be that long but set 15m to be safe. -->
-        <option name="per-binary-timeout" value="15m" />
-    </test>
-</configuration>
diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp
deleted file mode 100644
index a261535..0000000
--- a/libziparchive/ziptool.cpp
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright (C) 2017 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 <fcntl.h>
-#include <fnmatch.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <set>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <ziparchive/zip_archive.h>
-
-using android::base::EndsWith;
-using android::base::StartsWith;
-
-enum OverwriteMode {
-  kAlways,
-  kNever,
-  kPrompt,
-};
-
-enum Role {
-  kUnzip,
-  kZipinfo,
-};
-
-static Role role;
-static OverwriteMode overwrite_mode = kPrompt;
-static bool flag_1 = false;
-static std::string flag_d;
-static bool flag_l = false;
-static bool flag_p = false;
-static bool flag_q = false;
-static bool flag_v = false;
-static bool flag_x = false;
-static const char* archive_name = nullptr;
-static std::set<std::string> includes;
-static std::set<std::string> excludes;
-static uint64_t total_uncompressed_length = 0;
-static uint64_t total_compressed_length = 0;
-static size_t file_count = 0;
-
-static const char* g_progname;
-
-static void die(int error, const char* fmt, ...) {
-  va_list ap;
-
-  va_start(ap, fmt);
-  fprintf(stderr, "%s: ", g_progname);
-  vfprintf(stderr, fmt, ap);
-  if (error != 0) fprintf(stderr, ": %s", strerror(error));
-  fprintf(stderr, "\n");
-  va_end(ap);
-  exit(1);
-}
-
-static bool ShouldInclude(const std::string& name) {
-  // Explicitly excluded?
-  if (!excludes.empty()) {
-    for (const auto& exclude : excludes) {
-      if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
-    }
-  }
-
-  // Implicitly included?
-  if (includes.empty()) return true;
-
-  // Explicitly included?
-  for (const auto& include : includes) {
-    if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
-  }
-  return false;
-}
-
-static bool MakeDirectoryHierarchy(const std::string& path) {
-  // stat rather than lstat because a symbolic link to a directory is fine too.
-  struct stat sb;
-  if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
-
-  // Ensure the parent directories exist first.
-  if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
-
-  // Then try to create this directory.
-  return (mkdir(path.c_str(), 0777) != -1);
-}
-
-static float CompressionRatio(int64_t uncompressed, int64_t compressed) {
-  if (uncompressed == 0) return 0;
-  return static_cast<float>(100LL * (uncompressed - compressed)) /
-         static_cast<float>(uncompressed);
-}
-
-static void MaybeShowHeader(ZipArchiveHandle zah) {
-  if (role == kUnzip) {
-    // unzip has three formats.
-    if (!flag_q) printf("Archive:  %s\n", archive_name);
-    if (flag_v) {
-      printf(
-          " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
-          "--------  ------  ------- ---- ---------- ----- --------  ----\n");
-    } else if (flag_l) {
-      printf(
-          "  Length      Date    Time    Name\n"
-          "---------  ---------- -----   ----\n");
-    }
-  } else {
-    // zipinfo.
-    if (!flag_1 && includes.empty() && excludes.empty()) {
-      ZipArchiveInfo info{GetArchiveInfo(zah)};
-      printf("Archive:  %s\n", archive_name);
-      printf("Zip file size: %" PRId64 " bytes, number of entries: %" PRIu64 "\n",
-             info.archive_size, info.entry_count);
-    }
-  }
-}
-
-static void MaybeShowFooter() {
-  if (role == kUnzip) {
-    if (flag_v) {
-      printf(
-          "--------          -------  ---                            -------\n"
-          "%8" PRId64 "         %8" PRId64 " %3.0f%%                            %zu file%s\n",
-          total_uncompressed_length, total_compressed_length,
-          CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
-          (file_count == 1) ? "" : "s");
-    } else if (flag_l) {
-      printf(
-          "---------                     -------\n"
-          "%9" PRId64 "                     %zu file%s\n",
-          total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
-    }
-  } else {
-    if (!flag_1 && includes.empty() && excludes.empty()) {
-      printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed:  %.1f%%\n",
-             file_count, total_uncompressed_length, total_compressed_length,
-             CompressionRatio(total_uncompressed_length, total_compressed_length));
-    }
-  }
-}
-
-static bool PromptOverwrite(const std::string& dst) {
-  // TODO: [r]ename not implemented because it doesn't seem useful.
-  printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
-  fflush(stdout);
-  while (true) {
-    char* line = nullptr;
-    size_t n;
-    if (getline(&line, &n, stdin) == -1) {
-      die(0, "(EOF/read error; assuming [N]one...)");
-      overwrite_mode = kNever;
-      return false;
-    }
-    if (n == 0) continue;
-    char cmd = line[0];
-    free(line);
-    switch (cmd) {
-      case 'y':
-        return true;
-      case 'n':
-        return false;
-      case 'A':
-        overwrite_mode = kAlways;
-        return true;
-      case 'N':
-        overwrite_mode = kNever;
-        return false;
-    }
-  }
-}
-
-static void ExtractToPipe(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
-  // We need to extract to memory because ExtractEntryToFile insists on
-  // being able to seek and truncate, and you can't do that with stdout.
-  if (entry.uncompressed_length > SIZE_MAX) {
-    die(0, "entry size %" PRIu64 " is too large to extract.", entry.uncompressed_length);
-  }
-  auto uncompressed_length = static_cast<size_t>(entry.uncompressed_length);
-  uint8_t* buffer = new uint8_t[uncompressed_length];
-  int err = ExtractToMemory(zah, &entry, buffer, uncompressed_length);
-  if (err < 0) {
-    die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
-  }
-  if (!android::base::WriteFully(1, buffer, uncompressed_length)) {
-    die(errno, "failed to write %s to stdout", name.c_str());
-  }
-  delete[] buffer;
-}
-
-static void ExtractOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
-  // Bad filename?
-  if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
-    die(0, "bad filename %s", name.c_str());
-  }
-
-  // Where are we actually extracting to (for human-readable output)?
-  // flag_d is the empty string if -d wasn't used, or has a trailing '/'
-  // otherwise.
-  std::string dst = flag_d + name;
-
-  // Ensure the directory hierarchy exists.
-  if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
-    die(errno, "couldn't create directory hierarchy for %s", dst.c_str());
-  }
-
-  // An entry in a zip file can just be a directory itself.
-  if (EndsWith(name, "/")) {
-    if (mkdir(name.c_str(), entry.unix_mode) == -1) {
-      // If the directory already exists, that's fine.
-      if (errno == EEXIST) {
-        struct stat sb;
-        if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
-      }
-      die(errno, "couldn't extract directory %s", dst.c_str());
-    }
-    return;
-  }
-
-  // Create the file.
-  int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
-  if (fd == -1 && errno == EEXIST) {
-    if (overwrite_mode == kNever) return;
-    if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
-    // Either overwrite_mode is kAlways or the user consented to this specific case.
-    fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
-  }
-  if (fd == -1) die(errno, "couldn't create file %s", dst.c_str());
-
-  // Actually extract into the file.
-  if (!flag_q) printf("  inflating: %s\n", dst.c_str());
-  int err = ExtractEntryToFile(zah, &entry, fd);
-  if (err < 0) die(0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
-  close(fd);
-}
-
-static void ListOne(const ZipEntry64& entry, const std::string& name) {
-  tm t = entry.GetModificationTime();
-  char time[32];
-  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
-           t.tm_mday, t.tm_hour, t.tm_min);
-  if (flag_v) {
-    printf("%8" PRIu64 "  %s %8" PRIu64 " %3.0f%% %s %08x  %s\n", entry.uncompressed_length,
-           (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
-           CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
-           name.c_str());
-  } else {
-    printf("%9" PRIu64 "  %s   %s\n", entry.uncompressed_length, time, name.c_str());
-  }
-}
-
-static void InfoOne(const ZipEntry64& entry, const std::string& name) {
-  if (flag_1) {
-    // "android-ndk-r19b/sources/android/NOTICE"
-    printf("%s\n", name.c_str());
-    return;
-  }
-
-  int version = entry.version_made_by & 0xff;
-  int os = (entry.version_made_by >> 8) & 0xff;
-
-  // TODO: Support suid/sgid? Non-Unix/non-FAT host file system attributes?
-  const char* src_fs = "???";
-  char mode[] = "???       ";
-  if (os == 0) {
-    src_fs = "fat";
-    // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
-    int attrs = entry.external_file_attributes & 0xff;
-    mode[0] = (attrs & 0x10) ? 'd' : '-';
-    mode[1] = 'r';
-    mode[2] = (attrs & 0x01) ? '-' : 'w';
-    // The man page also mentions ".btm", but that seems to be obsolete?
-    mode[3] = EndsWith(name, ".exe") || EndsWith(name, ".com") || EndsWith(name, ".bat") ||
-                      EndsWith(name, ".cmd")
-                  ? 'x'
-                  : '-';
-    mode[4] = (attrs & 0x20) ? 'a' : '-';
-    mode[5] = (attrs & 0x02) ? 'h' : '-';
-    mode[6] = (attrs & 0x04) ? 's' : '-';
-  } else if (os == 3) {
-    src_fs = "unx";
-    mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
-    mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
-    mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
-    mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
-    mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
-    mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
-    mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
-    mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
-    mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
-    mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
-  }
-
-  char method[5] = "stor";
-  if (entry.method == kCompressDeflated) {
-    snprintf(method, sizeof(method), "def%c", "NXFS"[(entry.gpbf >> 1) & 0x3]);
-  }
-
-  // TODO: zipinfo (unlike unzip) sometimes uses time zone?
-  // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
-  tm t = entry.GetModificationTime();
-  char time[32];
-  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
-           t.tm_mday, t.tm_hour, t.tm_min);
-
-  // "-rw-r--r--  3.0 unx      577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
-  printf("%s %2d.%d %s %8" PRIu64 " %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
-         entry.uncompressed_length, entry.is_text ? 't' : 'b',
-         entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
-}
-
-static void ProcessOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
-  if (role == kUnzip) {
-    if (flag_l || flag_v) {
-      // -l or -lv or -lq or -v.
-      ListOne(entry, name);
-    } else {
-      // Actually extract.
-      if (flag_p) {
-        ExtractToPipe(zah, entry, name);
-      } else {
-        ExtractOne(zah, entry, name);
-      }
-    }
-  } else {
-    // zipinfo or zipinfo -1.
-    InfoOne(entry, name);
-  }
-  total_uncompressed_length += entry.uncompressed_length;
-  total_compressed_length += entry.compressed_length;
-  ++file_count;
-}
-
-static void ProcessAll(ZipArchiveHandle zah) {
-  MaybeShowHeader(zah);
-
-  // libziparchive iteration order doesn't match the central directory.
-  // We could sort, but that would cost extra and wouldn't match either.
-  void* cookie;
-  int err = StartIteration(zah, &cookie);
-  if (err != 0) {
-    die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
-  }
-
-  ZipEntry64 entry;
-  std::string name;
-  while ((err = Next(cookie, &entry, &name)) >= 0) {
-    if (ShouldInclude(name)) ProcessOne(zah, entry, name);
-  }
-
-  if (err < -1) die(0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
-  EndIteration(cookie);
-
-  MaybeShowFooter();
-}
-
-static void ShowHelp(bool full) {
-  if (role == kUnzip) {
-    fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
-    if (!full) exit(EXIT_FAILURE);
-
-    printf(
-        "\n"
-        "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
-        "exclude (-x) lists use shell glob patterns.\n"
-        "\n"
-        "-d DIR	Extract into DIR\n"
-        "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
-        "-n	Never overwrite files (default: prompt)\n"
-        "-o	Always overwrite files\n"
-        "-p	Pipe to stdout\n"
-        "-q	Quiet\n"
-        "-v	List contents verbosely\n"
-        "-x FILE	Exclude files\n");
-  } else {
-    fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
-    if (!full) exit(EXIT_FAILURE);
-
-    printf(
-        "\n"
-        "Show information about FILEs from ZIP archive. Default is all files.\n"
-        "Both the include and exclude (-x) lists use shell glob patterns.\n"
-        "\n"
-        "-1	Show filenames only, one per line\n"
-        "-x FILE	Exclude files\n");
-  }
-  exit(EXIT_SUCCESS);
-}
-
-static void HandleCommonOption(int opt) {
-  switch (opt) {
-    case 'h':
-      ShowHelp(true);
-      break;
-    case 'x':
-      flag_x = true;
-      break;
-    case 1:
-      // -x swallows all following arguments, so we use '-' in the getopt
-      // string and collect files here.
-      if (!archive_name) {
-        archive_name = optarg;
-      } else if (flag_x) {
-        excludes.insert(optarg);
-      } else {
-        includes.insert(optarg);
-      }
-      break;
-    default:
-      ShowHelp(false);
-      break;
-  }
-}
-
-int main(int argc, char* argv[]) {
-  // Who am I, and what am I doing?
-  g_progname = basename(argv[0]);
-  if (!strcmp(g_progname, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
-  if (!strcmp(g_progname, "unzip")) {
-    role = kUnzip;
-  } else if (!strcmp(g_progname, "zipinfo")) {
-    role = kZipinfo;
-  } else {
-    die(0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
-  }
-
-  static const struct option opts[] = {
-      {"help", no_argument, 0, 'h'},
-      {},
-  };
-
-  if (role == kUnzip) {
-    // `unzip -Z` is "zipinfo mode", so in that case just restart...
-    if (argc > 1 && !strcmp(argv[1], "-Z")) {
-      argv[1] = const_cast<char*>("zipinfo");
-      return main(argc - 1, argv + 1);
-    }
-
-    int opt;
-    while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
-      switch (opt) {
-        case 'd':
-          flag_d = optarg;
-          if (!EndsWith(flag_d, "/")) flag_d += '/';
-          break;
-        case 'l':
-          flag_l = true;
-          break;
-        case 'n':
-          overwrite_mode = kNever;
-          break;
-        case 'o':
-          overwrite_mode = kAlways;
-          break;
-        case 'p':
-          flag_p = flag_q = true;
-          break;
-        case 'q':
-          flag_q = true;
-          break;
-        case 'v':
-          flag_v = true;
-          break;
-        default:
-          HandleCommonOption(opt);
-          break;
-      }
-    }
-  } else {
-    int opt;
-    while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
-      switch (opt) {
-        case '1':
-          flag_1 = true;
-          break;
-        default:
-          HandleCommonOption(opt);
-          break;
-      }
-    }
-  }
-
-  if (!archive_name) die(0, "missing archive filename");
-
-  // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
-  ZipArchiveHandle zah;
-  int32_t err;
-  if ((err = OpenArchive(archive_name, &zah)) != 0) {
-    die(0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
-  }
-
-  // Implement -d by changing into that directory.
-  // We'll create implicit directories based on paths in the zip file, and we'll create
-  // the -d directory itself, but we require that *parents* of the -d directory already exists.
-  // This is pretty arbitrary, but it's the behavior of the original unzip.
-  if (!flag_d.empty()) {
-    if (mkdir(flag_d.c_str(), 0777) == -1 && errno != EEXIST) {
-      die(errno, "couldn't created %s", flag_d.c_str());
-    }
-    if (chdir(flag_d.c_str()) == -1) {
-      die(errno, "couldn't chdir to %s", flag_d.c_str());
-    }
-  }
-
-  ProcessAll(zah);
-
-  CloseArchive(zah);
-  return 0;
-}
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 3a55c4e..f3fdfd7 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -475,8 +475,8 @@
             continue;
         }
 
-        log_time tx((const char*)&t);
-        if (ts == tx) {
+        log_time* tx = reinterpret_cast<log_time*>(&t);
+        if (ts == *tx) {
             ++count;
         }
     }
@@ -521,8 +521,8 @@
                 continue;
             }
 
-            log_time tx((const char*)&t);
-            if (ts == tx) {
+            log_time* tx = reinterpret_cast<log_time*>(&t);
+            if (ts == *tx) {
                 ++count;
             }
         }
diff --git a/logd/Android.bp b/logd/Android.bp
index d203062..7e58f07 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -28,46 +28,53 @@
     "-DLIBLOG_LOG_TAG=1006",
 ]
 
-cc_library_static {
-    name: "liblogd",
-
-    srcs: [
-        "ChattyLogBuffer.cpp",
-        "CommandListener.cpp",
-        "LogListener.cpp",
-        "LogPermissions.cpp",
-        "LogReader.cpp",
-        "LogReaderList.cpp",
-        "LogReaderThread.cpp",
-        "LogBufferElement.cpp",
-        "LogStatistics.cpp",
-        "LogWhiteBlackList.cpp",
-        "libaudit.c",
-        "LogAudit.cpp",
-        "LogKlog.cpp",
-        "LogTags.cpp",
-    ],
-    logtags: ["event.logtags"],
+cc_defaults {
+    name: "logd_defaults",
 
     shared_libs: ["libbase"],
-
-    export_include_dirs: ["."],
-
     cflags: [
         "-Wextra",
         "-Wthread-safety",
     ] + event_flag,
 
     lto: {
-        thin: true
-    }
+        thin: true,
+    },
+}
+
+cc_library_static {
+    name: "liblogd",
+    defaults: ["logd_defaults"],
+    host_supported: true,
+    srcs: [
+        "ChattyLogBuffer.cpp",
+        "LogReaderList.cpp",
+        "LogReaderThread.cpp",
+        "LogBufferElement.cpp",
+        "LogStatistics.cpp",
+        "LogWhiteBlackList.cpp",
+        "LogTags.cpp",
+    ],
+    logtags: ["event.logtags"],
+
+    export_include_dirs: ["."],
 }
 
 cc_binary {
     name: "logd",
+    defaults: ["logd_defaults"],
     init_rc: ["logd.rc"],
 
-    srcs: ["main.cpp"],
+    srcs: [
+        "main.cpp",
+        "LogPermissions.cpp",
+        "CommandListener.cpp",
+        "LogListener.cpp",
+        "LogReader.cpp",
+        "LogAudit.cpp",
+        "LogKlog.cpp",
+        "libaudit.cpp",
+    ],
 
     static_libs: [
         "liblog",
@@ -77,34 +84,23 @@
     shared_libs: [
         "libsysutils",
         "libcutils",
-        "libbase",
         "libpackagelistparser",
         "libprocessgroup",
         "libcap",
     ],
-
-    cflags: [
-        "-Wextra",
-    ],
-
-    lto: {
-        thin: true
-    }
 }
 
 cc_binary {
     name: "auditctl",
 
-    srcs: ["auditctl.cpp"],
-
-    static_libs: [
-        "liblogd",
+    srcs: [
+        "auditctl.cpp",
+        "libaudit.cpp",
     ],
 
     shared_libs: ["libbase"],
 
     cflags: [
-        "-Wconversion",
         "-Wextra",
     ],
 }
@@ -114,3 +110,61 @@
     src: "logtagd.rc",
     sub_dir: "init",
 }
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+    name: "logd-unit-test-defaults",
+
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-fno-builtin",
+
+        "-DAUDITD_LOG_TAG=1003",
+        "-DCHATTY_LOG_TAG=1004",
+    ],
+
+    srcs: [
+        "logd_test.cpp",
+        "LogBufferTest.cpp",
+    ],
+
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "liblogd",
+        "libselinux",
+    ],
+}
+
+// Build tests for the logger. Run with:
+//   adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+    name: "logd-unit-tests",
+    host_supported: true,
+    defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+    name: "CtsLogdTestCases",
+    defaults: ["logd-unit-test-defaults"],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    test_suites: [
+        "cts",
+        "vts10",
+    ],
+}
diff --git a/logd/tests/AndroidTest.xml b/logd/AndroidTest.xml
similarity index 100%
rename from logd/tests/AndroidTest.xml
rename to logd/AndroidTest.xml
diff --git a/logd/ChattyLogBuffer.cpp b/logd/ChattyLogBuffer.cpp
index c6c9a7c..9e08e9d 100644
--- a/logd/ChattyLogBuffer.cpp
+++ b/logd/ChattyLogBuffer.cpp
@@ -426,31 +426,14 @@
 // Define a temporary mechanism to report the last LogBufferElement pointer
 // for the specified uid, pid and tid. Used below to help merge-sort when
 // pruning for worst UID.
-class LogBufferElementKey {
-    const union {
-        struct {
-            uint32_t uid;
-            uint16_t pid;
-            uint16_t tid;
-        } __packed;
-        uint64_t value;
-    } __packed;
-
-  public:
-    LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid) : uid(uid), pid(pid), tid(tid) {}
-    explicit LogBufferElementKey(uint64_t key) : value(key) {}
-
-    uint64_t getKey() { return value; }
-};
-
 class LogBufferElementLast {
     typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
     LogBufferElementMap map;
 
   public:
     bool coalesce(LogBufferElement* element, uint16_t dropped) {
-        LogBufferElementKey key(element->getUid(), element->getPid(), element->getTid());
-        LogBufferElementMap::iterator it = map.find(key.getKey());
+        uint64_t key = LogBufferElementKey(element->getUid(), element->getPid(), element->getTid());
+        LogBufferElementMap::iterator it = map.find(key);
         if (it != map.end()) {
             LogBufferElement* found = it->second;
             uint16_t moreDropped = found->getDropped();
@@ -465,8 +448,8 @@
     }
 
     void add(LogBufferElement* element) {
-        LogBufferElementKey key(element->getUid(), element->getPid(), element->getTid());
-        map[key.getKey()] = element;
+        uint64_t key = LogBufferElementKey(element->getUid(), element->getPid(), element->getTid());
+        map[key] = element;
     }
 
     void clear() { map.clear(); }
@@ -483,6 +466,11 @@
             }
         }
     }
+
+  private:
+    uint64_t LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid) {
+        return uint64_t(uid) << 32 | uint64_t(pid) << 16 | uint64_t(tid);
+    }
 };
 
 // If the selected reader is blocking our pruning progress, decide on
@@ -660,7 +648,7 @@
         if (leading) {
             it = GetOldest(id);
         }
-        static const timespec too_old = {EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
+        static const log_time too_old{EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
         LogBufferElementCollection::iterator lastt;
         lastt = mLogElements.end();
         --lastt;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 8499715..4f5cabd 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -183,16 +183,16 @@
     }
     if (name) {
         char* buf = nullptr;
-        asprintf(&buf, "(%s)", name);
-        if (buf) {
+        int result = asprintf(&buf, "(%s)", name);
+        if (result != -1) {
             free(const_cast<char*>(name));
             name = buf;
         }
     }
     if (commName) {
         char* buf = nullptr;
-        asprintf(&buf, " %s", commName);
-        if (buf) {
+        int result = asprintf(&buf, " %s", commName);
+        if (result != -1) {
             free(const_cast<char*>(commName));
             commName = buf;
         }
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
new file mode 100644
index 0000000..cc3cb76
--- /dev/null
+++ b/logd/LogBufferTest.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogBuffer.h"
+
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "ChattyLogBuffer.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
+#include "LogWriter.h"
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+#ifndef __ANDROID__
+unsigned long __android_logger_get_buffer_size(log_id_t) {
+    return 1024 * 1024;
+}
+
+bool __android_logger_valid_buffer_size(unsigned long) {
+    return true;
+}
+#endif
+
+void android::prdebug(const char* fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+}
+
+char* android::uidToName(uid_t) {
+    return nullptr;
+}
+
+class TestWriter : public LogWriter {
+  public:
+    TestWriter(std::vector<std::pair<logger_entry, std::string>>* msgs, bool* released)
+        : LogWriter(0, true, true), msgs_(msgs), released_(released) {}
+    bool Write(const logger_entry& entry, const char* message) override {
+        msgs_->emplace_back(entry, std::string(message, entry.len));
+        return true;
+    }
+
+    void Release() {
+        if (released_) *released_ = true;
+    }
+
+    std::string name() const override { return "test_writer"; }
+
+  private:
+    std::vector<std::pair<logger_entry, std::string>>* msgs_;
+    bool* released_;
+};
+
+class LogBufferTest : public testing::Test {
+  protected:
+    void SetUp() override {
+        log_buffer_.reset(new ChattyLogBuffer(&reader_list_, &tags_, &prune_, &stats_));
+    }
+
+    void FixupMessages(std::vector<std::pair<logger_entry, std::string>>* messages) {
+        for (auto& [entry, message] : *messages) {
+            entry.hdr_size = sizeof(logger_entry);
+            entry.len = message.size();
+        }
+    }
+
+    void LogMessages(const std::vector<std::pair<logger_entry, std::string>>& messages) {
+        for (auto& [entry, message] : messages) {
+            log_buffer_->Log(static_cast<log_id_t>(entry.lid), log_time(entry.sec, entry.nsec),
+                             entry.uid, entry.pid, entry.tid, message.c_str(), message.size());
+        }
+    }
+
+    std::vector<std::string> CompareLoggerEntries(const logger_entry& expected,
+                                                  const logger_entry& result) {
+        std::vector<std::string> errors;
+        if (expected.len != result.len) {
+            errors.emplace_back(
+                    StringPrintf("len: %" PRIu16 " vs %" PRIu16, expected.len, result.len));
+        }
+        if (expected.hdr_size != result.hdr_size) {
+            errors.emplace_back(StringPrintf("hdr_size: %" PRIu16 " vs %" PRIu16, expected.hdr_size,
+                                             result.hdr_size));
+        }
+        if (expected.pid != result.pid) {
+            errors.emplace_back(
+                    StringPrintf("pid: %" PRIi32 " vs %" PRIi32, expected.pid, result.pid));
+        }
+        if (expected.tid != result.tid) {
+            errors.emplace_back(
+                    StringPrintf("tid: %" PRIu32 " vs %" PRIu32, expected.tid, result.tid));
+        }
+        if (expected.sec != result.sec) {
+            errors.emplace_back(
+                    StringPrintf("sec: %" PRIu32 " vs %" PRIu32, expected.sec, result.sec));
+        }
+        if (expected.nsec != result.nsec) {
+            errors.emplace_back(
+                    StringPrintf("nsec: %" PRIu32 " vs %" PRIu32, expected.nsec, result.nsec));
+        }
+        if (expected.lid != result.lid) {
+            errors.emplace_back(
+                    StringPrintf("lid: %" PRIu32 " vs %" PRIu32, expected.lid, result.lid));
+        }
+        if (expected.uid != result.uid) {
+            errors.emplace_back(
+                    StringPrintf("uid: %" PRIu32 " vs %" PRIu32, expected.uid, result.uid));
+        }
+        return errors;
+    }
+
+    std::string CompareMessages(const std::string& expected, const std::string& result) {
+        if (expected == result) {
+            return {};
+        }
+        auto shorten_string = [](const std::string& in) {
+            if (in.size() > 10) {
+                return in.substr(0, 10) + "...";
+            }
+            return in;
+        };
+
+        size_t diff_index = 0;
+        for (; diff_index < std::min(expected.size(), result.size()); ++diff_index) {
+            if (expected[diff_index] != result[diff_index]) {
+                break;
+            }
+        }
+
+        if (diff_index < 10) {
+            auto expected_short = shorten_string(expected);
+            auto result_short = shorten_string(result);
+            return StringPrintf("msg: %s vs %s", expected_short.c_str(), result_short.c_str());
+        }
+
+        auto expected_short = shorten_string(expected.substr(diff_index));
+        auto result_short = shorten_string(result.substr(diff_index));
+        return StringPrintf("msg: index %zu: %s vs %s", diff_index, expected_short.c_str(),
+                            result_short.c_str());
+    }
+
+    void CompareLogMessages(const std::vector<std::pair<logger_entry, std::string>>& expected,
+                            const std::vector<std::pair<logger_entry, std::string>>& result) {
+        EXPECT_EQ(expected.size(), result.size());
+        size_t end = std::min(expected.size(), result.size());
+        size_t num_errors = 0;
+        for (size_t i = 0; i < end; ++i) {
+            auto errors = CompareLoggerEntries(expected[i].first, result[i].first);
+            auto msg_error = CompareMessages(expected[i].second, result[i].second);
+            if (!msg_error.empty()) {
+                errors.emplace_back(msg_error);
+            }
+            if (!errors.empty()) {
+                GTEST_LOG_(ERROR) << "Mismatch log message " << i << " " << Join(errors, " ");
+                ++num_errors;
+            }
+        }
+        EXPECT_EQ(0U, num_errors);
+    }
+
+    LogReaderList reader_list_;
+    LogTags tags_;
+    PruneList prune_;
+    LogStatistics stats_{false};
+    std::unique_ptr<LogBuffer> log_buffer_;
+};
+
+TEST_F(LogBufferTest, smoke) {
+    std::vector<std::pair<logger_entry, std::string>> log_messages = {
+            {{
+                     .pid = 1,
+                     .tid = 1,
+                     .sec = 1234,
+                     .nsec = 323001,
+                     .lid = LOG_ID_MAIN,
+                     .uid = 0,
+             },
+             "smoke test"},
+    };
+    FixupMessages(&log_messages);
+    LogMessages(log_messages);
+
+    std::vector<std::pair<logger_entry, std::string>> read_log_messages;
+    std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+    uint64_t flush_result = log_buffer_->FlushTo(test_writer.get(), 1, nullptr, nullptr);
+    EXPECT_EQ(1ULL, flush_result);
+    CompareLogMessages(log_messages, read_log_messages);
+}
+
+TEST_F(LogBufferTest, smoke_with_reader_thread) {
+    std::vector<std::pair<logger_entry, std::string>> log_messages = {
+            {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
+             "first"},
+            {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
+             "second"},
+            {{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_KERNEL, .uid = 0},
+             "third"},
+            {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20004, .lid = LOG_ID_MAIN, .uid = 0},
+             "fourth"},
+            {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20005, .lid = LOG_ID_RADIO, .uid = 0},
+             "fifth"},
+            {{.pid = 2, .tid = 2, .sec = 10000, .nsec = 20006, .lid = LOG_ID_RADIO, .uid = 0},
+             "sixth"},
+            {{.pid = 3, .tid = 2, .sec = 10000, .nsec = 20007, .lid = LOG_ID_RADIO, .uid = 0},
+             "seventh"},
+            {{.pid = 4, .tid = 2, .sec = 10000, .nsec = 20008, .lid = LOG_ID_MAIN, .uid = 0},
+             "eighth"},
+            {{.pid = 5, .tid = 2, .sec = 10000, .nsec = 20009, .lid = LOG_ID_CRASH, .uid = 0},
+             "nineth"},
+            {{.pid = 6, .tid = 2, .sec = 10000, .nsec = 20011, .lid = LOG_ID_MAIN, .uid = 0},
+             "tenth"},
+    };
+    FixupMessages(&log_messages);
+    LogMessages(log_messages);
+
+    std::vector<std::pair<logger_entry, std::string>> read_log_messages;
+    bool released = false;
+
+    {
+        auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+        std::unique_ptr<LogReaderThread> log_reader(
+                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+                                    0, ~0, 0, {}, 1, {}));
+        reader_list_.reader_threads().emplace_back(std::move(log_reader));
+    }
+
+    while (!released) {
+        usleep(5000);
+    }
+    {
+        auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+        EXPECT_EQ(0U, reader_list_.reader_threads().size());
+    }
+    CompareLogMessages(log_messages, read_log_messages);
+}
+
+// Generate random messages, set the 'sec' parameter explicit though, to be able to track the
+// expected order of messages.
+std::pair<logger_entry, std::string> GenerateRandomLogMessage(uint32_t sec) {
+    auto rand_uint32 = [](int max) -> uint32_t { return rand() % max; };
+    logger_entry entry = {
+            .hdr_size = sizeof(logger_entry),
+            .pid = rand() % 5000,
+            .tid = rand_uint32(5000),
+            .sec = sec,
+            .nsec = rand_uint32(NS_PER_SEC),
+            .lid = rand_uint32(LOG_ID_STATS),
+            .uid = rand_uint32(100000),
+    };
+
+    // See comment in ChattyLogBuffer::Log() for why this is disallowed.
+    if (entry.nsec % 1000 == 0) {
+        ++entry.nsec;
+    }
+
+    if (entry.lid == LOG_ID_EVENTS) {
+        entry.lid = LOG_ID_KERNEL;
+    }
+
+    std::string message;
+    char priority = ANDROID_LOG_INFO + rand() % 2;
+    message.push_back(priority);
+
+    int tag_length = 2 + rand() % 10;
+    for (int i = 0; i < tag_length; ++i) {
+        message.push_back('a' + rand() % 26);
+    }
+    message.push_back('\0');
+
+    int msg_length = 2 + rand() % 1000;
+    for (int i = 0; i < msg_length; ++i) {
+        message.push_back('a' + rand() % 26);
+    }
+    message.push_back('\0');
+
+    entry.len = message.size();
+
+    return {entry, message};
+}
+
+TEST_F(LogBufferTest, random_messages) {
+    srand(1);
+    std::vector<std::pair<logger_entry, std::string>> log_messages;
+    for (size_t i = 0; i < 1000; ++i) {
+        log_messages.emplace_back(GenerateRandomLogMessage(i));
+    }
+    LogMessages(log_messages);
+
+    std::vector<std::pair<logger_entry, std::string>> read_log_messages;
+    bool released = false;
+
+    {
+        auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+        std::unique_ptr<LogReaderThread> log_reader(
+                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+                                    0, ~0, 0, {}, 1, {}));
+        reader_list_.reader_threads().emplace_back(std::move(log_reader));
+    }
+
+    while (!released) {
+        usleep(5000);
+    }
+    {
+        auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+        EXPECT_EQ(0U, reader_list_.reader_threads().size());
+    }
+    CompareLogMessages(log_messages, read_log_messages);
+}
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 8250808..3afe3ee 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -32,6 +32,7 @@
 #include <android-base/macros.h>
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 #include <log/log_event_list.h>
 #include <log/log_properties.h>
 #include <log/log_read.h>
@@ -550,10 +551,10 @@
     clock_gettime(CLOCK_REALTIME, &ts);
 
     android_log_header_t header = {
-        .id = LOG_ID_EVENTS,
-        .tid = (uint16_t)gettid(),
-        .realtime.tv_sec = (uint32_t)ts.tv_sec,
-        .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+            .id = LOG_ID_EVENTS,
+            .tid = static_cast<uint16_t>(android::base::GetThreadId()),
+            .realtime.tv_sec = static_cast<uint32_t>(ts.tv_sec),
+            .realtime.tv_nsec = static_cast<uint32_t>(ts.tv_nsec),
     };
 
     uint32_t outTag = TAG_DEF_LOG_TAG;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index ce82b41..c472167 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -30,7 +30,7 @@
 
 // Furnished in main.cpp. Caller must own and free returned value
 char* uidToName(uid_t uid);
-void prdebug(const char* fmt, ...) __printflike(1, 2);
+void prdebug(const char* fmt, ...) __attribute__((__format__(printf, 1, 2)));
 
 // Caller must own and free returned value
 char* pidToName(pid_t pid);
diff --git a/logd/libaudit.c b/logd/libaudit.cpp
similarity index 67%
rename from logd/libaudit.c
rename to logd/libaudit.cpp
index f452c71..ccea0a2 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.cpp
@@ -18,11 +18,13 @@
  *
  */
 
+#include "libaudit.h"
+
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "libaudit.h"
+#include <limits>
 
 /**
  * Waits for an ack from the kernel
@@ -32,22 +34,15 @@
  *  This function returns 0 on success, else -errno.
  */
 static int get_ack(int fd) {
-    int rc;
-    struct audit_message rep;
-
-    /* Sanity check, this is an internal interface this shouldn't happen */
-    if (fd < 0) {
-        return -EINVAL;
-    }
-
-    rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+    struct audit_message rep = {};
+    int rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
     if (rc < 0) {
         return rc;
     }
 
     if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
         audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
-        rc = ((struct nlmsgerr*)rep.data)->error;
+        rc = reinterpret_cast<struct nlmsgerr*>(rep.data)->error;
         if (rc) {
             return -rc;
         }
@@ -70,19 +65,11 @@
  *  This function returns a positive sequence number on success, else -errno.
  */
 static int audit_send(int fd, int type, const void* data, size_t size) {
-    int rc;
-    static int16_t sequence = 0;
-    struct audit_message req;
-    struct sockaddr_nl addr;
-
-    memset(&req, 0, sizeof(req));
-    memset(&addr, 0, sizeof(addr));
-
-    /* We always send netlink messaged */
-    addr.nl_family = AF_NETLINK;
+    struct sockaddr_nl addr = {.nl_family = AF_NETLINK};
 
     /* Set up the netlink headers */
-    req.nlh.nlmsg_type = type;
+    struct audit_message req = {};
+    req.nlh.nlmsg_type = static_cast<uint16_t>(type);
     req.nlh.nlmsg_len = NLMSG_SPACE(size);
     req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
 
@@ -107,29 +94,23 @@
     /*
      * Only increment the sequence number on a guarantee
      * you will send it to the kernel.
-     *
-     * Also, the sequence is defined as a u32 in the kernel
-     * struct. Using an int here might not work on 32/64 bit splits. A
-     * signed 64 bit value can overflow a u32..but a u32
-     * might not fit in the response, so we need to use s32.
-     * Which is still kind of hackish since int could be 16 bits
-     * in size. The only safe type to use here is a signed 16
-     * bit value.
      */
-    req.nlh.nlmsg_seq = ++sequence;
+    static uint32_t sequence = 0;
+    if (sequence == std::numeric_limits<uint32_t>::max()) {
+        sequence = 1;
+    } else {
+        sequence++;
+    }
+    req.nlh.nlmsg_seq = sequence;
 
-    /* While failing and its due to interrupts */
-
-    rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
-                                   (struct sockaddr*)&addr, sizeof(addr)));
+    ssize_t rc = TEMP_FAILURE_RETRY(
+            sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr)));
 
     /* Not all the bytes were sent */
     if (rc < 0) {
-        rc = -errno;
-        goto out;
+        return -errno;
     } else if ((uint32_t)rc != req.nlh.nlmsg_len) {
-        rc = -EPROTO;
-        goto out;
+        return -EPROTO;
     }
 
     /* We sent all the bytes, get the ack */
@@ -138,32 +119,22 @@
     /* If the ack failed, return the error, else return the sequence number */
     rc = (rc == 0) ? (int)sequence : rc;
 
-out:
-    /* Don't let sequence roll to negative */
-    if (sequence < 0) {
-        sequence = 0;
-    }
-
     return rc;
 }
 
 int audit_setup(int fd, pid_t pid) {
-    int rc;
-    struct audit_message rep;
-    struct audit_status status;
-
-    memset(&status, 0, sizeof(status));
-
     /*
      * In order to set the auditd PID we send an audit message over the netlink
      * socket with the pid field of the status struct set to our current pid,
      * and the the mask set to AUDIT_STATUS_PID
      */
-    status.pid = pid;
-    status.mask = AUDIT_STATUS_PID;
+    struct audit_status status = {
+            .mask = AUDIT_STATUS_PID,
+            .pid = static_cast<uint32_t>(pid),
+    };
 
     /* Let the kernel know this pid will be registering for audit events */
-    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+    int rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
     if (rc < 0) {
         return rc;
     }
@@ -178,6 +149,7 @@
      * so I went to non-blocking and it seemed to fix the bug.
      * Need to investigate further.
      */
+    struct audit_message rep = {};
     audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
 
     return 0;
@@ -188,27 +160,18 @@
 }
 
 int audit_rate_limit(int fd, uint32_t limit) {
-    struct audit_status status;
-    memset(&status, 0, sizeof(status));
-    status.mask = AUDIT_STATUS_RATE_LIMIT;
-    status.rate_limit = limit; /* audit entries per second */
+    struct audit_status status = {
+            .mask = AUDIT_STATUS_RATE_LIMIT, .rate_limit = limit, /* audit entries per second */
+    };
     return audit_send(fd, AUDIT_SET, &status, sizeof(status));
 }
 
 int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
-    ssize_t len;
-    int flags;
-    int rc = 0;
-
-    struct sockaddr_nl nladdr;
-    socklen_t nladdrlen = sizeof(nladdr);
-
     if (fd < 0) {
         return -EBADF;
     }
 
-    /* Set up the flags for recv from */
-    flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+    int flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
     flags |= peek;
 
     /*
@@ -216,19 +179,20 @@
      * the interface shows that EINTR can never be returned, other errors,
      * however, can be returned.
      */
-    len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
-                                      (struct sockaddr*)&nladdr, &nladdrlen));
+    struct sockaddr_nl nladdr;
+    socklen_t nladdrlen = sizeof(nladdr);
+    ssize_t len = TEMP_FAILURE_RETRY(
+            recvfrom(fd, rep, sizeof(*rep), flags, (struct sockaddr*)&nladdr, &nladdrlen));
 
     /*
      * EAGAIN should be re-tried until success or another error manifests.
      */
     if (len < 0) {
-        rc = -errno;
-        if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+        if (block == GET_REPLY_NONBLOCKING && errno == EAGAIN) {
             /* If request is non blocking and errno is EAGAIN, just return 0 */
             return 0;
         }
-        return rc;
+        return -errno;
     }
 
     if (nladdrlen != sizeof(nladdr)) {
@@ -242,10 +206,10 @@
 
     /* Check if the reply from the kernel was ok */
     if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
-        rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+        return len == sizeof(*rep) ? -EFBIG : -EBADE;
     }
 
-    return rc;
+    return 0;
 }
 
 void audit_close(int fd) {
diff --git a/logd/libaudit.h b/logd/libaudit.h
index b4a92a8..27b0866 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -17,8 +17,7 @@
  * Written by William Roberts <w.roberts@sta.samsung.com>
  */
 
-#ifndef _LIBAUDIT_H_
-#define _LIBAUDIT_H_
+#pragma once
 
 #include <stdint.h>
 #include <sys/cdefs.h>
@@ -102,5 +101,3 @@
 extern int audit_rate_limit(int fd, uint32_t limit);
 
 __END_DECLS
-
-#endif
diff --git a/logd/tests/logd_test.cpp b/logd/logd_test.cpp
similarity index 98%
rename from logd/tests/logd_test.cpp
rename to logd/logd_test.cpp
index 55737e9..ed34ea4 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -39,7 +39,7 @@
 #include <selinux/selinux.h>
 #endif
 
-#include "../LogReader.h"  // pickup LOGD_SNDTIMEO
+#include "LogReader.h"  // pickup LOGD_SNDTIMEO
 
 #ifdef __ANDROID__
 static void send_to_control(char* buf, size_t len) {
@@ -904,10 +904,10 @@
             (log_msg.entry.len == (4 + 1 + 8))) {
             if (tag != 0) continue;
 
-            log_time tx(eventData + 4 + 1);
-            if (ts == tx) {
+            log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+            if (ts == *tx) {
                 ++count;
-            } else if (ts1 == tx) {
+            } else if (ts1 == *tx) {
                 ++second_count;
             }
         } else if (eventData[4] == EVENT_TYPE_STRING) {
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
deleted file mode 100644
index 9a5defa..0000000
--- a/logd/tests/Android.bp
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// -----------------------------------------------------------------------------
-// Unit tests.
-// -----------------------------------------------------------------------------
-
-cc_defaults {
-    name: "logd-unit-test-defaults",
-
-    cflags: [
-        "-fstack-protector-all",
-        "-g",
-        "-Wall",
-        "-Wextra",
-        "-Werror",
-        "-fno-builtin",
-
-        "-DAUDITD_LOG_TAG=1003",
-        "-DCHATTY_LOG_TAG=1004",
-    ],
-
-    srcs: ["logd_test.cpp"],
-
-    static_libs: [
-        "libbase",
-        "libcutils",
-        "libselinux",
-        "liblog",
-    ],
-}
-
-// Build tests for the logger. Run with:
-//   adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
-cc_test {
-    name: "logd-unit-tests",
-    defaults: ["logd-unit-test-defaults"],
-}
-
-cc_test {
-    name: "CtsLogdTestCases",
-    defaults: ["logd-unit-test-defaults"],
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
-    test_suites: [
-        "cts",
-        "vts10",
-    ],
-}