Merge "Create fuzzy-fastboot pen tester"
diff --git a/adb/Android.bp b/adb/Android.bp
index a18dc1a..3685687 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -138,6 +138,8 @@
         "client/usb_libusb.cpp",
         "client/usb_dispatch.cpp",
         "client/transport_mdns.cpp",
+        "client/fastdeploy.cpp",
+        "client/fastdeploycallbacks.cpp",
     ],
 
     target: {
@@ -170,6 +172,12 @@
         "libdiagnose_usb",
         "libmdnssd",
         "libusb",
+        "libandroidfw",
+        "libziparchive",
+        "libz",
+        "libutils",
+        "liblog",
+        "libcutils",
     ],
 }
 
@@ -237,6 +245,7 @@
         "client/file_sync_client.cpp",
         "client/main.cpp",
         "client/console.cpp",
+        "client/adb_install.cpp",
         "client/line_printer.cpp",
         "shell_service_protocol.cpp",
     ],
@@ -251,6 +260,12 @@
         "liblog",
         "libmdnssd",
         "libusb",
+        "libandroidfw",
+        "libziparchive",
+        "libz",
+        "libutils",
+        "liblog",
+        "libcutils",
     ],
 
     stl: "libc++_static",
@@ -353,7 +368,6 @@
         "libcutils",
         "libext4_utils",
         "libfec",
-        "libfec_rs",
         "libfs_mgr",
         "liblog",
         "libmdnssd",
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index fca435e..d467539 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _ADB_CLIENT_H_
-#define _ADB_CLIENT_H_
+#pragma once
 
 #include "adb.h"
 #include "sysdeps.h"
@@ -63,5 +62,3 @@
 
 // Get the feature set of the current preferred transport.
 bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
-
-#endif
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
new file mode 100644
index 0000000..95574ed
--- /dev/null
+++ b/adb/client/adb_install.cpp
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2018 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 ADB
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_install.h"
+#include "adb_utils.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploy.h"
+#include "sysdeps.h"
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+static bool _use_legacy_install() {
+    FeatureSet features;
+    std::string error;
+    if (!adb_get_feature_set(&features, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return true;
+    }
+    return !CanUseFeature(features, kFeatureCmd);
+}
+
+static int pm_command(int argc, const char** argv) {
+    std::string cmd = "pm";
+
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(cmd);
+}
+
+static int uninstall_app_streamed(int argc, const char** argv) {
+    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+    std::string cmd = "cmd package";
+    while (argc-- > 0) {
+        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+        if (strcmp(*argv, "-k") == 0) {
+            printf("The -k option uninstalls the application while retaining the "
+                   "data/cache.\n"
+                   "At the moment, there is no way to remove the remaining data.\n"
+                   "You will have to reinstall the application with the same "
+                   "signature, and fully "
+                   "uninstall it.\n"
+                   "If you truly wish to continue, execute 'adb shell cmd package "
+                   "uninstall -k'.\n");
+            return EXIT_FAILURE;
+        }
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(cmd);
+}
+
+static int uninstall_app_legacy(int argc, const char** argv) {
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-k")) {
+            printf("The -k option uninstalls the application while retaining the "
+                   "data/cache.\n"
+                   "At the moment, there is no way to remove the remaining data.\n"
+                   "You will have to reinstall the application with the same "
+                   "signature, and fully "
+                   "uninstall it.\n"
+                   "If you truly wish to continue, execute 'adb shell pm uninstall "
+                   "-k'\n.");
+            return EXIT_FAILURE;
+        }
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(argc, argv);
+}
+
+int uninstall_app(int argc, const char** argv) {
+    if (_use_legacy_install()) {
+        return uninstall_app_legacy(argc, argv);
+    }
+    return uninstall_app_streamed(argc, argv);
+}
+
+static void read_status_line(int fd, char* buf, size_t count) {
+    count--;
+    while (count > 0) {
+        int len = adb_read(fd, buf, count);
+        if (len <= 0) {
+            break;
+        }
+
+        buf += len;
+        count -= len;
+    }
+    *buf = '\0';
+}
+
+static int delete_device_patch_file(const char* apkPath) {
+    std::string patchDevicePath = get_patch_path(apkPath);
+    return delete_device_file(patchDevicePath);
+}
+
+static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
+                                bool use_localagent, const char* adb_path) {
+    printf("Performing Streamed Install\n");
+
+    // The last argument must be the APK file
+    const char* file = argv[argc - 1];
+    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
+        return syntax_error("filename doesn't end .apk: %s", file);
+    }
+
+    if (use_fastdeploy == true) {
+        TemporaryFile metadataTmpFile;
+        TemporaryFile patchTmpFile;
+
+        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
+        int metadata_len = extract_metadata(file, metadataFile);
+        fclose(metadataFile);
+
+        int result = -1;
+        if (metadata_len <= 0) {
+            printf("failed to extract metadata %d\n", metadata_len);
+            return 1;
+        } else {
+            int create_patch_result = create_patch(file, metadataTmpFile.path, patchTmpFile.path,
+                                                   use_localagent, adb_path);
+            if (create_patch_result != 0) {
+                printf("Patch creation failure, error code: %d\n", create_patch_result);
+                result = create_patch_result;
+                goto cleanup_streamed_apk;
+            } else {
+                std::vector<const char*> pm_args;
+                // pass all but 1st (command) and last (apk path) parameters through to pm for
+                // session creation
+                for (int i = 1; i < argc - 1; i++) {
+                    pm_args.push_back(argv[i]);
+                }
+                int apply_patch_result =
+                        install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
+                if (apply_patch_result != 0) {
+                    printf("Patch application failure, error code: %d\n", apply_patch_result);
+                    result = apply_patch_result;
+                    goto cleanup_streamed_apk;
+                }
+            }
+        }
+
+    cleanup_streamed_apk:
+        delete_device_patch_file(file);
+        return result;
+    } else {
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            return 1;
+        }
+
+        int localFd = adb_open(file, O_RDONLY);
+        if (localFd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            return 1;
+        }
+
+        std::string error;
+        std::string cmd = "exec:cmd package";
+
+        // don't copy the APK name, but, copy the rest of the arguments as-is
+        while (argc-- > 1) {
+            cmd += " " + escape_arg(std::string(*argv++));
+        }
+
+        // add size parameter [required for streaming installs]
+        // do last to override any user specified value
+        cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+
+        int remoteFd = adb_connect(cmd, &error);
+        if (remoteFd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            adb_close(localFd);
+            return 1;
+        }
+
+        char buf[BUFSIZ];
+        copy_to_file(localFd, remoteFd);
+        read_status_line(remoteFd, buf, sizeof(buf));
+
+        adb_close(localFd);
+        adb_close(remoteFd);
+
+        if (!strncmp("Success", buf, 7)) {
+            fputs(buf, stdout);
+            return 0;
+        }
+        fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+        return 1;
+    }
+}
+
+static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy, bool use_localagent,
+                              const char* adb_path) {
+    static const char* const DATA_DEST = "/data/local/tmp/%s";
+    static const char* const SD_DEST = "/sdcard/tmp/%s";
+    const char* where = DATA_DEST;
+
+    printf("Performing Push Install\n");
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-s")) {
+            where = SD_DEST;
+        }
+    }
+
+    // Find last APK argument.
+    // All other arguments passed through verbatim.
+    int last_apk = -1;
+    for (int i = argc - 1; i >= 0; i--) {
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
+            last_apk = i;
+            break;
+        }
+    }
+
+    if (last_apk == -1) return syntax_error("need APK file on command line");
+
+    int result = -1;
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest =
+            android::base::StringPrintf(where, android::base::Basename(argv[last_apk]).c_str());
+
+    TemporaryFile metadataTmpFile;
+    TemporaryFile patchTmpFile;
+
+    if (use_fastdeploy == true) {
+        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
+        int metadata_len = extract_metadata(apk_file[0], metadataFile);
+        fclose(metadataFile);
+
+        if (metadata_len <= 0) {
+            printf("failed to extract metadata %d\n", metadata_len);
+            return 1;
+        } else {
+            int create_patch_result = create_patch(apk_file[0], metadataTmpFile.path,
+                                                   patchTmpFile.path, use_localagent, adb_path);
+            if (create_patch_result != 0) {
+                printf("Patch creation failure, error code: %d\n", create_patch_result);
+                result = create_patch_result;
+                goto cleanup_apk;
+            } else {
+                int apply_patch_result =
+                        apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
+                if (apply_patch_result != 0) {
+                    printf("Patch application failure, error code: %d\n", apply_patch_result);
+                    result = apply_patch_result;
+                    goto cleanup_apk;
+                }
+            }
+        }
+    } else {
+        if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
+    }
+
+    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+    result = pm_command(argc, argv);
+
+cleanup_apk:
+    delete_device_patch_file(apk_file[0]);
+    delete_device_file(apk_dest);
+    return result;
+}
+
+int install_app(int argc, const char** argv) {
+    std::vector<int> processedArgIndicies;
+    enum installMode {
+        INSTALL_DEFAULT,
+        INSTALL_PUSH,
+        INSTALL_STREAM
+    } installMode = INSTALL_DEFAULT;
+    bool use_fastdeploy = false;
+    bool is_reinstall = false;
+    bool use_localagent = false;
+    FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "--streaming")) {
+            processedArgIndicies.push_back(i);
+            installMode = INSTALL_STREAM;
+        } else if (!strcmp(argv[i], "--no-streaming")) {
+            processedArgIndicies.push_back(i);
+            installMode = INSTALL_PUSH;
+        } else if (!strcmp(argv[i], "-r")) {
+            // Note that this argument is not added to processedArgIndicies because it
+            // must be passed through to pm
+            is_reinstall = true;
+        } else if (!strcmp(argv[i], "--fastdeploy")) {
+            processedArgIndicies.push_back(i);
+            use_fastdeploy = true;
+        } else if (!strcmp(argv[i], "--no-fastdeploy")) {
+            processedArgIndicies.push_back(i);
+            use_fastdeploy = false;
+        } else if (!strcmp(argv[i], "-f")) {
+            processedArgIndicies.push_back(i);
+            use_fastdeploy = true;
+        } else if (!strcmp(argv[i], "--force-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateAlways;
+        } else if (!strcmp(argv[i], "--date-check-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+        } else if (!strcmp(argv[i], "--version-check-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+#ifndef _WIN32
+        } else if (!strcmp(argv[i], "--local-agent")) {
+            processedArgIndicies.push_back(i);
+            use_localagent = true;
+#endif
+        }
+        // TODO: --installlog <filename>
+    }
+
+    if (installMode == INSTALL_DEFAULT) {
+        if (_use_legacy_install()) {
+            installMode = INSTALL_PUSH;
+        } else {
+            installMode = INSTALL_STREAM;
+        }
+    }
+
+    if (installMode == INSTALL_STREAM && _use_legacy_install() == true) {
+        return syntax_error("Attempting to use streaming install on unsupported deivce.");
+    }
+
+    if (use_fastdeploy == true && is_reinstall == false) {
+        printf("Fast Deploy is only available with -r.\n");
+        use_fastdeploy = false;
+    }
+
+    if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
+        printf("Fast Deploy is only compatible with devices of API version %d or higher, "
+               "ignoring.\n",
+               kFastDeployMinApi);
+        use_fastdeploy = false;
+    }
+
+    std::vector<const char*> passthrough_argv;
+    for (int i = 0; i < argc; i++) {
+        if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
+            processedArgIndicies.end()) {
+            passthrough_argv.push_back(argv[i]);
+        }
+    }
+
+    std::string adb_path = android::base::GetExecutablePath();
+
+    if (adb_path.length() == 0) {
+        return 1;
+    }
+    if (use_fastdeploy == true) {
+        bool agent_up_to_date =
+                update_agent(agent_update_strategy, use_localagent, adb_path.c_str());
+        if (agent_up_to_date == false) {
+            printf("Failed to update agent, exiting\n");
+            return 1;
+        }
+    }
+
+    switch (installMode) {
+        case INSTALL_PUSH:
+            return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
+                                      use_fastdeploy, use_localagent, adb_path.c_str());
+        case INSTALL_STREAM:
+            return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
+                                        use_fastdeploy, use_localagent, adb_path.c_str());
+        case INSTALL_DEFAULT:
+        default:
+            return 1;
+    }
+}
+
+int install_multiple_app(int argc, const char** argv) {
+    // Find all APK arguments starting at end.
+    // All other arguments passed through verbatim.
+    int first_apk = -1;
+    uint64_t total_size = 0;
+    for (int i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+
+        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+            struct stat sb;
+            if (stat(file, &sb) != -1) total_size += sb.st_size;
+            first_apk = i;
+        } else {
+            break;
+        }
+    }
+
+    if (first_apk == -1) return syntax_error("need APK file on command line");
+
+    std::string install_cmd;
+    if (_use_legacy_install()) {
+        install_cmd = "exec:pm";
+    } else {
+        install_cmd = "exec:cmd package";
+    }
+
+    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
+                                                  install_cmd.c_str(), total_size);
+    for (int i = 1; i < first_apk; i++) {
+        cmd += " " + escape_arg(argv[i]);
+    }
+
+    // Create install session
+    std::string error;
+    int fd = adb_connect(cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    char buf[BUFSIZ];
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    int session_id = -1;
+    if (!strncmp("Success", buf, 7)) {
+        char* start = strrchr(buf, '[');
+        char* end = strrchr(buf, ']');
+        if (start && end) {
+            *end = '\0';
+            session_id = strtol(start + 1, nullptr, 10);
+        }
+    }
+    if (session_id < 0) {
+        fprintf(stderr, "adb: failed to create session\n");
+        fputs(buf, stderr);
+        return EXIT_FAILURE;
+    }
+
+    // Valid session, now stream the APKs
+    int success = 1;
+    for (int i = first_apk; i < argc; i++) {
+        const char* file = argv[i];
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            success = 0;
+            goto finalize_session;
+        }
+
+        std::string cmd =
+                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
+                                            session_id, i, android::base::Basename(file).c_str());
+
+        int localFd = adb_open(file, O_RDONLY);
+        if (localFd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            success = 0;
+            goto finalize_session;
+        }
+
+        std::string error;
+        int remoteFd = adb_connect(cmd, &error);
+        if (remoteFd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            adb_close(localFd);
+            success = 0;
+            goto finalize_session;
+        }
+
+        copy_to_file(localFd, remoteFd);
+        read_status_line(remoteFd, buf, sizeof(buf));
+
+        adb_close(localFd);
+        adb_close(remoteFd);
+
+        if (strncmp("Success", buf, 7)) {
+            fprintf(stderr, "adb: failed to write %s\n", file);
+            fputs(buf, stderr);
+            success = 0;
+            goto finalize_session;
+        }
+    }
+
+finalize_session:
+    // Commit session if we streamed everything okay; otherwise abandon
+    std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
+                                                      success ? "commit" : "abandon", session_id);
+    fd = adb_connect(service, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
+    }
+    fprintf(stderr, "adb: failed to finalize session\n");
+    fputs(buf, stderr);
+    return EXIT_FAILURE;
+}
+
+int delete_device_file(const std::string& filename) {
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(cmd);
+}
+
+int delete_host_file(const std::string& filename) {
+#ifdef _WIN32
+    BOOL delete_return = DeleteFileA(filename.c_str());
+    if (delete_return != 0) {
+        return 0;
+    } else {
+        DWORD last_error = GetLastError();
+        printf("Error [%ld] deleting: %s\n", last_error, filename.c_str());
+        return delete_return;
+    }
+#else
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return system(cmd.c_str());
+#endif
+}
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
new file mode 100644
index 0000000..e9410a9
--- /dev/null
+++ b/adb/client/adb_install.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ADB_INSTALL_H
+#define ADB_INSTALL_H
+
+#include "fastdeploy.h"
+
+int install_app(int argc, const char** argv);
+int install_multiple_app(int argc, const char** argv);
+int uninstall_app(int argc, const char** argv);
+
+int delete_device_file(const std::string& filename);
+int delete_host_file(const std::string& filename);
+
+#endif
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 41ac663..b55ae95 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -52,23 +52,19 @@
 #include "adb.h"
 #include "adb_auth.h"
 #include "adb_client.h"
+#include "adb_install.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "bugreport.h"
 #include "client/file_sync_client.h"
 #include "commandline.h"
+#include "fastdeploy.h"
 #include "services.h"
 #include "shell_protocol.h"
 #include "sysdeps/chrono.h"
 #include "sysdeps/memory.h"
 
-static int install_app(int argc, const char** argv);
-static int install_multiple_app(int argc, const char** argv);
-static int uninstall_app(int argc, const char** argv);
-static int install_app_legacy(int argc, const char** argv);
-static int uninstall_app_legacy(int argc, const char** argv);
-
 extern int gListenAll;
 
 DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
@@ -160,6 +156,17 @@
         "     -p: partial application install (install-multiple only)\n"
         "     -g: grant all runtime permissions\n"
         "     --instant: cause the app to be installed as an ephemeral install app\n"
+        "     --no-streaming: always push APK to device and invoke Package Manager as separate steps\n"
+        "     --streaming: force streaming APK directly into Package Manager\n"
+        "     -f/--fastdeploy: use fast deploy (only valid with -r)\n"
+        "     --no-fastdeploy: prevent use of fast deploy (only valid with -r)\n"
+        "     --force-agent: force update of deployment agent when using fast deploy\n"
+        "     --date-check-agent: update deployment agent when local version is newer and using fast deploy\n"
+        "     --version-check-agent: update deployment agent when local version has different version code and using fast deploy\n"
+#ifndef _WIN32
+        "     --local-agent: locate agent files from local source build (instead of SDK location)\n"
+#endif
+        //TODO--installlog <filename>
         " uninstall [-k] PACKAGE\n"
         "     remove this app package from the device\n"
         "     '-k': keep the data and cache directories\n"
@@ -306,21 +313,6 @@
     return callback->Done(exit_code);
 }
 
-static void read_status_line(int fd, char* buf, size_t count)
-{
-    count--;
-    while (count > 0) {
-        int len = adb_read(fd, buf, count);
-        if (len <= 0) {
-            break;
-        }
-
-        buf += len;
-        count -= len;
-    }
-    *buf = '\0';
-}
-
 static void stdinout_raw_prologue(int inFd, int outFd, int& old_stdin_mode, int& old_stdout_mode) {
     if (inFd == STDIN_FILENO) {
         stdin_raw_init();
@@ -361,7 +353,7 @@
 #endif
 }
 
-static void copy_to_file(int inFd, int outFd) {
+void copy_to_file(int inFd, int outFd) {
     constexpr size_t BUFSIZE = 32 * 1024;
     std::vector<char> buf(BUFSIZE);
     int len;
@@ -1315,16 +1307,6 @@
 #endif
 }
 
-static bool _use_legacy_install() {
-    FeatureSet features;
-    std::string error;
-    if (!adb_get_feature_set(&features, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return true;
-    }
-    return !CanUseFeature(features, kFeatureCmd);
-}
-
 int adb_commandline(int argc, const char** argv) {
     bool no_daemon = false;
     bool is_daemon = false;
@@ -1706,9 +1688,6 @@
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return syntax_error("install requires an argument");
-        if (_use_legacy_install()) {
-            return install_app_legacy(argc, argv);
-        }
         return install_app(argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
@@ -1717,9 +1696,6 @@
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return syntax_error("uninstall requires an argument");
-        if (_use_legacy_install()) {
-            return uninstall_app_legacy(argc, argv);
-        }
         return uninstall_app(argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
@@ -1844,270 +1820,3 @@
     syntax_error("unknown command %s", argv[0]);
     return 1;
 }
-
-static int uninstall_app(int argc, const char** argv) {
-    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
-    std::string cmd = "cmd package";
-    while (argc-- > 0) {
-        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
-        if (strcmp(*argv, "-k") == 0) {
-            printf(
-                "The -k option uninstalls the application while retaining the data/cache.\n"
-                "At the moment, there is no way to remove the remaining data.\n"
-                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-                "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
-            return EXIT_FAILURE;
-        }
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    return send_shell_command(cmd);
-}
-
-static int install_app(int argc, const char** argv) {
-    // The last argument must be the APK file
-    const char* file = argv[argc - 1];
-    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
-        return syntax_error("filename doesn't end .apk: %s", file);
-    }
-
-    struct stat sb;
-    if (stat(file, &sb) == -1) {
-        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-        return 1;
-    }
-
-    int localFd = adb_open(file, O_RDONLY);
-    if (localFd < 0) {
-        fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-        return 1;
-    }
-
-    std::string error;
-    std::string cmd = "exec:cmd package";
-
-    // don't copy the APK name, but, copy the rest of the arguments as-is
-    while (argc-- > 1) {
-        cmd += " " + escape_arg(std::string(*argv++));
-    }
-
-    // add size parameter [required for streaming installs]
-    // do last to override any user specified value
-    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
-
-    int remoteFd = adb_connect(cmd, &error);
-    if (remoteFd < 0) {
-        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-        adb_close(localFd);
-        return 1;
-    }
-
-    char buf[BUFSIZ];
-    copy_to_file(localFd, remoteFd);
-    read_status_line(remoteFd, buf, sizeof(buf));
-
-    adb_close(localFd);
-    adb_close(remoteFd);
-
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
-    }
-    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
-    return 1;
-}
-
-static int install_multiple_app(int argc, const char** argv) {
-    // Find all APK arguments starting at end.
-    // All other arguments passed through verbatim.
-    int first_apk = -1;
-    uint64_t total_size = 0;
-    for (int i = argc - 1; i >= 0; i--) {
-        const char* file = argv[i];
-
-        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
-            android::base::EndsWithIgnoreCase(file, ".dm")) {
-            struct stat sb;
-            if (stat(file, &sb) != -1) total_size += sb.st_size;
-            first_apk = i;
-        } else {
-            break;
-        }
-    }
-
-    if (first_apk == -1) return syntax_error("need APK file on command line");
-
-    std::string install_cmd;
-    if (_use_legacy_install()) {
-        install_cmd = "exec:pm";
-    } else {
-        install_cmd = "exec:cmd package";
-    }
-
-    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
-    for (int i = 1; i < first_apk; i++) {
-        cmd += " " + escape_arg(argv[i]);
-    }
-
-    // Create install session
-    std::string error;
-    int fd = adb_connect(cmd, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
-        return EXIT_FAILURE;
-    }
-    char buf[BUFSIZ];
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
-
-    int session_id = -1;
-    if (!strncmp("Success", buf, 7)) {
-        char* start = strrchr(buf, '[');
-        char* end = strrchr(buf, ']');
-        if (start && end) {
-            *end = '\0';
-            session_id = strtol(start + 1, nullptr, 10);
-        }
-    }
-    if (session_id < 0) {
-        fprintf(stderr, "adb: failed to create session\n");
-        fputs(buf, stderr);
-        return EXIT_FAILURE;
-    }
-
-    // Valid session, now stream the APKs
-    int success = 1;
-    for (int i = first_apk; i < argc; i++) {
-        const char* file = argv[i];
-        struct stat sb;
-        if (stat(file, &sb) == -1) {
-            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-            success = 0;
-            goto finalize_session;
-        }
-
-        std::string cmd = android::base::StringPrintf(
-            "%s install-write -S %" PRIu64 " %d %s -", install_cmd.c_str(),
-            static_cast<uint64_t>(sb.st_size), session_id, android::base::Basename(file).c_str());
-
-        int localFd = adb_open(file, O_RDONLY);
-        if (localFd < 0) {
-            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-            success = 0;
-            goto finalize_session;
-        }
-
-        std::string error;
-        int remoteFd = adb_connect(cmd, &error);
-        if (remoteFd < 0) {
-            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-            adb_close(localFd);
-            success = 0;
-            goto finalize_session;
-        }
-
-        copy_to_file(localFd, remoteFd);
-        read_status_line(remoteFd, buf, sizeof(buf));
-
-        adb_close(localFd);
-        adb_close(remoteFd);
-
-        if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "adb: failed to write %s\n", file);
-            fputs(buf, stderr);
-            success = 0;
-            goto finalize_session;
-        }
-    }
-
-finalize_session:
-    // Commit session if we streamed everything okay; otherwise abandon
-    std::string service =
-            android::base::StringPrintf("%s install-%s %d",
-                                        install_cmd.c_str(), success ? "commit" : "abandon", session_id);
-    fd = adb_connect(service, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
-        return EXIT_FAILURE;
-    }
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
-
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
-    }
-    fprintf(stderr, "adb: failed to finalize session\n");
-    fputs(buf, stderr);
-    return EXIT_FAILURE;
-}
-
-static int pm_command(int argc, const char** argv) {
-    std::string cmd = "pm";
-
-    while (argc-- > 0) {
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    return send_shell_command(cmd);
-}
-
-static int uninstall_app_legacy(int argc, const char** argv) {
-    /* if the user choose the -k option, we refuse to do it until devices are
-       out with the option to uninstall the remaining data somehow (adb/ui) */
-    int i;
-    for (i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-k")) {
-            printf(
-                "The -k option uninstalls the application while retaining the data/cache.\n"
-                "At the moment, there is no way to remove the remaining data.\n"
-                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-                "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
-            return EXIT_FAILURE;
-        }
-    }
-
-    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
-    return pm_command(argc, argv);
-}
-
-static int delete_file(const std::string& filename) {
-    std::string cmd = "rm -f " + escape_arg(filename);
-    return send_shell_command(cmd);
-}
-
-static int install_app_legacy(int argc, const char** argv) {
-    static const char *const DATA_DEST = "/data/local/tmp/%s";
-    static const char *const SD_DEST = "/sdcard/tmp/%s";
-    const char* where = DATA_DEST;
-
-    for (int i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-s")) {
-            where = SD_DEST;
-        }
-    }
-
-    // Find last APK argument.
-    // All other arguments passed through verbatim.
-    int last_apk = -1;
-    for (int i = argc - 1; i >= 0; i--) {
-        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
-            last_apk = i;
-            break;
-        }
-    }
-
-    if (last_apk == -1) return syntax_error("need APK file on command line");
-
-    int result = -1;
-    std::vector<const char*> apk_file = {argv[last_apk]};
-    std::string apk_dest = android::base::StringPrintf(
-        where, android::base::Basename(argv[last_apk]).c_str());
-    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
-    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
-    result = pm_command(argc, argv);
-
-cleanup_apk:
-    delete_file(apk_dest);
-    return result;
-}
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 3aa03a7..6cfd4f7 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -96,6 +96,8 @@
 
 int adb_commandline(int argc, const char** argv);
 
+void copy_to_file(int inFd, int outFd);
+
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
 // if |callback| is non-null, stdout/stderr output will be handled by it.
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
new file mode 100644
index 0000000..cd42b56
--- /dev/null
+++ b/adb/client/fastdeploy.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2018 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 <androidfw/ResourceTypes.h>
+#include <androidfw/ZipFileRO.h>
+#include <libgen.h>
+#include <algorithm>
+
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploy.h"
+#include "fastdeploycallbacks.h"
+#include "utils/String16.h"
+
+const long kRequiredAgentVersion = 0x00000001;
+
+const char* kDeviceAgentPath = "/data/local/tmp/";
+
+long get_agent_version() {
+    std::vector<char> versionOutputBuffer;
+    std::vector<char> versionErrorBuffer;
+
+    int statusCode = capture_shell_command("/data/local/tmp/deployagent.sh version",
+                                           &versionOutputBuffer, &versionErrorBuffer);
+    long version = -1;
+
+    if (statusCode == 0 && versionOutputBuffer.size() > 0) {
+        version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
+    }
+
+    return version;
+}
+
+int get_device_api_level() {
+    std::vector<char> sdkVersionOutputBuffer;
+    std::vector<char> sdkVersionErrorBuffer;
+    int api_level = -1;
+
+    int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
+                                           &sdkVersionErrorBuffer);
+    if (statusCode == 0 && statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
+        api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
+    }
+
+    return api_level;
+}
+
+// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
+static bool get_agent_component_host_path(bool use_localagent, const char* adb_path,
+                                          const char* local_path, const char* sdk_path,
+                                          std::string* output_path) {
+    std::string mutable_adb_path = adb_path;
+    const char* adb_dir = dirname(&mutable_adb_path[0]);
+    if (adb_dir == nullptr) {
+        return false;
+    }
+
+    if (use_localagent) {
+        const char* product_out = getenv("ANDROID_PRODUCT_OUT");
+        if (product_out == nullptr) {
+            return false;
+        }
+        *output_path = android::base::StringPrintf("%s%s", product_out, local_path);
+        return true;
+    } else {
+        *output_path = android::base::StringPrintf("%s%s", adb_dir, sdk_path);
+        return true;
+    }
+    return false;
+}
+
+static bool deploy_agent(bool checkTimeStamps, bool use_localagent, const char* adb_path) {
+    std::vector<const char*> srcs;
+
+    std::string agent_jar_path;
+    if (get_agent_component_host_path(use_localagent, adb_path, "/system/framework/deployagent.jar",
+                                      "/deployagent.jar", &agent_jar_path)) {
+        srcs.push_back(agent_jar_path.c_str());
+    } else {
+        return false;
+    }
+
+    std::string agent_sh_path;
+    if (get_agent_component_host_path(use_localagent, adb_path, "/system/bin/deployagent.sh",
+                                      "/deployagent.sh", &agent_sh_path)) {
+        srcs.push_back(agent_sh_path.c_str());
+    } else {
+        return false;
+    }
+
+    if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
+        // on windows the shell script might have lost execute permission
+        // so need to set this explicitly
+        const char* kChmodCommandPattern = "chmod 777 %sdeployagent.sh";
+        std::string chmodCommand =
+                android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
+        int ret = send_shell_command(chmodCommand.c_str());
+        return (ret == 0);
+    } else {
+        return false;
+    }
+}
+
+bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy, bool use_localagent,
+                  const char* adb_path) {
+    long agent_version = get_agent_version();
+    switch (agentUpdateStrategy) {
+        case FastDeploy_AgentUpdateAlways:
+            if (deploy_agent(false, use_localagent, adb_path) == false) {
+                return false;
+            }
+            break;
+        case FastDeploy_AgentUpdateNewerTimeStamp:
+            if (deploy_agent(true, use_localagent, adb_path) == false) {
+                return false;
+            }
+            break;
+        case FastDeploy_AgentUpdateDifferentVersion:
+            if (agent_version != kRequiredAgentVersion) {
+                if (agent_version < 0) {
+                    printf("Could not detect agent on device, deploying\n");
+                } else {
+                    printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
+                           agent_version, kRequiredAgentVersion);
+                }
+                if (deploy_agent(false, use_localagent, adb_path) == false) {
+                    return false;
+                }
+            }
+            break;
+    }
+
+    agent_version = get_agent_version();
+    return (agent_version == kRequiredAgentVersion);
+}
+
+static std::string get_string_from_utf16(const char16_t* input, int input_len) {
+    ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
+    if (utf8_length <= 0) {
+        return {};
+    }
+
+    std::string utf8;
+    utf8.resize(utf8_length);
+    utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
+    return utf8;
+}
+
+// output is required to point to a valid output string (non-null)
+static bool get_packagename_from_apk(const char* apkPath, std::string* output) {
+    using namespace android;
+
+    ZipFileRO* zipFile = ZipFileRO::open(apkPath);
+    if (zipFile == nullptr) {
+        return false;
+    }
+
+    ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
+    if (entry == nullptr) {
+        return false;
+    }
+
+    uint32_t manifest_len = 0;
+    if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
+        return false;
+    }
+
+    std::vector<char> manifest_data(manifest_len);
+    if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
+        return false;
+    }
+
+    ResXMLTree tree;
+    status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
+    if (setto_status != NO_ERROR) {
+        return false;
+    }
+
+    ResXMLParser::event_code_t code;
+    while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
+           code != ResXMLParser::END_DOCUMENT) {
+        switch (code) {
+            case ResXMLParser::START_TAG: {
+                size_t element_name_length;
+                const char16_t* element_name = tree.getElementName(&element_name_length);
+                if (element_name == nullptr) {
+                    continue;
+                }
+
+                std::u16string element_name_string(element_name, element_name_length);
+                if (element_name_string == u"manifest") {
+                    for (int i = 0; i < (int)tree.getAttributeCount(); i++) {
+                        size_t attribute_name_length;
+                        const char16_t* attribute_name_text =
+                                tree.getAttributeName(i, &attribute_name_length);
+                        if (attribute_name_text == nullptr) {
+                            continue;
+                        }
+                        std::u16string attribute_name_string(attribute_name_text,
+                                                             attribute_name_length);
+
+                        if (attribute_name_string == u"package") {
+                            size_t attribute_value_length;
+                            const char16_t* attribute_value_text =
+                                    tree.getAttributeStringValue(i, &attribute_value_length);
+                            if (attribute_value_text == nullptr) {
+                                continue;
+                            }
+                            *output = get_string_from_utf16(attribute_value_text,
+                                                            attribute_value_length);
+                            return true;
+                        }
+                    }
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    return false;
+}
+
+int extract_metadata(const char* apkPath, FILE* outputFp) {
+    std::string packageName;
+    if (get_packagename_from_apk(apkPath, &packageName) == false) {
+        return -1;
+    }
+
+    const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent.sh extract %s";
+    std::string extractCommand =
+            android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
+
+    std::vector<char> extractErrorBuffer;
+    int statusCode;
+    DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
+    int ret = send_shell_command(extractCommand.c_str(), false, &cb);
+
+    if (ret == 0) {
+        return cb.getBytesWritten();
+    }
+
+    return ret;
+}
+
+// output is required to point to a valid output string (non-null)
+static bool patch_generator_command(bool use_localagent, const char* adb_path,
+                                    std::string* output) {
+    if (use_localagent) {
+        // This should never happen on a Windows machine
+        const char* kGeneratorCommandPattern = "java -jar %s/framework/deploypatchgenerator.jar";
+        const char* host_out = getenv("ANDROID_HOST_OUT");
+        if (host_out == nullptr) {
+            return false;
+        }
+        *output = android::base::StringPrintf(kGeneratorCommandPattern, host_out, host_out);
+        return true;
+    } else {
+        const char* kGeneratorCommandPattern = R"(java -jar "%s/deploypatchgenerator.jar")";
+        std::string mutable_adb_path = adb_path;
+        const char* adb_dir = dirname(&mutable_adb_path[0]);
+        if (adb_dir == nullptr) {
+            return false;
+        }
+
+        *output = android::base::StringPrintf(kGeneratorCommandPattern, adb_dir, adb_dir);
+        return true;
+    }
+    return false;
+}
+
+int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath,
+                 bool use_localagent, const char* adb_path) {
+    const char* kGeneratePatchCommandPattern = R"(%s "%s" "%s" > "%s")";
+    std::string patch_generator_command_string;
+    if (patch_generator_command(use_localagent, adb_path, &patch_generator_command_string) ==
+        false) {
+        return 1;
+    }
+    std::string generatePatchCommand = android::base::StringPrintf(
+            kGeneratePatchCommandPattern, patch_generator_command_string.c_str(), apkPath,
+            metadataPath, patchPath);
+    return system(generatePatchCommand.c_str());
+}
+
+std::string get_patch_path(const char* apkPath) {
+    std::string packageName;
+    if (get_packagename_from_apk(apkPath, &packageName) == false) {
+        return "";
+    }
+    std::string patchDevicePath =
+            android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
+    return patchDevicePath;
+}
+
+int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
+    const std::string kAgentApplyCommandPattern =
+            "/data/local/tmp/deployagent.sh apply %s %s -o %s";
+
+    std::string packageName;
+    if (get_packagename_from_apk(apkPath, &packageName) == false) {
+        return -1;
+    }
+    std::string patchDevicePath = get_patch_path(apkPath);
+
+    std::vector<const char*> srcs = {patchPath};
+    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
+
+    if (!push_ok) {
+        return -1;
+    }
+
+    std::string applyPatchCommand =
+            android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
+                                        patchDevicePath.c_str(), outputPath);
+    return send_shell_command(applyPatchCommand);
+}
+
+int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
+    const std::string kAgentApplyCommandPattern =
+            "/data/local/tmp/deployagent.sh apply %s %s -pm %s";
+
+    std::string packageName;
+    if (get_packagename_from_apk(apkPath, &packageName) == false) {
+        return -1;
+    }
+
+    std::vector<const char*> srcs;
+    std::string patchDevicePath =
+            android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
+    srcs.push_back(patchPath);
+    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
+
+    if (!push_ok) {
+        return -1;
+    }
+
+    std::vector<unsigned char> applyOutputBuffer;
+    std::vector<unsigned char> applyErrorBuffer;
+    std::string argsString;
+
+    for (int i = 0; i < argc; i++) {
+        argsString.append(argv[i]);
+        argsString.append(" ");
+    }
+
+    std::string applyPatchCommand =
+            android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
+                                        patchDevicePath.c_str(), argsString.c_str());
+    return send_shell_command(applyPatchCommand);
+}
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
new file mode 100644
index 0000000..d8acd30
--- /dev/null
+++ b/adb/client/fastdeploy.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 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.h"
+
+typedef enum EFastDeploy_AgentUpdateStrategy {
+    FastDeploy_AgentUpdateAlways,
+    FastDeploy_AgentUpdateNewerTimeStamp,
+    FastDeploy_AgentUpdateDifferentVersion
+} FastDeploy_AgentUpdateStrategy;
+
+static constexpr int kFastDeployMinApi = 24;
+
+int get_device_api_level();
+bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy, bool use_localagent,
+                  const char* adb_path);
+int extract_metadata(const char* apkPath, FILE* outputFp);
+int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath,
+                 bool use_localagent, const char* adb_path);
+int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
+int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
+std::string get_patch_path(const char* apkPath);
diff --git a/adb/client/fastdeploycallbacks.cpp b/adb/client/fastdeploycallbacks.cpp
new file mode 100644
index 0000000..6c9a21f
--- /dev/null
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 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 ADB
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "sysdeps.h"
+
+#include "fastdeploycallbacks.h"
+
+static void appendBuffer(std::vector<char>* buffer, const char* input, int length) {
+    if (buffer != NULL) {
+        buffer->insert(buffer->end(), input, input + length);
+    }
+}
+
+class DeployAgentBufferCallback : public StandardStreamsCallbackInterface {
+  public:
+    DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer,
+                              int* statusCode);
+
+    virtual void OnStdout(const char* buffer, int length);
+    virtual void OnStderr(const char* buffer, int length);
+    virtual int Done(int status);
+
+  private:
+    std::vector<char>* mpOutBuffer;
+    std::vector<char>* mpErrBuffer;
+    int* mpStatusCode;
+};
+
+int capture_shell_command(const char* command, std::vector<char>* outBuffer,
+                          std::vector<char>* errBuffer) {
+    int statusCode;
+    DeployAgentBufferCallback cb(outBuffer, errBuffer, &statusCode);
+    int ret = send_shell_command(command, false, &cb);
+
+    if (ret == 0) {
+        return statusCode;
+    } else {
+        return ret;
+    }
+}
+
+DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer,
+                                                 int* statusCode) {
+    mpOutFile = outputFile;
+    mpErrBuffer = errBuffer;
+    mpStatusCode = statusCode;
+    mBytesWritten = 0;
+}
+
+void DeployAgentFileCallback::OnStdout(const char* buffer, int length) {
+    if (mpOutFile != NULL) {
+        int bytes_written = fwrite(buffer, 1, length, mpOutFile);
+        if (bytes_written != length) {
+            printf("Write error %d\n", bytes_written);
+        }
+        mBytesWritten += bytes_written;
+    }
+}
+
+void DeployAgentFileCallback::OnStderr(const char* buffer, int length) {
+    appendBuffer(mpErrBuffer, buffer, length);
+}
+
+int DeployAgentFileCallback::Done(int status) {
+    if (mpStatusCode != NULL) {
+        *mpStatusCode = status;
+    }
+    return 0;
+}
+
+int DeployAgentFileCallback::getBytesWritten() {
+    return mBytesWritten;
+}
+
+DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
+                                                     std::vector<char>* errBuffer,
+                                                     int* statusCode) {
+    mpOutBuffer = outBuffer;
+    mpErrBuffer = errBuffer;
+    mpStatusCode = statusCode;
+}
+
+void DeployAgentBufferCallback::OnStdout(const char* buffer, int length) {
+    appendBuffer(mpOutBuffer, buffer, length);
+}
+
+void DeployAgentBufferCallback::OnStderr(const char* buffer, int length) {
+    appendBuffer(mpErrBuffer, buffer, length);
+}
+
+int DeployAgentBufferCallback::Done(int status) {
+    if (mpStatusCode != NULL) {
+        *mpStatusCode = status;
+    }
+    return 0;
+}
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
new file mode 100644
index 0000000..b428b50
--- /dev/null
+++ b/adb/client/fastdeploycallbacks.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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 <vector>
+#include "commandline.h"
+
+class DeployAgentFileCallback : public StandardStreamsCallbackInterface {
+  public:
+    DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer, int* statusCode);
+
+    virtual void OnStdout(const char* buffer, int length);
+    virtual void OnStderr(const char* buffer, int length);
+    virtual int Done(int status);
+
+    int getBytesWritten();
+
+  private:
+    FILE* mpOutFile;
+    std::vector<char>* mpErrBuffer;
+    int mBytesWritten;
+    int* mpStatusCode;
+};
+
+int capture_shell_command(const char* command, std::vector<char>* outBuffer,
+                          std::vector<char>* errBuffer);
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index dfcc52d..8417690 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -26,6 +26,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 
 #include <thread>
@@ -96,41 +98,44 @@
 
 void reboot_service(unique_fd fd, const std::string& arg) {
     std::string reboot_arg = arg;
-    bool auto_reboot = false;
-
-    if (reboot_arg == "sideload-auto-reboot") {
-        auto_reboot = true;
-        reboot_arg = "sideload";
-    }
-
-    // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
-    // in the command file.
-    if (reboot_arg == "sideload") {
-        if (getuid() != 0) {
-            WriteFdExactly(fd.get(), "'adb root' is required for 'adb reboot sideload'.\n");
-            return;
-        }
-
-        const std::vector<std::string> options = {auto_reboot ? "--sideload_auto_reboot"
-                                                              : "--sideload"};
-        std::string err;
-        if (!write_bootloader_message(options, &err)) {
-            D("Failed to set bootloader message: %s", err.c_str());
-            return;
-        }
-
-        reboot_arg = "recovery";
-    }
-
     sync();
 
     if (reboot_arg.empty()) reboot_arg = "adb";
     std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
-    if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
-        WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
-        return;
-    }
 
+    if (reboot_arg == "fastboot" && access("/dev/socket/recovery", F_OK) == 0) {
+        LOG(INFO) << "Recovery specific reboot fastboot";
+        /*
+         * The socket is created to allow switching between recovery and
+         * fastboot.
+         */
+        android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+        if (sock < 0) {
+            WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
+            PLOG(ERROR) << "Creating recovery socket failed";
+            return;
+        }
+
+        sockaddr_un addr = {.sun_family = AF_UNIX};
+        strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+        if (connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+            WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
+            PLOG(ERROR) << "Couldn't connect to recovery socket";
+            return;
+        }
+        const char msg_switch_to_fastboot = 'f';
+        auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
+        if (ret != sizeof(msg_switch_to_fastboot)) {
+            WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
+            PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
+            return;
+        }
+    } else {
+        if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+            WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
+            return;
+        }
+    }
     // Don't return early. Give the reboot command time to take effect
     // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
     while (true) {
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
new file mode 100644
index 0000000..30f4730
--- /dev/null
+++ b/adb/fastdeploy/Android.bp
@@ -0,0 +1,43 @@
+//
+// Copyright (C) 2018 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.
+//
+
+java_library {
+    name: "deployagent",
+    sdk_version: "24",
+    srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
+    static_libs: ["apkzlib_zip"],
+    proto: {
+        type: "lite",
+    }
+}
+
+cc_prebuilt_binary {
+    name: "deployagent.sh",
+
+    srcs: ["deployagent/deployagent.sh"],
+    required: ["deployagent"],
+    device_supported: true,
+}
+
+java_binary_host {
+    name: "deploypatchgenerator",
+    srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
+    static_libs: ["apkzlib"],
+    manifest: "deploypatchgenerator/manifest.txt",
+    proto: {
+        type: "full",
+    }
+}
diff --git a/adb/fastdeploy/deployagent/deployagent.sh b/adb/fastdeploy/deployagent/deployagent.sh
new file mode 100755
index 0000000..4f17eb7
--- /dev/null
+++ b/adb/fastdeploy/deployagent/deployagent.sh
@@ -0,0 +1,7 @@
+# Script to start "deployagent" on the device, which has a very rudimentary
+# shell.
+#
+base=/data/local/tmp
+export CLASSPATH=$base/deployagent.jar
+exec app_process $base com.android.fastdeploy.DeployAgent "$@"
+
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
new file mode 100644
index 0000000..cd6f168
--- /dev/null
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.fastdeploy;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Set;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.PatchUtils;
+
+public final class DeployAgent {
+    private static final int BUFFER_SIZE = 128 * 1024;
+    private static final int AGENT_VERSION = 0x00000001;
+
+    public static void main(String[] args) {
+        int exitCode = 0;
+        try {
+            if (args.length < 1) {
+                showUsage(0);
+            }
+
+            String commandString = args[0];
+
+            if (commandString.equals("extract")) {
+                if (args.length != 2) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                extractMetaData(packageName);
+            } else if (commandString.equals("apply")) {
+                if (args.length < 4) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                String patchPath = args[2];
+                String outputParam = args[3];
+
+                InputStream deltaInputStream = null;
+                if (patchPath.compareTo("-") == 0) {
+                    deltaInputStream = System.in;
+                } else {
+                    deltaInputStream = new FileInputStream(patchPath);
+                }
+
+                if (outputParam.equals("-o")) {
+                    OutputStream outputStream = null;
+                    if (args.length > 4) {
+                        String outputPath = args[4];
+                        if (!outputPath.equals("-")) {
+                            outputStream = new FileOutputStream(outputPath);
+                        }
+                    }
+                    if (outputStream == null) {
+                        outputStream = System.out;
+                    }
+                    File deviceFile = getFileFromPackageName(packageName);
+                    writePatchToStream(
+                            new RandomAccessFile(deviceFile, "r"), deltaInputStream, outputStream);
+                } else if (outputParam.equals("-pm")) {
+                    String[] sessionArgs = null;
+                    if (args.length > 4) {
+                        int numSessionArgs = args.length-4;
+                        sessionArgs = new String[numSessionArgs];
+                        for (int i=0 ; i<numSessionArgs ; i++) {
+                            sessionArgs[i] = args[i+4];
+                        }
+                    }
+                    exitCode = applyPatch(packageName, deltaInputStream, sessionArgs);
+                }
+            } else if (commandString.equals("version")) {
+                System.out.printf("0x%08X\n", AGENT_VERSION);
+            } else {
+                showUsage(1);
+            }
+        } catch (Exception e) {
+            System.err.println("Error: " + e);
+            e.printStackTrace();
+            System.exit(2);
+        }
+        System.exit(exitCode);
+    }
+
+    private static void showUsage(int exitCode) {
+        System.err.println(
+            "usage: deployagent <command> [<args>]\n\n" +
+            "commands:\n" +
+            "version                             get the version\n" +
+            "extract PKGNAME                     extract an installed package's metadata\n" +
+            "apply PKGNAME PATCHFILE [-o|-pm]    apply a patch from PATCHFILE (- for stdin) to an installed package\n" +
+            " -o <FILE> directs output to FILE, default or - for stdout\n" +
+            " -pm <ARGS> directs output to package manager, passes <ARGS> to 'pm install-create'\n"
+            );
+
+        System.exit(exitCode);
+    }
+
+    private static Process executeCommand(String command) throws IOException {
+        try {
+            Process p;
+            p = Runtime.getRuntime().exec(command);
+            p.waitFor();
+            return p;
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    private static File getFileFromPackageName(String packageName) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append("pm list packages -f " + packageName);
+
+        Process p = executeCommand(commandBuilder.toString());
+        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+
+        String packagePrefix = "package:";
+        String line = "";
+        while ((line = reader.readLine()) != null) {
+            int packageIndex = line.indexOf(packagePrefix);
+            int equalsIndex = line.indexOf("=" + packageName);
+            return new File(line.substring(packageIndex + packagePrefix.length(), equalsIndex));
+        }
+
+        return null;
+    }
+
+    private static void extractMetaData(String packageName) throws IOException {
+        File apkFile = getFileFromPackageName(packageName);
+        APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
+        apkMetaData.writeDelimitedTo(System.out);
+    }
+
+    private static int createInstallSession(String[] args) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append("pm install-create ");
+        for (int i=0 ; args != null && i<args.length ; i++) {
+            commandBuilder.append(args[i] + " ");
+        }
+
+        Process p = executeCommand(commandBuilder.toString());
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+        String line = "";
+        String successLineStart = "Success: created install session [";
+        String successLineEnd = "]";
+        while ((line = reader.readLine()) != null) {
+            if (line.startsWith(successLineStart) && line.endsWith(successLineEnd)) {
+                return Integer.parseInt(line.substring(successLineStart.length(), line.lastIndexOf(successLineEnd)));
+            }
+        }
+
+        return -1;
+    }
+
+    private static int commitInstallSession(int sessionId) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append(String.format("pm install-commit %d -- - ", sessionId));
+        Process p = executeCommand(commandBuilder.toString());
+        return p.exitValue();
+    }
+
+    private static int applyPatch(String packageName, InputStream deltaStream, String[] sessionArgs)
+            throws IOException, PatchFormatException {
+        File deviceFile = getFileFromPackageName(packageName);
+        int sessionId = createInstallSession(sessionArgs);
+        if (sessionId < 0) {
+            System.err.println("PM Create Session Failed");
+            return -1;
+        }
+
+        int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
+
+        if (writeExitCode == 0) {
+            return commitInstallSession(sessionId);
+        } else {
+            return -1;
+        }
+    }
+
+    private static long writePatchToStream(RandomAccessFile oldData, InputStream patchData,
+        OutputStream outputStream) throws IOException, PatchFormatException {
+        long newSize = readPatchHeader(patchData);
+        long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, outputStream);
+        outputStream.flush();
+        if (bytesWritten != newSize) {
+            throw new PatchFormatException(String.format(
+                "output size mismatch (expected %ld but wrote %ld)", newSize, bytesWritten));
+        }
+        return bytesWritten;
+    }
+
+    private static long readPatchHeader(InputStream patchData)
+        throws IOException, PatchFormatException {
+        byte[] signatureBuffer = new byte[PatchUtils.SIGNATURE.length()];
+        try {
+            PatchUtils.readFully(patchData, signatureBuffer, 0, signatureBuffer.length);
+        } catch (IOException e) {
+            throw new PatchFormatException("truncated signature");
+        }
+
+        String signature = new String(signatureBuffer, 0, signatureBuffer.length, "US-ASCII");
+        if (!PatchUtils.SIGNATURE.equals(signature)) {
+            throw new PatchFormatException("bad signature");
+        }
+
+        long newSize = PatchUtils.readBsdiffLong(patchData);
+        if (newSize < 0 || newSize > Integer.MAX_VALUE) {
+            throw new PatchFormatException("bad newSize");
+        }
+
+        return newSize;
+    }
+
+    // Note that this function assumes patchData has been seek'ed to the start of the delta stream
+    // (i.e. the signature has already been read by readPatchHeader). For a stream that points to the
+    // start of a patch file call writePatchToStream
+    private static long writePatchedDataToStream(RandomAccessFile oldData, long newSize,
+        InputStream patchData, OutputStream outputStream) throws IOException {
+        long newDataBytesWritten = 0;
+        byte[] buffer = new byte[BUFFER_SIZE];
+
+        while (newDataBytesWritten < newSize) {
+            long copyLen = PatchUtils.readFormattedLong(patchData);
+            if (copyLen > 0) {
+                PatchUtils.pipe(patchData, outputStream, buffer, (int) copyLen);
+            }
+
+            long oldDataOffset = PatchUtils.readFormattedLong(patchData);
+            long oldDataLen = PatchUtils.readFormattedLong(patchData);
+            oldData.seek(oldDataOffset);
+            if (oldDataLen > 0) {
+                PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
+            }
+
+            newDataBytesWritten += copyLen + oldDataLen;
+        }
+
+        return newDataBytesWritten;
+    }
+
+    private static int writePatchedDataToSession(RandomAccessFile oldData, InputStream patchData, int sessionId)
+            throws IOException, PatchFormatException {
+        try {
+            Process p;
+            long newSize = readPatchHeader(patchData);
+            StringBuilder commandBuilder = new StringBuilder();
+            commandBuilder.append(String.format("pm install-write -S %d %d -- -", newSize, sessionId));
+
+            String command = commandBuilder.toString();
+            p = Runtime.getRuntime().exec(command);
+
+            OutputStream sessionOutputStream = p.getOutputStream();
+            long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, sessionOutputStream);
+            sessionOutputStream.flush();
+            p.waitFor();
+            if (bytesWritten != newSize) {
+                throw new PatchFormatException(
+                        String.format("output size mismatch (expected %d but wrote %)", newSize, bytesWritten));
+            }
+            return p.exitValue();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return -1;
+    }
+}
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
new file mode 100644
index 0000000..f0655f3
--- /dev/null
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.fastdeploy;
+
+class PatchFormatException extends Exception {
+    /**
+     * Constructs a new exception with the specified message.
+     * @param message the message
+     */
+    public PatchFormatException(String message) { super(message); }
+
+    /**
+     * Constructs a new exception with the specified message and cause.
+     * @param message the message
+     * @param cause the cause of the error
+     */
+    public PatchFormatException(String message, Throwable cause) {
+        super(message);
+        initCause(cause);
+    }
+}
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
new file mode 100644
index 0000000..f0f00e1
--- /dev/null
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.fastdeploy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import com.android.tools.build.apkzlib.zip.ZFile;
+import com.android.tools.build.apkzlib.zip.ZFileOptions;
+import com.android.tools.build.apkzlib.zip.StoredEntry;
+import com.android.tools.build.apkzlib.zip.StoredEntryType;
+import com.android.tools.build.apkzlib.zip.CentralDirectoryHeaderCompressInfo;
+import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.APKEntry;
+
+class PatchUtils {
+    private static final long NEGATIVE_MASK = 1L << 63;
+    private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
+    public static final String SIGNATURE = "HAMADI/IHD";
+
+    private static long getOffsetFromEntry(StoredEntry entry) {
+        return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
+    }
+
+    public static APKMetaData getAPKMetaData(File apkFile) throws IOException {
+        APKMetaData.Builder apkEntriesBuilder = APKMetaData.newBuilder();
+        ZFileOptions options = new ZFileOptions();
+        ZFile zFile = new ZFile(apkFile, options);
+
+        ArrayList<StoredEntry> metaDataEntries = new ArrayList<StoredEntry>();
+
+        for (StoredEntry entry : zFile.entries()) {
+            if (entry.getType() != StoredEntryType.FILE) {
+                continue;
+            }
+            metaDataEntries.add(entry);
+        }
+
+        Collections.sort(metaDataEntries, new Comparator<StoredEntry>() {
+            private long getOffsetFromEntry(StoredEntry entry) {
+                return PatchUtils.getOffsetFromEntry(entry);
+            }
+
+            @Override
+            public int compare(StoredEntry lhs, StoredEntry rhs) {
+                // -1 - less than, 1 - greater than, 0 - equal, all inversed for descending
+                return Long.compare(getOffsetFromEntry(lhs), getOffsetFromEntry(rhs));
+            }
+        });
+
+        for (StoredEntry entry : metaDataEntries) {
+            CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader();
+            CentralDirectoryHeaderCompressInfo cdhci = cdh.getCompressionInfoWithWait();
+
+            APKEntry.Builder entryBuilder = APKEntry.newBuilder();
+            entryBuilder.setCrc32(cdh.getCrc32());
+            entryBuilder.setFileName(cdh.getName());
+            entryBuilder.setCompressedSize(cdhci.getCompressedSize());
+            entryBuilder.setUncompressedSize(cdh.getUncompressedSize());
+            entryBuilder.setDataOffset(getOffsetFromEntry(entry));
+
+            apkEntriesBuilder.addEntries(entryBuilder);
+            apkEntriesBuilder.build();
+        }
+        return apkEntriesBuilder.build();
+    }
+
+    /**
+     * Writes a 64-bit signed integer to the specified {@link OutputStream}. The least significant
+     * byte is written first and the most significant byte is written last.
+     * @param value the value to write
+     * @param outputStream the stream to write to
+     */
+    static void writeFormattedLong(final long value, OutputStream outputStream) throws IOException {
+        long y = value;
+        if (y < 0) {
+            y = (-y) | NEGATIVE_MASK;
+        }
+
+        for (int i = 0; i < 8; ++i) {
+            outputStream.write((byte) (y & 0xff));
+            y >>>= 8;
+        }
+    }
+
+    /**
+     * Reads a 64-bit signed integer written by {@link #writeFormattedLong(long, OutputStream)} from
+     * the specified {@link InputStream}.
+     * @param inputStream the stream to read from
+     */
+    static long readFormattedLong(InputStream inputStream) throws IOException {
+        long result = 0;
+        for (int bitshift = 0; bitshift < 64; bitshift += 8) {
+            result |= ((long) inputStream.read()) << bitshift;
+        }
+
+        if ((result - NEGATIVE_MASK) > 0) {
+            result = (result & ~NEGATIVE_MASK) * -1;
+        }
+        return result;
+    }
+
+    static final long readBsdiffLong(InputStream in) throws PatchFormatException, IOException {
+        long result = 0;
+        for (int bitshift = 0; bitshift < 64; bitshift += 8) {
+            result |= ((long) in.read()) << bitshift;
+        }
+
+        if (result == NEGATIVE_LONG_SIGN_MASK) {
+            // "Negative zero", which is valid in signed-magnitude format.
+            // NB: No sane patch generator should ever produce such a value.
+            throw new PatchFormatException("read negative zero");
+        }
+
+        if ((result & NEGATIVE_LONG_SIGN_MASK) != 0) {
+            result = -(result & ~NEGATIVE_LONG_SIGN_MASK);
+        }
+
+        return result;
+    }
+
+    static void readFully(final InputStream in, final byte[] destination, final int startAt,
+        final int numBytes) throws IOException {
+        int numRead = 0;
+        while (numRead < numBytes) {
+            int readNow = in.read(destination, startAt + numRead, numBytes - numRead);
+            if (readNow == -1) {
+                throw new IOException("truncated input stream");
+            }
+            numRead += readNow;
+        }
+    }
+
+    static void pipe(final InputStream in, final OutputStream out, final byte[] buffer,
+        long copyLength) throws IOException {
+        while (copyLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) copyLength);
+            readFully(in, buffer, 0, maxCopy);
+            out.write(buffer, 0, maxCopy);
+            copyLength -= maxCopy;
+        }
+    }
+
+    static void pipe(final RandomAccessFile in, final OutputStream out, final byte[] buffer,
+        long copyLength) throws IOException {
+        while (copyLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) copyLength);
+            in.readFully(buffer, 0, maxCopy);
+            out.write(buffer, 0, maxCopy);
+            copyLength -= maxCopy;
+        }
+    }
+
+    static void fill(byte value, final OutputStream out, final byte[] buffer, long fillLength)
+        throws IOException {
+        while (fillLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) fillLength);
+            Arrays.fill(buffer, 0, maxCopy, value);
+            out.write(buffer, 0, maxCopy);
+            fillLength -= maxCopy;
+        }
+    }
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/manifest.txt b/adb/fastdeploy/deploypatchgenerator/manifest.txt
new file mode 100644
index 0000000..5c00505
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.fastdeploy.DeployPatchGenerator
diff --git a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
new file mode 100644
index 0000000..5577364
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.fastdeploy;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.StringBuilder;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.ArrayList;
+
+import java.nio.charset.StandardCharsets;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.AbstractMap.SimpleEntry;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.APKEntry;
+
+public final class DeployPatchGenerator {
+    private static final int BUFFER_SIZE = 128 * 1024;
+
+    public static void main(String[] args) {
+        try {
+            if (args.length < 2) {
+                showUsage(0);
+            }
+
+            boolean verbose = false;
+            if (args.length > 2) {
+                String verboseFlag = args[2];
+                if (verboseFlag.compareTo("--verbose") == 0) {
+                    verbose = true;
+                }
+            }
+
+            StringBuilder sb = null;
+            String apkPath = args[0];
+            String deviceMetadataPath = args[1];
+            File hostFile = new File(apkPath);
+
+            List<APKEntry> deviceZipEntries = getMetadataFromFile(deviceMetadataPath);
+            if (verbose) {
+                sb = new StringBuilder();
+                for (APKEntry entry : deviceZipEntries) {
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
+                System.err.println(sb.toString());
+            }
+
+            List<APKEntry> hostFileEntries = PatchUtils.getAPKMetaData(hostFile).getEntriesList();
+            if (verbose) {
+                sb = new StringBuilder();
+                for (APKEntry entry : hostFileEntries) {
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println("Host Entries (" + hostFileEntries.size() + ")");
+                System.err.println(sb.toString());
+            }
+
+            List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet =
+                getIdenticalContents(deviceZipEntries, hostFileEntries);
+            reportIdenticalContents(identicalContentsEntrySet, hostFile);
+
+            if (verbose) {
+                sb = new StringBuilder();
+                for (SimpleEntry<APKEntry, APKEntry> identicalEntry : identicalContentsEntrySet) {
+                    APKEntry entry = identicalEntry.getValue();
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println("Identical Entries (" + identicalContentsEntrySet.size() + ")");
+                System.err.println(sb.toString());
+            }
+
+            createPatch(identicalContentsEntrySet, hostFile, System.out);
+        } catch (Exception e) {
+            System.err.println("Error: " + e);
+            e.printStackTrace();
+            System.exit(2);
+        }
+        System.exit(0);
+    }
+
+    private static void showUsage(int exitCode) {
+        System.err.println("usage: deploypatchgenerator <apkpath> <deviceapkmetadata> [--verbose]");
+        System.err.println("");
+        System.exit(exitCode);
+    }
+
+    private static void APKEntryToString(APKEntry entry, StringBuilder outputString) {
+        outputString.append(String.format("Filename: %s\n", entry.getFileName()));
+        outputString.append(String.format("CRC32: 0x%08X\n", entry.getCrc32()));
+        outputString.append(String.format("Data Offset: %d\n", entry.getDataOffset()));
+        outputString.append(String.format("Compressed Size: %d\n", entry.getCompressedSize()));
+        outputString.append(String.format("Uncompressed Size: %d\n", entry.getUncompressedSize()));
+    }
+
+    private static List<APKEntry> getMetadataFromFile(String deviceMetadataPath) throws IOException {
+        InputStream is = new FileInputStream(new File(deviceMetadataPath));
+        APKMetaData apkMetaData = APKMetaData.parseDelimitedFrom(is);
+        return apkMetaData.getEntriesList();
+    }
+
+    private static List<SimpleEntry<APKEntry, APKEntry>> getIdenticalContents(
+        List<APKEntry> deviceZipEntries, List<APKEntry> hostZipEntries) throws IOException {
+        List<SimpleEntry<APKEntry, APKEntry>> identicalContents =
+            new ArrayList<SimpleEntry<APKEntry, APKEntry>>();
+
+        for (APKEntry deviceZipEntry : deviceZipEntries) {
+            for (APKEntry hostZipEntry : hostZipEntries) {
+                if (deviceZipEntry.getCrc32() == hostZipEntry.getCrc32()) {
+                    identicalContents.add(new SimpleEntry(deviceZipEntry, hostZipEntry));
+                }
+            }
+        }
+
+        Collections.sort(identicalContents, new Comparator<SimpleEntry<APKEntry, APKEntry>>() {
+            @Override
+            public int compare(
+                SimpleEntry<APKEntry, APKEntry> p1, SimpleEntry<APKEntry, APKEntry> p2) {
+                return Long.compare(p1.getValue().getDataOffset(), p2.getValue().getDataOffset());
+            }
+        });
+
+        return identicalContents;
+    }
+
+    private static void reportIdenticalContents(
+        List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet, File hostFile)
+        throws IOException {
+        long totalEqualBytes = 0;
+        int totalEqualFiles = 0;
+        for (SimpleEntry<APKEntry, APKEntry> entries : identicalContentsEntrySet) {
+            APKEntry hostAPKEntry = entries.getValue();
+            totalEqualBytes += hostAPKEntry.getCompressedSize();
+            totalEqualFiles++;
+        }
+
+        float savingPercent = (float) (totalEqualBytes * 100) / hostFile.length();
+
+        System.err.println("Detected " + totalEqualFiles + " equal APK entries");
+        System.err.println(totalEqualBytes + " bytes are equal out of " + hostFile.length() + " ("
+            + savingPercent + "%)");
+    }
+
+    static void createPatch(List<SimpleEntry<APKEntry, APKEntry>> zipEntrySimpleEntrys,
+        File hostFile, OutputStream patchStream) throws IOException, PatchFormatException {
+        FileInputStream hostFileInputStream = new FileInputStream(hostFile);
+
+        patchStream.write(PatchUtils.SIGNATURE.getBytes(StandardCharsets.US_ASCII));
+        PatchUtils.writeFormattedLong(hostFile.length(), patchStream);
+
+        byte[] buffer = new byte[BUFFER_SIZE];
+        long totalBytesWritten = 0;
+        Iterator<SimpleEntry<APKEntry, APKEntry>> entrySimpleEntryIterator =
+            zipEntrySimpleEntrys.iterator();
+        while (entrySimpleEntryIterator.hasNext()) {
+            SimpleEntry<APKEntry, APKEntry> entrySimpleEntry = entrySimpleEntryIterator.next();
+            APKEntry deviceAPKEntry = entrySimpleEntry.getKey();
+            APKEntry hostAPKEntry = entrySimpleEntry.getValue();
+
+            long newDataLen = hostAPKEntry.getDataOffset() - totalBytesWritten;
+            long oldDataOffset = deviceAPKEntry.getDataOffset();
+            long oldDataLen = deviceAPKEntry.getCompressedSize();
+
+            PatchUtils.writeFormattedLong(newDataLen, patchStream);
+            PatchUtils.pipe(hostFileInputStream, patchStream, buffer, newDataLen);
+            PatchUtils.writeFormattedLong(oldDataOffset, patchStream);
+            PatchUtils.writeFormattedLong(oldDataLen, patchStream);
+
+            long skip = hostFileInputStream.skip(oldDataLen);
+            if (skip != oldDataLen) {
+                throw new PatchFormatException("skip error: attempted to skip " + oldDataLen
+                    + " bytes but return code was " + skip);
+            }
+            totalBytesWritten += oldDataLen + newDataLen;
+        }
+        long remainderLen = hostFile.length() - totalBytesWritten;
+        PatchUtils.writeFormattedLong(remainderLen, patchStream);
+        PatchUtils.pipe(hostFileInputStream, patchStream, buffer, remainderLen);
+        PatchUtils.writeFormattedLong(0, patchStream);
+        PatchUtils.writeFormattedLong(0, patchStream);
+        patchStream.flush();
+    }
+}
diff --git a/adb/fastdeploy/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
new file mode 100644
index 0000000..9460d15
--- /dev/null
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -0,0 +1,18 @@
+syntax = "proto2";
+
+package com.android.fastdeploy;
+
+option java_package = "com.android.fastdeploy";
+option java_multiple_files = true;
+
+message APKEntry {
+    required int64 crc32 = 1;
+    required string fileName = 2;
+    required int64 dataOffset = 3;
+    required int64 compressedSize = 4;
+    required int64 uncompressedSize = 5;
+}
+
+message APKMetaData {
+    repeated APKEntry entries = 1;
+}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f2911e0..0c2e45c 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -493,21 +493,7 @@
     return _fd_set_error_str(socket_local_server(name, namespace_id, type), error);
 }
 
-inline int network_connect(const std::string& host, int port, int type,
-                           int timeout, std::string* error) {
-  int getaddrinfo_error = 0;
-  int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
-                                         &getaddrinfo_error);
-  if (fd != -1) {
-    return fd;
-  }
-  if (getaddrinfo_error != 0) {
-    *error = gai_strerror(getaddrinfo_error);
-  } else {
-    *error = strerror(errno);
-  }
-  return -1;
-}
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
 
 static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
 {
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index ecd1fd2..33ddb4e 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -17,11 +17,15 @@
 #include "sysdeps/network.h"
 
 #include <errno.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 
 #include <string>
 
+#include <android-base/logging.h>
+#include <cutils/sockets.h>
+
 #include "adb_unique_fd.h"
 
 static void set_error(std::string* error) {
@@ -124,3 +128,19 @@
     }
     return rc;
 }
+
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
+    int getaddrinfo_error = 0;
+    int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
+    if (fd != -1) {
+        return fd;
+    }
+    if (getaddrinfo_error != 0) {
+        *error = gai_strerror(getaddrinfo_error);
+        LOG(WARNING) << "failed to resolve host '" << host << "': " << *error;
+    } else {
+        *error = strerror(errno);
+        LOG(WARNING) << "failed to connect to '" << host << "': " << *error;
+    }
+    return -1;
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index d4c98e4..cde2b22 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -27,7 +27,9 @@
 import socket
 import struct
 import subprocess
+import sys
 import threading
+import time
 import unittest
 
 
@@ -90,7 +92,7 @@
     server_thread.start()
 
     try:
-        yield port
+        yield port, writesock
     finally:
         writesock.close()
         server_thread.join()
@@ -120,7 +122,7 @@
 def adb_server():
     """Context manager for an ADB server.
 
-    This creates an ADB server and returns the port it"s listening on.
+    This creates an ADB server and returns the port it's listening on.
     """
 
     port = 5038
@@ -128,10 +130,19 @@
     subprocess.check_output(["adb", "-P", str(port), "kill-server"],
                             stderr=subprocess.STDOUT)
     read_pipe, write_pipe = os.pipe()
-    os.set_inheritable(write_pipe, True)
+
+    if sys.platform == "win32":
+        import msvcrt
+        write_handle = msvcrt.get_osfhandle(write_pipe)
+        os.set_handle_inheritable(write_handle, True)
+        reply_fd = str(write_handle)
+    else:
+        os.set_inheritable(write_pipe, True)
+        reply_fd = str(write_pipe)
+
     proc = subprocess.Popen(["adb", "-L", "tcp:localhost:{}".format(port),
                              "fork-server", "server",
-                             "--reply-fd", str(write_pipe)], close_fds=False)
+                             "--reply-fd", reply_fd], close_fds=False)
     try:
         os.close(write_pipe)
         greeting = os.read(read_pipe, 1024)
@@ -342,7 +353,7 @@
         Bug: http://b/78991667
         """
         with adb_server() as server_port:
-            with fake_adbd() as port:
+            with fake_adbd() as (port, _):
                 serial = "emulator-{}".format(port - 1)
                 # Ensure that the emulator is not there.
                 try:
@@ -380,7 +391,7 @@
         """
         for protocol in (socket.AF_INET, socket.AF_INET6):
             try:
-                with fake_adbd(protocol=protocol) as port:
+                with fake_adbd(protocol=protocol) as (port, _):
                     serial = "localhost:{}".format(port)
                     with adb_connect(self, serial):
                         pass
@@ -391,7 +402,7 @@
     def test_already_connected(self):
         """Ensure that an already-connected device stays connected."""
 
-        with fake_adbd() as port:
+        with fake_adbd() as (port, _):
             serial = "localhost:{}".format(port)
             with adb_connect(self, serial):
                 # b/31250450: this always returns 0 but probably shouldn't.
@@ -403,7 +414,7 @@
     def test_reconnect(self):
         """Ensure that a disconnected device reconnects."""
 
-        with fake_adbd() as port:
+        with fake_adbd() as (port, _):
             serial = "localhost:{}".format(port)
             with adb_connect(self, serial):
                 output = subprocess.check_output(["adb", "-s", serial,
@@ -439,6 +450,46 @@
                         "error: device '{}' not found".format(serial).encode("utf8"))
 
 
+class DisconnectionTest(unittest.TestCase):
+    """Tests for adb disconnect."""
+
+    def test_disconnect(self):
+        """Ensure that `adb disconnect` takes effect immediately."""
+
+        def _devices(port):
+            output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+            return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+        with adb_server() as server_port:
+            with fake_adbd() as (port, sock):
+                device_name = "localhost:{}".format(port)
+                output = subprocess.check_output(["adb", "-P", str(server_port),
+                                                  "connect", device_name])
+                self.assertEqual(output.strip(),
+                                  "connected to {}".format(device_name).encode("utf8"))
+
+
+                self.assertEqual(_devices(server_port), [[device_name, "device"]])
+
+                # Send a deliberately malformed packet to make the device go offline.
+                packet = struct.pack("IIIIII", 0, 0, 0, 0, 0, 0)
+                sock.sendall(packet)
+
+                # Wait a bit.
+                time.sleep(0.1)
+
+                self.assertEqual(_devices(server_port), [[device_name, "offline"]])
+
+                # Disconnect the device.
+                output = subprocess.check_output(["adb", "-P", str(server_port),
+                                                  "disconnect", device_name])
+
+                # Wait a bit.
+                time.sleep(0.1)
+
+                self.assertEqual(_devices(server_port), [])
+
+
 def main():
     """Main entrypoint."""
     random.seed(0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 4cd19f9..332e0f8 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -97,6 +97,9 @@
     // Adds the atransport* to the queue of reconnect attempts.
     void TrackTransport(atransport* transport);
 
+    // Wake up the ReconnectHandler thread to have it check for kicked transports.
+    void CheckForKicked();
+
   private:
     // The main thread loop.
     void Run();
@@ -166,6 +169,10 @@
     reconnect_cv_.notify_one();
 }
 
+void ReconnectHandler::CheckForKicked() {
+    reconnect_cv_.notify_one();
+}
+
 void ReconnectHandler::Run() {
     while (true) {
         ReconnectAttempt attempt;
@@ -184,10 +191,25 @@
             }
 
             if (!running_) return;
+
+            // Scan the whole list for kicked transports, so that we immediately handle an explicit
+            // disconnect request.
+            bool kicked = false;
+            for (auto it = reconnect_queue_.begin(); it != reconnect_queue_.end();) {
+                if (it->transport->kicked()) {
+                    D("transport %s was kicked. giving up on it.", it->transport->serial.c_str());
+                    remove_transport(it->transport);
+                    it = reconnect_queue_.erase(it);
+                } else {
+                    ++it;
+                }
+                kicked = true;
+            }
+
             if (reconnect_queue_.empty()) continue;
 
-            // Go back to sleep in case |reconnect_cv_| woke up spuriously and we still
-            // have more time to wait for the current attempt.
+            // Go back to sleep if we either woke up spuriously, or we were woken up to remove
+            // a kicked transport, and the first transport isn't ready for reconnection yet.
             auto now = std::chrono::steady_clock::now();
             if (reconnect_queue_.begin()->reconnect_time > now) {
                 continue;
@@ -195,11 +217,6 @@
 
             attempt = *reconnect_queue_.begin();
             reconnect_queue_.erase(reconnect_queue_.begin());
-            if (attempt.transport->kicked()) {
-                D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str());
-                remove_transport(attempt.transport);
-                continue;
-            }
         }
         D("attempting to reconnect %s", attempt.transport->serial.c_str());
 
@@ -448,6 +465,10 @@
     if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
         t->Kick();
     }
+
+#if ADB_HOST
+    reconnect_handler.CheckForKicked();
+#endif
 }
 
 static int transport_registration_send = -1;
@@ -1276,6 +1297,9 @@
             t->Kick();
         }
     }
+#if ADB_HOST
+    reconnect_handler.CheckForKicked();
+#endif
 }
 
 #endif
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 1431252..8b8fd51 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -74,6 +74,7 @@
     std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
     if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
+        D("failed to parse address: '%s'", address.c_str());
         return std::make_tuple(unique_fd(), port, serial);
     }
 
@@ -103,6 +104,7 @@
         return;
     }
 
+    D("connection requested to '%s'", address.c_str());
     unique_fd fd;
     int port;
     std::string serial;
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index 1fdf236..03e4e8e 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -75,7 +75,6 @@
       ALOGE("failed to read fdsan table entry %zu: %s", i, strerror(errno));
       return;
     }
-    ALOGE("fd %zu = %#" PRIx64, i, entry.close_tag.load());
     if (entry.close_tag) {
       (*list)[i].fdsan_owner = entry.close_tag.load();
     }
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 60c338c..a679143 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -82,6 +82,7 @@
 
 LOCAL_CFLAGS := $(fastboot_cflags)
 LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
+LOCAL_CPP_STD := c++17
 LOCAL_CXX_STL := $(fastboot_stl)
 LOCAL_HEADER_LIBRARIES := bootimg_headers
 LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index a5dead2..65cfea3 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -123,10 +123,14 @@
     }
     std::string slot_suffix = device->GetCurrentSlot();
     if (slot_suffix.empty()) {
-        return device->WriteFail("Invalid slot");
+        return device->WriteOkay("no");
     }
-    std::string result = (args[0] == "userdata" ? "no" : "yes");
-    return device->WriteOkay(result);
+    std::string partition_name = args[0] + slot_suffix;
+    if (FindPhysicalPartition(partition_name) ||
+        LogicalPartitionExists(partition_name, slot_suffix)) {
+        return device->WriteOkay("yes");
+    }
+    return device->WriteOkay("no");
 }
 
 bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args) {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index dc94952..db6d5d6 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -122,9 +122,9 @@
     { "dts",      "dt.img",           "dt.sig",       "dts",      true,  false },
     { "odm",      "odm.img",          "odm.sig",      "odm",      true,  false },
     { "product",  "product.img",      "product.sig",  "product",  true,  false },
-    { "product-services",
-                  "product-services.img",
-                                      "product-services.sig",
+    { "product_services",
+                  "product_services.img",
+                                      "product_services.sig",
                                                       "product_services",
                                                                   true,  true  },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 6329d54..3cce0e8 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -31,6 +31,8 @@
 }
 
 cc_library {
+    // Do not ever allow this library to be vendor_available as a shared library.
+    // It does not have a stable interface.
     name: "libfs_mgr",
     defaults: ["fs_mgr_defaults"],
     recovery_available: true,
@@ -46,18 +48,15 @@
         "fs_mgr_overlayfs.cpp",
     ],
     shared_libs: [
-        "libfec",
-        "libfec_rs",
         "libbase",
-        "libcrypto_utils",
         "libcrypto",
+        "libcrypto_utils",
         "libcutils",
         "libext4_utils",
-        "libkeyutils",
+        "libfec",
         "liblog",
-        "libsquashfs_utils",
-        "libselinux",
         "liblp",
+        "libselinux",
     ],
     static_libs: [
         "libavb",
@@ -90,6 +89,8 @@
 }
 
 cc_library_static {
+    // Do not ever make this a shared library as long as it is vendor_available.
+    // It does not have a stable interface.
     name: "libfstab",
     vendor_available: true,
     recovery_available: true,
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 74dfc00..78151d5 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -65,11 +65,10 @@
 // acceptable overlayfs backing storage
 const auto kOverlayMountPoint = "/cache"s;
 
-// return true if everything is mounted, but before adb is started.  At
-// 'trigger firmware_mounts_complete' after 'trigger load_persist_props_action'.
+// Return true if everything is mounted, but before adb is started.  Right
+// after 'trigger load_persist_props_action' is done.
 bool fs_mgr_boot_completed() {
-    return !android::base::GetProperty("ro.boottime.init", "").empty() &&
-           !!access("/dev/.booting", F_OK);
+    return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
 }
 
 bool fs_mgr_is_dir(const std::string& path) {
@@ -164,7 +163,9 @@
     // Overlayfs available in the kernel, and patched for override_creds?
     static signed char overlayfs_in_kernel = -1;  // cache for constant condition
     if (overlayfs_in_kernel == -1) {
+        auto save_errno = errno;
         overlayfs_in_kernel = !access("/sys/module/overlay/parameters/override_creds", F_OK);
+        errno = save_errno;
     }
     return overlayfs_in_kernel;
 }
@@ -430,7 +431,7 @@
     const auto newpath = oldpath + ".teardown";
     ret &= fs_mgr_rm_all(newpath);
     auto save_errno = errno;
-    if (rename(oldpath.c_str(), newpath.c_str())) {
+    if (!rename(oldpath.c_str(), newpath.c_str())) {
         if (change) *change = true;
     } else if (errno != ENOENT) {
         ret = false;
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index ad3b6f4..8bcbec2 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,11 +20,19 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 
 namespace android {
 namespace dm {
 
+DeviceMapper::DeviceMapper() : fd_(-1) {
+    fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+    if (fd_ < 0) {
+        PLOG(ERROR) << "Failed to open device-mapper";
+    }
+}
+
 DeviceMapper& DeviceMapper::Instance() {
     static DeviceMapper instance;
     return instance;
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index e2bc729..9b61ddc 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -29,8 +29,6 @@
 #include <utility>
 #include <vector>
 
-#include <android-base/logging.h>
-
 #include "dm_table.h"
 
 // The minimum expected device mapper major.minor version
@@ -144,12 +142,7 @@
 
     void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
 
-    DeviceMapper() : fd_(-1) {
-        fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
-        if (fd_ < 0) {
-            PLOG(ERROR) << "Failed to open device-mapper";
-        }
-    }
+    DeviceMapper();
 
     // Creates a device mapper device with given name.
     // Return 'true' on success and 'false' on failure to
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 31863c8..aab89e5 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -23,8 +23,6 @@
 #include <string>
 #include <vector>
 
-#include <android-base/logging.h>
-
 namespace android {
 namespace dm {
 
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index a361a5d..742b1d0 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -38,12 +38,24 @@
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
         return nullptr;
     }
-    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
-    if (!metadata) {
+    return ParseMetadata(geometry, fd);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes) {
+    if (bytes < LP_METADATA_GEOMETRY_SIZE) {
+        LERROR << __PRETTY_FUNCTION__ << ": " << bytes << " is smaller than geometry header";
         return nullptr;
     }
-    metadata->geometry = geometry;
-    return metadata;
+
+    LpMetadataGeometry geometry;
+    if (!ParseGeometry(data, &geometry)) {
+        return nullptr;
+    }
+
+    const uint8_t* metadata_buffer =
+            reinterpret_cast<const uint8_t*>(data) + LP_METADATA_GEOMETRY_SIZE;
+    size_t metadata_buffer_size = bytes - LP_METADATA_GEOMETRY_SIZE;
+    return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size);
 }
 
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 627aa8c..6da24f6 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -42,8 +42,7 @@
 // existing geometry, and should not be used for normal partition table
 // updates. False can be returned if the geometry is incompatible with the
 // block device or an I/O error occurs.
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                         uint32_t slot_number);
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata);
 
 // Update the partition table for a given metadata slot number. False is
 // returned if an error occurs, which can include:
@@ -64,6 +63,7 @@
                        const std::map<std::string, std::string>& images);
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
 
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 638f4b3..329a901 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -103,7 +103,7 @@
     if (!exported) {
         return {};
     }
-    if (!FlashPartitionTable(fd, *exported.get(), 0)) {
+    if (!FlashPartitionTable(fd, *exported.get())) {
         return {};
     }
     return fd;
@@ -132,7 +132,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get(), 0));
+    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get()));
 }
 
 // Test the basics of flashing a partition and reading it back.
@@ -147,7 +147,7 @@
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
 
     // Read back. Note that some fields are only filled in during
     // serialization, so exported and imported will not be identical. For
@@ -354,8 +354,7 @@
     ASSERT_GE(fd, 0);
 
     // Check that we are able to write our table.
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
-    ASSERT_TRUE(UpdatePartitionTable(fd, *exported.get(), 1));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
 
     // Check that adding one more partition overflows the metadata allotment.
     partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
@@ -395,6 +394,27 @@
     ASSERT_NE(imported, nullptr);
 }
 
+// Test that we can read images from buffers.
+TEST(liblp, ImageFilesInMemory) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    unique_ptr<LpMetadata> exported = builder->Export();
+
+    unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+    ASSERT_GE(fd, 0);
+    ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
+
+    int64_t offset = SeekFile64(fd, 0, SEEK_CUR);
+    ASSERT_GE(offset, 0);
+    ASSERT_EQ(SeekFile64(fd, 0, SEEK_SET), 0);
+
+    size_t bytes = static_cast<size_t>(offset);
+    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bytes);
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer.get(), bytes));
+    ASSERT_NE(ReadFromImageBlob(buffer.get(), bytes), nullptr);
+}
+
 class BadWriter {
   public:
     // When requested, write garbage instead of the requested bytes, then
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 117da59..117f5d5 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -30,9 +30,45 @@
 namespace android {
 namespace fs_mgr {
 
-// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
-// LP_METADATA_GEOMETRY_SIZE bytes in size.
-static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+// Helper class for reading descriptors and memory buffers in the same manner.
+class Reader {
+  public:
+    virtual ~Reader(){};
+    virtual bool ReadFully(void* buffer, size_t length) = 0;
+};
+
+class FileReader final : public Reader {
+  public:
+    explicit FileReader(int fd) : fd_(fd) {}
+    bool ReadFully(void* buffer, size_t length) override {
+        return android::base::ReadFully(fd_, buffer, length);
+    }
+
+  private:
+    int fd_;
+};
+
+class MemoryReader final : public Reader {
+  public:
+    MemoryReader(const void* buffer, size_t size)
+        : buffer_(reinterpret_cast<const uint8_t*>(buffer)), size_(size), pos_(0) {}
+    bool ReadFully(void* out, size_t length) override {
+        if (size_ - pos_ < length) {
+            errno = EINVAL;
+            return false;
+        }
+        memcpy(out, buffer_ + pos_, length);
+        pos_ += length;
+        return true;
+    }
+
+  private:
+    const uint8_t* buffer_;
+    size_t size_;
+    size_t pos_;
+};
+
+bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
     static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
     memcpy(geometry, buffer, sizeof(*geometry));
 
@@ -171,16 +207,18 @@
 
 // Parse and validate all metadata at the current position in the given file
 // descriptor.
-std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry,
+                                                 Reader* reader) {
     // First read and validate the header.
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
-    if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+    if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) {
         PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
         return nullptr;
     }
     if (!ValidateMetadataHeader(metadata->header)) {
         return nullptr;
     }
+    metadata->geometry = geometry;
 
     LpMetadataHeader& header = metadata->header;
 
@@ -191,7 +229,7 @@
         LERROR << "Out of memory reading logical partition tables.";
         return nullptr;
     }
-    if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+    if (!reader->ReadFully(buffer.get(), header.tables_size)) {
         PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
         return nullptr;
     }
@@ -231,10 +269,20 @@
 
         metadata->extents.push_back(extent);
     }
-
     return metadata;
 }
 
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size) {
+    MemoryReader reader(buffer, size);
+    return ParseMetadata(geometry, &reader);
+}
+
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd) {
+    FileReader reader(fd);
+    return ParseMetadata(geometry, &reader);
+}
+
 std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
                                                 uint32_t slot_number) {
     int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
@@ -242,7 +290,7 @@
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
         return nullptr;
     }
-    return ParseMetadata(fd);
+    return ParseMetadata(geometry, fd);
 }
 
 std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
@@ -252,7 +300,7 @@
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
         return nullptr;
     }
-    return ParseMetadata(fd);
+    return ParseMetadata(geometry, fd);
 }
 
 std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
@@ -268,13 +316,10 @@
 
     // Read the priamry copy, and if that fails, try the backup.
     std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
-    if (!metadata) {
-        metadata = ReadBackupMetadata(fd, geometry, slot_number);
-    }
     if (metadata) {
-        metadata->geometry = geometry;
+        return metadata;
     }
-    return metadata;
+    return ReadBackupMetadata(fd, geometry, slot_number);
 }
 
 std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 843b2f2..9f6ca6e 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -26,11 +26,16 @@
 namespace android {
 namespace fs_mgr {
 
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
 
 // Helper functions for manually reading geometry and metadata.
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size);
 bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
-std::unique_ptr<LpMetadata> ParseMetadata(int fd);
 
 // These functions assume a valid geometry and slot number.
 std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index fc9d83f..ad84b22 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -196,7 +196,7 @@
     return android::base::WriteFully(fd, blob.data(), blob.size());
 }
 
-bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+bool FlashPartitionTable(int fd, const LpMetadata& metadata) {
     // Before writing geometry and/or logical partition tables, perform some
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
@@ -224,8 +224,11 @@
         return false;
     }
 
-    // Write metadata to the correct slot, now that geometry is in place.
-    return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter);
+    bool ok = true;
+    for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
+        ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter);
+    }
+    return ok;
 }
 
 static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
@@ -297,14 +300,13 @@
     return WriteMetadata(fd, geometry, slot_number, blob, writer);
 }
 
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                         uint32_t slot_number) {
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {
     android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
         return false;
     }
-    if (!FlashPartitionTable(fd, metadata, slot_number)) {
+    if (!FlashPartitionTable(fd, metadata)) {
         return false;
     }
     LWARN << "Flashed new logical partition geometry to " << block_device;
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
index adbbebf..ab18d45 100644
--- a/fs_mgr/liblp/writer.h
+++ b/fs_mgr/liblp/writer.h
@@ -30,7 +30,7 @@
 
 // These variants are for testing only. The path-based functions should be used
 // for actual operation, so that open() is called with the correct flags.
-bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+bool FlashPartitionTable(int fd, const LpMetadata& metadata);
 bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
 
 bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 45a81af..879ba21 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <libdm/dm.h>
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 30f2cf3..706dc80 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -173,7 +173,7 @@
         cur_level = 100;
     }
 
-    if (cur_level <= 0) return;
+    if (cur_level < 0) return;
 
     const animation::text_field& field = anim->text_percent;
     if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 56a9f86..2eb5497 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -72,6 +72,7 @@
 #define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC)
 #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
+#define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
 
 #define LAST_KMSG_MAX_SZ (32 * 1024)
 
@@ -91,6 +92,7 @@
 struct charger {
     bool have_battery_state;
     bool charger_connected;
+    bool screen_blanked;
     int64_t next_screen_transition;
     int64_t next_key_check;
     int64_t next_pwr_check;
@@ -293,6 +295,7 @@
 
 #ifndef CHARGER_DISABLE_INIT_BLANK
         healthd_draw->blank_screen(true);
+        charger->screen_blanked = true;
 #endif
     }
 
@@ -301,6 +304,7 @@
         reset_animation(batt_anim);
         charger->next_screen_transition = -1;
         healthd_draw->blank_screen(true);
+        charger->screen_blanked = true;
         LOGV("[%" PRId64 "] animation done\n", now);
         if (charger->charger_connected) request_suspend(true);
         return;
@@ -308,8 +312,10 @@
 
     disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
 
-    /* unblank the screen on first cycle and first frame */
-    if (batt_anim->cur_cycle == 0 && batt_anim->cur_frame == 0) healthd_draw->blank_screen(false);
+    if (charger->screen_blanked) {
+        healthd_draw->blank_screen(false);
+        charger->screen_blanked = false;
+    }
 
     /* animation starting, set up the animation */
     if (batt_anim->cur_frame == 0) {
@@ -327,9 +333,15 @@
                     }
                 }
 
-                // repeat the first frame first_frame_repeats times
-                disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
-                            batt_anim->first_frame_repeats;
+                if (charger->charger_connected) {
+                    // repeat the first frame first_frame_repeats times
+                    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
+                                batt_anim->first_frame_repeats;
+                } else {
+                    disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim->num_cycles;
+                }
+
+                LOGV("cur_frame=%d disp_time=%d\n", batt_anim->cur_frame, disp_time);
             }
         }
     }
@@ -348,7 +360,7 @@
     }
 
     /* schedule next screen transition */
-    charger->next_screen_transition = now + disp_time;
+    charger->next_screen_transition = curr_time_ms() + disp_time;
 
     /* advance frame cntr to the next valid frame only if we are charging
      * if necessary, advance cycle cntr, and reset frame cntr
@@ -458,6 +470,7 @@
             /* if the power key got released, force screen state cycle */
             if (key->pending) {
                 kick_animation(charger->batt_anim);
+                request_suspend(false);
             }
         }
     }
@@ -476,12 +489,18 @@
     if (!charger->have_battery_state) return;
 
     if (!charger->charger_connected) {
-        /* Last cycle would have stopped at the extreme top of battery-icon
-         * Need to show the correct level corresponding to capacity.
-         */
-        kick_animation(charger->batt_anim);
         request_suspend(false);
         if (charger->next_pwr_check == -1) {
+            /* Last cycle would have stopped at the extreme top of battery-icon
+             * Need to show the correct level corresponding to capacity.
+             *
+             * Reset next_screen_transition to update screen immediately.
+             * Reset & kick animation to show complete animation cycles
+             * when charger disconnected.
+             */
+            charger->next_screen_transition = now - 1;
+            reset_animation(charger->batt_anim);
+            kick_animation(charger->batt_anim);
             charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
             LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
                  now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
@@ -494,8 +513,15 @@
     } else {
         /* online supply present, reset shutdown timer if set */
         if (charger->next_pwr_check != -1) {
-            LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
+            /* Reset next_screen_transition to update screen immediately.
+             * Reset & kick animation to show complete animation cycles
+             * when charger connected again.
+             */
+            request_suspend(false);
+            charger->next_screen_transition = now - 1;
+            reset_animation(charger->batt_anim);
             kick_animation(charger->batt_anim);
+            LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
         }
         charger->next_pwr_check = -1;
     }
@@ -523,6 +549,7 @@
     if (!charger->have_battery_state) {
         charger->have_battery_state = true;
         charger->next_screen_transition = curr_time_ms() - 1;
+        request_suspend(false);
         reset_animation(charger->batt_anim);
         kick_animation(charger->batt_anim);
     }
diff --git a/init/Android.mk b/init/Android.mk
index d20509b..ada87b8 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -81,7 +81,6 @@
 
 LOCAL_REQUIRED_MODULES := \
     init_second_stage \
-    init_second_stage.recovery \
 
 LOCAL_SANITIZE := signed-integer-overflow
 include $(BUILD_EXECUTABLE)
diff --git a/init/init.cpp b/init/init.cpp
index b550f1b..16564f4 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -114,8 +114,8 @@
         if (!parser.ParseConfig("/product/etc/init")) {
             late_import_paths.emplace_back("/product/etc/init");
         }
-        if (!parser.ParseConfig("/product-services/etc/init")) {
-            late_import_paths.emplace_back("/product-services/etc/init");
+        if (!parser.ParseConfig("/product_services/etc/init")) {
+            late_import_paths.emplace_back("/product_services/etc/init");
         }
         if (!parser.ParseConfig("/odm/etc/init")) {
             late_import_paths.emplace_back("/odm/etc/init");
diff --git a/init/property_service.cpp b/init/property_service.cpp
index cd2f630..5c8b92a 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -692,7 +692,7 @@
         }
     }
     load_properties_from_file("/product/build.prop", NULL);
-    load_properties_from_file("/product-services/build.prop", NULL);
+    load_properties_from_file("/product_services/build.prop", NULL);
     load_properties_from_file("/odm/default.prop", NULL);
     load_properties_from_file("/vendor/default.prop", NULL);
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 7401857..2f88121 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -473,7 +473,21 @@
                                   "bootloader_message: "
                                << err;
                 }
+            } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
+                       reboot_target == "fastboot") {
+                std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
+                                                                          : reboot_target;
+                const std::vector<std::string> options = {
+                        "--" + arg,
+                };
+                std::string err;
+                if (!write_bootloader_message(options, &err)) {
+                    LOG(ERROR) << "Failed to set bootloader message: " << err;
+                    return false;
+                }
+                reboot_target = "recovery";
             }
+
             // If there is an additional parameter, pass it along
             if ((cmd_params.size() == 3) && cmd_params[2].size()) {
                 reboot_target += "," + cmd_params[2];
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index af8f0a2..bd5f26f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -149,7 +149,7 @@
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },
     { 00600, AID_ROOT,      AID_ROOT,      0, "product/build.prop" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "product-services/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "product_services/build.prop" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index b3e36c2..c5f1f5e 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -2,11 +2,6 @@
 
 cc_library {
     name: "libsuspend",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
-
     srcs: [
         "autosuspend.c",
         "autosuspend_wakeup_count.cpp",
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index e00fb81..82f650d 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -340,27 +340,6 @@
            : 0);
 }
 
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2)
-{
-    const char16_t* e1 = s1H+n1;
-    const char16_t* e2 = s2N+n2;
-
-    while (s1H < e1 && s2N < e2) {
-        const char16_t c2 = ntohs(*s2N);
-        const int d = (int)*s1H++ - (int)c2;
-        s2N++;
-        if (d) {
-            return d;
-        }
-    }
-
-    return n1 < n2
-        ? (0 - (int)ntohs(*s2N))
-        : (n1 > n2
-           ? ((int)*s1H - 0)
-           : 0);
-}
-
 void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
 {
     if (src == nullptr || src_len == 0 || dst == nullptr) {
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 360fce5..3abce17 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -228,8 +228,10 @@
 
 template<typename T>
 void sp<T>::clear() {
-    if (m_ptr) {
-        m_ptr->decStrong(this);
+    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
+    if (oldPtr) {
+        oldPtr->decStrong(this);
+        if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
         m_ptr = nullptr;
     }
 }
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index 666b70f..61a1b4f 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -40,9 +40,6 @@
 // equivalent result as strcmp16 (unlike strncmp16).
 int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
 
-// Version of strzcmp16 for comparing strings in different endianness.
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
-
 // Standard string functions on char32_t strings.
 size_t strlen32(const char32_t *);
 size_t strnlen32(const char32_t *, size_t);
diff --git a/llkd/README.md b/llkd/README.md
index b2ba2a2..2314583 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -60,7 +60,7 @@
 Android Properties
 ------------------
 
-Android Properties llkd respond to (<prop>_ms parms are in milliseconds):
+Android Properties llkd respond to (*prop*_ms parms are in milliseconds):
 
 #### ro.config.low_ram
 default false, if true do not sysrq t (dump all threads).
@@ -99,19 +99,33 @@
 #### ro.llk.blacklist.process
 default 0,1,2 (kernel, init and [kthreadd]) plus process names
 init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
-[watchdogd],[watchdogd/0],...,[watchdogd/<get_nprocs-1>].
+[watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
+The string false is the equivalent to an empty list.
+Do not watch these processes.  A process can be comm, cmdline or pid reference.
+NB: automated default here can be larger than the current maximum property
+size of 92.
+NB: false is a very very very unlikely process to want to blacklist.
 
 #### ro.llk.blacklist.parent
 default 0,2 (kernel and [kthreadd]).
+The string false is the equivalent to an empty list.
+Do not watch processes that have this parent.
+A parent process can be comm, cmdline or pid reference.
 
 #### ro.llk.blacklist.uid
-default <empty>, comma separated list of uid numbers or names.
+default *empty* or false, comma separated list of uid numbers or names.
+The string false is the equivalent to an empty list.
+Do not watch processes that match this uid.
 
 Architectural Concerns
 ----------------------
 
+- built-in [khungtask] daemon is too generic and trips on driver code that
+  sits around in D state too much.  To switch to S instead makes the task(s)
+  killable, so the drivers should be able to resurrect them if needed.
+- Properties are limited to 92 characters.
 - Create kernel module and associated gTest to actually test panic.
-- Create gTest to test out blacklist (ro.llk.blacklist.<properties> generally
+- Create gTest to test out blacklist (ro.llk.blacklist.*properties* generally
   not be inputs).  Could require more test-only interfaces to libllkd.
-- Speed up gTest using something else than ro.llk.<properties>, which should
-  not be inputs.
+- Speed up gTest using something else than ro.llk.*properties*, which should
+  not be inputs as they should be baked into the product.
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 3b28775..48551f2 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -285,7 +285,7 @@
           schedUpdate(0),
           nrSwitches(0),
           update(llkUpdate),
-          count(0),
+          count(0ms),
           pid(pid),
           ppid(ppid),
           uid(-1),
@@ -574,15 +574,19 @@
 
 // We only officially support comma separators, but wetware being what they
 // are will take some liberty and I do not believe they should be punished.
-std::unordered_set<std::string> llkSplit(const std::string& s,
-                                         const std::string& delimiters = ", \t:") {
+std::unordered_set<std::string> llkSplit(const std::string& s) {
     std::unordered_set<std::string> result;
 
+    // Special case, allow boolean false to empty the list, otherwise expected
+    // source of input from android::base::GetProperty will supply the default
+    // value on empty content in the property.
+    if (s == "false") return result;
+
     size_t base = 0;
-    size_t found;
-    while (true) {
-        found = s.find_first_of(delimiters, base);
-        result.emplace(s.substr(base, found - base));
+    while (s.size() > base) {
+        auto found = s.find_first_of(", \t:", base);
+        // Only emplace content, empty entries are not an option
+        if (found != base) result.emplace(s.substr(base, found - base));
         if (found == s.npos) break;
         base = found + 1;
     }
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 59f17f2..02534f2 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -125,6 +125,7 @@
 static unsigned long kill_timeout_ms;
 static bool use_minfree_levels;
 static bool per_app_memcg;
+static int swap_free_low_percentage;
 
 static android_log_context ctx;
 
@@ -205,6 +206,7 @@
     MI_BUFFERS,
     MI_SHMEM,
     MI_UNEVICTABLE,
+    MI_TOTAL_SWAP,
     MI_FREE_SWAP,
     MI_ACTIVE_ANON,
     MI_INACTIVE_ANON,
@@ -227,6 +229,7 @@
     "Buffers:",
     "Shmem:",
     "Unevictable:",
+    "SwapTotal:",
     "SwapFree:",
     "Active(anon):",
     "Inactive(anon):",
@@ -249,6 +252,7 @@
         int64_t buffers;
         int64_t shmem;
         int64_t unevictable;
+        int64_t total_swap;
         int64_t free_swap;
         int64_t active_anon;
         int64_t inactive_anon;
@@ -1353,20 +1357,24 @@
         }
     }
 
-    // If the pressure is larger than downgrade_pressure lmk will not
-    // kill any process, since enough memory is available.
-    if (mem_pressure > downgrade_pressure) {
-        if (debug_process_killing) {
-            ALOGI("Ignore %s memory pressure", level_name[level]);
+    // If we still have enough swap space available, check if we want to
+    // ignore/downgrade pressure events.
+    if (mi.field.free_swap >=
+        mi.field.total_swap * swap_free_low_percentage / 100) {
+        // If the pressure is larger than downgrade_pressure lmk will not
+        // kill any process, since enough memory is available.
+        if (mem_pressure > downgrade_pressure) {
+            if (debug_process_killing) {
+                ALOGI("Ignore %s memory pressure", level_name[level]);
+            }
+            return;
+        } else if (level == VMPRESS_LEVEL_CRITICAL && mem_pressure > upgrade_pressure) {
+            if (debug_process_killing) {
+                ALOGI("Downgrade critical memory pressure");
+            }
+            // Downgrade event, since enough memory available.
+            level = downgrade_level(level);
         }
-        return;
-    } else if (level == VMPRESS_LEVEL_CRITICAL &&
-               mem_pressure > upgrade_pressure) {
-        if (debug_process_killing) {
-            ALOGI("Downgrade critical memory pressure");
-        }
-        // Downgrade event, since enough memory available.
-        level = downgrade_level(level);
     }
 
 do_kill:
@@ -1642,6 +1650,8 @@
         property_get_bool("ro.lmk.use_minfree_levels", false);
     per_app_memcg =
         property_get_bool("ro.config.per_app_memcg", low_ram_device);
+    swap_free_low_percentage =
+        property_get_int32("ro.lmk.swap_free_low_percentage", 10);
 
     ctx = create_android_logger(MEMINFO_LOG_TAG);
 
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 4ea7877..fc8c960 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/prctl.h>
@@ -344,8 +345,7 @@
 
         rc = logbuf->log(
             LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
-            (message_len <= USHRT_MAX) ? (unsigned short)message_len
-                                       : USHRT_MAX);
+            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
         if (rc >= 0) {
             notify |= 1 << LOG_ID_EVENTS;
         }
@@ -399,9 +399,9 @@
         strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
                 denial_metadata.c_str(), denial_metadata.length());
 
-        rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
-                         (message_len <= USHRT_MAX) ? (unsigned short)message_len
-                                                    : USHRT_MAX);
+        rc = logbuf->log(
+            LOG_ID_MAIN, now, uid, pid, tid, newstr,
+            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
 
         if (rc >= 0) {
             notify |= 1 << LOG_ID_MAIN;
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 9b04363..fd1b8b2 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -199,7 +199,7 @@
 }
 
 int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                   pid_t tid, const char* msg, unsigned short len) {
+                   pid_t tid, const char* msg, uint16_t len) {
     if (log_id >= LOG_ID_MAX) {
         return -EINVAL;
     }
@@ -240,7 +240,7 @@
     LogBufferElement* currentLast = lastLoggedElements[log_id];
     if (currentLast) {
         LogBufferElement* dropped = droppedElements[log_id];
-        unsigned short count = dropped ? dropped->getDropped() : 0;
+        uint16_t count = dropped ? dropped->getDropped() : 0;
         //
         // State Init
         //     incoming:
@@ -584,13 +584,13 @@
     LogBufferElementMap map;
 
    public:
-    bool coalesce(LogBufferElement* element, unsigned short dropped) {
+    bool coalesce(LogBufferElement* element, uint16_t dropped) {
         LogBufferElementKey key(element->getUid(), element->getPid(),
                                 element->getTid());
         LogBufferElementMap::iterator it = map.find(key.getKey());
         if (it != map.end()) {
             LogBufferElement* found = it->second;
-            unsigned short moreDropped = found->getDropped();
+            uint16_t moreDropped = found->getDropped();
             if ((dropped + moreDropped) > USHRT_MAX) {
                 map.erase(it);
             } else {
@@ -847,7 +847,7 @@
                 mLastSet[id] = true;
             }
 
-            unsigned short dropped = element->getDropped();
+            uint16_t dropped = element->getDropped();
 
             // remove any leading drops
             if (leading && dropped) {
@@ -927,7 +927,7 @@
 
             kick = true;
 
-            unsigned short len = element->getMsgLen();
+            uint16_t len = element->getMsgLen();
 
             // do not create any leading drops
             if (leading) {
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 0942987..774d4ab 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -115,7 +115,7 @@
     }
 
     int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
-            const char* msg, unsigned short len) override;
+            const char* msg, uint16_t len) override;
     // lastTid is an optional context to help detect if the last previous
     // valid message was from the same source so we can differentiate chatty
     // filter types (identical or expired)
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 2d627b9..19e6d68 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -35,7 +35,7 @@
 
 LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
                                    uid_t uid, pid_t pid, pid_t tid,
-                                   const char* msg, unsigned short len)
+                                   const char* msg, uint16_t len)
     : mUid(uid),
       mPid(pid),
       mTid(tid),
@@ -71,7 +71,7 @@
                : 0;
 }
 
-unsigned short LogBufferElement::setDropped(unsigned short value) {
+uint16_t LogBufferElement::setDropped(uint16_t value) {
     // The tag information is saved in mMsg data, if the tag is non-zero
     // save only the information needed to get the tag.
     if (getTag() != 0) {
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index b168645..57b0a95 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -18,6 +18,7 @@
 #define _LOGD_LOG_BUFFER_ELEMENT_H__
 
 #include <stdatomic.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <sys/types.h>
 
@@ -56,7 +57,7 @@
 
    public:
     LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                     pid_t tid, const char* msg, unsigned short len);
+                     pid_t tid, const char* msg, uint16_t len);
     LogBufferElement(const LogBufferElement& elem);
     ~LogBufferElement();
 
@@ -77,11 +78,11 @@
         return mTid;
     }
     uint32_t getTag() const;
-    unsigned short getDropped(void) const {
+    uint16_t getDropped(void) const {
         return mDropped ? mDroppedCount : 0;
     }
-    unsigned short setDropped(unsigned short value);
-    unsigned short getMsgLen() const {
+    uint16_t setDropped(uint16_t value);
+    uint16_t getMsgLen() const {
         return mDropped ? 0 : mMsgLen;
     }
     const char* getMsg() const {
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
index ff73a22..f31e244 100644
--- a/logd/LogBufferInterface.h
+++ b/logd/LogBufferInterface.h
@@ -31,7 +31,7 @@
     // Handles a log entry when available in LogListener.
     // Returns the size of the handled log message.
     virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                    pid_t tid, const char* msg, unsigned short len) = 0;
+                    pid_t tid, const char* msg, uint16_t len) = 0;
 
     virtual uid_t pidToUid(pid_t pid);
     virtual pid_t tidToPid(pid_t tid);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index ab980ac..e4393a3 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -821,8 +821,7 @@
     }
 
     // Log message
-    int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
-                         (unsigned short)n);
+    int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
 
     // notify readers
     if (rc > 0) {
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index e568ddc..2f22778 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -136,7 +136,7 @@
     if (logbuf != nullptr) {
         int res = logbuf->log(
             logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
-            ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+            ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
         if (res > 0 && reader != nullptr) {
             reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
         }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index cefacf7..116e08e 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -83,7 +83,7 @@
     if (element->getDropped()) return;
 
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizesTotal[log_id] += size;
     SizesTotal += size;
     ++mElementsTotal[log_id];
@@ -91,7 +91,7 @@
 
 void LogStatistics::add(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizes[log_id] += size;
     ++mElements[log_id];
 
@@ -161,7 +161,7 @@
 
 void LogStatistics::subtract(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
     --mElements[log_id];
     if (element->getDropped()) {
@@ -206,7 +206,7 @@
 // entry->setDropped(1) must follow this call, caller should do this explicitly.
 void LogStatistics::drop(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
-    unsigned short size = element->getMsgLen();
+    uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
     ++mDroppedElements[log_id];
 
@@ -613,13 +613,13 @@
 
 std::string LogStatistics::format(uid_t uid, pid_t pid,
                                   unsigned int logMask) const {
-    static const unsigned short spaces_total = 19;
+    static const uint16_t spaces_total = 19;
 
     // Report on total logging, current and for all time
 
     std::string output = "size/num";
     size_t oldLength;
-    short spaces = 1;
+    int16_t spaces = 1;
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) continue;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index ac3cf9a..d6b8ab3 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -520,7 +520,7 @@
             return;
         }
         ++msg;
-        unsigned short len = element->getMsgLen();
+        uint16_t len = element->getMsgLen();
         len = (len <= 1) ? 0 : strnlen(msg, len - 1);
         if (!len) {
             name = std::string_view("<NULL>", strlen("<NULL>"));
diff --git a/logd/main.cpp b/logd/main.cpp
index b697d44..8c38d9a 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -104,7 +104,8 @@
         return -1;
     }
 
-    if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+    if (!__android_logger_property_get_bool("ro.debuggable",
+                                            BOOL_DEFAULT_FALSE) &&
         prctl(PR_SET_DUMPABLE, 0) == -1) {
         android::prdebug("failed to clear PR_SET_DUMPABLE");
         return -1;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3a6a5e8..07f0797 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -94,9 +94,9 @@
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
 endif
 ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
-  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product-services
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product_services
 else
-  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product-services $(TARGET_ROOT_OUT)/product-services
+  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product_services $(TARGET_ROOT_OUT)/product_services
 endif
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -162,66 +162,6 @@
 )
 endef
 
-# Update namespace configuration file with library lists and VNDK version
-#
-# $(1): Input source file (ld.config.txt)
-# $(2): Output built module
-# $(3): VNDK version suffix
-# $(4): true if libz must be included in llndk not in vndk-sp
-define update_and_install_ld_config
-# If $(4) is true, move libz to llndk from vndk-sp.
-$(if $(filter true,$(4)),\
-  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES) libz) \
-  $(eval vndksp_libraries_list := $(filter-out libz,$(VNDK_SAMEPROCESS_LIBRARIES))),\
-  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES)) \
-  $(eval vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)))
-
-llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
-private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
-vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(vndksp_libraries_list))))
-vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
-sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-  $(UBSAN_RUNTIME_LIBRARY) \
-  $(TSAN_RUNTIME_LIBRARY) \
-  $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-  $(2ND_UBSAN_RUNTIME_LIBRARY) \
-  $(2ND_TSAN_RUNTIME_LIBRARY)))
-# If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.
-vndk_version_suffix := $(if $(strip $(3)),-$(strip $(3)))
-
-$(2): PRIVATE_LLNDK_LIBRARIES := $$(llndk_libraries)
-$(2): PRIVATE_PRIVATE_LLNDK_LIBRARIES := $$(private_llndk_libraries)
-$(2): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $$(vndk_sameprocess_libraries)
-$(2): PRIVATE_VNDK_CORE_LIBRARIES := $$(vndk_core_libraries)
-$(2): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $$(sanitizer_runtime_libraries)
-$(2): PRIVATE_VNDK_VERSION := $$(vndk_version_suffix)
-$(2): $(1)
-	@echo "Generate: $$< -> $$@"
-	@mkdir -p $$(dir $$@)
-	$$(hide) sed -e 's?%LLNDK_LIBRARIES%?$$(PRIVATE_LLNDK_LIBRARIES)?g' $$< >$$@
-	$$(hide) sed -i -e 's?%PRIVATE_LLNDK_LIBRARIES%?$$(PRIVATE_PRIVATE_LLNDK_LIBRARIES)?g' $$@
-	$$(hide) sed -i -e 's?%VNDK_SAMEPROCESS_LIBRARIES%?$$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES)?g' $$@
-	$$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
-	$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
-	$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
-	$$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
-	$$(hide) sed -i -e 's?%PRODUCTSERVICES%?$$(TARGET_COPY_OUT_PRODUCTSERVICES)?g' $$@
-
-llndk_libraries_list :=
-vndksp_libraries_list :=
-llndk_libraries :=
-private_llndk_libraries :=
-vndk_sameprocess_libraries :=
-vndk_core_libraries :=
-sanitizer_runtime_libraries :=
-vndk_version_suffix :=
-endef # update_and_install_ld_config
-
 
 #######################################
 # ld.config.txt selection variables
@@ -265,21 +205,19 @@
 # for VNDK enforced devices
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(eval $(call update_and_install_ld_config,\
-  $(LOCAL_PATH)/etc/ld.config.txt,\
-  $(LOCAL_BUILT_MODULE),\
-  $(PLATFORM_VNDK_VERSION)))
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+vndk_version := $(PLATFORM_VNDK_VERSION)
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
 
 else ifeq ($(_enforce_vndk_lite_at_runtime),true)
 
 # for treblized but VNDK lightly enforced devices
 LOCAL_MODULE_STEM := ld.config.vndk_lite.txt
 include $(BUILD_SYSTEM)/base_rules.mk
-$(eval $(call update_and_install_ld_config,\
-  $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
-  $(LOCAL_BUILT_MODULE),\
-  $(PLATFORM_VNDK_VERSION),\
-  true))
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt
+vndk_version := $(PLATFORM_VNDK_VERSION)
+libz_is_llndk := true
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
 
 else
 
@@ -290,6 +228,37 @@
 
 endif  # ifeq ($(_enforce_vndk_at_runtime),true)
 
+# ld.config.txt for VNDK versions older than PLATFORM_VNDK_VERSION
+# are built with the VNDK libraries lists under /prebuilts/vndk.
+#
+# ld.config.$(VER).txt is built and installed for all VNDK versions
+# listed in PRODUCT_EXTRA_VNDK_VERSIONS.
+#
+# $(1): VNDK version
+define build_versioned_ld_config
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.$(1).txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $$(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+vndk_version := $(1)
+lib_list_from_prebuilts := true
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
+endef
+
+# For VNDK snapshot versions prior to 28, ld.config.txt is installed from the
+# prebuilt under /prebuilts/vndk
+vndk_snapshots := $(wildcard prebuilts/vndk/*)
+supported_vndk_snapshot_versions := \
+  $(strip $(foreach ver,$(patsubst prebuilts/vndk/v%,%,$(vndk_snapshots)),\
+    $(if $(call math_gt_or_eq,$(ver),28),$(ver),)))
+$(eval $(foreach ver,$(supported_vndk_snapshot_versions),\
+  $(call build_versioned_ld_config,$(ver))))
+
+vndk_snapshots :=
+supported_vndk_snapshot_versions :=
 
 #######################################
 # ld.config.vndk_lite.txt
@@ -304,11 +273,10 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 include $(BUILD_SYSTEM)/base_rules.mk
-$(eval $(call update_and_install_ld_config,\
-  $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
-  $(LOCAL_BUILT_MODULE),\
-  $(PLATFORM_VNDK_VERSION),\
-  true))
+ld_config_template := $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt
+vndk_version := $(PLATFORM_VNDK_VERSION)
+libz_is_llndk := true
+include $(LOCAL_PATH)/update_and_install_ld_config.mk
 
 endif  # ifeq ($(_enforce_vndk_lite_at_runtime),false)
 
@@ -321,7 +289,7 @@
 LOCAL_MODULE := ld.config.recovery.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := etc/ld.config.recovery.txt
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc
 LOCAL_MODULE_STEM := ld.config.txt
 include $(BUILD_PREBUILT)
 
diff --git a/rootdir/etc/OWNERS b/rootdir/etc/OWNERS
new file mode 100644
index 0000000..8a65b23
--- /dev/null
+++ b/rootdir/etc/OWNERS
@@ -0,0 +1,4 @@
+danalbert@google.com
+enh@google.com
+jiyong@google.com
+rprichard@google.com
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 7d22a3a..f2892f8 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -8,7 +8,7 @@
 dir.system = /system/bin/
 dir.system = /system/xbin/
 dir.system = /%PRODUCT%/bin/
-dir.system = /%PRODUCTSERVICES%/bin/
+dir.system = /%PRODUCT_SERVICES%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -41,7 +41,7 @@
 
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 # We can't have entire /system/${LIB} as permitted paths because doing so
 # makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -54,7 +54,7 @@
 namespace.default.permitted.paths += /system/${LIB}/extractors
 namespace.default.permitted.paths += /system/${LIB}/hw
 namespace.default.permitted.paths += /%PRODUCT%/${LIB}
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
 # These are where odex files are located. libart has to be able to dlopen the files
 namespace.default.permitted.paths += /system/framework
 namespace.default.permitted.paths += /system/app
@@ -69,9 +69,9 @@
 namespace.default.permitted.paths += /%PRODUCT%/framework
 namespace.default.permitted.paths += /%PRODUCT%/app
 namespace.default.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/framework
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/app
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/priv-app
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/framework
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/app
+namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.permitted.paths += /data
 namespace.default.permitted.paths += /mnt/expand
 
@@ -79,8 +79,8 @@
 namespace.default.asan.search.paths +=           /system/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
 namespace.default.asan.search.paths +=           /product/${LIB}
-namespace.default.asan.search.paths += /data/asan/product-services/${LIB}
-namespace.default.asan.search.paths +=           /product-services/${LIB}
+namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.default.asan.search.paths +=           /product_services/${LIB}
 
 namespace.default.asan.permitted.paths  = /data
 namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -100,10 +100,10 @@
 namespace.default.asan.permitted.paths += /%PRODUCT%/framework
 namespace.default.asan.permitted.paths += /%PRODUCT%/app
 namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/${LIB}
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/framework
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/app
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
+namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
 
 ###############################################################################
@@ -340,7 +340,7 @@
 
 namespace.system.search.paths  = /system/${LIB}
 namespace.system.search.paths += /%PRODUCT%/${LIB}
-namespace.system.search.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.system.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
@@ -359,4 +359,4 @@
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index bf22951..db89ea8 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -49,6 +49,10 @@
     copy /proc/cmdline /dev/urandom
     copy /default.prop /dev/urandom
 
+    symlink /proc/self/fd/0 /dev/stdin
+    symlink /proc/self/fd/1 /dev/stdout
+    symlink /proc/self/fd/2 /dev/stderr
+
     symlink /system/bin /bin
     symlink /system/etc /etc
 
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
new file mode 100644
index 0000000..4d6df77
--- /dev/null
+++ b/rootdir/update_and_install_ld_config.mk
@@ -0,0 +1,125 @@
+#####################################################################
+# Builds linker config file, ld.config.txt, from the specified template
+# under $(LOCAL_PATH)/etc/*.
+#
+# Inputs:
+#   (expected to follow an include of $(BUILD_SYSTEM)/base_rules.mk)
+#   ld_config_template: template linker config file to use,
+#                       e.g. $(LOCAL_PATH)/etc/ld.config.txt
+#   vndk_version: version of the VNDK library lists used to update the
+#                 template linker config file, e.g. 28
+#   lib_list_from_prebuilts: should be set to 'true' if the VNDK library
+#                            lists should be read from /prebuilts/vndk/*
+#   libz_is_llndk: should be set to 'true' if libz must be included in
+#                  llndk and not in vndk-sp
+# Outputs:
+#   Builds and installs ld.config.$VER.txt or ld.config.vndk_lite.txt
+#####################################################################
+
+# Read inputs
+ld_config_template := $(strip $(ld_config_template))
+vndk_version := $(strip $(vndk_version))
+lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
+libz_is_llndk := $(strip $(libz_is_llndk))
+
+intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
+library_lists_dir := $(intermediates_dir)
+ifeq ($(lib_list_from_prebuilts),true)
+  library_lists_dir := prebuilts/vndk/v$(vndk_version)/$(TARGET_ARCH)/configs
+endif
+
+llndk_libraries_file := $(library_lists_dir)/llndk.libraries.$(vndk_version).txt
+vndksp_libraries_file := $(library_lists_dir)/vndksp.libraries.$(vndk_version).txt
+vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.txt
+vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.txt
+
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(UBSAN_RUNTIME_LIBRARY) \
+  $(TSAN_RUNTIME_LIBRARY) \
+  $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_UBSAN_RUNTIME_LIBRARY) \
+  $(2ND_TSAN_RUNTIME_LIBRARY)))
+# If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.
+vndk_version_suffix := $(if $(vndk_version),-$(vndk_version))
+
+ifneq ($(lib_list_from_prebuilts),true)
+ifeq ($(libz_is_llndk),true)
+  llndk_libraries_list := $(LLNDK_LIBRARIES) libz
+  vndksp_libraries_list := $(filter-out libz,$(VNDK_SAMEPROCESS_LIBRARIES))
+else
+  llndk_libraries_list := $(LLNDK_LIBRARIES)
+  vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)
+endif
+
+# $(1): list of libraries
+# $(2): output file to write the list of libraries to
+define write-libs-to-file
+$(2): PRIVATE_LIBRARIES := $(1)
+$(2):
+	echo -n > $$@ && $$(foreach lib,$$(PRIVATE_LIBRARIES),echo $$(lib).so >> $$@;)
+endef
+$(eval $(call write-libs-to-file,$(llndk_libraries_list),$(llndk_libraries_file)))
+$(eval $(call write-libs-to-file,$(vndksp_libraries_list),$(vndksp_libraries_file)))
+$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),$(vndkcore_libraries_file)))
+$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),$(vndkprivate_libraries_file)))
+endif # ifneq ($(lib_list_from_prebuilts),true)
+
+# Given a file with a list of libs, filter-out the VNDK private libraries
+# and write resulting list to a new file in "a:b:c" format
+#
+# $(1): libs file from which to filter-out VNDK private libraries
+# $(2): output file with the filtered list of lib names
+$(LOCAL_BUILT_MODULE): private-filter-out-private-libs = \
+  paste -sd ":" $(1) > $(2) && \
+  cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | xargs -n 1 -I privatelib bash -c "sed -i.bak 's/privatelib//' $(2)" && \
+  sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(2) && \
+  rm -f $(2).bak
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_FILE := $(llndk_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SP_LIBRARIES_FILE := $(vndksp_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES_FILE := $(vndkcore_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE := $(vndkprivate_libraries_file)
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_SUFFIX := $(vndk_version_suffix)
+$(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
+deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
+  $(vndkprivate_libraries_file)
+
+$(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
+	@echo "Generate: $< -> $@"
+	@mkdir -p $(dir $@)
+	$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
+	$(hide) sed -e "s?%LLNDK_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)?g" $< >$@
+	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_SP_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)
+	$(hide) sed -i.bak -e "s?%VNDK_SAMEPROCESS_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)?g" $@
+	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_CORE_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)
+	$(hide) sed -i.bak -e "s?%VNDK_CORE_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)?g" $@
+
+	$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
+	cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | \
+	xargs -n 1 -I privatelib bash -c "(grep privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk" && \
+	paste -sd ":" $(PRIVATE_INTERMEDIATES_DIR)/private_llndk | \
+	sed -i.bak -e "s?%PRIVATE_LLNDK_LIBRARIES%?$$(cat -)?g" $@
+
+	$(hide) sed -i.bak -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@
+	$(hide) sed -i.bak -e 's?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g' $@
+	$(hide) sed -i.bak -e 's?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g' $@
+	$(hide) sed -i.bak -e 's?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g' $@
+	$(hide) rm -f $@.bak
+
+ld_config_template :=
+vndk_version :=
+lib_list_from_prebuilts :=
+libz_is_llndk :=
+intermediates_dir :=
+library_lists_dir :=
+llndk_libraries_file :=
+vndksp_libraries_file :=
+vndkcore_libraries_file :=
+vndkprivate_libraries_file :=
+deps :=
+sanitizer_runtime_libraries :=
+vndk_version_suffix :=
+llndk_libraries_list :=
+vndksp_libraries_list :=
+write-libs-to-file :=
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 3a718fa..fffb3d2 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -68,31 +68,33 @@
 
 struct uid_record {
     string name;
-    struct uid_io_usage ios;
+    uid_io_usage ios;
 };
 
 struct uid_records {
     uint64_t start_ts;
-    vector<struct uid_record> entries;
+    vector<uid_record> entries;
 };
 
 class uid_monitor {
 private:
     FRIEND_TEST(storaged_test, uid_monitor);
+    FRIEND_TEST(storaged_test, load_uid_io_proto);
+
     // last dump from /proc/uid_io/stats, uid -> uid_info
-    unordered_map<uint32_t, uid_info> last_uid_io_stats;
+    unordered_map<uint32_t, uid_info> last_uid_io_stats_;
     // current io usage for next report, app name -> uid_io_usage
-    unordered_map<string, struct uid_io_usage> curr_io_stats;
+    unordered_map<string, uid_io_usage> curr_io_stats_;
     // io usage records, end timestamp -> {start timestamp, vector of records}
-    map<uint64_t, struct uid_records> io_history;
+    map<uint64_t, uid_records> io_history_;
     // charger ON/OFF
-    charger_stat_t charger_stat;
+    charger_stat_t charger_stat_;
     // protects curr_io_stats, last_uid_io_stats, records and charger_stat
-    Mutex uidm_mutex;
+    Mutex uidm_mutex_;
     // start time for IO records
-    uint64_t start_ts;
+    uint64_t start_ts_;
     // true if UID_IO_STATS_PATH is accessible
-    const bool enable;
+    const bool enabled_;
 
     // reads from /proc/uid_io/stats
     unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();
@@ -103,6 +105,10 @@
     // writes io_history to protobuf
     void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
 
+    // Ensure that io_history_ can append |n| items without exceeding
+    // MAX_UID_RECORDS_SIZE in size.
+    void maybe_shrink_history_for_items(size_t nitems);
+
 public:
     uid_monitor();
     // called by storaged main thread
@@ -110,16 +116,20 @@
     // called by storaged -u
     unordered_map<uint32_t, uid_info> get_uid_io_stats();
     // called by dumpsys
-    map<uint64_t, struct uid_records> dump(
+    map<uint64_t, uid_records> dump(
         double hours, uint64_t threshold, bool force_report);
     // called by battery properties listener
     void set_charger_state(charger_stat_t stat);
     // called by storaged periodic_chore or dump with force_report
-    bool enabled() { return enable; };
+    bool enabled() { return enabled_; };
     void report(unordered_map<int, StoragedProto>* protos);
     // restores io_history from protobuf
-    void load_uid_io_proto(const UidIOUsage& proto);
+    void load_uid_io_proto(userid_t user_id, const UidIOUsage& proto);
     void clear_user_history(userid_t user_id);
+
+    map<uint64_t, uid_records>& io_history() { return io_history_; }
+
+    static constexpr int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
 };
 
 #endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index f346c38..77c6167 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -194,7 +194,7 @@
         return;
     }
 
-    mUidm.load_uid_io_proto(proto.uid_io_usage());
+    mUidm.load_uid_io_proto(user_id, proto.uid_io_usage());
 
     if (user_id == USER_SYSTEM) {
         storage_info->load_perf_history_proto(proto.perf_history());
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 5745782..55380ba 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -21,6 +21,7 @@
 
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android-base/file.h>
@@ -50,7 +51,7 @@
 
 std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
     return get_uid_io_stats_locked();
 };
 
@@ -178,10 +179,10 @@
             uid_io_stats[u.uid].name = std::to_string(u.uid);
             uids.push_back(u.uid);
             uid_names.push_back(&uid_io_stats[u.uid].name);
-            if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+            if (last_uid_io_stats_.find(u.uid) == last_uid_io_stats_.end()) {
                 refresh_uid_names = true;
             } else {
-                uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+                uid_io_stats[u.uid].name = last_uid_io_stats_[u.uid].name;
             }
         } else {
             task_info t;
@@ -200,8 +201,6 @@
 
 namespace {
 
-const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
-
 inline size_t history_size(
     const std::map<uint64_t, struct uid_records>& history)
 {
@@ -218,12 +217,12 @@
 {
     // remove records more than 5 days old
     if (curr_ts > 5 * DAY_TO_SEC) {
-        auto it = io_history.lower_bound(curr_ts - 5 * DAY_TO_SEC);
-        io_history.erase(io_history.begin(), it);
+        auto it = io_history_.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+        io_history_.erase(io_history_.begin(), it);
     }
 
     struct uid_records new_records;
-    for (const auto& p : curr_io_stats) {
+    for (const auto& p : curr_io_stats_) {
         struct uid_record record = {};
         record.name = p.first;
         if (!p.second.uid_ios.is_zero()) {
@@ -237,23 +236,26 @@
         }
     }
 
-    curr_io_stats.clear();
-    new_records.start_ts = start_ts;
-    start_ts = curr_ts;
+    curr_io_stats_.clear();
+    new_records.start_ts = start_ts_;
+    start_ts_ = curr_ts;
 
     if (new_records.entries.empty())
       return;
 
     // make some room for new records
-    ssize_t overflow = history_size(io_history) +
-        new_records.entries.size() - MAX_UID_RECORDS_SIZE;
-    while (overflow > 0 && io_history.size() > 0) {
-        auto del_it = io_history.begin();
-        overflow -= del_it->second.entries.size();
-        io_history.erase(io_history.begin());
-    }
+    maybe_shrink_history_for_items(new_records.entries.size());
 
-    io_history[curr_ts] = new_records;
+    io_history_[curr_ts] = new_records;
+}
+
+void uid_monitor::maybe_shrink_history_for_items(size_t nitems) {
+    ssize_t overflow = history_size(io_history_) + nitems - MAX_UID_RECORDS_SIZE;
+    while (overflow > 0 && io_history_.size() > 0) {
+        auto del_it = io_history_.begin();
+        overflow -= del_it->second.entries.size();
+        io_history_.erase(io_history_.begin());
+    }
 }
 
 std::map<uint64_t, struct uid_records> uid_monitor::dump(
@@ -263,7 +265,7 @@
         report(nullptr);
     }
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     std::map<uint64_t, struct uid_records> dump_records;
     uint64_t first_ts = 0;
@@ -272,7 +274,7 @@
         first_ts = time(NULL) - hours * HOUR_TO_SEC;
     }
 
-    for (auto it = io_history.lower_bound(first_ts); it != io_history.end(); ++it) {
+    for (auto it = io_history_.lower_bound(first_ts); it != io_history_.end(); ++it) {
         const std::vector<struct uid_record>& recs = it->second.entries;
         struct uid_records filtered;
 
@@ -311,29 +313,29 @@
 
     for (const auto& it : uid_io_stats) {
         const uid_info& uid = it.second;
-        if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
-            curr_io_stats[uid.name] = {};
+        if (curr_io_stats_.find(uid.name) == curr_io_stats_.end()) {
+            curr_io_stats_[uid.name] = {};
         }
 
-        struct uid_io_usage& usage = curr_io_stats[uid.name];
+        struct uid_io_usage& usage = curr_io_stats_[uid.name];
         usage.user_id = multiuser_get_user_id(uid.uid);
 
         int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
-            last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
+            last_uid_io_stats_[uid.uid].io[FOREGROUND].read_bytes;
         int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
-            last_uid_io_stats[uid.uid].io[BACKGROUND].read_bytes;
+            last_uid_io_stats_[uid.uid].io[BACKGROUND].read_bytes;
         int64_t fg_wr_delta = uid.io[FOREGROUND].write_bytes -
-            last_uid_io_stats[uid.uid].io[FOREGROUND].write_bytes;
+            last_uid_io_stats_[uid.uid].io[FOREGROUND].write_bytes;
         int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
-            last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
+            last_uid_io_stats_[uid.uid].io[BACKGROUND].write_bytes;
 
-        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat_] +=
             (fg_rd_delta < 0) ? 0 : fg_rd_delta;
-        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat_] +=
             (bg_rd_delta < 0) ? 0 : bg_rd_delta;
-        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat_] +=
             (fg_wr_delta < 0) ? 0 : fg_wr_delta;
-        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat_] +=
             (bg_wr_delta < 0) ? 0 : bg_wr_delta;
 
         for (const auto& task_it : uid.tasks) {
@@ -341,34 +343,34 @@
             const pid_t pid = task_it.first;
             const std::string& comm = task_it.second.comm;
             int64_t task_fg_rd_delta = task.io[FOREGROUND].read_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
             int64_t task_bg_rd_delta = task.io[BACKGROUND].read_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
             int64_t task_fg_wr_delta = task.io[FOREGROUND].write_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
             int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
 
             io_usage& task_usage = usage.task_ios[comm];
-            task_usage.bytes[READ][FOREGROUND][charger_stat] +=
+            task_usage.bytes[READ][FOREGROUND][charger_stat_] +=
                 (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
-            task_usage.bytes[READ][BACKGROUND][charger_stat] +=
+            task_usage.bytes[READ][BACKGROUND][charger_stat_] +=
                 (task_bg_rd_delta < 0) ? 0 : task_bg_rd_delta;
-            task_usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+            task_usage.bytes[WRITE][FOREGROUND][charger_stat_] +=
                 (task_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;
-            task_usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+            task_usage.bytes[WRITE][BACKGROUND][charger_stat_] +=
                 (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;
         }
     }
 
-    last_uid_io_stats = uid_io_stats;
+    last_uid_io_stats_ = uid_io_stats;
 }
 
 void uid_monitor::report(unordered_map<int, StoragedProto>* protos)
 {
     if (!enabled()) return;
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
@@ -408,7 +410,7 @@
 
 void uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)
 {
-    for (const auto& item : io_history) {
+    for (const auto& item : io_history_) {
         const uint64_t& end_ts = item.first;
         const struct uid_records& recs = item.second;
         unordered_map<userid_t, UidIOItem*> user_items;
@@ -448,9 +450,9 @@
 
 void uid_monitor::clear_user_history(userid_t user_id)
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
-    for (auto& item : io_history) {
+    for (auto& item : io_history_) {
         vector<uid_record>* entries = &item.second.entries;
         entries->erase(
             remove_if(entries->begin(), entries->end(),
@@ -459,27 +461,42 @@
             entries->end());
     }
 
-    for (auto it = io_history.begin(); it != io_history.end(); ) {
+    for (auto it = io_history_.begin(); it != io_history_.end(); ) {
         if (it->second.entries.empty()) {
-            it = io_history.erase(it);
+            it = io_history_.erase(it);
         } else {
             it++;
         }
     }
 }
 
-void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto)
+void uid_monitor::load_uid_io_proto(userid_t user_id, const UidIOUsage& uid_io_proto)
 {
     if (!enabled()) return;
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     for (const auto& item_proto : uid_io_proto.uid_io_items()) {
         const UidIORecords& records_proto = item_proto.records();
-        struct uid_records* recs = &io_history[item_proto.end_ts()];
+        struct uid_records* recs = &io_history_[item_proto.end_ts()];
+
+        // It's possible that the same uid_io_proto file gets loaded more than
+        // once, for example, if system_server crashes. In this case we avoid
+        // adding duplicate entries, so we build a quick way to check for
+        // duplicates.
+        std::unordered_set<std::string> existing_uids;
+        for (const auto& rec : recs->entries) {
+            if (rec.ios.user_id == user_id) {
+                existing_uids.emplace(rec.name);
+            }
+        }
 
         recs->start_ts = records_proto.start_ts();
         for (const auto& rec_proto : records_proto.entries()) {
+            if (existing_uids.find(rec_proto.uid_name()) != existing_uids.end()) {
+                continue;
+            }
+
             struct uid_record record;
             record.name = rec_proto.uid_name();
             record.ios.user_id = rec_proto.user_id();
@@ -492,29 +509,35 @@
             }
             recs->entries.push_back(record);
         }
+
+        // We already added items, so this will just cull down to the maximum
+        // length. We do not remove anything if there is only one entry.
+        if (io_history_.size() > 1) {
+            maybe_shrink_history_for_items(0);
+        }
     }
 }
 
 void uid_monitor::set_charger_state(charger_stat_t stat)
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
-    if (charger_stat == stat) {
+    if (charger_stat_ == stat) {
         return;
     }
 
     update_curr_io_stats_locked();
-    charger_stat = stat;
+    charger_stat_ = stat;
 }
 
 void uid_monitor::init(charger_stat_t stat)
 {
-    charger_stat = stat;
+    charger_stat_ = stat;
 
-    start_ts = time(NULL);
-    last_uid_io_stats = get_uid_io_stats();
+    start_ts_ = time(NULL);
+    last_uid_io_stats_ = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
-    : enable(!access(UID_IO_STATS_PATH, R_OK)) {
+    : enabled_(!access(UID_IO_STATS_PATH, R_OK)) {
 }
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index ec47b65..64009c2 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -443,8 +443,9 @@
 
 TEST(storaged_test, uid_monitor) {
     uid_monitor uidm;
+    auto& io_history = uidm.io_history();
 
-    uidm.io_history[200] = {
+    io_history[200] = {
         .start_ts = 100,
         .entries = {
             { "app1", {
@@ -466,7 +467,7 @@
         },
     };
 
-    uidm.io_history[300] = {
+    io_history[300] = {
         .start_ts = 200,
         .entries = {
             { "app1", {
@@ -526,9 +527,9 @@
     EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL);
     EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL);
 
-    uidm.io_history.clear();
+    io_history.clear();
 
-    uidm.io_history[300] = {
+    io_history[300] = {
         .start_ts = 200,
         .entries = {
             { "app1", {
@@ -539,7 +540,7 @@
         },
     };
 
-    uidm.io_history[400] = {
+    io_history[400] = {
         .start_ts = 300,
         .entries = {
             { "app1", {
@@ -550,16 +551,16 @@
         },
     };
 
-    uidm.load_uid_io_proto(protos[0].uid_io_usage());
-    uidm.load_uid_io_proto(protos[1].uid_io_usage());
+    uidm.load_uid_io_proto(0, protos[0].uid_io_usage());
+    uidm.load_uid_io_proto(1, protos[1].uid_io_usage());
 
-    EXPECT_EQ(uidm.io_history.size(), 3UL);
-    EXPECT_EQ(uidm.io_history.count(200), 1UL);
-    EXPECT_EQ(uidm.io_history.count(300), 1UL);
-    EXPECT_EQ(uidm.io_history.count(400), 1UL);
+    EXPECT_EQ(io_history.size(), 3UL);
+    EXPECT_EQ(io_history.count(200), 1UL);
+    EXPECT_EQ(io_history.count(300), 1UL);
+    EXPECT_EQ(io_history.count(400), 1UL);
 
-    EXPECT_EQ(uidm.io_history[200].start_ts, 100UL);
-    const vector<struct uid_record>& entries_0 = uidm.io_history[200].entries;
+    EXPECT_EQ(io_history[200].start_ts, 100UL);
+    const vector<struct uid_record>& entries_0 = io_history[200].entries;
     EXPECT_EQ(entries_0.size(), 3UL);
     EXPECT_EQ(entries_0[0].name, "app1");
     EXPECT_EQ(entries_0[0].ios.user_id, 0UL);
@@ -572,8 +573,8 @@
     EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
     EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
 
-    EXPECT_EQ(uidm.io_history[300].start_ts, 200UL);
-    const vector<struct uid_record>& entries_1 = uidm.io_history[300].entries;
+    EXPECT_EQ(io_history[300].start_ts, 200UL);
+    const vector<struct uid_record>& entries_1 = io_history[300].entries;
     EXPECT_EQ(entries_1.size(), 3UL);
     EXPECT_EQ(entries_1[0].name, "app1");
     EXPECT_EQ(entries_1[0].ios.user_id, 0UL);
@@ -585,8 +586,8 @@
     EXPECT_EQ(entries_1[2].ios.user_id, 1UL);
     EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
 
-    EXPECT_EQ(uidm.io_history[400].start_ts, 300UL);
-    const vector<struct uid_record>& entries_2 = uidm.io_history[400].entries;
+    EXPECT_EQ(io_history[400].start_ts, 300UL);
+    const vector<struct uid_record>& entries_2 = io_history[400].entries;
     EXPECT_EQ(entries_2.size(), 1UL);
     EXPECT_EQ(entries_2[0].name, "app1");
     EXPECT_EQ(entries_2[0].ios.user_id, 0UL);
@@ -615,14 +616,71 @@
 
     uidm.clear_user_history(0);
 
-    EXPECT_EQ(uidm.io_history.size(), 2UL);
-    EXPECT_EQ(uidm.io_history.count(200), 1UL);
-    EXPECT_EQ(uidm.io_history.count(300), 1UL);
+    EXPECT_EQ(io_history.size(), 2UL);
+    EXPECT_EQ(io_history.count(200), 1UL);
+    EXPECT_EQ(io_history.count(300), 1UL);
 
-    EXPECT_EQ(uidm.io_history[200].entries.size(), 1UL);
-    EXPECT_EQ(uidm.io_history[300].entries.size(), 1UL);
+    EXPECT_EQ(io_history[200].entries.size(), 1UL);
+    EXPECT_EQ(io_history[300].entries.size(), 1UL);
 
     uidm.clear_user_history(1);
 
-    EXPECT_EQ(uidm.io_history.size(), 0UL);
+    EXPECT_EQ(io_history.size(), 0UL);
+}
+
+TEST(storaged_test, load_uid_io_proto) {
+    uid_monitor uidm;
+    auto& io_history = uidm.io_history();
+
+    static const uint64_t kProtoTime = 200;
+    io_history[kProtoTime] = {
+        .start_ts = 100,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+            { "app2", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 2000,
+              }
+            },
+            { "app3", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 3000,
+              }
+            },
+        },
+    };
+
+    unordered_map<int, StoragedProto> protos;
+    uidm.update_uid_io_proto(&protos);
+    ASSERT_EQ(protos.size(), size_t(1));
+
+    // Loading the same proto many times should not add duplicate entries.
+    UidIOUsage user_0 = protos[0].uid_io_usage();
+    for (size_t i = 0; i < 10000; i++) {
+        uidm.load_uid_io_proto(0, user_0);
+    }
+    ASSERT_EQ(io_history.size(), size_t(1));
+    ASSERT_EQ(io_history[kProtoTime].entries.size(), size_t(3));
+
+    // Create duplicate entries until we go over the limit.
+    auto record = io_history[kProtoTime];
+    io_history.clear();
+    for (size_t i = 0; i < uid_monitor::MAX_UID_RECORDS_SIZE * 2; i++) {
+        if (i == kProtoTime) {
+            continue;
+        }
+        io_history[i] = record;
+    }
+    ASSERT_GT(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));
+
+    // After loading, the history should be truncated.
+    for (auto& item : *user_0.mutable_uid_io_items()) {
+        item.set_end_ts(io_history.size());
+    }
+    uidm.load_uid_io_proto(0, user_0);
+    ASSERT_LE(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));
 }
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
new file mode 100644
index 0000000..8e3b3b1
--- /dev/null
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -0,0 +1,448 @@
+/*
+ **
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.keymaster@3.0-impl.trusty"
+
+#include <authorization_set.h>
+#include <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <trusty_keymaster/TrustyKeymaster3Device.h>
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::AddEntropyRequest;
+using ::keymaster::AddEntropyResponse;
+using ::keymaster::AttestKeyRequest;
+using ::keymaster::AttestKeyResponse;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::BeginOperationRequest;
+using ::keymaster::BeginOperationResponse;
+using ::keymaster::ExportKeyRequest;
+using ::keymaster::ExportKeyResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::GenerateKeyRequest;
+using ::keymaster::GenerateKeyResponse;
+using ::keymaster::GetKeyCharacteristicsRequest;
+using ::keymaster::GetKeyCharacteristicsResponse;
+using ::keymaster::ImportKeyRequest;
+using ::keymaster::ImportKeyResponse;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using ::keymaster::ng::Tag;
+
+namespace keymaster {
+
+namespace {
+
+inline keymaster_tag_t legacy_enum_conversion(const Tag value) {
+    return keymaster_tag_t(value);
+}
+inline Tag legacy_enum_conversion(const keymaster_tag_t value) {
+    return Tag(value);
+}
+inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
+    return keymaster_purpose_t(value);
+}
+inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
+    return keymaster_key_format_t(value);
+}
+inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
+    return ErrorCode(value);
+}
+
+inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
+    return keymaster_tag_get_type(tag);
+}
+
+class KmParamSet : public keymaster_key_param_set_t {
+  public:
+    KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
+        params = new keymaster_key_param_t[keyParams.size()];
+        length = keyParams.size();
+        for (size_t i = 0; i < keyParams.size(); ++i) {
+            auto tag = legacy_enum_conversion(keyParams[i].tag);
+            switch (typeFromTag(tag)) {
+                case KM_ENUM:
+                case KM_ENUM_REP:
+                    params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
+                    break;
+                case KM_UINT:
+                case KM_UINT_REP:
+                    params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
+                    break;
+                case KM_ULONG:
+                case KM_ULONG_REP:
+                    params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
+                    break;
+                case KM_DATE:
+                    params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
+                    break;
+                case KM_BOOL:
+                    if (keyParams[i].f.boolValue)
+                        params[i] = keymaster_param_bool(tag);
+                    else
+                        params[i].tag = KM_TAG_INVALID;
+                    break;
+                case KM_BIGNUM:
+                case KM_BYTES:
+                    params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],
+                                                     keyParams[i].blob.size());
+                    break;
+                case KM_INVALID:
+                default:
+                    params[i].tag = KM_TAG_INVALID;
+                    /* just skip */
+                    break;
+            }
+        }
+    }
+    KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} {
+        other.length = 0;
+        other.params = nullptr;
+    }
+    KmParamSet(const KmParamSet&) = delete;
+    ~KmParamSet() { delete[] params; }
+};
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);
+    return result;
+}
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);
+    return result;
+}
+
+inline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {
+    hidl_vec<uint8_t> result;
+    result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());
+    return result;
+}
+
+inline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(
+        const keymaster_cert_chain_t& cert_chain) {
+    hidl_vec<hidl_vec<uint8_t>> result;
+    if (!cert_chain.entry_count || !cert_chain.entries) return result;
+
+    result.resize(cert_chain.entry_count);
+    for (size_t i = 0; i < cert_chain.entry_count; ++i) {
+        result[i] = kmBlob2hidlVec(cert_chain.entries[i]);
+    }
+
+    return result;
+}
+
+static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
+    hidl_vec<KeyParameter> result;
+    if (set.length == 0 || set.params == nullptr) return result;
+
+    result.resize(set.length);
+    keymaster_key_param_t* params = set.params;
+    for (size_t i = 0; i < set.length; ++i) {
+        auto tag = params[i].tag;
+        result[i].tag = legacy_enum_conversion(tag);
+        switch (typeFromTag(tag)) {
+            case KM_ENUM:
+            case KM_ENUM_REP:
+                result[i].f.integer = params[i].enumerated;
+                break;
+            case KM_UINT:
+            case KM_UINT_REP:
+                result[i].f.integer = params[i].integer;
+                break;
+            case KM_ULONG:
+            case KM_ULONG_REP:
+                result[i].f.longInteger = params[i].long_integer;
+                break;
+            case KM_DATE:
+                result[i].f.dateTime = params[i].date_time;
+                break;
+            case KM_BOOL:
+                result[i].f.boolValue = params[i].boolean;
+                break;
+            case KM_BIGNUM:
+            case KM_BYTES:
+                result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),
+                                             params[i].blob.data_length);
+                break;
+            case KM_INVALID:
+            default:
+                params[i].tag = KM_TAG_INVALID;
+                /* just skip */
+                break;
+        }
+    }
+    return result;
+}
+
+void addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+                         ::keymaster::AuthorizationSet* params) {
+    params->Clear();
+    if (clientId.size()) {
+        params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());
+    }
+    if (appData.size()) {
+        params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
+    }
+}
+
+}  // anonymous namespace
+
+TrustyKeymaster3Device::TrustyKeymaster3Device(TrustyKeymaster* impl) : impl_(impl) {}
+
+TrustyKeymaster3Device::~TrustyKeymaster3Device() {}
+
+Return<void> TrustyKeymaster3Device::getHardwareFeatures(getHardwareFeatures_cb _hidl_cb) {
+    _hidl_cb(true /* is_secure */, true /* supports_ec */,
+             true /* supports_symmetric_cryptography */, true /* supports_attestation */,
+             true /* supportsAllDigests */, "TrustyKeymaster", "Google");
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
+    if (data.size() == 0) return ErrorCode::OK;
+    AddEntropyRequest request;
+    request.random_data.Reinitialize(data.data(), data.size());
+
+    AddEntropyResponse response;
+    impl_->AddRngEntropy(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<void> TrustyKeymaster3Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
+                                                 generateKey_cb _hidl_cb) {
+    GenerateKeyRequest request;
+    request.key_description.Reinitialize(KmParamSet(keyParams));
+
+    GenerateKeyResponse response;
+    impl_->GenerateKey(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+                                                           const hidl_vec<uint8_t>& clientId,
+                                                           const hidl_vec<uint8_t>& appData,
+                                                           getKeyCharacteristics_cb _hidl_cb) {
+    GetKeyCharacteristicsRequest request;
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    addClientAndAppData(clientId, appData, &request.additional_params);
+
+    GetKeyCharacteristicsResponse response;
+    impl_->GetKeyCharacteristics(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    if (response.error == KM_ERROR_OK) {
+        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::importKey(const hidl_vec<KeyParameter>& params,
+                                               KeyFormat keyFormat,
+                                               const hidl_vec<uint8_t>& keyData,
+                                               importKey_cb _hidl_cb) {
+    ImportKeyRequest request;
+    request.key_description.Reinitialize(KmParamSet(params));
+    request.key_format = legacy_enum_conversion(keyFormat);
+    request.SetKeyMaterial(keyData.data(), keyData.size());
+
+    ImportKeyResponse response;
+    impl_->ImportKey(request, &response);
+
+    KeyCharacteristics resultCharacteristics;
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);
+        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::exportKey(KeyFormat exportFormat,
+                                               const hidl_vec<uint8_t>& keyBlob,
+                                               const hidl_vec<uint8_t>& clientId,
+                                               const hidl_vec<uint8_t>& appData,
+                                               exportKey_cb _hidl_cb) {
+    ExportKeyRequest request;
+    request.key_format = legacy_enum_conversion(exportFormat);
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    addClientAndAppData(clientId, appData, &request.additional_params);
+
+    ExportKeyResponse response;
+    impl_->ExportKey(request, &response);
+
+    hidl_vec<uint8_t> resultKeyBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultKeyBlob.setToExternal(response.key_data, response.key_data_length);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
+                                               const hidl_vec<KeyParameter>& attestParams,
+                                               attestKey_cb _hidl_cb) {
+    AttestKeyRequest request;
+    request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
+    request.attest_params.Reinitialize(KmParamSet(attestParams));
+
+    AttestKeyResponse response;
+    impl_->AttestKey(request, &response);
+
+    hidl_vec<hidl_vec<uint8_t>> resultCertChain;
+    if (response.error == KM_ERROR_OK) {
+        resultCertChain = kmCertChain2Hidl(response.certificate_chain);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+                                                const hidl_vec<KeyParameter>& upgradeParams,
+                                                upgradeKey_cb _hidl_cb) {
+    UpgradeKeyRequest request;
+    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+    UpgradeKeyResponse response;
+    impl_->UpgradeKey(request, &response);
+
+    if (response.error == KM_ERROR_OK) {
+        _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));
+    } else {
+        _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());
+    }
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+    DeleteKeyRequest request;
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+    DeleteKeyResponse response;
+    impl_->DeleteKey(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::deleteAllKeys() {
+    DeleteAllKeysRequest request;
+    DeleteAllKeysResponse response;
+    impl_->DeleteAllKeys(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::destroyAttestationIds() {
+    return ErrorCode::UNIMPLEMENTED;
+}
+
+Return<void> TrustyKeymaster3Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+                                           const hidl_vec<KeyParameter>& inParams,
+                                           begin_cb _hidl_cb) {
+    BeginOperationRequest request;
+    request.purpose = legacy_enum_conversion(purpose);
+    request.SetKeyMaterial(key.data(), key.size());
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    BeginOperationResponse response;
+    impl_->BeginOperation(request, &response);
+
+    hidl_vec<KeyParameter> resultParams;
+    if (response.error == KM_ERROR_OK) {
+        resultParams = kmParamSet2Hidl(response.output_params);
+    }
+
+    _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::update(uint64_t operationHandle,
+                                            const hidl_vec<KeyParameter>& inParams,
+                                            const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
+    UpdateOperationRequest request;
+    request.op_handle = operationHandle;
+    request.input.Reinitialize(input.data(), input.size());
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    UpdateOperationResponse response;
+    impl_->UpdateOperation(request, &response);
+
+    uint32_t resultConsumed = 0;
+    hidl_vec<KeyParameter> resultParams;
+    hidl_vec<uint8_t> resultBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultConsumed = response.input_consumed;
+        resultParams = kmParamSet2Hidl(response.output_params);
+        resultBlob = kmBuffer2hidlVec(response.output);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
+    return Void();
+}
+
+Return<void> TrustyKeymaster3Device::finish(uint64_t operationHandle,
+                                            const hidl_vec<KeyParameter>& inParams,
+                                            const hidl_vec<uint8_t>& input,
+                                            const hidl_vec<uint8_t>& signature,
+                                            finish_cb _hidl_cb) {
+    FinishOperationRequest request;
+    request.op_handle = operationHandle;
+    request.input.Reinitialize(input.data(), input.size());
+    request.signature.Reinitialize(signature.data(), signature.size());
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    FinishOperationResponse response;
+    impl_->FinishOperation(request, &response);
+
+    hidl_vec<KeyParameter> resultParams;
+    hidl_vec<uint8_t> resultBlob;
+    if (response.error == KM_ERROR_OK) {
+        resultParams = kmParamSet2Hidl(response.output_params);
+        resultBlob = kmBuffer2hidlVec(response.output);
+    }
+    _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);
+    return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster3Device::abort(uint64_t operationHandle) {
+    AbortOperationRequest request;
+    request.op_handle = operationHandle;
+
+    AbortOperationResponse response;
+    impl_->AbortOperation(request, &response);
+
+    return legacy_enum_conversion(response.error);
+}
+}  // namespace keymaster
diff --git a/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
new file mode 100644
index 0000000..e9d3054
--- /dev/null
+++ b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.keymaster-3-0 /vendor/bin/hw/android.hardware.keymaster@3.0-service.trusty
+    class early_hal
+    user nobody
+    group system drmrpc
diff --git a/trusty/keymaster/3.0/service.cpp b/trusty/keymaster/3.0/service.cpp
new file mode 100644
index 0000000..0d8436e
--- /dev/null
+++ b/trusty/keymaster/3.0/service.cpp
@@ -0,0 +1,43 @@
+/*
+**
+** Copyright 2018, 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 <android-base/logging.h>
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <hidl/HidlTransportSupport.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+#include <trusty_keymaster/TrustyKeymaster3Device.h>
+
+int main() {
+    ::android::hardware::configureRpcThreadpool(1, true);
+    auto trustyKeymaster = new keymaster::TrustyKeymaster();
+    int err = trustyKeymaster->Initialize();
+    if (err != 0) {
+        LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
+        return -1;
+    }
+
+    auto keymaster = new ::keymaster::TrustyKeymaster3Device(trustyKeymaster);
+
+    auto status = keymaster->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for Keymaster 3.0 (" << status << ")";
+        return -1;
+    }
+
+    android::hardware::joinRpcThreadpool();
+    return -1;  // Should never get here.
+}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 52a879e..819851f 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -77,3 +77,34 @@
     ],
     header_libs: ["libhardware_headers"],
 }
+
+cc_binary {
+    name: "android.hardware.keymaster@3.0-service.trusty",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["3.0/android.hardware.keymaster@3.0-service.trusty.rc"],
+    srcs: [
+        "3.0/service.cpp",
+        "3.0/TrustyKeymaster3Device.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "TrustyKeymaster.cpp",
+    ],
+
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libdl",
+        "libbase",
+        "libutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "libtrusty",
+        "libkeymaster_messages",
+        "libkeymaster3device",
+        "android.hardware.keymaster@3.0"
+    ],
+}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
new file mode 100644
index 0000000..7f5e87f
--- /dev/null
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2018 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 <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/keymaster_configuration.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+namespace keymaster {
+
+int TrustyKeymaster::Initialize() {
+    int err;
+
+    err = trusty_keymaster_connect();
+    if (err) {
+        ALOGE("Failed to connect to trusty keymaster %d", err);
+        return err;
+    }
+
+    ConfigureRequest req;
+    req.os_version = GetOsVersion();
+    req.os_patchlevel = GetOsPatchlevel();
+
+    ConfigureResponse rsp;
+    Configure(req, &rsp);
+
+    if (rsp.error != KM_ERROR_OK) {
+        ALOGE("Failed to configure keymaster %d", rsp.error);
+        return -1;
+    }
+
+    return 0;
+}
+
+TrustyKeymaster::TrustyKeymaster() {}
+
+TrustyKeymaster::~TrustyKeymaster() {
+    trusty_keymaster_disconnect();
+}
+
+static void ForwardCommand(enum keymaster_command command, const Serializable& req,
+                           KeymasterResponse* rsp) {
+    keymaster_error_t err;
+    err = trusty_keymaster_send(command, req, rsp);
+    if (err != KM_ERROR_OK) {
+        ALOGE("Failed to send cmd %d err: %d", command, err);
+        rsp->error = err;
+    }
+}
+
+void TrustyKeymaster::GetVersion(const GetVersionRequest& request, GetVersionResponse* response) {
+    ForwardCommand(KM_GET_VERSION, request, response);
+}
+
+void TrustyKeymaster::SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
+                                          SupportedAlgorithmsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_ALGORITHMS, request, response);
+}
+
+void TrustyKeymaster::SupportedBlockModes(const SupportedBlockModesRequest& request,
+                                          SupportedBlockModesResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_BLOCK_MODES, request, response);
+}
+
+void TrustyKeymaster::SupportedPaddingModes(const SupportedPaddingModesRequest& request,
+                                            SupportedPaddingModesResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_PADDING_MODES, request, response);
+}
+
+void TrustyKeymaster::SupportedDigests(const SupportedDigestsRequest& request,
+                                       SupportedDigestsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_DIGESTS, request, response);
+}
+
+void TrustyKeymaster::SupportedImportFormats(const SupportedImportFormatsRequest& request,
+                                             SupportedImportFormatsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_IMPORT_FORMATS, request, response);
+}
+
+void TrustyKeymaster::SupportedExportFormats(const SupportedExportFormatsRequest& request,
+                                             SupportedExportFormatsResponse* response) {
+    ForwardCommand(KM_GET_SUPPORTED_EXPORT_FORMATS, request, response);
+}
+
+void TrustyKeymaster::AddRngEntropy(const AddEntropyRequest& request,
+                                    AddEntropyResponse* response) {
+    ForwardCommand(KM_ADD_RNG_ENTROPY, request, response);
+}
+
+void TrustyKeymaster::Configure(const ConfigureRequest& request, ConfigureResponse* response) {
+    ForwardCommand(KM_CONFIGURE, request, response);
+}
+
+void TrustyKeymaster::GenerateKey(const GenerateKeyRequest& request,
+                                  GenerateKeyResponse* response) {
+    GenerateKeyRequest datedRequest(request.message_version);
+    datedRequest.key_description = request.key_description;
+
+    if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
+        datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+    }
+
+    ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
+}
+
+void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+                                            GetKeyCharacteristicsResponse* response) {
+    ForwardCommand(KM_GET_KEY_CHARACTERISTICS, request, response);
+}
+
+void TrustyKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) {
+    ForwardCommand(KM_IMPORT_KEY, request, response);
+}
+
+void TrustyKeymaster::ImportWrappedKey(const ImportWrappedKeyRequest& request,
+                                       ImportWrappedKeyResponse* response) {
+    ForwardCommand(KM_IMPORT_WRAPPED_KEY, request, response);
+}
+
+void TrustyKeymaster::ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response) {
+    ForwardCommand(KM_EXPORT_KEY, request, response);
+}
+
+void TrustyKeymaster::AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response) {
+    ForwardCommand(KM_ATTEST_KEY, request, response);
+}
+
+void TrustyKeymaster::UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response) {
+    ForwardCommand(KM_UPGRADE_KEY, request, response);
+}
+
+void TrustyKeymaster::DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response) {
+    ForwardCommand(KM_DELETE_KEY, request, response);
+}
+
+void TrustyKeymaster::DeleteAllKeys(const DeleteAllKeysRequest& request,
+                                    DeleteAllKeysResponse* response) {
+    ForwardCommand(KM_DELETE_ALL_KEYS, request, response);
+}
+
+void TrustyKeymaster::BeginOperation(const BeginOperationRequest& request,
+                                     BeginOperationResponse* response) {
+    ForwardCommand(KM_BEGIN_OPERATION, request, response);
+}
+
+void TrustyKeymaster::UpdateOperation(const UpdateOperationRequest& request,
+                                      UpdateOperationResponse* response) {
+    ForwardCommand(KM_UPDATE_OPERATION, request, response);
+}
+
+void TrustyKeymaster::FinishOperation(const FinishOperationRequest& request,
+                                      FinishOperationResponse* response) {
+    ForwardCommand(KM_FINISH_OPERATION, request, response);
+}
+
+void TrustyKeymaster::AbortOperation(const AbortOperationRequest& request,
+                                     AbortOperationResponse* response) {
+    ForwardCommand(KM_ABORT_OPERATION, request, response);
+}
+
+/* Methods for Keymaster 4.0 functionality -- not yet implemented */
+GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
+    GetHmacSharingParametersResponse response;
+    response.error = KM_ERROR_UNIMPLEMENTED;
+    return response;
+}
+
+ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
+        const ComputeSharedHmacRequest& /* request */) {
+    ComputeSharedHmacResponse response;
+    response.error = KM_ERROR_UNIMPLEMENTED;
+    return response;
+}
+
+VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
+        const VerifyAuthorizationRequest& /* request */) {
+    VerifyAuthorizationResponse response;
+    response.error = KM_ERROR_UNIMPLEMENTED;
+    return response;
+}
+
+}  // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
new file mode 100644
index 0000000..030b645
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRUSTY_KEYMASTER_H_
+#define TRUSTY_KEYMASTER_H_
+
+#include <keymaster/android_keymaster_messages.h>
+
+namespace keymaster {
+
+class TrustyKeymaster {
+  public:
+    TrustyKeymaster();
+    ~TrustyKeymaster();
+    int Initialize();
+    void GetVersion(const GetVersionRequest& request, GetVersionResponse* response);
+    void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
+                             SupportedAlgorithmsResponse* response);
+    void SupportedBlockModes(const SupportedBlockModesRequest& request,
+                             SupportedBlockModesResponse* response);
+    void SupportedPaddingModes(const SupportedPaddingModesRequest& request,
+                               SupportedPaddingModesResponse* response);
+    void SupportedDigests(const SupportedDigestsRequest& request,
+                          SupportedDigestsResponse* response);
+    void SupportedImportFormats(const SupportedImportFormatsRequest& request,
+                                SupportedImportFormatsResponse* response);
+    void SupportedExportFormats(const SupportedExportFormatsRequest& request,
+                                SupportedExportFormatsResponse* response);
+    void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
+    void Configure(const ConfigureRequest& request, ConfigureResponse* response);
+    void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
+    void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+                               GetKeyCharacteristicsResponse* response);
+    void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
+    void ImportWrappedKey(const ImportWrappedKeyRequest& request,
+                          ImportWrappedKeyResponse* response);
+    void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response);
+    void AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response);
+    void UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response);
+    void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);
+    void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response);
+    void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
+    void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
+    void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
+    void AbortOperation(const AbortOperationRequest& request, AbortOperationResponse* response);
+    GetHmacSharingParametersResponse GetHmacSharingParameters();
+    ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
+    VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
+};
+
+}  // namespace keymaster
+
+#endif  // TRUSTY_KEYMASTER_H_
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster3Device.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster3Device.h
new file mode 100644
index 0000000..6fc79ce
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster3Device.h
@@ -0,0 +1,84 @@
+/*
+ **
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#ifndef HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_
+#define HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_
+
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace keymaster {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::keymaster::V3_0::ErrorCode;
+using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
+using ::android::hardware::keymaster::V3_0::KeyCharacteristics;
+using ::android::hardware::keymaster::V3_0::KeyFormat;
+using ::android::hardware::keymaster::V3_0::KeyParameter;
+using ::android::hardware::keymaster::V3_0::KeyPurpose;
+
+class TrustyKeymaster3Device : public IKeymasterDevice {
+  public:
+    TrustyKeymaster3Device(TrustyKeymaster* impl);
+    virtual ~TrustyKeymaster3Device();
+
+    Return<void> getHardwareFeatures(getHardwareFeatures_cb _hidl_cb);
+    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+                             generateKey_cb _hidl_cb) override;
+    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+                                       const hidl_vec<uint8_t>& clientId,
+                                       const hidl_vec<uint8_t>& appData,
+                                       getKeyCharacteristics_cb _hidl_cb) override;
+    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
+    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+                           exportKey_cb _hidl_cb) override;
+    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+                           const hidl_vec<KeyParameter>& attestParams,
+                           attestKey_cb _hidl_cb) override;
+    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+                            const hidl_vec<KeyParameter>& upgradeParams,
+                            upgradeKey_cb _hidl_cb) override;
+    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+    Return<ErrorCode> deleteAllKeys() override;
+    Return<ErrorCode> destroyAttestationIds() override;
+    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+                       const hidl_vec<KeyParameter>& inParams, begin_cb _hidl_cb) override;
+    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, update_cb _hidl_cb) override;
+    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+                        finish_cb _hidl_cb) override;
+    Return<ErrorCode> abort(uint64_t operationHandle) override;
+
+  private:
+    std::unique_ptr<TrustyKeymaster> impl_;
+};
+
+}  // namespace keymaster
+
+#endif  // HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index d63757b..13e6725 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -46,6 +46,13 @@
     KM_ATTEST_KEY                   = (16 << KEYMASTER_REQ_SHIFT),
     KM_UPGRADE_KEY                  = (17 << KEYMASTER_REQ_SHIFT),
     KM_CONFIGURE                    = (18 << KEYMASTER_REQ_SHIFT),
+    KM_GET_HMAC_SHARING_PARAMETERS  = (19 << KEYMASTER_REQ_SHIFT),
+    KM_COMPUTE_SHARED_HMAC          = (20 << KEYMASTER_REQ_SHIFT),
+    KM_VERIFY_AUTHORIZATION         = (21 << KEYMASTER_REQ_SHIFT),
+    KM_DELETE_KEY                   = (22 << KEYMASTER_REQ_SHIFT),
+    KM_DELETE_ALL_KEYS              = (23 << KEYMASTER_REQ_SHIFT),
+    KM_DESTROY_ATTESTATION_IDS      = (24 << KEYMASTER_REQ_SHIFT),
+    KM_IMPORT_WRAPPED_KEY           = (25 << KEYMASTER_REQ_SHIFT),
 };
 
 #ifdef __ANDROID__
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 9c3a7df..0a0ecec 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -20,7 +20,7 @@
 #
 
 PRODUCT_PACKAGES += \
-	keystore.trusty \
+	android.hardware.keymaster@3.0-service.trusty \
 	gatekeeper.trusty
 
 PRODUCT_PROPERTY_OVERRIDES += \