Merge "Store reason in case of userspace-reboot watchdog reboot"
diff --git a/adb/Android.bp b/adb/Android.bp
index c71138a..32581a2 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -341,6 +341,8 @@
         "client/line_printer.cpp",
         "client/fastdeploy.cpp",
         "client/fastdeploycallbacks.cpp",
+        "client/incremental.cpp",
+        "client/incremental_server.cpp",
         "shell_service_protocol.cpp",
     ],
 
@@ -360,6 +362,7 @@
         "libfastdeploy_host",
         "libdiagnose_usb",
         "liblog",
+        "liblz4",
         "libmdnssd",
         "libprotobuf-cpp-lite",
         "libusb",
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 80f146c..cea24fe 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -118,22 +118,22 @@
         return;
     }
 
-    std::unordered_map<std::string, int> trace_flags = {
-        {"1", -1},
-        {"all", -1},
-        {"adb", ADB},
-        {"sockets", SOCKETS},
-        {"packets", PACKETS},
-        {"rwx", RWX},
-        {"usb", USB},
-        {"sync", SYNC},
-        {"sysdeps", SYSDEPS},
-        {"transport", TRANSPORT},
-        {"jdwp", JDWP},
-        {"services", SERVICES},
-        {"auth", AUTH},
-        {"fdevent", FDEVENT},
-        {"shell", SHELL}};
+    std::unordered_map<std::string, int> trace_flags = {{"1", -1},
+                                                        {"all", -1},
+                                                        {"adb", ADB},
+                                                        {"sockets", SOCKETS},
+                                                        {"packets", PACKETS},
+                                                        {"rwx", RWX},
+                                                        {"usb", USB},
+                                                        {"sync", SYNC},
+                                                        {"sysdeps", SYSDEPS},
+                                                        {"transport", TRANSPORT},
+                                                        {"jdwp", JDWP},
+                                                        {"services", SERVICES},
+                                                        {"auth", AUTH},
+                                                        {"fdevent", FDEVENT},
+                                                        {"shell", SHELL},
+                                                        {"incremental", INCREMENTAL}};
 
     std::vector<std::string> elements = android::base::Split(trace_setting, " ");
     for (const auto& elem : elements) {
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 1d2c8c7..ed4be88 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -25,19 +25,20 @@
  * the adb_trace_init() function implemented in adb_trace.cpp.
  */
 enum AdbTrace {
-    ADB = 0,   /* 0x001 */
+    ADB = 0, /* 0x001 */
     SOCKETS,
     PACKETS,
     TRANSPORT,
-    RWX,       /* 0x010 */
+    RWX, /* 0x010 */
     USB,
     SYNC,
     SYSDEPS,
-    JDWP,      /* 0x100 */
+    JDWP, /* 0x100 */
     SERVICES,
     AUTH,
     FDEVENT,
-    SHELL
+    SHELL,
+    INCREMENTAL,
 };
 
 #define VLOG_IS_ON(TAG) \
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 2bcd0a6..982a96b 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -36,8 +36,10 @@
 #include "client/file_sync_client.h"
 #include "commandline.h"
 #include "fastdeploy.h"
+#include "incremental.h"
 
 static constexpr int kFastDeployMinApi = 24;
+static constexpr int kIncrementalMinApi = 29;
 
 namespace {
 
@@ -45,8 +47,8 @@
     INSTALL_DEFAULT,
     INSTALL_PUSH,
     INSTALL_STREAM,
+    INSTALL_INCREMENTAL,
 };
-
 }
 
 static bool can_use_feature(const char* feature) {
@@ -70,6 +72,10 @@
     return can_use_feature(kFeatureApex);
 }
 
+static bool is_abb_exec_supported() {
+    return can_use_feature(kFeatureAbbExec);
+}
+
 static int pm_command(int argc, const char** argv) {
     std::string cmd = "pm";
 
@@ -193,14 +199,14 @@
     posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
 #endif
 
-    const bool use_abb = can_use_feature(kFeatureAbbExec);
+    const bool use_abb_exec = is_abb_exec_supported();
     std::string error;
-    std::vector<std::string> cmd_args = {use_abb ? "package" : "exec:cmd package"};
+    std::vector<std::string> cmd_args = {use_abb_exec ? "package" : "exec:cmd package"};
     cmd_args.reserve(argc + 3);
 
     // don't copy the APK name, but, copy the rest of the arguments as-is
     while (argc-- > 1) {
-        if (use_abb) {
+        if (use_abb_exec) {
             cmd_args.push_back(*argv++);
         } else {
             cmd_args.push_back(escape_arg(*argv++));
@@ -217,7 +223,7 @@
     }
 
     unique_fd remote_fd;
-    if (use_abb) {
+    if (use_abb_exec) {
         remote_fd = send_abb_exec_command(cmd_args, &error);
     } else {
         remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
@@ -287,8 +293,60 @@
     return result;
 }
 
+template <class TimePoint>
+static int msBetween(TimePoint start, TimePoint end) {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
+
+static int install_app_incremental(int argc, const char** argv) {
+    printf("Performing Incremental Install\n");
+    using clock = std::chrono::high_resolution_clock;
+    const auto start = clock::now();
+    int first_apk = -1;
+    int last_apk = -1;
+    std::string cert_path;
+    bool wait = false;
+    std::vector<std::string_view> args = {"package"};
+    for (int i = 0; i < argc; ++i) {
+        const auto arg = std::string_view(argv[i]);
+        if (android::base::EndsWithIgnoreCase(arg, ".apk")) {
+            last_apk = i;
+            if (first_apk == -1) {
+                first_apk = i;
+            }
+        } else if (arg == "--wait") {
+            wait = true;
+        } else if (arg.starts_with("install-")) {
+            // incremental installation command on the device is the same for all its variations in
+            // the adb, e.g. install-multiple or install-multi-package
+            args.push_back("install");
+        } else {
+            args.push_back(arg);
+        }
+    }
+
+    if (first_apk == -1) error_exit("Need at least one APK file on command line");
+
+    const auto afterApk = clock::now();
+
+    auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1});
+    if (!server_process) {
+        return -1;
+    }
+
+    const auto end = clock::now();
+    printf("Install command complete (ms: %d total, %d apk prep, %d install)\n",
+           msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end));
+
+    if (wait) {
+        (*server_process).wait();
+    }
+
+    return 0;
+}
+
 int install_app(int argc, const char** argv) {
-    std::vector<int> processedArgIndicies;
+    std::vector<int> processedArgIndices;
     InstallMode installMode = INSTALL_DEFAULT;
     bool use_fastdeploy = false;
     bool is_reinstall = false;
@@ -296,30 +354,42 @@
 
     for (int i = 1; i < argc; i++) {
         if (!strcmp(argv[i], "--streaming")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             installMode = INSTALL_STREAM;
         } else if (!strcmp(argv[i], "--no-streaming")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             installMode = INSTALL_PUSH;
         } else if (!strcmp(argv[i], "-r")) {
-            // Note that this argument is not added to processedArgIndicies because it
+            // Note that this argument is not added to processedArgIndices because it
             // must be passed through to pm
             is_reinstall = true;
         } else if (!strcmp(argv[i], "--fastdeploy")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             use_fastdeploy = true;
         } else if (!strcmp(argv[i], "--no-fastdeploy")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             use_fastdeploy = false;
         } else if (!strcmp(argv[i], "--force-agent")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateAlways;
         } else if (!strcmp(argv[i], "--date-check-agent")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
         } else if (!strcmp(argv[i], "--version-check-agent")) {
-            processedArgIndicies.push_back(i);
+            processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+        } else if (!strcmp(argv[i], "--incremental")) {
+            processedArgIndices.push_back(i);
+            installMode = INSTALL_INCREMENTAL;
+        } else if (!strcmp(argv[i], "--no-incremental")) {
+            processedArgIndices.push_back(i);
+            installMode = INSTALL_DEFAULT;
+        }
+    }
+
+    if (installMode == INSTALL_INCREMENTAL) {
+        if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) {
+            error_exit("Attempting to use incremental install on unsupported device");
         }
     }
 
@@ -341,8 +411,8 @@
 
     std::vector<const char*> passthrough_argv;
     for (int i = 0; i < argc; i++) {
-        if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
-            processedArgIndicies.end()) {
+        if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
+            processedArgIndices.end()) {
             passthrough_argv.push_back(argv[i]);
         }
     }
@@ -357,6 +427,8 @@
         case INSTALL_STREAM:
             return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
                                         use_fastdeploy);
+        case INSTALL_INCREMENTAL:
+            return install_app_incremental(passthrough_argv.size(), passthrough_argv.data());
         case INSTALL_DEFAULT:
         default:
             return 1;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index c302965..84c0e01 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -60,6 +60,7 @@
 #include "client/file_sync_client.h"
 #include "commandline.h"
 #include "fastdeploy.h"
+#include "incremental_server.h"
 #include "services.h"
 #include "shell_protocol.h"
 #include "sysdeps/chrono.h"
@@ -1959,6 +1960,18 @@
                 error_exit("usage: adb reconnect [device|offline]");
             }
         }
+    } else if (!strcmp(argv[0], "inc-server")) {
+        if (argc < 3) {
+            error_exit("usage: adb inc-server FD FILE1 FILE2 ...");
+        }
+        int fd = atoi(argv[1]);
+        if (fd < 3) {
+            // Disallow invalid FDs and stdin/out/err as well.
+            error_exit("Invalid fd number given: %d", fd);
+        }
+        fd = adb_register_socket(fd);
+        close_on_exec(fd);
+        return incremental::serve(fd, argc - 2, argv + 2);
     }
 
     error_exit("unknown command %s", argv[0]);
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 5fa0edb..c5fc12f 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -81,17 +81,21 @@
 }  // namespace
 
 int get_device_api_level() {
-    REPORT_FUNC_TIME();
-    std::vector<char> sdk_version_output_buffer;
-    std::vector<char> sdk_version_error_buffer;
-    int api_level = -1;
+    static const int api_level = [] {
+        REPORT_FUNC_TIME();
+        std::vector<char> sdk_version_output_buffer;
+        std::vector<char> sdk_version_error_buffer;
+        int api_level = -1;
 
-    int statusCode = capture_shell_command("getprop ro.build.version.sdk",
-                                           &sdk_version_output_buffer, &sdk_version_error_buffer);
-    if (statusCode == 0 && sdk_version_output_buffer.size() > 0) {
-        api_level = strtol((char*)sdk_version_output_buffer.data(), NULL, 10);
-    }
+        int status_code =
+                capture_shell_command("getprop ro.build.version.sdk", &sdk_version_output_buffer,
+                                      &sdk_version_error_buffer);
+        if (status_code == 0 && sdk_version_output_buffer.size() > 0) {
+            api_level = strtol((char*)sdk_version_output_buffer.data(), nullptr, 10);
+        }
 
+        return api_level;
+    }();
     return api_level;
 }
 
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
new file mode 100644
index 0000000..b728a6b
--- /dev/null
+++ b/adb/client/incremental.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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 "incremental.h"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <openssl/base64.h>
+
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "commandline.h"
+#include "sysdeps.h"
+
+#ifndef _WIN32
+#include <endian.h>
+#else
+#define be32toh(x) _byteswap_ulong(x)
+#endif
+
+using namespace std::literals;
+
+namespace incremental {
+
+namespace {
+
+static constexpr auto IDSIG = ".idsig"sv;
+
+using android::base::StringPrintf;
+
+using Size = int64_t;
+
+static inline int32_t read_int32(borrowed_fd fd) {
+    int32_t result;
+    ReadFully(fd, &result, sizeof(result));
+    return result;
+}
+
+static inline int32_t read_be_int32(borrowed_fd fd) {
+    return int32_t(be32toh(read_int32(fd)));
+}
+
+static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
+    int32_t be_val = read_int32(fd);
+    auto old_size = bytes->size();
+    bytes->resize(old_size + sizeof(be_val));
+    memcpy(bytes->data() + old_size, &be_val, sizeof(be_val));
+}
+
+static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
+    int32_t be_size = read_int32(fd);
+    int32_t size = int32_t(be32toh(be_size));
+    auto old_size = bytes->size();
+    bytes->resize(old_size + sizeof(be_size) + size);
+    memcpy(bytes->data() + old_size, &be_size, sizeof(be_size));
+    ReadFully(fd, bytes->data() + old_size + sizeof(be_size), size);
+}
+
+static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
+    std::vector<char> result;
+    append_int(fd, &result);              // version
+    append_bytes_with_size(fd, &result);  // verityRootHash
+    append_bytes_with_size(fd, &result);  // v3Digest
+    append_bytes_with_size(fd, &result);  // pkcs7SignatureBlock
+    auto tree_size = read_be_int32(fd);   // size of the verity tree
+    return {std::move(result), tree_size};
+}
+
+static inline Size verity_tree_size_for_file(Size fileSize) {
+    constexpr int INCFS_DATA_FILE_BLOCK_SIZE = 4096;
+    constexpr int SHA256_DIGEST_SIZE = 32;
+    constexpr int digest_size = SHA256_DIGEST_SIZE;
+    constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
+
+    Size total_tree_block_count = 0;
+
+    auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
+    auto hash_block_count = block_count;
+    for (auto i = 0; hash_block_count > 1; i++) {
+        hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+        total_tree_block_count += hash_block_count;
+    }
+    return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
+}
+
+// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
+                                                                   std::string signature_file) {
+    signature_file += IDSIG;
+
+    struct stat st;
+    if (stat(signature_file.c_str(), &st)) {
+        fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
+        return {};
+    }
+
+    unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) {
+        fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
+        return {};
+    }
+
+    auto [signature, tree_size] = read_id_sig_headers(fd);
+    if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
+        fprintf(stderr,
+                "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+                signature_file.c_str(), (long long)tree_size, (long long)expected);
+        return {};
+    }
+
+    size_t base64_len = 0;
+    if (!EVP_EncodedLength(&base64_len, signature.size())) {
+        fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
+        return {};
+    }
+    std::string encoded_signature;
+    encoded_signature.resize(base64_len);
+    encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
+                                             (const uint8_t*)signature.data(), signature.size()));
+
+    return {std::move(fd), std::move(encoded_signature)};
+}
+
+// Send install-incremental to the device along with properly configured file descriptors in
+// streaming format. Once connection established, send all fs-verity tree bytes.
+static unique_fd start_install(const std::vector<std::string>& files) {
+    std::vector<std::string> command_args{"package", "install-incremental"};
+
+    // fd's with positions at the beginning of fs-verity
+    std::vector<unique_fd> signature_fds;
+    signature_fds.reserve(files.size());
+    for (int i = 0, size = files.size(); i < size; ++i) {
+        const auto& file = files[i];
+
+        struct stat st;
+        if (stat(file.c_str(), &st)) {
+            fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+            return {};
+        }
+
+        auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file);
+        if (!signature_fd.ok()) {
+            return {};
+        }
+
+        auto file_desc =
+                StringPrintf("%s:%lld:%s:%s", android::base::Basename(file).c_str(),
+                             (long long)st.st_size, std::to_string(i).c_str(), signature.c_str());
+        command_args.push_back(std::move(file_desc));
+
+        signature_fds.push_back(std::move(signature_fd));
+    }
+
+    std::string error;
+    auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
+    if (connection_fd < 0) {
+        fprintf(stderr, "Failed to run: %s, error: %s\n",
+                android::base::Join(command_args, " ").c_str(), error.c_str());
+        return {};
+    }
+
+    // Pushing verity trees for all installation files.
+    for (auto&& local_fd : signature_fds) {
+        if (!copy_to_file(local_fd.get(), connection_fd.get())) {
+            fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
+            return {};
+        }
+    }
+
+    return connection_fd;
+}
+
+}  // namespace
+
+std::optional<Process> install(std::vector<std::string> files) {
+    auto connection_fd = start_install(files);
+    if (connection_fd < 0) {
+        fprintf(stderr, "adb: failed to initiate installation on device.\n");
+        return {};
+    }
+
+    std::string adb_path = android::base::GetExecutablePath();
+
+    auto osh = adb_get_os_handle(connection_fd.get());
+#ifdef _WIN32
+    auto fd_param = std::to_string(reinterpret_cast<intptr_t>(osh));
+#else /* !_WIN32 a.k.a. Unix */
+    auto fd_param = std::to_string(osh);
+#endif
+
+    std::vector<std::string> args(std::move(files));
+    args.insert(args.begin(), {"inc-server", fd_param});
+    auto child = adb_launch_process(adb_path, std::move(args), {connection_fd.get()});
+    if (!child) {
+        fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+        return {};
+    }
+
+    auto killOnExit = [](Process* p) { p->kill(); };
+    std::unique_ptr<Process, decltype(killOnExit)> serverKiller(&child, killOnExit);
+    // TODO: Terminate server process if installation fails.
+    serverKiller.release();
+
+    return child;
+}
+
+}  // namespace incremental
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
new file mode 100644
index 0000000..4b9f6bd
--- /dev/null
+++ b/adb/client/incremental.h
@@ -0,0 +1,30 @@
+/*
+ * 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 "adb_unique_fd.h"
+
+#include <optional>
+#include <string>
+
+#include "sysdeps.h"
+
+namespace incremental {
+
+std::optional<Process> install(std::vector<std::string> files);
+
+}  // namespace incremental
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
new file mode 100644
index 0000000..d9fd77a
--- /dev/null
+++ b/adb/client/incremental_server.cpp
@@ -0,0 +1,545 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG INCREMENTAL
+
+#include "incremental_server.h"
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+#ifndef _WIN32
+#include <endian.h>
+#else
+#define be64toh(x) _byteswap_uint64(x)
+#define be32toh(x) _byteswap_ulong(x)
+#define be16toh(x) _byteswap_ushort(x)
+#define htobe64(x) _byteswap_uint64(x)
+#define htobe32(x) _byteswap_ulong(x)
+#define htobe16(x) _byteswap_ushort(x)
+#endif
+
+#include <android-base/strings.h>
+#include <lz4.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <fstream>
+#include <thread>
+#include <type_traits>
+#include <unordered_set>
+
+namespace incremental {
+
+static constexpr int kBlockSize = 4096;
+static constexpr int kCompressedSizeMax = kBlockSize * 0.95;
+static constexpr short kCompressionNone = 0;
+static constexpr short kCompressionLZ4 = 1;
+static constexpr int kCompressBound = std::max(kBlockSize, LZ4_COMPRESSBOUND(kBlockSize));
+static constexpr auto kReadBufferSize = 128 * 1024;
+
+using BlockSize = int16_t;
+using FileId = int16_t;
+using BlockIdx = int32_t;
+using NumBlocks = int32_t;
+using CompressionType = int16_t;
+using RequestType = int16_t;
+using ChunkHeader = int32_t;
+using MagicType = uint32_t;
+
+static constexpr MagicType INCR = 0x494e4352;  // LE INCR
+
+static constexpr RequestType EXIT = 0;
+static constexpr RequestType BLOCK_MISSING = 1;
+static constexpr RequestType PREFETCH = 2;
+
+static constexpr inline int64_t roundDownToBlockOffset(int64_t val) {
+    return val & ~(kBlockSize - 1);
+}
+
+static constexpr inline int64_t roundUpToBlockOffset(int64_t val) {
+    return roundDownToBlockOffset(val + kBlockSize - 1);
+}
+
+static constexpr inline NumBlocks numBytesToNumBlocks(int64_t bytes) {
+    return roundUpToBlockOffset(bytes) / kBlockSize;
+}
+
+static constexpr inline off64_t blockIndexToOffset(BlockIdx blockIdx) {
+    return static_cast<off64_t>(blockIdx) * kBlockSize;
+}
+
+template <typename T>
+static inline constexpr T toBigEndian(T t) {
+    using unsigned_type = std::make_unsigned_t<T>;
+    if constexpr (std::is_same_v<T, int16_t>) {
+        return htobe16(static_cast<unsigned_type>(t));
+    } else if constexpr (std::is_same_v<T, int32_t>) {
+        return htobe32(static_cast<unsigned_type>(t));
+    } else if constexpr (std::is_same_v<T, int64_t>) {
+        return htobe64(static_cast<unsigned_type>(t));
+    } else {
+        return t;
+    }
+}
+
+template <typename T>
+static inline constexpr T readBigEndian(void* data) {
+    using unsigned_type = std::make_unsigned_t<T>;
+    if constexpr (std::is_same_v<T, int16_t>) {
+        return static_cast<T>(be16toh(*reinterpret_cast<unsigned_type*>(data)));
+    } else if constexpr (std::is_same_v<T, int32_t>) {
+        return static_cast<T>(be32toh(*reinterpret_cast<unsigned_type*>(data)));
+    } else if constexpr (std::is_same_v<T, int64_t>) {
+        return static_cast<T>(be64toh(*reinterpret_cast<unsigned_type*>(data)));
+    } else {
+        return T();
+    }
+}
+
+// Received from device
+// !Does not include magic!
+struct RequestCommand {
+    RequestType request_type;  // 2 bytes
+    FileId file_id;            // 2 bytes
+    union {
+        BlockIdx block_idx;
+        NumBlocks num_blocks;
+    };  // 4 bytes
+} __attribute__((packed));
+
+// Placed before actual data bytes of each block
+struct ResponseHeader {
+    FileId file_id;                    // 2 bytes
+    CompressionType compression_type;  // 2 bytes
+    BlockIdx block_idx;                // 4 bytes
+    BlockSize block_size;              // 2 bytes
+} __attribute__((packed));
+
+// Holds streaming state for a file
+class File {
+  public:
+    // Plain file
+    File(const char* filepath, FileId id, int64_t size, unique_fd fd) : File(filepath, id, size) {
+        this->fd_ = std::move(fd);
+    }
+    int64_t ReadBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed,
+                      std::string* error) const {
+        char* buf_ptr = static_cast<char*>(buf);
+        int64_t bytes_read = -1;
+        const off64_t offsetStart = blockIndexToOffset(block_idx);
+        bytes_read = adb_pread(fd_, &buf_ptr[sizeof(ResponseHeader)], kBlockSize, offsetStart);
+        return bytes_read;
+    }
+
+    const unique_fd& RawFd() const { return fd_; }
+
+    std::vector<bool> sentBlocks;
+    NumBlocks sentBlocksCount;
+
+    const char* const filepath;
+    const FileId id;
+    const int64_t size;
+
+  private:
+    File(const char* filepath, FileId id, int64_t size) : filepath(filepath), id(id), size(size) {
+        sentBlocks.resize(numBytesToNumBlocks(size));
+    }
+    unique_fd fd_;
+};
+
+class IncrementalServer {
+  public:
+    IncrementalServer(unique_fd fd, std::vector<File> files)
+        : adb_fd_(std::move(fd)), files_(std::move(files)) {
+        buffer_.reserve(kReadBufferSize);
+    }
+
+    bool Serve();
+
+  private:
+    struct PrefetchState {
+        const File* file;
+        BlockIdx overallIndex = 0;
+        BlockIdx overallEnd = 0;
+
+        PrefetchState(const File& f) : file(&f), overallEnd((BlockIdx)f.sentBlocks.size()) {}
+        PrefetchState(const File& f, BlockIdx start, int count)
+            : file(&f),
+              overallIndex(start),
+              overallEnd(std::min<BlockIdx>(start + count, f.sentBlocks.size())) {}
+
+        bool done() const { return overallIndex >= overallEnd; }
+    };
+
+    bool SkipToRequest(void* buffer, size_t* size, bool blocking);
+    std::optional<RequestCommand> ReadRequest(bool blocking);
+
+    void erase_buffer_head(int count) { buffer_.erase(buffer_.begin(), buffer_.begin() + count); }
+
+    enum class SendResult { Sent, Skipped, Error };
+    SendResult SendBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+    bool SendDone();
+    void RunPrefetching();
+
+    void Send(const void* data, size_t size, bool flush);
+    void Flush();
+    using TimePoint = decltype(std::chrono::high_resolution_clock::now());
+    bool Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent);
+
+    unique_fd const adb_fd_;
+    std::vector<File> files_;
+
+    // Incoming data buffer.
+    std::vector<char> buffer_;
+
+    std::deque<PrefetchState> prefetches_;
+    int compressed_ = 0, uncompressed_ = 0;
+    long long sentSize_ = 0;
+
+    std::vector<char> pendingBlocks_;
+};
+
+bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
+    while (true) {
+        // Looking for INCR magic.
+        bool magic_found = false;
+        int bcur = 0;
+        for (int bsize = buffer_.size(); bcur + 4 < bsize; ++bcur) {
+            uint32_t magic = be32toh(*(uint32_t*)(buffer_.data() + bcur));
+            if (magic == INCR) {
+                magic_found = true;
+                break;
+            }
+        }
+
+        if (bcur > 0) {
+            // Stream the rest to stderr.
+            fprintf(stderr, "%.*s", bcur, buffer_.data());
+            erase_buffer_head(bcur);
+        }
+
+        if (magic_found && buffer_.size() >= *size + sizeof(INCR)) {
+            // fine, return
+            memcpy(buffer, buffer_.data() + sizeof(INCR), *size);
+            erase_buffer_head(*size + sizeof(INCR));
+            return true;
+        }
+
+        adb_pollfd pfd = {adb_fd_.get(), POLLIN, 0};
+        auto res = adb_poll(&pfd, 1, blocking ? -1 : 0);
+        if (res != 1) {
+            if (res < 0) {
+                fprintf(stderr, "Failed to poll: %s\n", strerror(errno));
+                return false;
+            }
+            *size = 0;
+            return true;
+        }
+
+        auto bsize = buffer_.size();
+        buffer_.resize(kReadBufferSize);
+        int r = adb_read(adb_fd_, buffer_.data() + bsize, kReadBufferSize - bsize);
+        if (r > 0) {
+            buffer_.resize(bsize + r);
+            continue;
+        }
+
+        if (r == -1) {
+            fprintf(stderr, "Failed to read from fd %d: %d. Exit\n", adb_fd_.get(), errno);
+            return true;
+        }
+
+        // socket is closed
+        return false;
+    }
+}
+
+std::optional<RequestCommand> IncrementalServer::ReadRequest(bool blocking) {
+    uint8_t commandBuf[sizeof(RequestCommand)];
+    auto size = sizeof(commandBuf);
+    if (!SkipToRequest(&commandBuf, &size, blocking)) {
+        return {{EXIT}};
+    }
+    if (size < sizeof(RequestCommand)) {
+        return {};
+    }
+    RequestCommand request;
+    request.request_type = readBigEndian<RequestType>(&commandBuf[0]);
+    request.file_id = readBigEndian<FileId>(&commandBuf[2]);
+    request.block_idx = readBigEndian<BlockIdx>(&commandBuf[4]);
+    return request;
+}
+
+auto IncrementalServer::SendBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
+    auto& file = files_[fileId];
+    if (blockIdx >= static_cast<long>(file.sentBlocks.size())) {
+        fprintf(stderr, "Failed to read file %s at block %" PRId32 " (past end).\n", file.filepath,
+                blockIdx);
+        return SendResult::Error;
+    }
+    if (file.sentBlocks[blockIdx]) {
+        return SendResult::Skipped;
+    }
+    std::string error;
+    char raw[sizeof(ResponseHeader) + kBlockSize];
+    bool isZipCompressed = false;
+    const int64_t bytesRead = file.ReadBlock(blockIdx, &raw, &isZipCompressed, &error);
+    if (bytesRead < 0) {
+        fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%s).\n", file.filepath, blockIdx,
+                error.c_str());
+        return SendResult::Error;
+    }
+
+    ResponseHeader* header = nullptr;
+    char data[sizeof(ResponseHeader) + kCompressBound];
+    char* compressed = data + sizeof(*header);
+    int16_t compressedSize = 0;
+    if (!isZipCompressed) {
+        compressedSize =
+                LZ4_compress_default(raw + sizeof(*header), compressed, bytesRead, kCompressBound);
+    }
+    int16_t blockSize;
+    if (compressedSize > 0 && compressedSize < kCompressedSizeMax) {
+        ++compressed_;
+        blockSize = compressedSize;
+        header = reinterpret_cast<ResponseHeader*>(data);
+        header->compression_type = toBigEndian(kCompressionLZ4);
+    } else {
+        ++uncompressed_;
+        blockSize = bytesRead;
+        header = reinterpret_cast<ResponseHeader*>(raw);
+        header->compression_type = toBigEndian(kCompressionNone);
+    }
+
+    header->file_id = toBigEndian(fileId);
+    header->block_size = toBigEndian(blockSize);
+    header->block_idx = toBigEndian(blockIdx);
+
+    file.sentBlocks[blockIdx] = true;
+    file.sentBlocksCount += 1;
+    Send(header, sizeof(*header) + blockSize, flush);
+    return SendResult::Sent;
+}
+
+bool IncrementalServer::SendDone() {
+    ResponseHeader header;
+    header.file_id = -1;
+    header.compression_type = 0;
+    header.block_idx = 0;
+    header.block_size = 0;
+    Send(&header, sizeof(header), true);
+    return true;
+}
+
+void IncrementalServer::RunPrefetching() {
+    constexpr auto kPrefetchBlocksPerIteration = 128;
+
+    int blocksToSend = kPrefetchBlocksPerIteration;
+    while (!prefetches_.empty() && blocksToSend > 0) {
+        auto& prefetch = prefetches_.front();
+        const auto& file = *prefetch.file;
+        for (auto& i = prefetch.overallIndex; blocksToSend > 0 && i < prefetch.overallEnd; ++i) {
+            if (auto res = SendBlock(file.id, i); res == SendResult::Sent) {
+                --blocksToSend;
+            } else if (res == SendResult::Error) {
+                fprintf(stderr, "Failed to send block %" PRId32 "\n", i);
+            }
+        }
+        if (prefetch.done()) {
+            prefetches_.pop_front();
+        }
+    }
+}
+
+void IncrementalServer::Send(const void* data, size_t size, bool flush) {
+    constexpr auto kChunkFlushSize = 31 * kBlockSize;
+
+    if (pendingBlocks_.empty()) {
+        pendingBlocks_.resize(sizeof(ChunkHeader));
+    }
+    pendingBlocks_.insert(pendingBlocks_.end(), static_cast<const char*>(data),
+                          static_cast<const char*>(data) + size);
+    if (flush || pendingBlocks_.size() > kChunkFlushSize) {
+        Flush();
+    }
+}
+
+void IncrementalServer::Flush() {
+    if (pendingBlocks_.empty()) {
+        return;
+    }
+
+    *(ChunkHeader*)pendingBlocks_.data() =
+            toBigEndian<int32_t>(pendingBlocks_.size() - sizeof(ChunkHeader));
+    if (!WriteFdExactly(adb_fd_, pendingBlocks_.data(), pendingBlocks_.size())) {
+        fprintf(stderr, "Failed to write %d bytes\n", int(pendingBlocks_.size()));
+    }
+    sentSize_ += pendingBlocks_.size();
+    pendingBlocks_.clear();
+}
+
+bool IncrementalServer::Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent) {
+    using namespace std::chrono;
+    auto endTime = high_resolution_clock::now();
+    fprintf(stderr,
+            "Connection failed or received exit command. Exit.\n"
+            "Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
+            "%d, mb: %.3f\n"
+            "Total time taken: %.3fms\n",
+            missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
+            duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() /
+                    1000.0);
+    return true;
+}
+
+bool IncrementalServer::Serve() {
+    // Initial handshake to verify connection is still alive
+    if (!SendOkay(adb_fd_)) {
+        fprintf(stderr, "Connection is dead. Abort.\n");
+        return false;
+    }
+
+    std::unordered_set<FileId> prefetchedFiles;
+    bool doneSent = false;
+    int missesCount = 0;
+    int missesSent = 0;
+
+    using namespace std::chrono;
+    std::optional<TimePoint> startTime;
+
+    while (true) {
+        if (!doneSent && prefetches_.empty() &&
+            std::all_of(files_.begin(), files_.end(), [](const File& f) {
+                return f.sentBlocksCount == NumBlocks(f.sentBlocks.size());
+            })) {
+            fprintf(stdout, "All files should be loaded. Notifying the device.\n");
+            SendDone();
+            doneSent = true;
+        }
+
+        const bool blocking = prefetches_.empty();
+        if (blocking) {
+            // We've no idea how long the blocking call is, so let's flush whatever is still unsent.
+            Flush();
+        }
+        auto request = ReadRequest(blocking);
+
+        if (!startTime) {
+            startTime = high_resolution_clock::now();
+        }
+
+        if (request) {
+            FileId fileId = request->file_id;
+            BlockIdx blockIdx = request->block_idx;
+
+            switch (request->request_type) {
+                case EXIT: {
+                    // Stop everything.
+                    return Exit(startTime, missesCount, missesSent);
+                }
+                case BLOCK_MISSING: {
+                    ++missesCount;
+                    // Sends one single block ASAP.
+                    if (fileId < 0 || fileId >= (FileId)files_.size() || blockIdx < 0 ||
+                        blockIdx >= (BlockIdx)files_[fileId].sentBlocks.size()) {
+                        fprintf(stderr,
+                                "Received invalid data request for file_id %" PRId16
+                                " block_idx %" PRId32 ".\n",
+                                fileId, blockIdx);
+                        break;
+                    }
+                    // fprintf(stderr, "\treading file %d block %04d\n", (int)fileId,
+                    // (int)blockIdx);
+                    if (auto res = SendBlock(fileId, blockIdx, true); res == SendResult::Error) {
+                        fprintf(stderr, "Failed to send block %" PRId32 ".\n", blockIdx);
+                    } else if (res == SendResult::Sent) {
+                        ++missesSent;
+                        // Make sure we send more pages from this place onward, in case if the OS is
+                        // reading a bigger block.
+                        prefetches_.emplace_front(files_[fileId], blockIdx + 1, 7);
+                    }
+                    break;
+                }
+                case PREFETCH: {
+                    // Start prefetching for a file
+                    if (fileId < 0) {
+                        fprintf(stderr,
+                                "Received invalid prefetch request for file_id %" PRId16 "\n",
+                                fileId);
+                        break;
+                    }
+                    if (!prefetchedFiles.insert(fileId).second) {
+                        fprintf(stderr,
+                                "Received duplicate prefetch request for file_id %" PRId16 "\n",
+                                fileId);
+                        break;
+                    }
+                    D("Received prefetch request for file_id %" PRId16 ".\n", fileId);
+                    prefetches_.emplace_back(files_[fileId]);
+                    break;
+                }
+                default:
+                    fprintf(stderr, "Invalid request %" PRId16 ",%" PRId16 ",%" PRId32 ".\n",
+                            request->request_type, fileId, blockIdx);
+                    break;
+            }
+        }
+
+        RunPrefetching();
+    }
+}
+
+bool serve(int adb_fd, int argc, const char** argv) {
+    auto connection_fd = unique_fd(adb_fd);
+    if (argc <= 0) {
+        error_exit("inc-server: must specify at least one file.");
+    }
+
+    std::vector<File> files;
+    files.reserve(argc);
+    for (int i = 0; i < argc; ++i) {
+        auto filepath = argv[i];
+
+        struct stat st;
+        if (stat(filepath, &st)) {
+            fprintf(stderr, "Failed to stat input file %s. Abort.\n", filepath);
+            return {};
+        }
+
+        unique_fd fd(adb_open(filepath, O_RDONLY));
+        if (fd < 0) {
+            error_exit("inc-server: failed to open file '%s'.", filepath);
+        }
+        files.emplace_back(filepath, i, st.st_size, std::move(fd));
+    }
+
+    IncrementalServer server(std::move(connection_fd), std::move(files));
+    printf("Serving...\n");
+    fclose(stdin);
+    fclose(stdout);
+    return server.Serve();
+}
+
+}  // namespace incremental
diff --git a/adb/client/incremental_server.h b/adb/client/incremental_server.h
new file mode 100644
index 0000000..53f011e
--- /dev/null
+++ b/adb/client/incremental_server.h
@@ -0,0 +1,26 @@
+/*
+ * 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
+
+namespace incremental {
+
+// Expecting arguments like:
+// {FILE1 FILE2 ...}
+// Where FILE* are files to serve.
+bool serve(int adbFd, int argc, const char** argv);
+
+}  // namespace incremental
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 0c5a6b4..2318395 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -267,6 +267,39 @@
 
 #define getcwd adb_getcwd
 
+// A very simple wrapper over a launched child process
+class Process {
+  public:
+    constexpr explicit Process(HANDLE h = nullptr) : h_(h) {}
+    ~Process() { close(); }
+    constexpr explicit operator bool() const { return h_ != nullptr; }
+
+    void wait() {
+        if (*this) {
+            ::WaitForSingleObject(h_, INFINITE);
+            close();
+        }
+    }
+    void kill() {
+        if (*this) {
+            ::TerminateProcess(h_, -1);
+        }
+    }
+
+  private:
+    void close() {
+        if (*this) {
+            ::CloseHandle(h_);
+            h_ = nullptr;
+        }
+    }
+
+    HANDLE h_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit = {});
+
 // Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
 // passed to main().
 class NarrowArgs {
@@ -432,11 +465,11 @@
     return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
 }
 
-static __inline__ int adb_pread(int fd, void* buf, size_t len, off64_t offset) {
+static __inline__ int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
 #if defined(__APPLE__)
-    return TEMP_FAILURE_RETRY(pread(fd, buf, len, offset));
+    return TEMP_FAILURE_RETRY(pread(fd.get(), buf, len, offset));
 #else
-    return TEMP_FAILURE_RETRY(pread64(fd, buf, len, offset));
+    return TEMP_FAILURE_RETRY(pread64(fd.get(), buf, len, offset));
 #endif
 }
 
@@ -612,6 +645,32 @@
     return fd.get();
 }
 
+// A very simple wrapper over a launched child process
+class Process {
+  public:
+    constexpr explicit Process(pid_t pid) : pid_(pid) {}
+    constexpr explicit operator bool() const { return pid_ >= 0; }
+
+    void wait() {
+        if (*this) {
+            int status;
+            ::waitpid(pid_, &status, 0);
+            pid_ = -1;
+        }
+    }
+    void kill() {
+        if (*this) {
+            ::kill(pid_, SIGTERM);
+        }
+    }
+
+  private:
+    pid_t pid_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit = {});
+
 #endif /* !_WIN32 */
 
 static inline void disable_tcp_nagle(borrowed_fd fd) {
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
index 3fdc917..e565706 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -56,3 +56,37 @@
 
     return true;
 }
+
+static __inline__ void disable_close_on_exec(borrowed_fd fd) {
+    const auto oldFlags = fcntl(fd.get(), F_GETFD);
+    const auto newFlags = (oldFlags & ~FD_CLOEXEC);
+    if (newFlags != oldFlags) {
+        fcntl(fd.get(), F_SETFD, newFlags);
+    }
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit) {
+    const auto pid = fork();
+    if (pid != 0) {
+        // parent, includes the case when failed to fork()
+        return Process(pid);
+    }
+    // child
+    std::vector<std::string> copies;
+    copies.reserve(args.size() + 1);
+    copies.emplace_back(executable);
+    copies.insert(copies.end(), std::make_move_iterator(args.begin()),
+                  std::make_move_iterator(args.end()));
+
+    std::vector<char*> rawArgs;
+    rawArgs.reserve(copies.size() + 1);
+    for (auto&& str : copies) {
+        rawArgs.push_back(str.data());
+    }
+    rawArgs.push_back(nullptr);
+    for (auto fd : fds_to_inherit) {
+        disable_close_on_exec(fd);
+    }
+    exit(execv(copies.front().data(), rawArgs.data()));
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index d9cc36f..e33d51c 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2771,6 +2771,66 @@
     return buf;
 }
 
+void enable_inherit(borrowed_fd fd) {
+    auto osh = adb_get_os_handle(fd);
+    const auto h = reinterpret_cast<HANDLE>(osh);
+    ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+}
+
+void disable_inherit(borrowed_fd fd) {
+    auto osh = adb_get_os_handle(fd);
+    const auto h = reinterpret_cast<HANDLE>(osh);
+    ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0);
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+                           std::initializer_list<int> fds_to_inherit) {
+    std::wstring wexe;
+    if (!android::base::UTF8ToWide(executable.data(), executable.size(), &wexe)) {
+        return Process();
+    }
+
+    std::wstring wargs = L"\"" + wexe + L"\"";
+    std::wstring warg;
+    for (auto arg : args) {
+        warg.clear();
+        if (!android::base::UTF8ToWide(arg.data(), arg.size(), &warg)) {
+            return Process();
+        }
+        wargs += L" \"";
+        wargs += warg;
+        wargs += L'\"';
+    }
+
+    STARTUPINFOW sinfo = {sizeof(sinfo)};
+    PROCESS_INFORMATION pinfo = {};
+
+    // TODO: use the Vista+ API to pass the list of inherited handles explicitly;
+    // see http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx
+    for (auto fd : fds_to_inherit) {
+        enable_inherit(fd);
+    }
+    const auto created = CreateProcessW(wexe.c_str(), wargs.data(),
+                                        nullptr,                    // process attributes
+                                        nullptr,                    // thread attributes
+                                        fds_to_inherit.size() > 0,  // inherit any handles?
+                                        0,                          // flags
+                                        nullptr,                    // environment
+                                        nullptr,                    // current directory
+                                        &sinfo,                     // startup info
+                                        &pinfo);
+    for (auto fd : fds_to_inherit) {
+        disable_inherit(fd);
+    }
+
+    if (!created) {
+        return Process();
+    }
+
+    ::CloseHandle(pinfo.hThread);
+    return Process(pinfo.hProcess);
+}
+
 // The SetThreadDescription API was brought in version 1607 of Windows 10.
 typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
 
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index e3ce531..f28c778 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -171,6 +171,7 @@
 
     srcs: [
         "libdebuggerd/backtrace.cpp",
+        "libdebuggerd/gwp_asan.cpp",
         "libdebuggerd/open_files_list.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/utility.cpp",
@@ -181,7 +182,10 @@
 
     // Needed for private/bionic_fdsan.h
     include_dirs: ["bionic/libc"],
-    header_libs: ["bionic_libc_platform_headers"],
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "gwp_asan_headers",
+    ],
 
     static_libs: [
         "libdexfile_support_static",  // libunwindstack dependency
@@ -192,6 +196,8 @@
         "liblog",
     ],
 
+    whole_static_libs: ["gwp_asan_crash_handler"],
+
     target: {
         recovery: {
             exclude_static_libs: [
@@ -246,10 +252,12 @@
 
     static_libs: [
         "libdebuggerd",
+        "libgmock",
     ],
 
     header_libs: [
         "bionic_libc_platform_headers",
+        "gwp_asan_headers",
     ],
 
     local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 2545cd6..ebb8d86 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -73,8 +73,8 @@
   unique_fd pipe_read, pipe_write;
   ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
 
-  // 64 MiB should be enough for everyone.
-  constexpr int PIPE_SIZE = 64 * 1024 * 1024;
+  // 16 MiB should be enough for everyone.
+  constexpr int PIPE_SIZE = 16 * 1024 * 1024;
   ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
 
   // Wait for a bit to let the child spawn all of its threads.
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index e8f366f..3e99880 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -255,7 +255,8 @@
 
 static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
                           std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
-                          uintptr_t* fdsan_table_address) {
+                          uintptr_t* fdsan_table_address, uintptr_t* gwp_asan_state,
+                          uintptr_t* gwp_asan_metadata) {
   std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
   CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
   ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
@@ -272,6 +273,10 @@
         expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
         break;
 
+      case 3:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
+        break;
+
       default:
         LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
         break;
@@ -284,7 +289,13 @@
   }
 
   *fdsan_table_address = 0;
+  *gwp_asan_state = 0;
+  *gwp_asan_metadata = 0;
   switch (crash_info->header.version) {
+    case 3:
+      *gwp_asan_state = crash_info->data.v3.gwp_asan_state;
+      *gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata;
+      FALLTHROUGH_INTENDED;
     case 2:
       *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
       FALLTHROUGH_INTENDED;
@@ -416,6 +427,8 @@
   DebuggerdDumpType dump_type;
   uintptr_t abort_msg_address = 0;
   uintptr_t fdsan_table_address = 0;
+  uintptr_t gwp_asan_state = 0;
+  uintptr_t gwp_asan_metadata = 0;
 
   Initialize(argv);
   ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -477,7 +490,7 @@
       if (thread == g_target_thread) {
         // Read the thread's registers along with the rest of the crash info out of the pipe.
         ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
-                      &fdsan_table_address);
+                      &fdsan_table_address, &gwp_asan_state, &gwp_asan_metadata);
         info.siginfo = &siginfo;
         info.signo = info.siginfo->si_signo;
       } else {
@@ -592,7 +605,8 @@
     {
       ATRACE_NAME("engrave_tombstone");
       engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
-                        abort_msg_address, &open_files, &amfd_data);
+                        abort_msg_address, &open_files, &amfd_data, gwp_asan_state,
+                        gwp_asan_metadata);
     }
   }
 
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index f8192b5..8b4b630 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -49,6 +49,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <bionic/reserved_signals.h>
@@ -298,6 +299,8 @@
   void* ucontext;
   uintptr_t abort_msg;
   uintptr_t fdsan_table;
+  uintptr_t gwp_asan_state;
+  uintptr_t gwp_asan_metadata;
 };
 
 // Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -342,23 +345,25 @@
   }
 
   // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
-  uint32_t version = 2;
-  constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+  uint32_t version = 3;
+  constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
 
   errno = 0;
   if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
     fatal_errno("failed to set pipe buffer size");
   }
 
-  struct iovec iovs[5] = {
+  struct iovec iovs[] = {
       {.iov_base = &version, .iov_len = sizeof(version)},
       {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
       {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
       {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
       {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
+      {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)},
+      {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)},
   };
 
-  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 5));
+  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));
   if (rc == -1) {
     fatal_errno("failed to write crash info");
   } else if (rc != expected) {
@@ -485,6 +490,8 @@
   }
 
   void* abort_message = nullptr;
+  const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
+  const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
   uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
   if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
     if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
@@ -499,6 +506,12 @@
     if (g_callbacks.get_abort_message) {
       abort_message = g_callbacks.get_abort_message();
     }
+    if (g_callbacks.get_gwp_asan_state) {
+      gwp_asan_state = g_callbacks.get_gwp_asan_state();
+    }
+    if (g_callbacks.get_gwp_asan_metadata) {
+      gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata();
+    }
   }
 
   // If sival_int is ~0, it means that the fallback handler has been called
@@ -532,6 +545,8 @@
       .ucontext = context,
       .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
       .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
+      .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state),
+      .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata),
   };
 
   // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index cd6fc05..4f24360 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -24,11 +24,20 @@
 
 __BEGIN_DECLS
 
+// Forward declare these classes so not everyone has to include GWP-ASan
+// headers.
+namespace gwp_asan {
+struct AllocatorState;
+struct AllocationMetadata;
+};  // namespace gwp_asan
+
 // These callbacks are called in a signal handler, and thus must be async signal safe.
 // If null, the callbacks will not be called.
 typedef struct {
   struct abort_msg_t* (*get_abort_message)();
   void (*post_dump)();
+  const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)();
+  const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)();
 } debuggerd_callbacks_t;
 
 void debuggerd_init(debuggerd_callbacks_t* callbacks);
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
new file mode 100644
index 0000000..53df783
--- /dev/null
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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 "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/utility.h"
+
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+// Retrieve GWP-ASan state from `state_addr` inside the process at
+// `process_memory`. Place the state into `*state`.
+static bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr,
+                                    gwp_asan::AllocatorState* state) {
+  return process_memory->ReadFully(state_addr, state, sizeof(*state));
+}
+
+// Retrieve the GWP-ASan metadata pool from `metadata_addr` inside the process
+// at `process_memory`. The number of metadata slots is retrieved from the
+// allocator state provided. This function returns a heap-allocated copy of the
+// metadata pool whose ownership should be managed by the caller. Returns
+// nullptr on failure.
+static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
+    unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,
+    uintptr_t metadata_addr) {
+  if (state.MaxSimultaneousAllocations > 1024) {
+    ALOGE(
+        "Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
+        "exceeds maximum allowed (1024).",
+        state.MaxSimultaneousAllocations);
+    return nullptr;
+  }
+
+  gwp_asan::AllocationMetadata* meta =
+      new gwp_asan::AllocationMetadata[state.MaxSimultaneousAllocations];
+  if (!process_memory->ReadFully(metadata_addr, meta,
+                                 sizeof(*meta) * state.MaxSimultaneousAllocations)) {
+    ALOGE(
+        "Error when retrieving GWP-ASan metadata, could not retrieve %zu "
+        "pieces of metadata.",
+        state.MaxSimultaneousAllocations);
+    delete[] meta;
+    meta = nullptr;
+  }
+  return meta;
+}
+
+GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
+                                   uintptr_t gwp_asan_state_ptr, uintptr_t gwp_asan_metadata_ptr,
+                                   const ThreadInfo& thread_info) {
+  if (!process_memory || !gwp_asan_metadata_ptr || !gwp_asan_state_ptr) return;
+  // Extract the GWP-ASan regions from the dead process.
+  if (!retrieve_gwp_asan_state(process_memory, gwp_asan_state_ptr, &state_)) return;
+  metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, gwp_asan_metadata_ptr));
+  if (!metadata_.get()) return;
+
+  // Get the external crash address from the thread info.
+  crash_address_ = 0u;
+  if (signal_has_si_addr(thread_info.siginfo)) {
+    crash_address_ = reinterpret_cast<uintptr_t>(thread_info.siginfo->si_addr);
+  }
+
+  // Ensure the error belongs to GWP-ASan.
+  if (!__gwp_asan_error_is_mine(&state_, crash_address_)) return;
+
+  is_gwp_asan_responsible_ = true;
+  thread_id_ = thread_info.tid;
+
+  // Grab the internal error address, if it exists.
+  uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_);
+  if (internal_crash_address) {
+    crash_address_ = internal_crash_address;
+  }
+
+  // Get other information from the internal state.
+  error_ = __gwp_asan_diagnose_error(&state_, metadata_.get(), crash_address_);
+  error_string_ = gwp_asan::ErrorToString(error_);
+  responsible_allocation_ = __gwp_asan_get_metadata(&state_, metadata_.get(), crash_address_);
+}
+
+bool GwpAsanCrashData::CrashIsMine() const {
+  return is_gwp_asan_responsible_;
+}
+
+void GwpAsanCrashData::DumpCause(log_t* log) const {
+  if (!CrashIsMine()) {
+    ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
+    return;
+  }
+
+  if (error_ == gwp_asan::Error::UNKNOWN) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: Unknown error occurred at 0x%" PRIxPTR ".\n",
+         crash_address_);
+    return;
+  }
+
+  if (!responsible_allocation_) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s at 0x%" PRIxPTR ".\n", error_string_,
+         crash_address_);
+    return;
+  }
+
+  uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
+  size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
+
+  if (crash_address_ == alloc_address) {
+    // Use After Free on a 41-byte allocation at 0xdeadbeef.
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s on a %zu-byte allocation at 0x%" PRIxPTR "\n",
+         error_string_, alloc_size, alloc_address);
+    return;
+  }
+
+  uintptr_t diff;
+  const char* location_str;
+
+  if (crash_address_ < alloc_address) {
+    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+    location_str = "left of";
+    diff = alloc_address - crash_address_;
+  } else if (crash_address_ - alloc_address < alloc_size) {
+    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+    location_str = "into";
+    diff = crash_address_ - alloc_address;
+  } else {
+    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef, or
+    // Invalid Free, 47 bytes right of a 41-byte allocation at 0xdeadbeef.
+    location_str = "right of";
+    diff = crash_address_ - alloc_address;
+    if (error_ == gwp_asan::Error::BUFFER_OVERFLOW) {
+      diff -= alloc_size;
+    }
+  }
+
+  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+  const char* byte_suffix = "s";
+  if (diff == 1) {
+    byte_suffix = "";
+  }
+  _LOG(log, logtype::HEADER,
+       "Cause: [GWP-ASan]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+       error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
+}
+
+// Build a frame for symbolization using the maps from the provided unwinder.
+// The constructed frame contains just enough information to be used to
+// symbolize a GWP-ASan stack trace.
+static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
+                                         size_t frame_num) {
+  unwindstack::FrameData frame;
+  frame.num = frame_num;
+
+  unwindstack::Maps* maps = unwinder->GetMaps();
+  unwindstack::MapInfo* map_info = maps->Find(pc);
+  if (!map_info) {
+    frame.rel_pc = pc;
+    return frame;
+  }
+
+  unwindstack::Elf* elf =
+      map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
+
+  uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+  // Create registers just to get PC adjustment. Doesn't matter what they point
+  // to.
+  unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
+  uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
+  relative_pc -= pc_adjustment;
+  // The debug PC may be different if the PC comes from the JIT.
+  uint64_t debug_pc = relative_pc;
+
+  // If we don't have a valid ELF file, check the JIT.
+  if (!elf->valid()) {
+    unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
+    uint64_t jit_pc = pc - pc_adjustment;
+    unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+    if (jit_elf != nullptr) {
+      debug_pc = jit_pc;
+      elf = jit_elf;
+    }
+  }
+
+  // Copy all the things we need into the frame for symbolization.
+  frame.rel_pc = relative_pc;
+  frame.pc = pc - pc_adjustment;
+  frame.map_name = map_info->name;
+  frame.map_elf_start_offset = map_info->elf_start_offset;
+  frame.map_exact_offset = map_info->offset;
+  frame.map_start = map_info->start;
+  frame.map_end = map_info->end;
+  frame.map_flags = map_info->flags;
+  frame.map_load_bias = elf->GetLoadBias();
+
+  if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+    frame.function_name = "";
+    frame.function_offset = 0;
+  }
+  return frame;
+}
+
+constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
+
+bool GwpAsanCrashData::HasDeallocationTrace() const {
+  assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
+  if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
+    return false;
+  }
+  return true;
+}
+
+void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
+  uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
+
+  std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+  size_t num_frames =
+      __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+    _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+  }
+}
+
+bool GwpAsanCrashData::HasAllocationTrace() const {
+  assert(CrashIsMine() && "HasAllocationTrace(): Crash is not mine!");
+  return responsible_allocation_ != nullptr;
+}
+
+void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
+  uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
+
+  std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+  size_t num_frames =
+      __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+    _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+  }
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
new file mode 100644
index 0000000..aef4c62
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <log/log.h>
+#include <unwindstack/Memory.h>
+
+#include "gwp_asan/common.h"
+#include "types.h"
+#include "utility.h"
+
+class GwpAsanCrashData {
+ public:
+  GwpAsanCrashData() = delete;
+  ~GwpAsanCrashData() = default;
+
+  // Construct the crash data object. Takes a handle to the object that can
+  // supply the memory of the dead process, and pointers to the GWP-ASan state
+  // and metadata regions within that process. Also takes the thread information
+  // of the crashed process. If the process didn't crash via SEGV, GWP-ASan may
+  // still be responsible, as it terminates when it detects an internal error
+  // (double free, invalid free). In these cases, we will retrieve the fault
+  // address from the GWP-ASan allocator's state.
+  GwpAsanCrashData(unwindstack::Memory* process_memory, uintptr_t gwp_asan_state_ptr,
+                   uintptr_t gwp_asan_metadata_ptr, const ThreadInfo& thread_info);
+
+  // Is GWP-ASan responsible for this crash.
+  bool CrashIsMine() const;
+
+  // Returns the fault address. The fault address may be the same as provided
+  // during construction, or it may have been retrieved from GWP-ASan's internal
+  // allocator crash state.
+  uintptr_t GetFaultAddress() const;
+
+  // Dump the GWP-ASan stringified cause of this crash. May only be called if
+  // CrashIsMine() returns true.
+  void DumpCause(log_t* log) const;
+
+  // Returns whether this crash has a deallocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasDeallocationTrace() const;
+
+  // Dump the GWP-ASan deallocation trace for this crash. May only be called if
+  // HasDeallocationTrace() returns true.
+  void DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+  // Returns whether this crash has a allocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasAllocationTrace() const;
+
+  // Dump the GWP-ASan allocation trace for this crash. May only be called if
+  // HasAllocationTrace() returns true.
+  void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ protected:
+  // Is GWP-ASan responsible for this crash.
+  bool is_gwp_asan_responsible_ = false;
+
+  // Thread ID of the crash.
+  size_t thread_id_;
+
+  // The type of error that GWP-ASan caused (and the stringified version),
+  // Undefined if GWP-ASan isn't responsible for the crash.
+  gwp_asan::Error error_;
+  const char* error_string_;
+
+  // Pointer to the crash address. Holds the internal crash address if it
+  // exists, otherwise the address provided at construction.
+  uintptr_t crash_address_ = 0u;
+
+  // Pointer to the metadata for the responsible allocation, nullptr if it
+  // doesn't exist.
+  const gwp_asan::AllocationMetadata* responsible_allocation_ = nullptr;
+
+  // Internal state.
+  gwp_asan::AllocatorState state_;
+  std::unique_ptr<const gwp_asan::AllocationMetadata> metadata_;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7133f77..291d994 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -55,6 +55,7 @@
 void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
-                       std::string* amfd_data);
+                       std::string* amfd_data, uintptr_t gwp_asan_state,
+                       uintptr_t gwp_asan_metadata);
 
 #endif  // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index b33adf3..eed95bc 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -23,6 +23,7 @@
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "libdebuggerd/utility.h"
@@ -31,8 +32,13 @@
 #include "host_signal_fixup.h"
 #include "log_fake.h"
 
+// Include tombstone.cpp to define log_tag before GWP-ASan includes log.
 #include "tombstone.cpp"
 
+#include "gwp_asan.cpp"
+
+using ::testing::MatchesRegex;
+
 class TombstoneTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
@@ -359,3 +365,131 @@
   dump_timestamp(&log_, 0);
   ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
 }
+
+class GwpAsanCrashDataTest : public GwpAsanCrashData {
+public:
+  GwpAsanCrashDataTest(
+      gwp_asan::Error error,
+      const gwp_asan::AllocationMetadata *responsible_allocation) :
+      GwpAsanCrashData(nullptr, 0u, 0u, ThreadInfo{}) {
+    is_gwp_asan_responsible_ = true;
+    error_ = error;
+    responsible_allocation_ = responsible_allocation;
+    error_string_ = gwp_asan::ErrorToString(error_);
+  }
+
+  void SetCrashAddress(uintptr_t crash_address) {
+    crash_address_ = crash_address;
+  }
+};
+
+TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(tombstone_contents,
+              MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free on a 32-byte "
+                           "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(tombstone_contents,
+              MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free on a 32-byte "
+                           "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
+  crash_data.SetCrashAddress(0x1025);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Buffer Overflow, 5 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
+  crash_data.SetCrashAddress(0xffe);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Buffer Underflow, 2 bytes left of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1001);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 1 byte into a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1021);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 4e7f35c..fd52e81 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -53,9 +53,13 @@
 #include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/gwp_asan.h"
 #include "libdebuggerd/open_files_list.h"
 #include "libdebuggerd/utility.h"
 
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
 using android::base::GetBoolProperty;
 using android::base::GetProperty;
 using android::base::StringPrintf;
@@ -372,7 +376,8 @@
 }
 
 static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
-                        uint64_t abort_msg_address, bool primary_thread) {
+                        uint64_t abort_msg_address, bool primary_thread,
+                        const GwpAsanCrashData& gwp_asan_crash_data) {
   log->current_tid = thread_info.tid;
   if (!primary_thread) {
     _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -381,7 +386,13 @@
 
   if (thread_info.siginfo) {
     dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
-    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
+  }
+
+  if (primary_thread && gwp_asan_crash_data.CrashIsMine()) {
+    gwp_asan_crash_data.DumpCause(log);
+  } else if (thread_info.siginfo) {
+    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
+                        thread_info.registers.get());
   }
 
   if (primary_thread) {
@@ -402,6 +413,14 @@
   }
 
   if (primary_thread) {
+    if (gwp_asan_crash_data.HasDeallocationTrace()) {
+      gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
+    }
+
+    if (gwp_asan_crash_data.HasAllocationTrace()) {
+      gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
+    }
+
     unwindstack::Maps* maps = unwinder->GetMaps();
     dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
                          thread_info.registers.get());
@@ -583,13 +602,14 @@
   }
 
   engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
-                    nullptr, nullptr);
+                    nullptr, nullptr, 0u, 0u);
 }
 
 void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
-                       std::string* amfd_data) {
+                       std::string* amfd_data, uintptr_t gwp_asan_state_ptr,
+                       uintptr_t gwp_asan_metadata_ptr) {
   // don't copy log messages to tombstone unless this is a dev device
   bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
 
@@ -607,7 +627,13 @@
   if (it == threads.end()) {
     LOG(FATAL) << "failed to find target thread";
   }
-  dump_thread(&log, unwinder, it->second, abort_msg_address, true);
+
+  GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(),
+                                       gwp_asan_state_ptr,
+                                       gwp_asan_metadata_ptr, it->second);
+
+  dump_thread(&log, unwinder, it->second, abort_msg_address, true,
+              gwp_asan_crash_data);
 
   if (want_logs) {
     dump_logs(&log, it->second.pid, 50);
@@ -618,7 +644,7 @@
       continue;
     }
 
-    dump_thread(&log, unwinder, thread_info, 0, false);
+    dump_thread(&log, unwinder, thread_info, 0, false, gwp_asan_crash_data);
   }
 
   if (open_files) {
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index bfd0fbb..bf53864 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -95,10 +95,16 @@
   uintptr_t fdsan_table_address;
 };
 
+struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 {
+  uintptr_t gwp_asan_state;
+  uintptr_t gwp_asan_metadata;
+};
+
 struct __attribute__((__packed__)) CrashInfo {
   CrashInfoHeader header;
   union {
     CrashInfoDataV1 v1;
     CrashInfoDataV2 v2;
+    CrashInfoDataV3 v3;
   } data;
 };
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index f30058a..abd48fc 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -405,7 +405,7 @@
   static struct cache2_char security = {
       PTHREAD_MUTEX_INITIALIZER, 0,
       "persist.logd.security",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
-      "ro.device_owner",         {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      "ro.organization_owned",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
       evaluate_security};
 
   return do_cache2_char(&security);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 75a26bf..a60d2df 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1637,7 +1637,7 @@
 TEST(liblog, __security) {
 #ifdef __ANDROID__
   static const char persist_key[] = "persist.logd.security";
-  static const char readonly_key[] = "ro.device_owner";
+  static const char readonly_key[] = "ro.organization_owned";
   // A silly default value that can never be in readonly_key so
   // that it can be determined the property is not set.
   static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
@@ -1657,7 +1657,7 @@
   if (!strcmp(readonly, nothing_val)) {
     // Lets check if we can set the value (we should not be allowed to do so)
     EXPECT_FALSE(__android_log_security());
-    fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
+    fprintf(stderr, "WARNING: setting ro.organization_owned to a domain\n");
     static const char domain[] = "com.google.android.SecOps.DeviceOwner";
     EXPECT_NE(0, property_set(readonly_key, domain));
     useconds_t total_time = 0;
diff --git a/logd/README.property b/logd/README.property
index d2a2cbb..1b7e165 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -6,7 +6,7 @@
 ro.logd.auditd.main        bool   true   selinux audit messages sent to main.
 ro.logd.auditd.events      bool   true   selinux audit messages sent to events.
 persist.logd.security      bool   false  Enable security buffer.
-ro.device_owner            bool   false  Override persist.logd.security to false
+ro.organization_owned      bool   false  Override persist.logd.security to false
 ro.logd.kernel             bool+ svelte+ Enable klogd daemon
 ro.logd.statistics         bool+ svelte+ Enable logcat -S statistics.
 ro.debuggable              number        if not "1", logd.statistics &
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index f4a846f..a643062 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -317,7 +317,7 @@
       {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
       {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
       {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
-      {"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
+      {"ro.organization_owned", "u:object_r:device_logging_prop:s0", "string", false},
 
       {"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
 
@@ -669,7 +669,7 @@
       {"ro.crypto.type", "u:object_r:vold_prop:s0"},
       {"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
       {"ro.debuggable", "u:object_r:default_prop:s0"},
-      {"ro.device_owner", "u:object_r:device_logging_prop:s0"},
+      {"ro.organization_owned", "u:object_r:device_logging_prop:s0"},
       {"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
       {"ro.frp.pst", "u:object_r:default_prop:s0"},
       {"ro.hardware", "u:object_r:default_prop:s0"},
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 80573fb..f96ffdd 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -46,6 +46,21 @@
 include $(BUILD_PREBUILT)
 
 #######################################
+# r-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-developer-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
 # s-gsi.avbpubkey
 include $(CLEAR_VARS)
 
@@ -59,3 +74,18 @@
 endif
 
 include $(BUILD_PREBUILT)
+
+#######################################
+# s-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-developer-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
diff --git a/rootdir/avb/r-developer-gsi.avbpubkey b/rootdir/avb/r-developer-gsi.avbpubkey
new file mode 100644
index 0000000..aac39cc
--- /dev/null
+++ b/rootdir/avb/r-developer-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-developer-gsi.avbpubkey b/rootdir/avb/s-developer-gsi.avbpubkey
new file mode 100644
index 0000000..f0a6c11
--- /dev/null
+++ b/rootdir/avb/s-developer-gsi.avbpubkey
Binary files differ