Merge "init: add missing TEMP_FAILURE_RETRY"
diff --git a/adb/Android.bp b/adb/Android.bp
index 1113ce3..81d20c1 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -317,6 +317,7 @@
         "libandroidfw",
         "libapp_processes_protos_full",
         "libbase",
+        "libbrotli",
         "libcutils",
         "libcrypto_utils",
         "libcrypto",
@@ -469,6 +470,7 @@
     static_libs: [
         "libadbconnection_server",
         "libadbd_core",
+        "libbrotli",
         "libdiagnose_usb",
     ],
 
@@ -527,6 +529,7 @@
     name: "libadbd",
     defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
+    apex_available: ["com.android.adbd"],
 
     // avoid getting duplicate symbol of android::build::getbuildnumber().
     use_version_lib: false,
@@ -566,6 +569,7 @@
     },
 
     static_libs: [
+        "libbrotli",
         "libcutils_sockets",
         "libdiagnose_usb",
         "libmdnssd",
@@ -581,6 +585,7 @@
     defaults: ["adbd_defaults", "host_adbd_supported"],
     stl: "libc++_static",
     recovery_available: true,
+    apex_available: ["com.android.adbd"],
 
     srcs: [
         "daemon/main.cpp",
@@ -604,6 +609,7 @@
         "libapp_processes_protos_lite",
         "libasyncio",
         "libbase",
+        "libbrotli",
         "libcap",
         "libcrypto_utils",
         "libcutils_sockets",
diff --git a/adb/brotli_utils.h b/adb/brotli_utils.h
new file mode 100644
index 0000000..c5be73d
--- /dev/null
+++ b/adb/brotli_utils.h
@@ -0,0 +1,144 @@
+/*
+ * 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 <span>
+
+#include <brotli/decode.h>
+#include <brotli/encode.h>
+
+#include "types.h"
+
+enum class BrotliDecodeResult {
+    Error,
+    Done,
+    NeedInput,
+    MoreOutput,
+};
+
+struct BrotliDecoder {
+    explicit BrotliDecoder(std::span<char> output_buffer)
+        : output_buffer_(output_buffer),
+          decoder_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
+                   BrotliDecoderDestroyInstance) {}
+
+    void Append(Block&& block) { input_buffer_.append(std::move(block)); }
+
+    BrotliDecodeResult Decode(std::span<char>* output) {
+        size_t available_in = input_buffer_.front_size();
+        const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
+
+        size_t available_out = output_buffer_.size();
+        uint8_t* next_out = reinterpret_cast<uint8_t*>(output_buffer_.data());
+
+        BrotliDecoderResult r = BrotliDecoderDecompressStream(
+                decoder_.get(), &available_in, &next_in, &available_out, &next_out, nullptr);
+
+        size_t bytes_consumed = input_buffer_.front_size() - available_in;
+        input_buffer_.drop_front(bytes_consumed);
+
+        size_t bytes_emitted = output_buffer_.size() - available_out;
+        *output = std::span<char>(output_buffer_.data(), bytes_emitted);
+
+        switch (r) {
+            case BROTLI_DECODER_RESULT_SUCCESS:
+                return BrotliDecodeResult::Done;
+            case BROTLI_DECODER_RESULT_ERROR:
+                return BrotliDecodeResult::Error;
+            case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+                // Brotli guarantees as one of its invariants that if it returns NEEDS_MORE_INPUT,
+                // it will consume the entire input buffer passed in, so we don't have to worry
+                // about bytes left over in the front block with more input remaining.
+                return BrotliDecodeResult::NeedInput;
+            case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+                return BrotliDecodeResult::MoreOutput;
+        }
+    }
+
+  private:
+    IOVector input_buffer_;
+    std::span<char> output_buffer_;
+    std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState*)> decoder_;
+};
+
+enum class BrotliEncodeResult {
+    Error,
+    Done,
+    NeedInput,
+    MoreOutput,
+};
+
+template <size_t OutputBlockSize>
+struct BrotliEncoder {
+    explicit BrotliEncoder()
+        : output_block_(OutputBlockSize),
+          output_bytes_left_(OutputBlockSize),
+          encoder_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
+                   BrotliEncoderDestroyInstance) {
+        BrotliEncoderSetParameter(encoder_.get(), BROTLI_PARAM_QUALITY, 1);
+    }
+
+    void Append(Block input) { input_buffer_.append(std::move(input)); }
+    void Finish() { finished_ = true; }
+
+    BrotliEncodeResult Encode(Block* output) {
+        output->clear();
+        while (true) {
+            size_t available_in = input_buffer_.front_size();
+            const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
+
+            size_t available_out = output_bytes_left_;
+            uint8_t* next_out = reinterpret_cast<uint8_t*>(output_block_.data() +
+                                                           (OutputBlockSize - output_bytes_left_));
+
+            BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
+            if (finished_) {
+                op = BROTLI_OPERATION_FINISH;
+            }
+
+            if (!BrotliEncoderCompressStream(encoder_.get(), op, &available_in, &next_in,
+                                             &available_out, &next_out, nullptr)) {
+                return BrotliEncodeResult::Error;
+            }
+
+            size_t bytes_consumed = input_buffer_.front_size() - available_in;
+            input_buffer_.drop_front(bytes_consumed);
+
+            output_bytes_left_ = available_out;
+
+            if (BrotliEncoderIsFinished(encoder_.get())) {
+                output_block_.resize(OutputBlockSize - output_bytes_left_);
+                *output = std::move(output_block_);
+                return BrotliEncodeResult::Done;
+            } else if (output_bytes_left_ == 0) {
+                *output = std::move(output_block_);
+                output_block_.resize(OutputBlockSize);
+                output_bytes_left_ = OutputBlockSize;
+                return BrotliEncodeResult::MoreOutput;
+            } else if (input_buffer_.empty()) {
+                return BrotliEncodeResult::NeedInput;
+            }
+        }
+    }
+
+  private:
+    bool finished_ = false;
+    IOVector input_buffer_;
+    Block output_block_;
+    size_t output_bytes_left_;
+    std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;
+};
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 21b8f49..fe9182e 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -286,7 +286,7 @@
         }
     }
 
-    if (do_sync_push(apk_file, apk_dest.c_str(), false)) {
+    if (do_sync_push(apk_file, apk_dest.c_str(), false, true)) {
         result = pm_command(argc, argv);
         delete_device_file(apk_dest);
     }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 0c96104..04b250d 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -129,15 +129,21 @@
         " reverse --remove-all     remove all reverse socket connections from device\n"
         "\n"
         "file transfer:\n"
-        " push [--sync] LOCAL... REMOTE\n"
+        " push [--sync] [-zZ] LOCAL... REMOTE\n"
         "     copy local files/directories to device\n"
         "     --sync: only push files that are newer on the host than the device\n"
-        " pull [-a] REMOTE... LOCAL\n"
+        "     -z: enable compression\n"
+        "     -Z: disable compression\n"
+        " pull [-azZ] REMOTE... LOCAL\n"
         "     copy files/dirs from device\n"
         "     -a: preserve file timestamp and mode\n"
-        " sync [all|data|odm|oem|product|system|system_ext|vendor]\n"
+        "     -z: enable compression\n"
+        "     -Z: disable compression\n"
+        " sync [-lzZ] [all|data|odm|oem|product|system|system_ext|vendor]\n"
         "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
         "     -l: list files that would be copied, but don't copy them\n"
+        "     -z: enable compression\n"
+        "     -Z: disable compression\n"
         "\n"
         "shell:\n"
         " shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
@@ -1309,8 +1315,12 @@
 }
 
 static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
-                                 const char** dst, bool* copy_attrs, bool* sync) {
+                                 const char** dst, bool* copy_attrs, bool* sync, bool* compressed) {
     *copy_attrs = false;
+    const char* adb_compression = getenv("ADB_COMPRESSION");
+    if (adb_compression && strcmp(adb_compression, "0") == 0) {
+        *compressed = false;
+    }
 
     srcs->clear();
     bool ignore_flags = false;
@@ -1322,6 +1332,14 @@
                 // Silently ignore for backwards compatibility.
             } else if (!strcmp(*arg, "-a")) {
                 *copy_attrs = true;
+            } else if (!strcmp(*arg, "-z")) {
+                if (compressed != nullptr) {
+                    *compressed = true;
+                }
+            } else if (!strcmp(*arg, "-Z")) {
+                if (compressed != nullptr) {
+                    *compressed = false;
+                }
             } else if (!strcmp(*arg, "--sync")) {
                 if (sync != nullptr) {
                     *sync = true;
@@ -1876,20 +1894,22 @@
     } else if (!strcmp(argv[0], "push")) {
         bool copy_attrs = false;
         bool sync = false;
+        bool compressed = true;
         std::vector<const char*> srcs;
         const char* dst = nullptr;
 
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync, &compressed);
         if (srcs.empty() || !dst) error_exit("push requires an argument");
-        return do_sync_push(srcs, dst, sync) ? 0 : 1;
+        return do_sync_push(srcs, dst, sync, compressed) ? 0 : 1;
     } else if (!strcmp(argv[0], "pull")) {
         bool copy_attrs = false;
+        bool compressed = true;
         std::vector<const char*> srcs;
         const char* dst = ".";
 
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr, &compressed);
         if (srcs.empty()) error_exit("pull requires an argument");
-        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
+        return do_sync_pull(srcs, dst, copy_attrs, compressed) ? 0 : 1;
     } else if (!strcmp(argv[0], "install")) {
         if (argc < 2) error_exit("install requires an argument");
         return install_app(argc, argv);
@@ -1905,18 +1925,38 @@
     } else if (!strcmp(argv[0], "sync")) {
         std::string src;
         bool list_only = false;
-        if (argc < 2) {
-            // No partition specified: sync all of them.
-        } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
-            list_only = true;
-            if (argc == 3) src = argv[2];
-        } else if (argc == 2) {
-            src = argv[1];
-        } else {
-            error_exit("usage: adb sync [-l] [PARTITION]");
+        bool compressed = true;
+
+        const char* adb_compression = getenv("ADB_COMPRESSION");
+        if (adb_compression && strcmp(adb_compression, "0") == 0) {
+            compressed = false;
         }
 
-        if (src.empty()) src = "all";
+        int opt;
+        while ((opt = getopt(argc, const_cast<char**>(argv), "lzZ")) != -1) {
+            switch (opt) {
+                case 'l':
+                    list_only = true;
+                    break;
+                case 'z':
+                    compressed = true;
+                    break;
+                case 'Z':
+                    compressed = false;
+                    break;
+                default:
+                    error_exit("usage: adb sync [-lzZ] [PARTITION]");
+            }
+        }
+
+        if (optind == argc) {
+            src = "all";
+        } else if (optind + 1 == argc) {
+            src = argv[optind];
+        } else {
+            error_exit("usage: adb sync [-lzZ] [PARTITION]");
+        }
+
         std::vector<std::string> partitions{"data",   "odm",        "oem",   "product",
                                             "system", "system_ext", "vendor"};
         bool found = false;
@@ -1925,7 +1965,7 @@
                 std::string src_dir{product_file(partition)};
                 if (!directory_exists(src_dir)) continue;
                 found = true;
-                if (!do_sync_sync(src_dir, "/" + partition, list_only)) return 1;
+                if (!do_sync_sync(src_dir, "/" + partition, list_only, compressed)) return 1;
             }
         }
         if (!found) error_exit("don't know how to sync %s partition", src.c_str());
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index c5fc12f..de82e14 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -112,7 +112,7 @@
     // but can't be removed until after the push.
     unix_close(tf.release());
 
-    if (!do_sync_push(srcs, dst, sync)) {
+    if (!do_sync_push(srcs, dst, sync, true)) {
         error_exit("Failed to push fastdeploy agent to device.");
     }
 }
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 599a3e3..56568c2 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -42,6 +42,7 @@
 #include "adb_client.h"
 #include "adb_io.h"
 #include "adb_utils.h"
+#include "brotli_utils.h"
 #include "file_sync_protocol.h"
 #include "line_printer.h"
 #include "sysdeps/errno.h"
@@ -233,6 +234,8 @@
         } else {
             have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
             have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
+            have_sendrecv_v2_ = CanUseFeature(features_, kFeatureSendRecv2);
+            have_sendrecv_v2_brotli_ = CanUseFeature(features_, kFeatureSendRecv2Brotli);
             fd.reset(adb_connect("sync:", &error));
             if (fd < 0) {
                 Error("connect failed: %s", error.c_str());
@@ -256,6 +259,9 @@
         line_printer_.KeepInfoLine();
     }
 
+    bool HaveSendRecv2() const { return have_sendrecv_v2_; }
+    bool HaveSendRecv2Brotli() const { return have_sendrecv_v2_brotli_; }
+
     const FeatureSet& Features() const { return features_; }
 
     bool IsValid() { return fd >= 0; }
@@ -314,6 +320,62 @@
         req->path_length = path.length();
         char* data = reinterpret_cast<char*>(req + 1);
         memcpy(data, path.data(), path.length());
+        return WriteFdExactly(fd, buf.data(), buf.size());
+    }
+
+    bool SendSend2(std::string_view path, mode_t mode, bool compressed) {
+        if (path.length() > 1024) {
+            Error("SendRequest failed: path too long: %zu", path.length());
+            errno = ENAMETOOLONG;
+            return false;
+        }
+
+        Block buf;
+
+        SyncRequest req;
+        req.id = ID_SEND_V2;
+        req.path_length = path.length();
+
+        syncmsg msg;
+        msg.send_v2_setup.id = ID_SEND_V2;
+        msg.send_v2_setup.mode = mode;
+        msg.send_v2_setup.flags = compressed ? kSyncFlagBrotli : kSyncFlagNone;
+
+        buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.send_v2_setup));
+
+        void* p = buf.data();
+
+        p = mempcpy(p, &req, sizeof(SyncRequest));
+        p = mempcpy(p, path.data(), path.length());
+        p = mempcpy(p, &msg.send_v2_setup, sizeof(msg.send_v2_setup));
+
+        return WriteFdExactly(fd, buf.data(), buf.size());
+    }
+
+    bool SendRecv2(const std::string& path) {
+        if (path.length() > 1024) {
+            Error("SendRequest failed: path too long: %zu", path.length());
+            errno = ENAMETOOLONG;
+            return false;
+        }
+
+        Block buf;
+
+        SyncRequest req;
+        req.id = ID_RECV_V2;
+        req.path_length = path.length();
+
+        syncmsg msg;
+        msg.recv_v2_setup.id = ID_RECV_V2;
+        msg.recv_v2_setup.flags = kSyncFlagBrotli;
+
+        buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.recv_v2_setup));
+
+        void* p = buf.data();
+
+        p = mempcpy(p, &req, sizeof(SyncRequest));
+        p = mempcpy(p, path.data(), path.length());
+        p = mempcpy(p, &msg.recv_v2_setup, sizeof(msg.recv_v2_setup));
 
         return WriteFdExactly(fd, buf.data(), buf.size());
     }
@@ -370,8 +432,8 @@
             }
 
             if (msg.stat_v1.id != ID_LSTAT_V1) {
-                PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
-                            << msg.stat_v1.id;
+                LOG(FATAL) << "protocol fault: stat response has wrong message id: "
+                           << msg.stat_v1.id;
             }
 
             if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.mtime == 0) {
@@ -445,7 +507,7 @@
         char* p = &buf[0];
 
         SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
-        req_send->id = ID_SEND;
+        req_send->id = ID_SEND_V1;
         req_send->path_length = path_and_mode.length();
         p += sizeof(SyncRequest);
         memcpy(p, path_and_mode.data(), path_and_mode.size());
@@ -471,11 +533,92 @@
         return true;
     }
 
+    bool SendLargeFileCompressed(const std::string& path, mode_t mode, const std::string& lpath,
+                                 const std::string& rpath, unsigned mtime) {
+        if (!SendSend2(path, mode, true)) {
+            Error("failed to send ID_SEND_V2 message '%s': %s", path.c_str(), strerror(errno));
+            return false;
+        }
+
+        struct stat st;
+        if (stat(lpath.c_str(), &st) == -1) {
+            Error("cannot stat '%s': %s", lpath.c_str(), strerror(errno));
+            return false;
+        }
+
+        uint64_t total_size = st.st_size;
+        uint64_t bytes_copied = 0;
+
+        unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY | O_CLOEXEC));
+        if (lfd < 0) {
+            Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
+            return false;
+        }
+
+        syncsendbuf sbuf;
+        sbuf.id = ID_DATA;
+
+        BrotliEncoder<SYNC_DATA_MAX> encoder;
+        bool sending = true;
+        while (sending) {
+            Block input(SYNC_DATA_MAX);
+            int r = adb_read(lfd.get(), input.data(), input.size());
+            if (r < 0) {
+                Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
+                return false;
+            }
+
+            if (r == 0) {
+                encoder.Finish();
+            } else {
+                input.resize(r);
+                encoder.Append(std::move(input));
+                RecordBytesTransferred(r);
+                bytes_copied += r;
+                ReportProgress(rpath, bytes_copied, total_size);
+            }
+
+            while (true) {
+                Block output;
+                BrotliEncodeResult result = encoder.Encode(&output);
+                if (result == BrotliEncodeResult::Error) {
+                    Error("compressing '%s' locally failed", lpath.c_str());
+                    return false;
+                }
+
+                if (!output.empty()) {
+                    sbuf.size = output.size();
+                    memcpy(sbuf.data, output.data(), output.size());
+                    WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + output.size());
+                }
+
+                if (result == BrotliEncodeResult::Done) {
+                    sending = false;
+                    break;
+                } else if (result == BrotliEncodeResult::NeedInput) {
+                    break;
+                } else if (result == BrotliEncodeResult::MoreOutput) {
+                    continue;
+                }
+            }
+        }
+
+        syncmsg msg;
+        msg.data.id = ID_DONE;
+        msg.data.size = mtime;
+        RecordFileSent(lpath, rpath);
+        return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
+    }
+
     bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath,
-                       const std::string& rpath, unsigned mtime) {
+                       const std::string& rpath, unsigned mtime, bool compressed) {
+        if (compressed && HaveSendRecv2Brotli()) {
+            return SendLargeFileCompressed(path, mode, lpath, rpath, mtime);
+        }
+
         std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
-        if (!SendRequest(ID_SEND, path_and_mode)) {
-            Error("failed to send ID_SEND message '%s': %s", path_and_mode.c_str(),
+        if (!SendRequest(ID_SEND_V1, path_and_mode.c_str())) {
+            Error("failed to send ID_SEND_V1 message '%s': %s", path_and_mode.c_str(),
                   strerror(errno));
             return false;
         }
@@ -489,7 +632,7 @@
         uint64_t total_size = st.st_size;
         uint64_t bytes_copied = 0;
 
-        unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY));
+        unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY | O_CLOEXEC));
         if (lfd < 0) {
             Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
             return false;
@@ -497,8 +640,9 @@
 
         syncsendbuf sbuf;
         sbuf.id = ID_DATA;
+
         while (true) {
-            int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
+            int bytes_read = adb_read(lfd, sbuf.data, max);
             if (bytes_read == -1) {
                 Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
                 return false;
@@ -511,7 +655,6 @@
 
             RecordBytesTransferred(bytes_read);
             bytes_copied += bytes_read;
-
             ReportProgress(rpath, bytes_copied, total_size);
         }
 
@@ -695,6 +838,8 @@
     FeatureSet features_;
     bool have_stat_v2_;
     bool have_ls_v2_;
+    bool have_sendrecv_v2_;
+    bool have_sendrecv_v2_brotli_;
 
     TransferLedger global_ledger_;
     TransferLedger current_ledger_;
@@ -776,7 +921,7 @@
 }
 
 static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::string& rpath,
-                      unsigned mtime, mode_t mode, bool sync) {
+                      unsigned mtime, mode_t mode, bool sync, bool compressed) {
     if (sync) {
         struct stat st;
         if (sync_lstat(sc, rpath, &st)) {
@@ -819,16 +964,16 @@
             return false;
         }
     } else {
-        if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime)) {
+        if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compressed)) {
             return false;
         }
     }
     return sc.ReadAcknowledgements();
 }
 
-static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
-                      const char* name, uint64_t expected_size) {
-    if (!sc.SendRequest(ID_RECV, rpath)) return false;
+static bool sync_recv_v1(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
+                         uint64_t expected_size) {
+    if (!sc.SendRequest(ID_RECV_V1, rpath)) return false;
 
     adb_unlink(lpath);
     unique_fd lfd(adb_creat(lpath, 0644));
@@ -881,6 +1026,114 @@
     return true;
 }
 
+static bool sync_recv_v2(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
+                         uint64_t expected_size) {
+    if (!sc.SendRecv2(rpath)) return false;
+
+    adb_unlink(lpath);
+    unique_fd lfd(adb_creat(lpath, 0644));
+    if (lfd < 0) {
+        sc.Error("cannot create '%s': %s", lpath, strerror(errno));
+        return false;
+    }
+
+    uint64_t bytes_copied = 0;
+
+    Block buffer(SYNC_DATA_MAX);
+    BrotliDecoder decoder(std::span(buffer.data(), buffer.size()));
+    bool reading = true;
+    while (reading) {
+        syncmsg msg;
+        if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+            adb_unlink(lpath);
+            return false;
+        }
+
+        if (msg.data.id == ID_DONE) {
+            adb_unlink(lpath);
+            sc.Error("unexpected ID_DONE");
+            return false;
+        }
+
+        if (msg.data.id != ID_DATA) {
+            adb_unlink(lpath);
+            sc.ReportCopyFailure(rpath, lpath, msg);
+            return false;
+        }
+
+        if (msg.data.size > sc.max) {
+            sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
+            adb_unlink(lpath);
+            return false;
+        }
+
+        Block block(msg.data.size);
+        if (!ReadFdExactly(sc.fd, block.data(), msg.data.size)) {
+            adb_unlink(lpath);
+            return false;
+        }
+        decoder.Append(std::move(block));
+
+        while (true) {
+            std::span<char> output;
+            BrotliDecodeResult result = decoder.Decode(&output);
+
+            if (result == BrotliDecodeResult::Error) {
+                sc.Error("decompress failed");
+                adb_unlink(lpath);
+                return false;
+            }
+
+            if (!output.empty()) {
+                if (!WriteFdExactly(lfd, output.data(), output.size())) {
+                    sc.Error("cannot write '%s': %s", lpath, strerror(errno));
+                    adb_unlink(lpath);
+                    return false;
+                }
+            }
+
+            bytes_copied += output.size();
+
+            sc.RecordBytesTransferred(msg.data.size);
+            sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
+
+            if (result == BrotliDecodeResult::NeedInput) {
+                break;
+            } else if (result == BrotliDecodeResult::MoreOutput) {
+                continue;
+            } else if (result == BrotliDecodeResult::Done) {
+                reading = false;
+                break;
+            } else {
+                LOG(FATAL) << "invalid BrotliDecodeResult: " << static_cast<int>(result);
+            }
+        }
+    }
+
+    syncmsg msg;
+    if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+        sc.Error("failed to read ID_DONE");
+        return false;
+    }
+
+    if (msg.data.id != ID_DONE) {
+        sc.Error("unexpected message after transfer: id = %d (expected ID_DONE)", msg.data.id);
+        return false;
+    }
+
+    sc.RecordFilesTransferred(1);
+    return true;
+}
+
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
+                      uint64_t expected_size, bool compressed) {
+    if (sc.HaveSendRecv2() && compressed) {
+        return sync_recv_v2(sc, rpath, lpath, name, expected_size);
+    } else {
+        return sync_recv_v1(sc, rpath, lpath, name, expected_size);
+    }
+}
+
 bool do_sync_ls(const char* path) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
@@ -956,9 +1209,8 @@
     return true;
 }
 
-static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
-                                  std::string rpath, bool check_timestamps,
-                                  bool list_only) {
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, std::string rpath,
+                                  bool check_timestamps, bool list_only, bool compressed) {
     sc.NewTransfer();
 
     // Make sure that both directory paths end in a slash.
@@ -1040,7 +1292,7 @@
             if (list_only) {
                 sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
             } else {
-                if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false)) {
+                if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false, compressed)) {
                     return false;
                 }
             }
@@ -1055,7 +1307,8 @@
     return success;
 }
 
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
+                  bool compressed) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
@@ -1120,7 +1373,7 @@
                 dst_dir.append(android::base::Basename(src_path));
             }
 
-            success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false);
+            success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false, compressed);
             continue;
         } else if (!should_push_file(st.st_mode)) {
             sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1141,7 +1394,7 @@
 
         sc.NewTransfer();
         sc.SetExpectedTotalBytes(st.st_size);
-        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
+        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync, compressed);
         sc.ReportTransferRate(src_path, TransferDirection::push);
     }
 
@@ -1226,8 +1479,8 @@
     return r1 ? r1 : r2;
 }
 
-static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
-                                  std::string lpath, bool copy_attrs) {
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, std::string lpath,
+                                  bool copy_attrs, bool compressed) {
     sc.NewTransfer();
 
     // Make sure that both directory paths end in a slash.
@@ -1257,7 +1510,7 @@
                 continue;
             }
 
-            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
+            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size, compressed)) {
                 return false;
             }
 
@@ -1274,8 +1527,8 @@
     return true;
 }
 
-bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
-                  bool copy_attrs, const char* name) {
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+                  bool compressed, const char* name) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
@@ -1349,7 +1602,7 @@
                 dst_dir.append(android::base::Basename(src_path));
             }
 
-            success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs);
+            success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs, compressed);
             continue;
         } else if (!should_pull_file(src_st.st_mode)) {
             sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
@@ -1368,7 +1621,7 @@
 
         sc.NewTransfer();
         sc.SetExpectedTotalBytes(src_st.st_size);
-        if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
+        if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size, compressed)) {
             success = false;
             continue;
         }
@@ -1384,11 +1637,12 @@
     return success;
 }
 
-bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
+                  bool compressed) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
-    bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only);
+    bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only, compressed);
     if (!list_only) {
         sc.ReportOverallTransferRate(TransferDirection::push);
     }
diff --git a/adb/client/file_sync_client.h b/adb/client/file_sync_client.h
index df7f14c..de3f192 100644
--- a/adb/client/file_sync_client.h
+++ b/adb/client/file_sync_client.h
@@ -20,8 +20,10 @@
 #include <vector>
 
 bool do_sync_ls(const char* path);
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
+                  bool compressed);
 bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
-                  const char* name = nullptr);
+                  bool compressed, const char* name = nullptr);
 
-bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
+                  bool compressed);
diff --git a/adb/client/pairing/pairing_client.cpp b/adb/client/pairing/pairing_client.cpp
index 2f878bf..04bbceb 100644
--- a/adb/client/pairing/pairing_client.cpp
+++ b/adb/client/pairing/pairing_client.cpp
@@ -141,7 +141,12 @@
                                           cert_.size(), priv_key_.data(), priv_key_.size()));
     CHECK(connection_);
 
-    if (!pairing_connection_start(connection_.get(), fd.release(), OnPairingResult, this)) {
+#ifdef _WIN32
+    int osh = cast_handle_to_int(adb_get_os_handle(fd.release()));
+#else
+    int osh = adb_get_os_handle(fd.release());
+#endif
+    if (!pairing_connection_start(connection_.get(), osh, OnPairingResult, this)) {
         LOG(ERROR) << "PairingClient failed to start the PairingConnection";
         state_ = State::Stopped;
         return false;
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index edf5683..07f6e65 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -32,6 +32,8 @@
 #include <utime.h>
 
 #include <memory>
+#include <optional>
+#include <span>
 #include <string>
 #include <vector>
 
@@ -55,10 +57,12 @@
 #include "adb_io.h"
 #include "adb_trace.h"
 #include "adb_utils.h"
+#include "brotli_utils.h"
 #include "file_sync_protocol.h"
 #include "security_log_tags.h"
 #include "sysdeps/errno.h"
 
+using android::base::borrowed_fd;
 using android::base::Dirname;
 using android::base::StringPrintf;
 
@@ -249,7 +253,7 @@
 // Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
 #pragma GCC poison SendFail
 
-static bool SendSyncFail(int fd, const std::string& reason) {
+static bool SendSyncFail(borrowed_fd fd, const std::string& reason) {
     D("sync: failure: %s", reason.c_str());
 
     syncmsg msg;
@@ -258,13 +262,89 @@
     return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
 }
 
-static bool SendSyncFailErrno(int fd, const std::string& reason) {
+static bool SendSyncFailErrno(borrowed_fd fd, const std::string& reason) {
     return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
-static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
-                             uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
-                             bool do_unlink) {
+static bool handle_send_file_compressed(borrowed_fd s, unique_fd fd, uint32_t* timestamp) {
+    syncmsg msg;
+    Block decode_buffer(SYNC_DATA_MAX);
+    BrotliDecoder decoder(std::span(decode_buffer.data(), decode_buffer.size()));
+    while (true) {
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
+
+        if (msg.data.id != ID_DATA) {
+            if (msg.data.id == ID_DONE) {
+                *timestamp = msg.data.size;
+                return true;
+            }
+            SendSyncFail(s, "invalid data message");
+            return false;
+        }
+
+        Block block(msg.data.size);
+        if (!ReadFdExactly(s, block.data(), msg.data.size)) return false;
+        decoder.Append(std::move(block));
+
+        while (true) {
+            std::span<char> output;
+            BrotliDecodeResult result = decoder.Decode(&output);
+            if (result == BrotliDecodeResult::Error) {
+                SendSyncFailErrno(s, "decompress failed");
+                return false;
+            }
+
+            if (!WriteFdExactly(fd, output.data(), output.size())) {
+                SendSyncFailErrno(s, "write failed");
+                return false;
+            }
+
+            if (result == BrotliDecodeResult::NeedInput) {
+                break;
+            } else if (result == BrotliDecodeResult::MoreOutput) {
+                continue;
+            } else if (result == BrotliDecodeResult::Done) {
+                break;
+            } else {
+                LOG(FATAL) << "invalid BrotliDecodeResult: " << static_cast<int>(result);
+            }
+        }
+    }
+
+    __builtin_unreachable();
+}
+
+static bool handle_send_file_uncompressed(borrowed_fd s, unique_fd fd, uint32_t* timestamp,
+                                          std::vector<char>& buffer) {
+    syncmsg msg;
+
+    while (true) {
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
+
+        if (msg.data.id != ID_DATA) {
+            if (msg.data.id == ID_DONE) {
+                *timestamp = msg.data.size;
+                return true;
+            }
+            SendSyncFail(s, "invalid data message");
+            return false;
+        }
+
+        if (msg.data.size > buffer.size()) {  // TODO: resize buffer?
+            SendSyncFail(s, "oversize data message");
+            return false;
+        }
+        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) return false;
+        if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
+            SendSyncFailErrno(s, "write failed");
+            return false;
+        }
+    }
+}
+
+static bool handle_send_file(borrowed_fd s, const char* path, uint32_t* timestamp, uid_t uid,
+                             gid_t gid, uint64_t capabilities, mode_t mode, bool compressed,
+                             std::vector<char>& buffer, bool do_unlink) {
     int rc;
     syncmsg msg;
 
@@ -302,45 +382,33 @@
         fchmod(fd.get(), mode);
     }
 
-    rc = posix_fadvise(fd.get(), 0, 0,
-                       POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
-    if (rc != 0) {
-        D("[ Failed to fadvise: %s ]", strerror(rc));
-    }
-
-    while (true) {
-        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
-
-        if (msg.data.id != ID_DATA) {
-            if (msg.data.id == ID_DONE) {
-                *timestamp = msg.data.size;
-                break;
-            }
-            SendSyncFail(s, "invalid data message");
-            goto abort;
+    {
+        rc = posix_fadvise(fd.get(), 0, 0,
+                           POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
+        if (rc != 0) {
+            D("[ Failed to fadvise: %s ]", strerror(rc));
         }
 
-        if (msg.data.size > buffer.size()) {  // TODO: resize buffer?
-            SendSyncFail(s, "oversize data message");
-            goto abort;
+        bool result;
+        if (compressed) {
+            result = handle_send_file_compressed(s, std::move(fd), timestamp);
+        } else {
+            result = handle_send_file_uncompressed(s, std::move(fd), timestamp, buffer);
         }
 
-        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
-
-        if (!WriteFdExactly(fd.get(), &buffer[0], msg.data.size)) {
-            SendSyncFailErrno(s, "write failed");
+        if (!result) {
             goto fail;
         }
-    }
 
-    if (!update_capabilities(path, capabilities)) {
-        SendSyncFailErrno(s, "update_capabilities failed");
-        goto fail;
-    }
+        if (!update_capabilities(path, capabilities)) {
+            SendSyncFailErrno(s, "update_capabilities failed");
+            goto fail;
+        }
 
-    msg.status.id = ID_OKAY;
-    msg.status.msglen = 0;
-    return WriteFdExactly(s, &msg.status, sizeof(msg.status));
+        msg.status.id = ID_OKAY;
+        msg.status.msglen = 0;
+        return WriteFdExactly(s, &msg.status, sizeof(msg.status));
+    }
 
 fail:
     // If there's a problem on the device, we'll send an ID_FAIL message and
@@ -371,7 +439,6 @@
         if (!ReadFdExactly(s, &buffer[0], msg.data.size)) break;
     }
 
-abort:
     if (do_unlink) adb_unlink(path);
     return false;
 }
@@ -432,23 +499,8 @@
 }
 #endif
 
-static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
-    // 'spec' is of the form "/some/path,0755". Break it up.
-    size_t comma = spec.find_last_of(',');
-    if (comma == std::string::npos) {
-        SendSyncFail(s, "missing , in ID_SEND");
-        return false;
-    }
-
-    std::string path = spec.substr(0, comma);
-
-    errno = 0;
-    mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
-    if (errno != 0) {
-        SendSyncFail(s, "bad mode");
-        return false;
-    }
-
+static bool send_impl(int s, const std::string& path, mode_t mode, bool compressed,
+                      std::vector<char>& buffer) {
     // Don't delete files before copying if they are not "regular" or symlinks.
     struct stat st;
     bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
@@ -474,8 +526,8 @@
             adbd_fs_config(path.c_str(), 0, nullptr, &uid, &gid, &mode, &capabilities);
         }
 
-        result = handle_send_file(s, path.c_str(), &timestamp, uid, gid, capabilities, mode, buffer,
-                                  do_unlink);
+        result = handle_send_file(s, path.c_str(), &timestamp, uid, gid, capabilities, mode,
+                                  compressed, buffer, do_unlink);
     }
 
     if (!result) {
@@ -491,7 +543,125 @@
     return true;
 }
 
-static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+static bool do_send_v1(int s, const std::string& spec, std::vector<char>& buffer) {
+    // 'spec' is of the form "/some/path,0755". Break it up.
+    size_t comma = spec.find_last_of(',');
+    if (comma == std::string::npos) {
+        SendSyncFail(s, "missing , in ID_SEND_V1");
+        return false;
+    }
+
+    std::string path = spec.substr(0, comma);
+
+    errno = 0;
+    mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
+    if (errno != 0) {
+        SendSyncFail(s, "bad mode");
+        return false;
+    }
+
+    return send_impl(s, path, mode, false, buffer);
+}
+
+static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer) {
+    // Read the setup packet.
+    syncmsg msg;
+    int rc = ReadFdExactly(s, &msg.send_v2_setup, sizeof(msg.send_v2_setup));
+    if (rc == 0) {
+        LOG(ERROR) << "failed to read send_v2 setup packet: EOF";
+        return false;
+    } else if (rc < 0) {
+        PLOG(ERROR) << "failed to read send_v2 setup packet";
+    }
+
+    bool compressed = false;
+    if (msg.send_v2_setup.flags & kSyncFlagBrotli) {
+        msg.send_v2_setup.flags &= ~kSyncFlagBrotli;
+        compressed = true;
+    }
+    if (msg.send_v2_setup.flags) {
+        SendSyncFail(s, android::base::StringPrintf("unknown flags: %d", msg.send_v2_setup.flags));
+        return false;
+    }
+
+    errno = 0;
+    return send_impl(s, path, msg.send_v2_setup.mode, compressed, buffer);
+}
+
+static bool recv_uncompressed(borrowed_fd s, unique_fd fd, std::vector<char>& buffer) {
+    syncmsg msg;
+    msg.data.id = ID_DATA;
+    std::optional<BrotliEncoder<SYNC_DATA_MAX>> encoder;
+    while (true) {
+        int r = adb_read(fd.get(), &buffer[0], buffer.size() - sizeof(msg.data));
+        if (r <= 0) {
+            if (r == 0) break;
+            SendSyncFailErrno(s, "read failed");
+            return false;
+        }
+        msg.data.size = r;
+
+        if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool recv_compressed(borrowed_fd s, unique_fd fd) {
+    syncmsg msg;
+    msg.data.id = ID_DATA;
+
+    BrotliEncoder<SYNC_DATA_MAX> encoder;
+
+    bool sending = true;
+    while (sending) {
+        Block input(SYNC_DATA_MAX);
+        int r = adb_read(fd.get(), input.data(), input.size());
+        if (r < 0) {
+            SendSyncFailErrno(s, "read failed");
+            return false;
+        }
+
+        if (r == 0) {
+            encoder.Finish();
+        } else {
+            input.resize(r);
+            encoder.Append(std::move(input));
+        }
+
+        while (true) {
+            Block output;
+            BrotliEncodeResult result = encoder.Encode(&output);
+            if (result == BrotliEncodeResult::Error) {
+                SendSyncFailErrno(s, "compress failed");
+                return false;
+            }
+
+            if (!output.empty()) {
+                msg.data.size = output.size();
+                if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
+                    !WriteFdExactly(s, output.data(), output.size())) {
+                    return false;
+                }
+            }
+
+            if (result == BrotliEncodeResult::Done) {
+                sending = false;
+                break;
+            } else if (result == BrotliEncodeResult::NeedInput) {
+                break;
+            } else if (result == BrotliEncodeResult::MoreOutput) {
+                continue;
+            }
+        }
+    }
+
+    return true;
+}
+
+static bool recv_impl(borrowed_fd s, const char* path, bool compressed, std::vector<char>& buffer) {
     __android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
 
     unique_fd fd(adb_open(path, O_RDONLY | O_CLOEXEC));
@@ -505,26 +675,51 @@
         D("[ Failed to fadvise: %s ]", strerror(rc));
     }
 
-    syncmsg msg;
-    msg.data.id = ID_DATA;
-    while (true) {
-        int r = adb_read(fd.get(), &buffer[0], buffer.size() - sizeof(msg.data));
-        if (r <= 0) {
-            if (r == 0) break;
-            SendSyncFailErrno(s, "read failed");
-            return false;
-        }
-        msg.data.size = r;
-        if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
-            return false;
-        }
+    bool result;
+    if (compressed) {
+        result = recv_compressed(s, std::move(fd));
+    } else {
+        result = recv_uncompressed(s, std::move(fd), buffer);
     }
 
+    if (!result) {
+        return false;
+    }
+
+    syncmsg msg;
     msg.data.id = ID_DONE;
     msg.data.size = 0;
     return WriteFdExactly(s, &msg.data, sizeof(msg.data));
 }
 
+static bool do_recv_v1(borrowed_fd s, const char* path, std::vector<char>& buffer) {
+    return recv_impl(s, path, false, buffer);
+}
+
+static bool do_recv_v2(borrowed_fd s, const char* path, std::vector<char>& buffer) {
+    syncmsg msg;
+    // Read the setup packet.
+    int rc = ReadFdExactly(s, &msg.recv_v2_setup, sizeof(msg.recv_v2_setup));
+    if (rc == 0) {
+        LOG(ERROR) << "failed to read recv_v2 setup packet: EOF";
+        return false;
+    } else if (rc < 0) {
+        PLOG(ERROR) << "failed to read recv_v2 setup packet";
+    }
+
+    bool compressed = false;
+    if (msg.recv_v2_setup.flags & kSyncFlagBrotli) {
+        msg.recv_v2_setup.flags &= ~kSyncFlagBrotli;
+        compressed = true;
+    }
+    if (msg.recv_v2_setup.flags) {
+        SendSyncFail(s, android::base::StringPrintf("unknown flags: %d", msg.recv_v2_setup.flags));
+        return false;
+    }
+
+    return recv_impl(s, path, compressed, buffer);
+}
+
 static const char* sync_id_to_name(uint32_t id) {
   switch (id) {
     case ID_LSTAT_V1:
@@ -537,10 +732,14 @@
       return "list_v1";
     case ID_LIST_V2:
       return "list_v2";
-    case ID_SEND:
-      return "send";
-    case ID_RECV:
-      return "recv";
+    case ID_SEND_V1:
+        return "send_v1";
+    case ID_SEND_V2:
+        return "send_v2";
+    case ID_RECV_V1:
+        return "recv_v1";
+    case ID_RECV_V2:
+        return "recv_v2";
     case ID_QUIT:
         return "quit";
     default:
@@ -585,11 +784,17 @@
         case ID_LIST_V2:
             if (!do_list_v2(fd, name)) return false;
             break;
-        case ID_SEND:
-            if (!do_send(fd, name, buffer)) return false;
+        case ID_SEND_V1:
+            if (!do_send_v1(fd, name, buffer)) return false;
             break;
-        case ID_RECV:
-            if (!do_recv(fd, name, buffer)) return false;
+        case ID_SEND_V2:
+            if (!do_send_v2(fd, name, buffer)) return false;
+            break;
+        case ID_RECV_V1:
+            if (!do_recv_v1(fd, name, buffer)) return false;
+            break;
+        case ID_RECV_V2:
+            if (!do_recv_v2(fd, name, buffer)) return false;
             break;
         case ID_QUIT:
             return false;
diff --git a/adb/file_sync_protocol.h b/adb/file_sync_protocol.h
index 87ede0c..fd9a516 100644
--- a/adb/file_sync_protocol.h
+++ b/adb/file_sync_protocol.h
@@ -27,8 +27,10 @@
 #define ID_DENT_V1 MKID('D', 'E', 'N', 'T')
 #define ID_DENT_V2 MKID('D', 'N', 'T', '2')
 
-#define ID_SEND MKID('S', 'E', 'N', 'D')
-#define ID_RECV MKID('R', 'E', 'C', 'V')
+#define ID_SEND_V1 MKID('S', 'E', 'N', 'D')
+#define ID_SEND_V2 MKID('S', 'N', 'D', '2')
+#define ID_RECV_V1 MKID('R', 'E', 'C', 'V')
+#define ID_RECV_V2 MKID('R', 'C', 'V', '2')
 #define ID_DONE MKID('D', 'O', 'N', 'E')
 #define ID_DATA MKID('D', 'A', 'T', 'A')
 #define ID_OKAY MKID('O', 'K', 'A', 'Y')
@@ -87,6 +89,26 @@
     uint32_t namelen;
 };  // followed by `namelen` bytes of the name.
 
+enum SyncFlag : uint32_t {
+    kSyncFlagNone = 0,
+    kSyncFlagBrotli = 1,
+};
+
+// send_v1 sent the path in a buffer, followed by a comma and the mode as a string.
+// send_v2 sends just the path in the first request, and then sends another syncmsg (with the
+// same ID!) with details.
+struct __attribute__((packed)) sync_send_v2 {
+    uint32_t id;
+    uint32_t mode;
+    uint32_t flags;
+};
+
+// Likewise, recv_v1 just sent the path without any accompanying data.
+struct __attribute__((packed)) sync_recv_v2 {
+    uint32_t id;
+    uint32_t flags;
+};
+
 struct __attribute__((packed)) sync_data {
     uint32_t id;
     uint32_t size;
@@ -104,6 +126,8 @@
     sync_dent_v2 dent_v2;
     sync_data data;
     sync_status status;
+    sync_send_v2 send_v2_setup;
+    sync_recv_v2 recv_v2_setup;
 };
 
 #define SYNC_DATA_MAX (64 * 1024)
diff --git a/adb/libs/adbconnection/Android.bp b/adb/libs/adbconnection/Android.bp
index f6b0a42..ce2ab51 100644
--- a/adb/libs/adbconnection/Android.bp
+++ b/adb/libs/adbconnection/Android.bp
@@ -18,6 +18,11 @@
     use_version_lib: false,
 
     recovery_available: true,
+    apex_available: [
+        "com.android.adbd",
+        // TODO(b/151398197) remove the below
+        "//apex_available:platform",
+    ],
     compile_multilib: "both",
 }
 
diff --git a/adb/pairing_auth/aes_128_gcm.cpp b/adb/pairing_auth/aes_128_gcm.cpp
index 2978834..51520d8 100644
--- a/adb/pairing_auth/aes_128_gcm.cpp
+++ b/adb/pairing_auth/aes_128_gcm.cpp
@@ -19,7 +19,6 @@
 #include <android-base/endian.h>
 #include <android-base/logging.h>
 
-#include <openssl/crypto.h>
 #include <openssl/evp.h>
 #include <openssl/hkdf.h>
 #include <openssl/rand.h>
@@ -28,155 +27,64 @@
 namespace pairing {
 
 namespace {
-static const size_t kHkdfKeyLength = 256;
-
-struct Header {
-    uint32_t payload;
-    uint8_t iv[AES_128_GCM_IV_SIZE];
-    uint8_t tag[AES_128_GCM_TAG_SIZE];
-} __attribute__((packed));
+// Size of AES-128-GCM key, in bytes
+static constexpr size_t kHkdfKeyLength = 16;
 
 }  // namespace
 
-// static
-const EVP_CIPHER* Aes128Gcm::cipher_ = EVP_aes_128_gcm();
-
 Aes128Gcm::Aes128Gcm(const uint8_t* key_material, size_t key_material_len) {
     CHECK(key_material);
     CHECK_NE(key_material_len, 0ul);
-    context_.reset(EVP_CIPHER_CTX_new());
-    CHECK(context_.get());
 
-    // Start with a random number for our counter
-    CHECK_EQ(RAND_bytes(counter_.data(), counter_.size()), 1);
-
-    uint8_t key[kHkdfKeyLength] = {};
-    uint8_t salt[64] = "this is the salt";
-    uint8_t info[64] = "this is the info";
-    CHECK_EQ(HKDF(key, sizeof(key), EVP_sha256(), key_material, key_material_len, salt,
-                  sizeof(salt), info, sizeof(info)),
+    uint8_t key[kHkdfKeyLength];
+    uint8_t info[] = "adb pairing_auth aes-128-gcm key";
+    CHECK_EQ(HKDF(key, sizeof(key), EVP_sha256(), key_material, key_material_len, nullptr, 0, info,
+                  sizeof(info) - 1),
              1);
-    CHECK_EQ(AES_set_encrypt_key(key, sizeof(key), &aes_key_), 0);
+    CHECK(EVP_AEAD_CTX_init(context_.get(), EVP_aead_aes_128_gcm(), key, sizeof(key),
+                            EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr));
 }
 
-int Aes128Gcm::Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
-    if (out_len < EncryptedSize(in_len)) {
-        LOG(ERROR) << "out buffer size (sz=" << out_len
-                   << ") not big enough (sz=" << EncryptedSize(in_len) << ")";
-        return -1;
-    }
-    auto& header = *reinterpret_cast<Header*>(out);
-    // Place the IV in the header
-    memcpy(header.iv, counter_.data(), counter_.size());
-    int status = EVP_EncryptInit_ex(context_.get(), cipher_, nullptr,
-                                    reinterpret_cast<const uint8_t*>(&aes_key_), counter_.data());
-    counter_.Increase();
-    if (status != 1) {
-        return -1;
+std::optional<size_t> Aes128Gcm::Encrypt(const uint8_t* in, size_t in_len, uint8_t* out,
+                                         size_t out_len) {
+    std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(EVP_AEAD_CTX_aead(context_.get())), 0);
+    memcpy(nonce.data(), &enc_sequence_, sizeof(enc_sequence_));
+    size_t written_sz;
+    if (!EVP_AEAD_CTX_seal(context_.get(), out, &written_sz, out_len, nonce.data(), nonce.size(),
+                           in, in_len, nullptr, 0)) {
+        LOG(ERROR) << "Failed to encrypt (in_len=" << in_len << ", out_len=" << out_len
+                   << ", out_len_needed=" << EncryptedSize(in_len) << ")";
+        return std::nullopt;
     }
 
-    int cipherLen = 0;
-    out += sizeof(header);
-    status = EVP_EncryptUpdate(context_.get(), out, &cipherLen, in, in_len);
-    if (status != 1 || cipherLen < 0) {
-        return -1;
-    }
-
-    // Padding is enabled by default, so EVP_EncryptFinal_ex will pad any
-    // remaining partial data up to the block size.
-    int padding = 0;
-    status = EVP_EncryptFinal_ex(context_.get(), out + cipherLen, &padding);
-    if (status != 1 || padding < 0) {
-        return -1;
-    }
-
-    // Place the tag in the header
-    status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_GET_TAG, sizeof(header.tag),
-                                 header.tag);
-    if (status != 1) {
-        return -1;
-    }
-    // Place the payload size in the header
-    uint32_t totalLen = sizeof(header) + cipherLen + padding;
-    header.payload = htonl(static_cast<uint32_t>(cipherLen) + static_cast<uint32_t>(padding));
-    return totalLen;
+    ++enc_sequence_;
+    return written_sz;
 }
 
-int Aes128Gcm::Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
-    if (in_len < sizeof(Header)) {
-        return 0;
-    }
-    if (out_len < DecryptedSize(in, in_len)) {
-        return 0;
-    }
-    const auto& header = *reinterpret_cast<const Header*>(in);
-    uint32_t payload = ntohl(header.payload);
-    uint32_t expected_inlen = sizeof(Header) + payload;
-    if (in_len < expected_inlen) {
-        // Not enough data available
-        return 0;
-    }
-    // Initialized with expected IV from header
-    int status = EVP_DecryptInit_ex(context_.get(), cipher_, nullptr,
-                                    reinterpret_cast<const uint8_t*>(&aes_key_), header.iv);
-    if (status != 1) {
-        return -1;
+std::optional<size_t> Aes128Gcm::Decrypt(const uint8_t* in, size_t in_len, uint8_t* out,
+                                         size_t out_len) {
+    std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(EVP_AEAD_CTX_aead(context_.get())), 0);
+    memcpy(nonce.data(), &dec_sequence_, sizeof(dec_sequence_));
+    size_t written_sz;
+    if (!EVP_AEAD_CTX_open(context_.get(), out, &written_sz, out_len, nonce.data(), nonce.size(),
+                           in, in_len, nullptr, 0)) {
+        LOG(ERROR) << "Failed to decrypt (in_len=" << in_len << ", out_len=" << out_len
+                   << ", out_len_needed=" << DecryptedSize(in_len) << ")";
+        return std::nullopt;
     }
 
-    int decrypted_len = 0;
-    status = EVP_DecryptUpdate(context_.get(), out, &decrypted_len, in + sizeof(header), payload);
-    if (status != 1 || decrypted_len < 0) {
-        return -1;
-    }
-
-    // Set expected tag from header
-    status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_SET_TAG, sizeof(header.tag),
-                                 const_cast<uint8_t*>(header.tag));
-    if (status != 1) {
-        return -1;
-    }
-
-    // This is the padding. It can be ignored.
-    int len = 0;
-    status = EVP_DecryptFinal_ex(context_.get(), out + decrypted_len, &len);
-    if (status != 1) {
-        LOG(ERROR) << "EVP_DecryptFinal_ex failed. Tag mismatch";
-        return -1;
-    }
-
-    // Return the length without the padding.
-    return decrypted_len;
+    ++dec_sequence_;
+    return written_sz;
 }
 
 size_t Aes128Gcm::EncryptedSize(size_t size) {
-    // We need to account for block alignment of the encrypted data.
-    // According to openssl.org/docs/man1.0.2/man3/EVP_EncryptUpdate.html,
-    // "The amount of data written depends on the block alignment of the
-    // encrypted data ..."
-    // ".. the amount of data written may be anything from zero bytes to
-    // (inl + cipher_block_size - 1) ..."
-    const size_t cipher_block_size = EVP_CIPHER_block_size(cipher_);
-    size_t padding = cipher_block_size - (size % cipher_block_size);
-    if (padding != cipher_block_size) {
-        size += padding;
-    }
-    return size + sizeof(Header);
+    // https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_AEAD_CTX_seal
+    return size + EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(context_.get()));
 }
 
-size_t Aes128Gcm::DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size) {
-    if (encrypted_size < sizeof(Header)) {
-        // Not enough data yet
-        return 0;
-    }
-    auto header = reinterpret_cast<const Header*>(encrypted_data);
-    uint32_t payload = ntohl(header->payload);
-    size_t total_size = payload + sizeof(Header);
-    if (encrypted_size < total_size) {
-        // There's enough data for the header but not enough data for the
-        // payload. Indicate that there's not enough data for now.
-        return 0;
-    }
-    return payload;
+size_t Aes128Gcm::DecryptedSize(size_t size) {
+    // https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_AEAD_CTX_open
+    return size;
 }
 
 }  // namespace pairing
diff --git a/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h b/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
index 490dd12..6be5856 100644
--- a/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
+++ b/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
@@ -16,17 +16,12 @@
 
 #pragma once
 
-#include <openssl/aes.h>
-#include <openssl/cipher.h>
-
 #include <stdint.h>
 
-#include "adb/pairing/counter.h"
+#include <optional>
+#include <vector>
 
-// This is the default size of the initialization vector (iv) for AES-128-GCM
-#define AES_128_GCM_IV_SIZE 12
-// This is the full tag size for AES-128-GCM
-#define AES_128_GCM_TAG_SIZE 16
+#include <openssl/aead.h>
 
 namespace adb {
 namespace pairing {
@@ -42,7 +37,7 @@
     // suitable for decryption with this class.
     // The method returns the number of bytes placed in |out| on success and a
     // negative value if an error occurs.
-    int Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
+    std::optional<size_t> Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
     // Decrypt a block of data in |in| of length |in_len|, this consumes all data
     // in |in_len| bytes of data. The decrypted output is placed in the |out|
     // buffer of length |out_len|. On successful decryption the number of bytes in
@@ -50,22 +45,18 @@
     // The method returns the number of bytes consumed from the |in| buffer. If
     // there is not enough data available in |in| the method returns zero. If
     // an error occurs the method returns a negative value.
-    int Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
+    std::optional<size_t> Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
 
     // Return a safe amount of buffer storage needed to encrypt |size| bytes.
     size_t EncryptedSize(size_t size);
-    // Return a safe amount of buffer storage needed to decrypt the encrypted
-    // data in |encrypted_data| which is of length |encrypted_size|. Returns 0 if
-    // there is not enough data available to determine the required size.
-    size_t DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size);
-
-    static const EVP_CIPHER* cipher_;
+    // Return a safe amount of buffer storage needed to decrypt |size| bytes.
+    size_t DecryptedSize(size_t size);
 
   private:
-    bssl::UniquePtr<EVP_CIPHER_CTX> context_;
-    AES_KEY aes_key_;
-    // We're going to use this counter for our iv so that it never repeats
-    Counter<AES_128_GCM_IV_SIZE> counter_;
+    bssl::ScopedEVP_AEAD_CTX context_;
+    // Sequence numbers to use as nonces in the encryption scheme
+    uint64_t dec_sequence_ = 0;
+    uint64_t enc_sequence_ = 0;
 };
 
 }  // namespace pairing
diff --git a/adb/pairing_auth/include/adb/pairing/counter.h b/adb/pairing_auth/include/adb/pairing/counter.h
deleted file mode 100644
index 263ceb7..0000000
--- a/adb/pairing_auth/include/adb/pairing/counter.h
+++ /dev/null
@@ -1,49 +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 <stddef.h>
-#include <stdint.h>
-
-namespace adb {
-namespace pairing {
-
-template <size_t N>
-class Counter {
-  public:
-    void Increase() {
-        for (size_t i = sizeof(counter_) - 1; i < sizeof(counter_); --i) {
-            if (++counter_[i] != 0) {
-                break;
-            }
-        }
-    }
-
-    uint8_t* data() { return counter_; }
-    const uint8_t* data() const { return counter_; }
-
-    constexpr size_t size() const { return sizeof(counter_); }
-
-    uint8_t& operator[](size_t index) { return counter_[index]; }
-    const uint8_t& operator[](size_t index) const { return counter_[index]; }
-
-  private:
-    uint8_t counter_[N];
-};
-
-}  // namespace pairing
-}  // namespace adb
diff --git a/adb/pairing_auth/pairing_auth.cpp b/adb/pairing_auth/pairing_auth.cpp
index 96bc110..0ac04e6 100644
--- a/adb/pairing_auth/pairing_auth.cpp
+++ b/adb/pairing_auth/pairing_auth.cpp
@@ -75,8 +75,8 @@
     // Returns a safe buffer size for encrypting a buffer of size |len|.
     size_t SafeEncryptedSize(size_t len);
 
-    // Returns a safe buffer size for decrypting a buffer |buf|.
-    size_t SafeDecryptedSize(const Data& buf);
+    // Returns a safe buffer size for decrypting a buffer of size |len|.
+    size_t SafeDecryptedSize(size_t len);
 
   private:
     Data our_msg_;
@@ -167,12 +167,12 @@
 
     // Determine the size for the encrypted data based on the raw data.
     Data encrypted(cipher_->EncryptedSize(data.size()));
-    int bytes = cipher_->Encrypt(data.data(), data.size(), encrypted.data(), encrypted.size());
-    if (bytes < 0) {
+    auto out_size = cipher_->Encrypt(data.data(), data.size(), encrypted.data(), encrypted.size());
+    if (!out_size.has_value() || *out_size == 0) {
         LOG(ERROR) << "Unable to encrypt data";
         return Data();
     }
-    encrypted.resize(bytes);
+    encrypted.resize(*out_size);
 
     return encrypted;
 }
@@ -182,14 +182,14 @@
     CHECK(!data.empty());
 
     // Determine the size for the decrypted data based on the raw data.
-    Data decrypted(cipher_->DecryptedSize(data.data(), data.size()));
+    Data decrypted(cipher_->DecryptedSize(data.size()));
     size_t decrypted_size = decrypted.size();
-    int bytes = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
-    if (bytes <= 0) {
+    auto out_size = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
+    if (!out_size.has_value() || *out_size == 0) {
         LOG(ERROR) << "Unable to decrypt data";
         return Data();
     }
-    decrypted.resize(bytes);
+    decrypted.resize(*out_size);
 
     return decrypted;
 }
@@ -199,9 +199,9 @@
     return cipher_->EncryptedSize(len);
 }
 
-size_t PairingAuthCtx::SafeDecryptedSize(const PairingAuthCtx::Data& buf) {
+size_t PairingAuthCtx::SafeDecryptedSize(size_t len) {
     CHECK(cipher_);
-    return cipher_->DecryptedSize(buf.data(), buf.size());
+    return cipher_->DecryptedSize(len);
 }
 
 PairingAuthCtx* pairing_auth_server_new(const uint8_t* pswd, size_t len) {
@@ -271,8 +271,8 @@
     CHECK(ctx);
     CHECK(buf);
     CHECK_GT(len, 0U);
-    std::vector<uint8_t> p(buf, buf + len);
-    return ctx->SafeDecryptedSize(p);
+    // We no longer need buf for EVP_AEAD
+    return ctx->SafeDecryptedSize(len);
 }
 
 bool pairing_auth_decrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
diff --git a/adb/pairing_auth/tests/Android.bp b/adb/pairing_auth/tests/Android.bp
index 292fff5..213123d 100644
--- a/adb/pairing_auth/tests/Android.bp
+++ b/adb/pairing_auth/tests/Android.bp
@@ -18,7 +18,6 @@
     name: "adb_pairing_auth_test",
     srcs: [
         "aes_128_gcm_test.cpp",
-        "counter_test.cpp",
         "pairing_auth_test.cpp",
     ],
 
diff --git a/adb/pairing_auth/tests/aes_128_gcm_test.cpp b/adb/pairing_auth/tests/aes_128_gcm_test.cpp
index e1a20e8..55689d6 100644
--- a/adb/pairing_auth/tests/aes_128_gcm_test.cpp
+++ b/adb/pairing_auth/tests/aes_128_gcm_test.cpp
@@ -39,7 +39,7 @@
     const uint8_t msg[] = "alice and bob, sitting in a binary tree";
     uint8_t material[256];
     uint8_t encrypted[1024];
-    uint8_t out_buf[1024];
+    uint8_t out_buf[1024] = {};
 
     RAND_bytes(material, sizeof(material));
     Aes128Gcm alice(material, sizeof(material));
@@ -47,82 +47,16 @@
     ;
 
     ASSERT_GE(alice.EncryptedSize(sizeof(msg)), sizeof(msg));
-    int encrypted_size = alice.Encrypt(msg, sizeof(msg), encrypted, sizeof(encrypted));
-    ASSERT_GT(encrypted_size, 0);
+    auto encrypted_size = alice.Encrypt(msg, sizeof(msg), encrypted, sizeof(encrypted));
+    ASSERT_TRUE(encrypted_size.has_value());
+    ASSERT_GT(*encrypted_size, 0);
     size_t out_size = sizeof(out_buf);
-    ASSERT_GE(bob.DecryptedSize(encrypted, sizeof(encrypted)), sizeof(msg));
-    int decrypted_size = bob.Decrypt(encrypted, sizeof(encrypted), out_buf, out_size);
-    ASSERT_EQ(sizeof(msg), decrypted_size);
-    memset(out_buf + decrypted_size, 0, sizeof(out_buf) - decrypted_size);
+    ASSERT_GE(bob.DecryptedSize(*encrypted_size), sizeof(msg));
+    auto decrypted_size = bob.Decrypt(encrypted, *encrypted_size, out_buf, out_size);
+    ASSERT_TRUE(decrypted_size.has_value());
+    ASSERT_EQ(sizeof(msg), *decrypted_size);
     ASSERT_STREQ(reinterpret_cast<const char*>(msg), reinterpret_cast<const char*>(out_buf));
 }
 
-TEST(Aes128GcmTest, padding) {
-    // Test with block-align data as well as unaligned data.
-    const size_t cipher_block_size = EVP_CIPHER_block_size(Aes128Gcm::cipher_);
-    uint8_t material[256];
-    RAND_bytes(material, sizeof(material));
-    Aes128Gcm alice(material, sizeof(material));
-    Aes128Gcm bob(material, sizeof(material));
-    ;
-    std::vector<uint8_t> msg;
-    std::vector<uint8_t> encrypted;
-    std::vector<uint8_t> decrypted;
-
-    // Test with aligned data
-    {
-        msg.resize(cipher_block_size);
-        RAND_bytes(msg.data(), msg.size());
-
-        // encrypt
-        size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
-        ASSERT_GE(safe_encrypted_sz, msg.size());
-        encrypted.resize(safe_encrypted_sz);
-        int encrypted_size =
-                alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
-        ASSERT_GT(encrypted_size, 0);
-        ASSERT_LE(encrypted_size, safe_encrypted_sz);
-        encrypted.resize(encrypted_size);
-
-        // decrypt
-        size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
-        ASSERT_GE(safe_decrypted_size, msg.size());
-        decrypted.resize(safe_decrypted_size);
-        int decrypted_size =
-                bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
-        ASSERT_GT(decrypted_size, 0);
-        ASSERT_LE(decrypted_size, safe_decrypted_size);
-        ASSERT_EQ(msg.size(), decrypted_size);
-        ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
-    }
-
-    // Test with unaligned data
-    {
-        msg.resize(cipher_block_size + 1);
-        RAND_bytes(msg.data(), msg.size());
-
-        // encrypt
-        size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
-        ASSERT_GE(safe_encrypted_sz, msg.size());
-        encrypted.resize(safe_encrypted_sz);
-        int encrypted_size =
-                alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
-        ASSERT_GT(encrypted_size, 0);
-        ASSERT_LE(encrypted_size, safe_encrypted_sz);
-        encrypted.resize(encrypted_size);
-
-        // decrypt
-        size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
-        ASSERT_GE(safe_decrypted_size, msg.size());
-        decrypted.resize(safe_decrypted_size);
-        int decrypted_size =
-                bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
-        ASSERT_GT(decrypted_size, 0);
-        ASSERT_LE(decrypted_size, safe_decrypted_size);
-        ASSERT_EQ(msg.size(), decrypted_size);
-        ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
-    }
-}
-
 }  // namespace pairing
 }  // namespace adb
diff --git a/adb/pairing_auth/tests/counter_test.cpp b/adb/pairing_auth/tests/counter_test.cpp
deleted file mode 100644
index b338551..0000000
--- a/adb/pairing_auth/tests/counter_test.cpp
+++ /dev/null
@@ -1,70 +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 <gtest/gtest.h>
-
-#include <adb/pairing/counter.h>
-
-namespace adb {
-namespace pairing {
-
-static constexpr size_t kTestCounterSize = 13;
-static const uint8_t kZeroes[64] = {0};
-
-TEST(AdbCounterTest, size_match) {
-    Counter<kTestCounterSize> counter;
-    ASSERT_EQ(kTestCounterSize, counter.size());
-}
-
-TEST(AdbCounterTest, Increase) {
-    Counter<kTestCounterSize> counter;
-    memset(counter.data(), 0, counter.size());
-    counter.Increase();
-    EXPECT_EQ(1, counter[counter.size() - 1]);
-    EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 1));
-}
-
-TEST(AdbCounterTest, rollover_first_byte) {
-    Counter<kTestCounterSize> counter;
-    memset(counter.data(), 0, counter.size());
-    counter[counter.size() - 1] = 0xFF;
-    counter.Increase();
-    EXPECT_EQ(0, counter[counter.size() - 1]);
-    EXPECT_EQ(1, counter[counter.size() - 2]);
-    EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 2));
-}
-
-TEST(AdbCounterTest, multiple_rollover) {
-    Counter<kTestCounterSize> counter;
-    memset(counter.data(), 0xFF, counter.size());
-    memset(counter.data(), 0, counter.size() - 3);
-    counter.Increase();
-    EXPECT_EQ(0, counter[counter.size() - 5]);
-    EXPECT_EQ(1, counter[counter.size() - 4]);
-    EXPECT_EQ(0, counter[counter.size() - 3]);
-    EXPECT_EQ(0, counter[counter.size() - 2]);
-    EXPECT_EQ(0, counter[counter.size() - 1]);
-}
-
-TEST(AdbCounterTest, full_rollover) {
-    Counter<kTestCounterSize> counter;
-    memset(counter.data(), 0xFF, counter.size());
-    counter.Increase();
-    EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size()));
-}
-
-}  // namespace pairing
-}  // namespace adb
diff --git a/adb/pairing_connection/pairing_connection.cpp b/adb/pairing_connection/pairing_connection.cpp
index a26a6b4..ffe49a9 100644
--- a/adb/pairing_connection/pairing_connection.cpp
+++ b/adb/pairing_connection/pairing_connection.cpp
@@ -278,13 +278,13 @@
     if (fd < 0) {
         return false;
     }
+    fd_.reset(fd);
 
     State expected = State::Ready;
     if (!state_.compare_exchange_strong(expected, State::ExchangingMsgs)) {
         return false;
     }
 
-    fd_.reset(fd);
     cb_ = cb;
     opaque_ = opaque;
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 447a8fe..ed4a93b 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -82,6 +82,8 @@
 const char* const kFeatureAbbExec = "abb_exec";
 const char* const kFeatureRemountShell = "remount_shell";
 const char* const kFeatureTrackApp = "track_app";
+const char* const kFeatureSendRecv2 = "sendrecv_v2";
+const char* const kFeatureSendRecv2Brotli = "sendrecv_v2_brotli";
 
 namespace {
 
@@ -498,12 +500,18 @@
     auto x509 = GenerateX509Certificate(evp_pkey.get());
     auto x509_str = X509ToPEMString(x509.get());
     auto evp_str = Key::ToPEMString(evp_pkey.get());
+#ifdef _WIN32
+    int osh = cast_handle_to_int(adb_get_os_handle(fd_));
+#else
+    int osh = adb_get_os_handle(fd_);
+#endif
+
 #if ADB_HOST
     tls_ = TlsConnection::Create(TlsConnection::Role::Client,
 #else
     tls_ = TlsConnection::Create(TlsConnection::Role::Server,
 #endif
-                                 x509_str, evp_str, fd_);
+                                 x509_str, evp_str, osh);
     CHECK(tls_);
 #if ADB_HOST
     // TLS 1.3 gives the client no message if the server rejected the
@@ -1177,6 +1185,8 @@
             kFeatureAbbExec,
             kFeatureRemountShell,
             kFeatureTrackApp,
+            kFeatureSendRecv2,
+            kFeatureSendRecv2Brotli,
             // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
             // to know about. Otherwise, the client can be stuck running an old
             // version of the server even after upgrading their copy of adb.
diff --git a/adb/transport.h b/adb/transport.h
index a62349e..3a77cf6 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -81,9 +81,14 @@
 extern const char* const kFeatureAbbExec;
 // adbd properly updates symlink timestamps on push.
 extern const char* const kFeatureFixedPushSymlinkTimestamp;
+// Implement `adb remount` via shelling out to /system/bin/remount.
 extern const char* const kFeatureRemountShell;
 // adbd supports `track-app` service reporting debuggable/profileable apps.
 extern const char* const kFeatureTrackApp;
+// adbd supports version 2 of send/recv.
+extern const char* const kFeatureSendRecv2;
+// adbd supports brotli for send/recv v2.
+extern const char* const kFeatureSendRecv2Brotli;
 
 TransportId NextTransportId();
 
diff --git a/adb/types.h b/adb/types.h
index c619fff..deca7ea 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -150,6 +150,22 @@
     IOVector& operator=(const IOVector& copy) = delete;
     IOVector& operator=(IOVector&& move) noexcept;
 
+    const value_type* front_data() const {
+        if (chain_.empty()) {
+            return nullptr;
+        }
+
+        return chain_.front().data() + begin_offset_;
+    }
+
+    size_type front_size() const {
+        if (chain_.empty()) {
+            return 0;
+        }
+
+        return chain_.front().size() - begin_offset_;
+    }
+
     size_type size() const { return chain_length_ - begin_offset_; }
     bool empty() const { return size() == 0; }
 
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index 6bee28c..93d13bd 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -3,6 +3,11 @@
     cflags: ["-Wall", "-Wextra", "-Werror"],
     host_supported: true,
     recovery_available: true,
+    apex_available: [
+        "com.android.adbd",
+        // TODO(b/151398197) remove the below
+        "//apex_available:platform",
+    ],
     target: {
         windows: {
             enabled: true,
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 4ab439d..44e7933 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -28,6 +28,10 @@
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
     recovery_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.adbd",
+    ],
     host_supported: true,
     srcs: [
         "AsyncIO.cpp",
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index c6bda4a..ad86a4e 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -4,6 +4,11 @@
     name: "libqemu_pipe",
     vendor_available: true,
     recovery_available: true,
+    apex_available: [
+        "com.android.adbd",
+        // TODO(b/151398197) remove the below
+        "//apex_available:platform",
+    ],
     sanitize: {
         misc_undefined: ["integer"],
     },