Merge "Increase fastboot timeout to 30 seconds"
diff --git a/adb/Android.bp b/adb/Android.bp
index 07f052f..a18dc1a 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -277,26 +277,113 @@
     },
 }
 
+// libadbd_core contains the common sources to build libadbd and libadbd_services.
 cc_library_static {
+    name: "libadbd_core",
+    defaults: ["adb_defaults"],
+    recovery_available: true,
+
+    // libminadbd wants both, as it's used to build native tests.
+    compile_multilib: "both",
+
+    srcs: libadb_srcs + libadb_posix_srcs + [
+        "daemon/auth.cpp",
+        "daemon/jdwp_service.cpp",
+        "daemon/usb.cpp",
+    ],
+
+    local_include_dirs: [
+        "daemon/include",
+    ],
+
+    static_libs: [
+        "libdiagnose_usb",
+        "libqemu_pipe",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+    ],
+}
+
+cc_library {
+    name: "libadbd_services",
+    defaults: ["adb_defaults"],
+    recovery_available: true,
+    compile_multilib: "both",
+
+    srcs: [
+        "daemon/file_sync_service.cpp",
+        "daemon/framebuffer_service.cpp",
+        "daemon/mdns.cpp",
+        "daemon/remount_service.cpp",
+        "daemon/services.cpp",
+        "daemon/set_verity_enable_state_service.cpp",
+        "daemon/shell_service.cpp",
+        "shell_service_protocol.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    static_libs: [
+        "libadbd_core",
+        "libavb_user",
+        "libdiagnose_usb",
+        "libqemu_pipe",
+
+        // `daemon/shell_service.cpp` uses selinux_android_setcon(), which is not exposed by
+        // libselinux.
+        "libselinux",
+    ],
+
+    shared_libs: [
+        "libasyncio",
+        "libbase",
+        "libbootloader_message",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "libext4_utils",
+        "libfec",
+        "libfec_rs",
+        "libfs_mgr",
+        "liblog",
+        "libmdnssd",
+    ],
+}
+
+cc_library {
     name: "libadbd",
     defaults: ["adb_defaults"],
     recovery_available: true,
 
-    // libminadbd wants both, for some reason.
+    // Avoid getting duplicate symbol of android::build::GetBuildNumber().
+    use_version_lib: false,
+
+    // libminadbd wants both, as it's used to build native tests.
     compile_multilib: "both",
-    srcs: libadb_srcs + libadb_posix_srcs + [
-        "daemon/auth.cpp",
-        "daemon/usb.cpp",
-        "daemon/jdwp_service.cpp",
+
+    // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
+    whole_static_libs: [
+        "libadbd_core",
     ],
 
-    static_libs: [
+    shared_libs: [
+        "libadbd_services",
         "libasyncio",
-        "libcrypto_utils",
-        "libcrypto",
-        "libdiagnose_usb",
-        "libqemu_pipe",
         "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
     ],
 
     export_include_dirs: [
@@ -307,19 +394,10 @@
 cc_binary {
     name: "adbd",
     defaults: ["adb_defaults"],
-
     recovery_available: true,
 
     srcs: [
         "daemon/main.cpp",
-        "daemon/mdns.cpp",
-        "daemon/file_sync_service.cpp",
-        "daemon/framebuffer_service.cpp",
-        "daemon/remount_service.cpp",
-        "daemon/set_verity_enable_state_service.cpp",
-        "daemon/services.cpp",
-        "daemon/shell_service.cpp",
-        "shell_service_protocol.cpp",
     ],
 
     cflags: [
@@ -331,27 +409,16 @@
         keep_symbols: true,
     },
 
-    static_libs: [
+    shared_libs: [
         "libadbd",
-        "libasyncio",
-        "libavb_user",
-        "libbootloader_message",
-        "libcrypto_utils",
+        "libadbd_services",
+        "libbase",
+        "libcap",
         "libcrypto",
-        "libdiagnose_usb",
-        "libfec",
-        "libfec_rs",
-        "libfs_mgr",
+        "libcutils",
         "liblog",
-        "libext4_utils",
-        "libmdnssd",
         "libminijail",
         "libselinux",
-        "libsquashfs_utils",
-        "libqemu_pipe",
-
-        "libbase",
-        "libcutils",
     ],
 }
 
@@ -382,21 +449,20 @@
     test_suites: ["device-tests"],
 }
 
-python_binary_host {
+python_test_host {
     name: "adb_integration_test_adb",
     main: "test_adb.py",
     srcs: [
         "test_adb.py",
     ],
-    libs: [
-        "adb_py",
-    ],
+    test_config: "adb_integration_test_adb.xml",
+    test_suites: ["general-tests"],
     version: {
         py2: {
-            enabled: true,
+            enabled: false,
         },
         py3: {
-            enabled: false,
+            enabled: true,
         },
     },
 }
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 38c6f62..8e028f4 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -891,6 +891,10 @@
         // child side of the fork
         pipe_read.reset();
 
+        // android::base::Pipe unconditionally opens the pipe with O_CLOEXEC.
+        // Undo this manually.
+        fcntl(pipe_write.get(), F_SETFD, 0);
+
         char reply_fd[30];
         snprintf(reply_fd, sizeof(reply_fd), "%d", pipe_write.get());
         // child process
@@ -1040,7 +1044,7 @@
     return 0;
 }
 
-int handle_host_request(const char* service, TransportType type, const char* serial,
+bool handle_host_request(const char* service, TransportType type, const char* serial,
                         TransportId transport_id, int reply_fd, asocket* s) {
     if (strcmp(service, "kill") == 0) {
         fprintf(stderr, "adb server killed by remote request\n");
@@ -1066,7 +1070,7 @@
             transport_id = strtoll(service, const_cast<char**>(&service), 10);
             if (*service != '\0') {
                 SendFail(reply_fd, "invalid transport id");
-                return 1;
+                return true;
             }
         } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
             type = kTransportUsb;
@@ -1084,10 +1088,13 @@
         if (t != nullptr) {
             s->transport = t;
             SendOkay(reply_fd);
+
+            // We succesfully handled the device selection, but there's another request coming.
+            return false;
         } else {
             SendFail(reply_fd, error);
+            return true;
         }
-        return 1;
     }
 
     // return a list of all connected devices
@@ -1097,9 +1104,9 @@
             D("Getting device list...");
             std::string device_list = list_transports(long_listing);
             D("Sending device list...");
-            return SendOkay(reply_fd, device_list);
+            SendOkay(reply_fd, device_list);
         }
-        return 1;
+        return true;
     }
 
     if (!strcmp(service, "reconnect-offline")) {
@@ -1115,7 +1122,7 @@
             response.resize(response.size() - 1);
         }
         SendOkay(reply_fd, response);
-        return 0;
+        return true;
     }
 
     if (!strcmp(service, "features")) {
@@ -1126,7 +1133,7 @@
         } else {
             SendFail(reply_fd, error);
         }
-        return 0;
+        return true;
     }
 
     if (!strcmp(service, "host-features")) {
@@ -1137,7 +1144,7 @@
         }
         features.insert(kFeaturePushSync);
         SendOkay(reply_fd, FeatureSetToString(features));
-        return 0;
+        return true;
     }
 
     // remove TCP transport
@@ -1145,7 +1152,8 @@
         const std::string address(service + 11);
         if (address.empty()) {
             kick_all_tcp_devices();
-            return SendOkay(reply_fd, "disconnected everything");
+            SendOkay(reply_fd, "disconnected everything");
+            return true;
         }
 
         std::string serial;
@@ -1153,21 +1161,24 @@
         int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
         std::string error;
         if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
-            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
-                                                                  address.c_str(), error.c_str()));
+            SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
+                                                           address.c_str(), error.c_str()));
+            return true;
         }
         atransport* t = find_transport(serial.c_str());
         if (t == nullptr) {
-            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
-                                                                  serial.c_str()));
+            SendFail(reply_fd, android::base::StringPrintf("no such device '%s'", serial.c_str()));
+            return true;
         }
         kick_transport(t);
-        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
+        SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
+        return true;
     }
 
     // Returns our value for ADB_SERVER_VERSION.
     if (!strcmp(service, "version")) {
-        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
+        SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
+        return true;
     }
 
     // These always report "unknown" rather than the actual error, for scripts.
@@ -1175,28 +1186,31 @@
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
+            SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
-            return SendFail(reply_fd, error);
+            SendFail(reply_fd, error);
         }
+        return true;
     }
     if (!strcmp(service, "get-devpath")) {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
+            SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
-            return SendFail(reply_fd, error);
+            SendFail(reply_fd, error);
         }
+        return true;
     }
     if (!strcmp(service, "get-state")) {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->connection_state_name());
+            SendOkay(reply_fd, t->connection_state_name());
         } else {
-            return SendFail(reply_fd, error);
+            SendFail(reply_fd, error);
         }
+        return true;
     }
 
     // Indicates a new emulator instance has started.
@@ -1204,7 +1218,7 @@
         int  port = atoi(service+9);
         local_connect(port);
         /* we don't even need to send a reply */
-        return 0;
+        return true;
     }
 
     if (!strcmp(service, "reconnect")) {
@@ -1215,7 +1229,8 @@
             response =
                 "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
         }
-        return SendOkay(reply_fd, response);
+        SendOkay(reply_fd, response);
+        return true;
     }
 
     if (handle_forward_request(service,
@@ -1224,10 +1239,10 @@
                                                                 error);
                                },
                                reply_fd)) {
-        return 0;
+        return true;
     }
 
-    return -1;
+    return false;
 }
 
 static auto& init_mutex = *new std::mutex();
diff --git a/adb/adb.h b/adb/adb.h
index e6af780..f434e2d 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -210,8 +210,8 @@
 #define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
 #endif
 
-int handle_host_request(const char* service, TransportType type, const char* serial,
-                        TransportId transport_id, int reply_fd, asocket* s);
+bool handle_host_request(const char* service, TransportType type, const char* serial,
+                         TransportId transport_id, int reply_fd, asocket* s);
 
 void handle_online(atransport* t);
 void handle_offline(atransport* t);
diff --git a/adb/adb_integration_test_adb.xml b/adb/adb_integration_test_adb.xml
new file mode 100644
index 0000000..e722956
--- /dev/null
+++ b/adb/adb_integration_test_adb.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config to run adb integration tests">
+    <option name="test-suite-tag" value="adb_tests" />
+    <option name="test-suite-tag" value="adb_integration" />
+    <target_preparer class="com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer">
+        <option name="disable" value="false" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.adb.AdbStopServerPreparer" />
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="adb_integration_test_adb" />
+        <option name="inject-android-serial" value="true" />
+        <option name="test-timeout" value="2m" />
+    </test>
+</configuration>
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
index 2079be1..dec73bc 100644
--- a/adb/adb_unique_fd.cpp
+++ b/adb/adb_unique_fd.cpp
@@ -21,45 +21,8 @@
 
 #include "sysdeps.h"
 
-#if !defined(_WIN32)
-bool Pipe(unique_fd* read, unique_fd* write, int flags) {
-    int pipefd[2];
-#if !defined(__APPLE__)
-    if (pipe2(pipefd, flags) != 0) {
-        return false;
-    }
-#else
-    // Darwin doesn't have pipe2. Implement it ourselves.
-    if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
-        errno = EINVAL;
-        return false;
-    }
-
-    if (pipe(pipefd) != 0) {
-        return false;
-    }
-
-    if (flags & O_CLOEXEC) {
-        if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
-            fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
-            adb_close(pipefd[0]);
-            adb_close(pipefd[1]);
-            return false;
-        }
-    }
-
-    if (flags & O_NONBLOCK) {
-        if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
-            fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
-            adb_close(pipefd[0]);
-            adb_close(pipefd[1]);
-            return false;
-        }
-    }
-#endif
-
-    read->reset(pipefd[0]);
-    write->reset(pipefd[1]);
-    return true;
+#if defined(_WIN32)
+void AdbCloser::Close(int fd) {
+    adb_close(fd);
 }
 #endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index bad501a..d47213d 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -21,15 +21,15 @@
 
 #include <android-base/unique_fd.h>
 
+#if defined(_WIN32)
 // Helper to automatically close an FD when it goes out of scope.
 struct AdbCloser {
     static void Close(int fd);
 };
 
 using unique_fd = android::base::unique_fd_impl<AdbCloser>;
-
-#if !defined(_WIN32)
-bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
+#else
+using unique_fd = android::base::unique_fd;
 #endif
 
 template <typename T>
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 0c3327f..ffac315 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -274,10 +274,6 @@
     return android_dir;
 }
 
-void AdbCloser::Close(int fd) {
-    adb_close(fd);
-}
-
 int syntax_error(const char* fmt, ...) {
     fprintf(stderr, "adb: usage: ");
 
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 5fbef09..71c19b8 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -465,6 +465,7 @@
     if (key == nullptr) {
         // No more private keys to try, send the public key.
         t->SetConnectionState(kCsUnauthorized);
+        t->SetConnectionEstablished(true);
         send_auth_publickey(t);
         return;
     }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 3fb14f3..41ac663 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1326,9 +1326,9 @@
 }
 
 int adb_commandline(int argc, const char** argv) {
-    int no_daemon = 0;
-    int is_daemon = 0;
-    int is_server = 0;
+    bool no_daemon = false;
+    bool is_daemon = false;
+    bool is_server = false;
     int r;
     TransportType transport_type = kTransportAny;
     int ack_reply_fd = -1;
@@ -1348,12 +1348,12 @@
 
     while (argc > 0) {
         if (!strcmp(argv[0],"server")) {
-            is_server = 1;
+            is_server = true;
         } else if (!strcmp(argv[0],"nodaemon")) {
-            no_daemon = 1;
+            no_daemon = true;
         } else if (!strcmp(argv[0], "fork-server")) {
             /* this is a special flag used only when the ADB client launches the ADB Server */
-            is_daemon = 1;
+            is_daemon = true;
         } else if (!strcmp(argv[0], "--reply-fd")) {
             if (argc < 2) return syntax_error("--reply-fd requires an argument");
             const char* reply_fd_str = argv[1];
@@ -1925,7 +1925,8 @@
     for (int i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
 
-        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+        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;
@@ -1986,9 +1987,8 @@
         }
 
         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());
+            "%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) {
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index de6c723..095ad98 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -118,10 +118,20 @@
 
     init_transport_registration();
     init_reconnect_handler();
-    init_mdns_transport_discovery();
 
-    usb_init();
-    local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    if (!getenv("ADB_MDNS") || strcmp(getenv("ADB_MDNS"), "0") != 0) {
+        init_mdns_transport_discovery();
+    }
+
+    if (!getenv("ADB_USB") || strcmp(getenv("ADB_USB"), "0") != 0) {
+        usb_init();
+    } else {
+        adb_notify_device_scan_complete();
+    }
+
+    if (!getenv("ADB_EMU") || strcmp(getenv("ADB_EMU"), "0") != 0) {
+        local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    }
 
     std::string error;
 
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 0e79d82..f7017dd 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -30,6 +30,7 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <memory>
 #include <set>
 #include <string>
 #include <vector>
@@ -38,28 +39,30 @@
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
 
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
-#include "fs_mgr.h"
 #include "set_verity_enable_state_service.h"
 
-// Returns the device used to mount a directory in /proc/mounts.
+// Returns the last device used to mount a directory in /proc/mounts.
+// This will find overlayfs entry where upperdir=lowerdir, to make sure
+// remount is associated with the correct directory.
 static std::string find_proc_mount(const char* dir) {
     std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
-    if (!fp) {
-        return "";
-    }
+    std::string mnt_fsname;
+    if (!fp) return mnt_fsname;
 
     mntent* e;
     while ((e = getmntent(fp.get())) != nullptr) {
         if (strcmp(dir, e->mnt_dir) == 0) {
-            return e->mnt_fsname;
+            mnt_fsname = e->mnt_fsname;
         }
     }
-    return "";
+    return mnt_fsname;
 }
 
 // Returns the device used to mount a directory in the fstab.
@@ -81,6 +84,7 @@
 }
 
 bool make_block_device_writable(const std::string& dev) {
+    if ((dev == "overlay") || (dev == "overlayfs")) return true;
     int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         return false;
@@ -234,6 +238,14 @@
         partitions.push_back("/system");
     }
 
+    bool verity_enabled = (system_verified || vendor_verified);
+
+    // If we can use overlayfs, lets get it in place first
+    // before we struggle with determining deduplication operations.
+    if (!verity_enabled && fs_mgr_overlayfs_setup() && fs_mgr_overlayfs_mount_all()) {
+        WriteFdExactly(fd.get(), "overlayfs mounted\n");
+    }
+
     // Find partitions that are deduplicated, and can be un-deduplicated.
     std::set<std::string> dedup;
     for (const auto& partition : partitions) {
@@ -246,8 +258,6 @@
         }
     }
 
-    bool verity_enabled = (system_verified || vendor_verified);
-
     // Reboot now if the user requested it (and an operation needs a reboot).
     if (user_requested_reboot) {
         if (!dedup.empty() || verity_enabled) {
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 1f59d64..dfcc52d 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -94,7 +94,7 @@
     WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
 
-bool reboot_service_impl(unique_fd fd, const std::string& arg) {
+void reboot_service(unique_fd fd, const std::string& arg) {
     std::string reboot_arg = arg;
     bool auto_reboot = false;
 
@@ -108,7 +108,7 @@
     if (reboot_arg == "sideload") {
         if (getuid() != 0) {
             WriteFdExactly(fd.get(), "'adb root' is required for 'adb reboot sideload'.\n");
-            return false;
+            return;
         }
 
         const std::vector<std::string> options = {auto_reboot ? "--sideload_auto_reboot"
@@ -116,7 +116,7 @@
         std::string err;
         if (!write_bootloader_message(options, &err)) {
             D("Failed to set bootloader message: %s", err.c_str());
-            return false;
+            return;
         }
 
         reboot_arg = "recovery";
@@ -128,16 +128,9 @@
     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 false;
-    }
-
-    return true;
-}
-
-void reboot_service(unique_fd fd, const std::string& arg) {
-    if (!reboot_service_impl(std::move(fd), arg)) {
         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/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 2b6ec21..3676de5 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -19,6 +19,7 @@
 #include "set_verity_enable_state_service.h"
 #include "sysdeps.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <libavb_user/libavb_user.h>
@@ -28,12 +29,14 @@
 
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
 #include <log/log_properties.h>
 
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
-#include "fs_mgr.h"
 #include "remount_service.h"
 
 #include "fec/io.h"
@@ -46,6 +49,10 @@
 static const bool kAllowDisableVerity = false;
 #endif
 
+void suggest_run_adb_root(int fd) {
+    if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
+}
+
 /* Turn verity on/off */
 static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
                                      bool enable) {
@@ -59,7 +66,7 @@
 
     if (!fh) {
         WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
-        WriteFdFmt(fd, "Maybe run adb root?\n");
+        suggest_run_adb_root(fd);
         return false;
     }
 
@@ -87,6 +94,17 @@
         return false;
     }
 
+    auto change = false;
+    errno = 0;
+    if (enable ? fs_mgr_overlayfs_teardown(mount_point, &change)
+               : fs_mgr_overlayfs_setup(nullptr, mount_point, &change)) {
+        if (change) {
+            WriteFdFmt(fd, "%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
+        }
+    } else if (errno) {
+        WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
+                   mount_point, strerror(errno));
+    }
     WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
     return true;
 }
@@ -103,6 +121,22 @@
     return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
 }
 
+static bool overlayfs_setup(int fd, bool enable) {
+    auto change = false;
+    errno = 0;
+    if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
+               : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
+        if (change) {
+            WriteFdFmt(fd, "%s overlayfs\n", enable ? "disabling" : "using");
+        }
+    } else if (errno) {
+        WriteFdFmt(fd, "Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup",
+                   strerror(errno));
+        suggest_run_adb_root(fd);
+    }
+    return change;
+}
+
 /* Use AVB to turn verity on/off */
 static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
     std::string ab_suffix = get_ab_suffix();
@@ -128,6 +162,7 @@
         return false;
     }
 
+    overlayfs_setup(fd, enable_verity);
     WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
     return true;
 }
@@ -150,6 +185,7 @@
         }
 
         if (!android::base::GetBoolProperty("ro.secure", false)) {
+            overlayfs_setup(fd, enable);
             WriteFdExactly(fd.get(), "verity not enabled - ENG build\n");
             return;
         }
@@ -177,13 +213,14 @@
         // Not using AVB - assume VB1.0.
 
         // read all fstab entries at once from all sources
-        fstab = fs_mgr_read_fstab_default();
+        if (!fstab) fstab = fs_mgr_read_fstab_default();
         if (!fstab) {
-            WriteFdExactly(fd.get(), "Failed to read fstab\nMaybe run adb root?\n");
+            WriteFdExactly(fd.get(), "Failed to read fstab\n");
+            suggest_run_adb_root(fd.get());
             return;
         }
 
-        // Loop through entries looking for ones that vold manages.
+        // Loop through entries looking for ones that verity manages.
         for (int i = 0; i < fstab->num_entries; i++) {
             if (fs_mgr_is_verified(&fstab->recs[i])) {
                 if (set_verity_enabled_state(fd.get(), fstab->recs[i].blk_device,
@@ -193,6 +230,7 @@
             }
         }
     }
+    if (!any_changed) any_changed = overlayfs_setup(fd, enable);
 
     if (any_changed) {
         WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 69b5180..1534792 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -685,13 +685,9 @@
     if (service) {
         asocket* s2;
 
-        /* some requests are handled immediately -- in that
-        ** case the handle_host_request() routine has sent
-        ** the OKAY or FAIL message and all we have to do
-        ** is clean up.
-        */
-        if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) {
-            /* XXX fail message? */
+        // Some requests are handled immediately -- in that case the handle_host_request() routine
+        // has sent the OKAY or FAIL message and all we have to do is clean up.
+        if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s)) {
             D("SS(%d): handled host service '%s'", s->id, service);
             goto fail;
         }
diff --git a/adb/test_adb.py b/adb/test_adb.py
old mode 100644
new mode 100755
index ddd3ff0..d4c98e4
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2015 The Android Open Source Project
 #
@@ -19,9 +19,7 @@
 This differs from things in test_device.py in that there is no API for these
 things. Most of these tests involve specific error messages or the help text.
 """
-from __future__ import print_function
 
-import binascii
 import contextlib
 import os
 import random
@@ -32,8 +30,6 @@
 import threading
 import unittest
 
-import adb
-
 
 @contextlib.contextmanager
 def fake_adbd(protocol=socket.AF_INET, port=0):
@@ -42,61 +38,61 @@
     serversock = socket.socket(protocol, socket.SOCK_STREAM)
     serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     if protocol == socket.AF_INET:
-        serversock.bind(('127.0.0.1', port))
+        serversock.bind(("127.0.0.1", port))
     else:
-        serversock.bind(('::1', port))
+        serversock.bind(("::1", port))
     serversock.listen(1)
 
     # A pipe that is used to signal the thread that it should terminate.
-    readpipe, writepipe = os.pipe()
+    readsock, writesock = socket.socketpair()
 
-    def _adb_packet(command, arg0, arg1, data):
-        bin_command = struct.unpack('I', command)[0]
-        buf = struct.pack('IIIIII', bin_command, arg0, arg1, len(data), 0,
+    def _adb_packet(command: bytes, arg0: int, arg1: int, data: bytes) -> bytes:
+        bin_command = struct.unpack("I", command)[0]
+        buf = struct.pack("IIIIII", bin_command, arg0, arg1, len(data), 0,
                           bin_command ^ 0xffffffff)
         buf += data
         return buf
 
-    def _handle():
-        rlist = [readpipe, serversock]
-        cnxn_sent = {}
-        while True:
-            read_ready, _, _ = select.select(rlist, [], [])
-            for ready in read_ready:
-                if ready == readpipe:
-                    # Closure pipe
-                    os.close(ready)
-                    serversock.shutdown(socket.SHUT_RDWR)
-                    serversock.close()
-                    return
-                elif ready == serversock:
-                    # Server socket
-                    conn, _ = ready.accept()
-                    rlist.append(conn)
-                else:
-                    # Client socket
-                    data = ready.recv(1024)
-                    if not data or data.startswith('OPEN'):
+    def _handle(sock):
+        with contextlib.closing(sock) as serversock:
+            rlist = [readsock, serversock]
+            cnxn_sent = {}
+            while True:
+                read_ready, _, _ = select.select(rlist, [], [])
+                for ready in read_ready:
+                    if ready == readsock:
+                        # Closure pipe
+                        for f in rlist:
+                            f.close()
+                        return
+                    elif ready == serversock:
+                        # Server socket
+                        conn, _ = ready.accept()
+                        rlist.append(conn)
+                    else:
+                        # Client socket
+                        data = ready.recv(1024)
+                        if not data or data.startswith(b"OPEN"):
+                            if ready in cnxn_sent:
+                                del cnxn_sent[ready]
+                            ready.shutdown(socket.SHUT_RDWR)
+                            ready.close()
+                            rlist.remove(ready)
+                            continue
                         if ready in cnxn_sent:
-                            del cnxn_sent[ready]
-                        ready.shutdown(socket.SHUT_RDWR)
-                        ready.close()
-                        rlist.remove(ready)
-                        continue
-                    if ready in cnxn_sent:
-                        continue
-                    cnxn_sent[ready] = True
-                    ready.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
-                                              'device::ro.product.name=fakeadb'))
+                            continue
+                        cnxn_sent[ready] = True
+                        ready.sendall(_adb_packet(b"CNXN", 0x01000001, 1024 * 1024,
+                                                  b"device::ro.product.name=fakeadb"))
 
     port = serversock.getsockname()[1]
-    server_thread = threading.Thread(target=_handle)
+    server_thread = threading.Thread(target=_handle, args=(serversock,))
     server_thread.start()
 
     try:
         yield port
     finally:
-        os.close(writepipe)
+        writesock.close()
         server_thread.join()
 
 
@@ -107,14 +103,15 @@
     This automatically disconnects when done with the connection.
     """
 
-    output = subprocess.check_output(['adb', 'connect', serial])
-    unittest.assertEqual(output.strip(), 'connected to {}'.format(serial))
+    output = subprocess.check_output(["adb", "connect", serial])
+    unittest.assertEqual(output.strip(),
+                        "connected to {}".format(serial).encode("utf8"))
 
     try:
         yield
     finally:
         # Perform best-effort disconnection. Discard the output.
-        subprocess.Popen(['adb', 'disconnect', serial],
+        subprocess.Popen(["adb", "disconnect", serial],
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE).communicate()
 
@@ -123,21 +120,22 @@
 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
     # Kill any existing server on this non-default port.
-    subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+    subprocess.check_output(["adb", "-P", str(port), "kill-server"],
                             stderr=subprocess.STDOUT)
     read_pipe, write_pipe = os.pipe()
-    proc = subprocess.Popen(['adb', '-L', 'tcp:localhost:{}'.format(port),
-                             'fork-server', 'server',
-                             '--reply-fd', str(write_pipe)])
+    os.set_inheritable(write_pipe, True)
+    proc = subprocess.Popen(["adb", "-L", "tcp:localhost:{}".format(port),
+                             "fork-server", "server",
+                             "--reply-fd", str(write_pipe)], close_fds=False)
     try:
         os.close(write_pipe)
         greeting = os.read(read_pipe, 1024)
-        assert greeting == 'OK\n', repr(greeting)
+        assert greeting == b"OK\n", repr(greeting)
         yield port
     finally:
         proc.terminate()
@@ -150,35 +148,37 @@
     def test_help(self):
         """Make sure we get _something_ out of help."""
         out = subprocess.check_output(
-            ['adb', 'help'], stderr=subprocess.STDOUT)
+            ["adb", "help"], stderr=subprocess.STDOUT)
         self.assertGreater(len(out), 0)
 
     def test_version(self):
         """Get a version number out of the output of adb."""
-        lines = subprocess.check_output(['adb', 'version']).splitlines()
+        lines = subprocess.check_output(["adb", "version"]).splitlines()
         version_line = lines[0]
-        self.assertRegexpMatches(
-            version_line, r'^Android Debug Bridge version \d+\.\d+\.\d+$')
+        self.assertRegex(
+            version_line, rb"^Android Debug Bridge version \d+\.\d+\.\d+$")
         if len(lines) == 2:
             # Newer versions of ADB have a second line of output for the
             # version that includes a specific revision (git SHA).
             revision_line = lines[1]
-            self.assertRegexpMatches(
-                revision_line, r'^Revision [0-9a-f]{12}-android$')
+            self.assertRegex(
+                revision_line, rb"^Revision [0-9a-f]{12}-android$")
 
     def test_tcpip_error_messages(self):
         """Make sure 'adb tcpip' parsing is sane."""
-        proc = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+        proc = subprocess.Popen(["adb", "tcpip"],
+                                stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
         out, _ = proc.communicate()
         self.assertEqual(1, proc.returncode)
-        self.assertIn('requires an argument', out)
+        self.assertIn(b"requires an argument", out)
 
-        proc = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+        proc = subprocess.Popen(["adb", "tcpip", "foo"],
+                                stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
         out, _ = proc.communicate()
         self.assertEqual(1, proc.returncode)
-        self.assertIn('invalid port', out)
+        self.assertIn(b"invalid port", out)
 
 
 class ServerTest(unittest.TestCase):
@@ -214,12 +214,12 @@
 
         port = 5038
         # Kill any existing server on this non-default port.
-        subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+        subprocess.check_output(["adb", "-P", str(port), "kill-server"],
                                 stderr=subprocess.STDOUT)
 
         try:
             # Run the adb client and have it start the adb server.
-            proc = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
+            proc = subprocess.Popen(["adb", "-P", str(port), "start-server"],
                                     stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
@@ -229,14 +229,12 @@
             stdout_thread = threading.Thread(
                 target=ServerTest._read_pipe_and_set_event,
                 args=(proc.stdout, stdout_event))
-            stdout_thread.daemon = True
             stdout_thread.start()
 
             stderr_event = threading.Event()
             stderr_thread = threading.Thread(
                 target=ServerTest._read_pipe_and_set_event,
                 args=(proc.stderr, stderr_event))
-            stderr_thread.daemon = True
             stderr_thread.start()
 
             # Wait for the adb client to finish. Once that has occurred, if
@@ -250,7 +248,8 @@
             # probably letting the adb server inherit stdin which would be
             # wrong.
             with self.assertRaises(IOError):
-                proc.stdin.write('x')
+                proc.stdin.write(b"x")
+                proc.stdin.flush()
 
             # Wait a few seconds for stdout/stderr to be closed (in the success
             # case, this won't wait at all). If there is a timeout, that means
@@ -259,9 +258,11 @@
             # inherit stdout/stderr which would be wrong.
             self.assertTrue(stdout_event.wait(5), "adb stdout not closed")
             self.assertTrue(stderr_event.wait(5), "adb stderr not closed")
+            stdout_thread.join()
+            stderr_thread.join()
         finally:
             # If we started a server, kill it.
-            subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+            subprocess.check_output(["adb", "-P", str(port), "kill-server"],
                                     stderr=subprocess.STDOUT)
 
 
@@ -271,7 +272,7 @@
     def _reset_socket_on_close(self, sock):
         """Use SO_LINGER to cause TCP RST segment to be sent on socket close."""
         # The linger structure is two shorts on Windows, but two ints on Unix.
-        linger_format = 'hh' if os.name == 'nt' else 'ii'
+        linger_format = "hh" if os.name == "nt" else "ii"
         l_onoff = 1
         l_linger = 0
 
@@ -292,33 +293,33 @@
             # Use SO_REUSEADDR so subsequent runs of the test can grab the port
             # even if it is in TIME_WAIT.
             listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-            listener.bind(('127.0.0.1', 0))
+            listener.bind(("127.0.0.1", 0))
             listener.listen(4)
             port = listener.getsockname()[1]
 
             # Now that listening has started, start adb emu kill, telling it to
             # connect to our mock emulator.
             proc = subprocess.Popen(
-                ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
+                ["adb", "-s", "emulator-" + str(port), "emu", "kill"],
                 stderr=subprocess.STDOUT)
 
             accepted_connection, addr = listener.accept()
             with contextlib.closing(accepted_connection) as conn:
                 # If WSAECONNABORTED (10053) is raised by any socket calls,
                 # then adb probably isn't reading the data that we sent it.
-                conn.sendall('Android Console: type \'help\' for a list ' +
-                             'of commands\r\n')
-                conn.sendall('OK\r\n')
+                conn.sendall(("Android Console: type 'help' for a list "
+                             "of commands\r\n").encode("utf8"))
+                conn.sendall(b"OK\r\n")
 
                 with contextlib.closing(conn.makefile()) as connf:
                     line = connf.readline()
-                    if line.startswith('auth'):
+                    if line.startswith("auth"):
                         # Ignore the first auth line.
                         line = connf.readline()
-                    self.assertEqual('kill\n', line)
-                    self.assertEqual('quit\n', connf.readline())
+                    self.assertEqual("kill\n", line)
+                    self.assertEqual("quit\n", connf.readline())
 
-                conn.sendall('OK: killing emulator, bye bye\r\n')
+                conn.sendall(b"OK: killing emulator, bye bye\r\n")
 
                 # Use SO_LINGER to send TCP RST segment to test whether adb
                 # ignores WSAECONNRESET on Windows. This happens with the
@@ -342,31 +343,31 @@
         """
         with adb_server() as server_port:
             with fake_adbd() as port:
-                serial = 'emulator-{}'.format(port - 1)
+                serial = "emulator-{}".format(port - 1)
                 # Ensure that the emulator is not there.
                 try:
-                    subprocess.check_output(['adb', '-P', str(server_port),
-                                             '-s', serial, 'get-state'],
+                    subprocess.check_output(["adb", "-P", str(server_port),
+                                             "-s", serial, "get-state"],
                                             stderr=subprocess.STDOUT)
-                    self.fail('Device should not be available')
+                    self.fail("Device should not be available")
                 except subprocess.CalledProcessError as err:
                     self.assertEqual(
                         err.output.strip(),
-                        'error: device \'{}\' not found'.format(serial))
+                        "error: device '{}' not found".format(serial).encode("utf8"))
 
                 # Let the ADB server know that the emulator has started.
                 with contextlib.closing(
-                    socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
-                    sock.connect(('localhost', server_port))
-                    command = 'host:emulator:{}'.format(port)
-                    sock.sendall('%04x%s' % (len(command), command))
+                        socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
+                    sock.connect(("localhost", server_port))
+                    command = "host:emulator:{}".format(port).encode("utf8")
+                    sock.sendall(b"%04x%s" % (len(command), command))
 
                 # Ensure the emulator is there.
-                subprocess.check_call(['adb', '-P', str(server_port),
-                                       '-s', serial, 'wait-for-device'])
-                output = subprocess.check_output(['adb', '-P', str(server_port),
-                                                  '-s', serial, 'get-state'])
-                self.assertEqual(output.strip(), 'device')
+                subprocess.check_call(["adb", "-P", str(server_port),
+                                       "-s", serial, "wait-for-device"])
+                output = subprocess.check_output(["adb", "-P", str(server_port),
+                                                  "-s", serial, "get-state"])
+                self.assertEqual(output.strip(), b"device")
 
 
 class ConnectionTest(unittest.TestCase):
@@ -380,7 +381,7 @@
         for protocol in (socket.AF_INET, socket.AF_INET6):
             try:
                 with fake_adbd(protocol=protocol) as port:
-                    serial = 'localhost:{}'.format(port)
+                    serial = "localhost:{}".format(port)
                     with adb_connect(self, serial):
                         pass
             except socket.error:
@@ -391,49 +392,51 @@
         """Ensure that an already-connected device stays connected."""
 
         with fake_adbd() as port:
-            serial = 'localhost:{}'.format(port)
+            serial = "localhost:{}".format(port)
             with adb_connect(self, serial):
                 # b/31250450: this always returns 0 but probably shouldn't.
-                output = subprocess.check_output(['adb', 'connect', serial])
+                output = subprocess.check_output(["adb", "connect", serial])
                 self.assertEqual(
-                    output.strip(), 'already connected to {}'.format(serial))
+                    output.strip(),
+                    "already connected to {}".format(serial).encode("utf8"))
 
     def test_reconnect(self):
         """Ensure that a disconnected device reconnects."""
 
         with fake_adbd() as port:
-            serial = 'localhost:{}'.format(port)
+            serial = "localhost:{}".format(port)
             with adb_connect(self, serial):
-                output = subprocess.check_output(['adb', '-s', serial,
-                                                  'get-state'])
-                self.assertEqual(output.strip(), 'device')
+                output = subprocess.check_output(["adb", "-s", serial,
+                                                  "get-state"])
+                self.assertEqual(output.strip(), b"device")
 
                 # This will fail.
-                proc = subprocess.Popen(['adb', '-s', serial, 'shell', 'true'],
+                proc = subprocess.Popen(["adb", "-s", serial, "shell", "true"],
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.STDOUT)
                 output, _ = proc.communicate()
-                self.assertEqual(output.strip(), 'error: closed')
+                self.assertEqual(output.strip(), b"error: closed")
 
-                subprocess.check_call(['adb', '-s', serial, 'wait-for-device'])
+                subprocess.check_call(["adb", "-s", serial, "wait-for-device"])
 
-                output = subprocess.check_output(['adb', '-s', serial,
-                                                  'get-state'])
-                self.assertEqual(output.strip(), 'device')
+                output = subprocess.check_output(["adb", "-s", serial,
+                                                  "get-state"])
+                self.assertEqual(output.strip(), b"device")
 
                 # Once we explicitly kick a device, it won't attempt to
                 # reconnect.
-                output = subprocess.check_output(['adb', 'disconnect', serial])
+                output = subprocess.check_output(["adb", "disconnect", serial])
                 self.assertEqual(
-                    output.strip(), 'disconnected {}'.format(serial))
+                    output.strip(),
+                    "disconnected {}".format(serial).encode("utf8"))
                 try:
-                    subprocess.check_output(['adb', '-s', serial, 'get-state'],
+                    subprocess.check_output(["adb", "-s", serial, "get-state"],
                                             stderr=subprocess.STDOUT)
-                    self.fail('Device should not be available')
+                    self.fail("Device should not be available")
                 except subprocess.CalledProcessError as err:
                     self.assertEqual(
                         err.output.strip(),
-                        'error: device \'{}\' not found'.format(serial))
+                        "error: device '{}' not found".format(serial).encode("utf8"))
 
 
 def main():
@@ -442,5 +445,5 @@
     unittest.main(verbosity=3)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/adb/test_device.py b/adb/test_device.py
index 4abe7a7..42aadc4 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -762,9 +762,10 @@
             os.chmod(host_dir, 0o700)
 
             # Create an empty directory.
-            os.mkdir(os.path.join(host_dir, 'empty'))
+            empty_dir_path = os.path.join(host_dir, 'empty')
+            os.mkdir(empty_dir_path);
 
-            self.device.push(host_dir, self.DEVICE_TEMP_DIR)
+            self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
 
             test_empty_cmd = ['[', '-d',
                               os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
@@ -1032,7 +1033,8 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    def test_pull_symlink_dir(self):
+    # selinux prevents adbd from accessing symlinks on /data/local/tmp.
+    def disabled_test_pull_symlink_dir(self):
         """Pull a symlink to a directory of symlinks to files."""
         try:
             host_dir = tempfile.mkdtemp()
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 9222008..4cd19f9 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -33,7 +33,7 @@
 #include <deque>
 #include <list>
 #include <mutex>
-#include <queue>
+#include <set>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -108,9 +108,11 @@
         size_t attempts_left;
 
         bool operator<(const ReconnectAttempt& rhs) const {
-            // std::priority_queue returns the largest element first, so we want attempts that have
-            // less time remaining (i.e. smaller time_points) to compare greater.
-            return reconnect_time > rhs.reconnect_time;
+            if (reconnect_time == rhs.reconnect_time) {
+                return reinterpret_cast<uintptr_t>(transport) <
+                       reinterpret_cast<uintptr_t>(rhs.transport);
+            }
+            return reconnect_time < rhs.reconnect_time;
         }
     };
 
@@ -123,7 +125,7 @@
     bool running_ GUARDED_BY(reconnect_mutex_) = true;
     std::thread handler_thread_;
     std::condition_variable reconnect_cv_;
-    std::priority_queue<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
+    std::set<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
 
     DISALLOW_COPY_AND_ASSIGN(ReconnectHandler);
 };
@@ -145,8 +147,8 @@
     // Drain the queue to free all resources.
     std::lock_guard<std::mutex> lock(reconnect_mutex_);
     while (!reconnect_queue_.empty()) {
-        ReconnectAttempt attempt = reconnect_queue_.top();
-        reconnect_queue_.pop();
+        ReconnectAttempt attempt = *reconnect_queue_.begin();
+        reconnect_queue_.erase(reconnect_queue_.begin());
         remove_transport(attempt.transport);
     }
 }
@@ -176,7 +178,7 @@
                 //        system_clock as its clock, so we're probably hosed if the clock changes,
                 //        even if we use steady_clock throughout. This problem goes away once we
                 //        switch to libc++.
-                reconnect_cv_.wait_until(lock, reconnect_queue_.top().reconnect_time);
+                reconnect_cv_.wait_until(lock, reconnect_queue_.begin()->reconnect_time);
             } else {
                 reconnect_cv_.wait(lock);
             }
@@ -187,12 +189,12 @@
             // Go back to sleep in case |reconnect_cv_| woke up spuriously and we still
             // have more time to wait for the current attempt.
             auto now = std::chrono::steady_clock::now();
-            if (reconnect_queue_.top().reconnect_time > now) {
+            if (reconnect_queue_.begin()->reconnect_time > now) {
                 continue;
             }
 
-            attempt = reconnect_queue_.top();
-            reconnect_queue_.pop();
+            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);
@@ -1190,43 +1192,40 @@
 }
 #endif  // ADB_HOST
 
-int register_socket_transport(unique_fd s, const char* serial, int port, int local,
-                              atransport::ReconnectCallback reconnect) {
+bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
+                               atransport::ReconnectCallback reconnect, int* error) {
     atransport* t = new atransport(std::move(reconnect), kCsOffline);
 
-    if (!serial) {
-        char buf[32];
-        snprintf(buf, sizeof(buf), "T-%p", t);
-        serial = buf;
-    }
-
-    D("transport: %s init'ing for socket %d, on port %d", serial, s.get(), port);
+    D("transport: %s init'ing for socket %d, on port %d", serial.c_str(), s.get(), port);
     if (init_socket_transport(t, std::move(s), port, local) < 0) {
         delete t;
-        return -1;
+        if (error) *error = errno;
+        return false;
     }
 
     std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& transport : pending_list) {
-        if (strcmp(serial, transport->serial.c_str()) == 0) {
+        if (serial == transport->serial) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in pending_list and fails to register";
             delete t;
-            return -EALREADY;
+            if (error) *error = EALREADY;
+            return false;
         }
     }
 
     for (const auto& transport : transport_list) {
-        if (strcmp(serial, transport->serial.c_str()) == 0) {
+        if (serial == transport->serial) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in transport_list and fails to register";
             delete t;
-            return -EALREADY;
+            if (error) *error = EALREADY;
+            return false;
         }
     }
 
+    t->serial = std::move(serial);
     pending_list.push_front(t);
-    t->serial = serial;
 
     lock.unlock();
 
@@ -1235,10 +1234,20 @@
 
     if (local == 1) {
         // Do not wait for emulator transports.
-        return 0;
+        return true;
     }
 
-    return waitable->WaitForConnection(std::chrono::seconds(10)) ? 0 : -1;
+    if (!waitable->WaitForConnection(std::chrono::seconds(10))) {
+        if (error) *error = ETIMEDOUT;
+        return false;
+    }
+
+    if (t->GetConnectionState() == kCsUnauthorized) {
+        if (error) *error = EPERM;
+        return false;
+    }
+
+    return true;
 }
 
 #if ADB_HOST
diff --git a/adb/transport.h b/adb/transport.h
index 1844ae8..f362f24 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -364,8 +364,8 @@
 void connect_device(const std::string& address, std::string* response);
 
 /* cause new transports to be init'd and added to the list */
-int register_socket_transport(unique_fd s, const char* serial, int port, int local,
-                              atransport::ReconnectCallback reconnect);
+bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
+                               atransport::ReconnectCallback reconnect, int* error = nullptr);
 
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9398ceb..1431252 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -125,11 +125,12 @@
         return init_socket_transport(t, std::move(fd), port, 0) >= 0;
     };
 
-    int ret =
-            register_socket_transport(std::move(fd), serial.c_str(), port, 0, std::move(reconnect));
-    if (ret < 0) {
-        if (ret == -EALREADY) {
+    int error;
+    if (!register_socket_transport(std::move(fd), serial, port, 0, std::move(reconnect), &error)) {
+        if (error == EALREADY) {
             *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+        } else if (error == EPERM) {
+            *response = android::base::StringPrintf("failed to authenticate to %s", serial.c_str());
         } else {
             *response = android::base::StringPrintf("failed to connect to %s", serial.c_str());
         }
@@ -162,8 +163,8 @@
         close_on_exec(fd.get());
         disable_tcp_nagle(fd.get());
         std::string serial = getEmulatorSerialString(console_port);
-        if (register_socket_transport(std::move(fd), serial.c_str(), adb_port, 1,
-                                      [](atransport*) { return false; }) == 0) {
+        if (register_socket_transport(std::move(fd), std::move(serial), adb_port, 1,
+                                      [](atransport*) { return false; })) {
             return 0;
         }
     }
@@ -265,7 +266,7 @@
             close_on_exec(fd.get());
             disable_tcp_nagle(fd.get());
             std::string serial = android::base::StringPrintf("host-%d", fd.get());
-            register_socket_transport(std::move(fd), serial.c_str(), port, 1,
+            register_socket_transport(std::move(fd), std::move(serial), port, 1,
                                       [](atransport*) { return false; });
         }
     }
@@ -362,7 +363,7 @@
                  * exchange. */
                 std::string serial = android::base::StringPrintf("host-%d", fd.get());
                 WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
-                register_socket_transport(std::move(fd), serial.c_str(), port, 1,
+                register_socket_transport(std::move(fd), std::move(serial), port, 1,
                                           [](atransport*) { return false; });
             }
 
@@ -454,10 +455,6 @@
     return it->second;
 }
 
-std::string getEmulatorSerialString(int console_port) {
-    return android::base::StringPrintf("emulator-%d", console_port);
-}
-
 atransport* find_emulator_transport_by_adb_port(int adb_port) {
     std::lock_guard<std::mutex> lock(local_transports_lock);
     return find_emulator_transport_by_adb_port_locked(adb_port);
@@ -468,6 +465,10 @@
 }
 #endif
 
+std::string getEmulatorSerialString(int console_port) {
+    return android::base::StringPrintf("emulator-%d", console_port);
+}
+
 int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
     int fail = 0;
 
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
index 8eef2ea..49e3721 100644
--- a/adf/libadf/Android.bp
+++ b/adf/libadf/Android.bp
@@ -14,6 +14,7 @@
 
 cc_library {
     name: "libadf",
+    recovery_available: true,
     vendor_available: true,
     vndk: {
         enabled: true,
diff --git a/adf/libadf/adf.cpp b/adf/libadf/adf.cpp
index 60d8ef0..fd9c208 100644
--- a/adf/libadf/adf.cpp
+++ b/adf/libadf/adf.cpp
@@ -132,8 +132,11 @@
 void adf_free_device_data(struct adf_device_data *data)
 {
     delete [] data->attachments;
+    data->attachments = nullptr;
     delete [] data->allowed_attachments;
+    data->allowed_attachments = nullptr;
     delete [] static_cast<char *>(data->custom_data);
+    data->custom_data = nullptr;
 }
 
 int adf_device_post(struct adf_device *dev,
@@ -236,9 +239,10 @@
         return err;
 
     std::vector<adf_id_t> ids;
-    for (size_t i = 0; i < data.n_allowed_attachments; i++)
-        if (data.allowed_attachments[i].overlay_engine == overlay_engine)
-            ids.push_back(data.allowed_attachments[i].interface);
+    if (data.allowed_attachments != nullptr)
+        for (size_t i = 0; i < data.n_allowed_attachments; i++)
+            if (data.allowed_attachments[i].overlay_engine == overlay_engine)
+              ids.push_back(data.allowed_attachments[i].interface);
 
     adf_free_device_data(&data);
     return adf_id_vector_to_array(ids, interfaces);
@@ -450,9 +454,10 @@
         return err;
 
     std::vector<adf_id_t> ids;
-    for (size_t i = 0; i < data.n_allowed_attachments; i++)
-        if (data.allowed_attachments[i].interface == interface)
-            ids.push_back(data.allowed_attachments[i].overlay_engine);
+    if (data.allowed_attachments != nullptr)
+        for (size_t i = 0; i < data.n_allowed_attachments; i++)
+            if (data.allowed_attachments[i].interface == interface)
+                ids.push_back(data.allowed_attachments[i].overlay_engine);
 
     return adf_id_vector_to_array(ids, overlay_engines);
 }
@@ -551,7 +556,9 @@
 void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
 {
     delete [] data->supported_formats;
+    data->supported_formats = nullptr;
     delete [] static_cast<char *>(data->custom_data);
+    data->custom_data = nullptr;
 }
 
 bool adf_overlay_engine_supports_format(int fd, __u32 format)
@@ -564,10 +571,12 @@
     if (err < 0)
         return false;
 
-    for (i = 0; i < data.n_supported_formats; i++) {
-        if (data.supported_formats[i] == format) {
-            ret = true;
-            break;
+    if (data.supported_formats != nullptr) {
+        for (i = 0; i < data.n_supported_formats; i++) {
+            if (data.supported_formats[i] == format) {
+                ret = true;
+                break;
+            }
         }
     }
 
@@ -638,18 +647,18 @@
         const __u32 *formats, size_t n_formats,
         adf_id_t interface, adf_id_t *overlay_engine)
 {
-    adf_id_t *engs;
+    adf_id_t *engs = nullptr;
     ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
 
-    if (n_engs <= 0)
+    if (engs == nullptr)
         return false;
 
-    adf_id_t *filtered_engs;
+    adf_id_t *filtered_engs = nullptr;
     ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
             formats, n_formats, engs, n_engs, &filtered_engs);
     free(engs);
 
-    if (n_filtered_engs <= 0)
+    if (filtered_engs == nullptr)
         return false;
 
     *overlay_engine = filtered_engs[0];
@@ -700,17 +709,17 @@
 
     if (n_intfs < 0)
         return n_intfs;
-    else if (!n_intfs)
+    else if (!intfs)
         return -ENODEV;
 
-    adf_id_t *primary_intfs;
+    adf_id_t *primary_intfs = nullptr;
     ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
             ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
     free(intfs);
 
     if (n_primary_intfs < 0)
         return n_primary_intfs;
-    else if (!n_primary_intfs)
+    else if (!primary_intfs)
         return -ENODEV;
 
     if (!formats) {
diff --git a/base/Android.bp b/base/Android.bp
index 46a0233..3d80d97 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -135,7 +135,6 @@
         "strings_test.cpp",
         "test_main.cpp",
         "test_utils_test.cpp",
-        "unique_fd_test.cpp",
     ],
     target: {
         android: {
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
index c273c61..ccffba2 100644
--- a/base/include/android-base/parsedouble.h
+++ b/base/include/android-base/parsedouble.h
@@ -20,28 +20,58 @@
 #include <stdlib.h>
 
 #include <limits>
+#include <string>
 
 namespace android {
 namespace base {
 
-// Parse double value in the string 's' and sets 'out' to that value.
+// Parse floating value in the string 's' and sets 'out' to that value if it exists.
 // Optionally allows the caller to define a 'min' and 'max' beyond which
 // otherwise valid values will be rejected. Returns boolean success.
-static inline bool ParseDouble(const char* s, double* out,
-                               double min = std::numeric_limits<double>::lowest(),
-                               double max = std::numeric_limits<double>::max()) {
+template <typename T, T (*strtox)(const char* str, char** endptr)>
+static inline bool ParseFloatingPoint(const char* s, T* out, T min, T max) {
   errno = 0;
   char* end;
-  double result = strtod(s, &end);
+  T result = strtox(s, &end);
   if (errno != 0 || s == end || *end != '\0') {
     return false;
   }
   if (result < min || max < result) {
     return false;
   }
-  *out = result;
+  if (out != nullptr) {
+    *out = result;
+  }
   return true;
 }
 
+// Parse double value in the string 's' and sets 'out' to that value if it exists.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+static inline bool ParseDouble(const char* s, double* out,
+                               double min = std::numeric_limits<double>::lowest(),
+                               double max = std::numeric_limits<double>::max()) {
+  return ParseFloatingPoint<double, strtod>(s, out, min, max);
+}
+static inline bool ParseDouble(const std::string& s, double* out,
+                               double min = std::numeric_limits<double>::lowest(),
+                               double max = std::numeric_limits<double>::max()) {
+  return ParseFloatingPoint<double, strtod>(s.c_str(), out, min, max);
+}
+
+// Parse float value in the string 's' and sets 'out' to that value if it exists.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+static inline bool ParseFloat(const char* s, float* out,
+                              float min = std::numeric_limits<float>::lowest(),
+                              float max = std::numeric_limits<float>::max()) {
+  return ParseFloatingPoint<float, strtof>(s, out, min, max);
+}
+static inline bool ParseFloat(const std::string& s, float* out,
+                              float min = std::numeric_limits<float>::lowest(),
+                              float max = std::numeric_limits<float>::max()) {
+  return ParseFloatingPoint<float, strtof>(s.c_str(), out, min, max);
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index bb54c99..55f1ed3 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -27,9 +27,9 @@
 namespace base {
 
 // Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
-// 'out' to that value. Optionally allows the caller to define a 'max' beyond
-// which otherwise valid values will be rejected. Returns boolean success; 'out'
-// is untouched if parsing fails.
+// 'out' to that value if it is specified. Optionally allows the caller to define
+// a 'max' beyond which otherwise valid values will be rejected. Returns boolean
+// success; 'out' is untouched if parsing fails.
 template <typename T>
 bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
                bool allow_suffixes = false) {
@@ -72,9 +72,9 @@
 }
 
 // Parses the signed decimal or hexadecimal integer in the string 's' and sets
-// 'out' to that value. Optionally allows the caller to define a 'min' and 'max'
-// beyond which otherwise valid values will be rejected. Returns boolean
-// success; 'out' is untouched if parsing fails.
+// 'out' to that value if it is specified. Optionally allows the caller to define
+// a 'min' and 'max' beyond which otherwise valid values will be rejected. Returns
+// boolean success; 'out' is untouched if parsing fails.
 template <typename T>
 bool ParseInt(const char* s, T* out,
               T min = std::numeric_limits<T>::min(),
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
old mode 100755
new mode 100644
diff --git a/base/parsedouble_test.cpp b/base/parsedouble_test.cpp
index 8734c42..ec3c10c 100644
--- a/base/parsedouble_test.cpp
+++ b/base/parsedouble_test.cpp
@@ -18,7 +18,7 @@
 
 #include <gtest/gtest.h>
 
-TEST(parsedouble, smoke) {
+TEST(parsedouble, double_smoke) {
   double d;
   ASSERT_FALSE(android::base::ParseDouble("", &d));
   ASSERT_FALSE(android::base::ParseDouble("x", &d));
@@ -35,4 +35,33 @@
   ASSERT_FALSE(android::base::ParseDouble("3.0", &d, -1.0, 2.0));
   ASSERT_TRUE(android::base::ParseDouble("1.0", &d, 0.0, 2.0));
   ASSERT_DOUBLE_EQ(1.0, d);
+
+  ASSERT_FALSE(android::base::ParseDouble("123.4x", nullptr));
+  ASSERT_TRUE(android::base::ParseDouble("-123.4", nullptr));
+  ASSERT_FALSE(android::base::ParseDouble("3.0", nullptr, -1.0, 2.0));
+  ASSERT_TRUE(android::base::ParseDouble("1.0", nullptr, 0.0, 2.0));
+}
+
+TEST(parsedouble, float_smoke) {
+  float f;
+  ASSERT_FALSE(android::base::ParseFloat("", &f));
+  ASSERT_FALSE(android::base::ParseFloat("x", &f));
+  ASSERT_FALSE(android::base::ParseFloat("123.4x", &f));
+
+  ASSERT_TRUE(android::base::ParseFloat("123.4", &f));
+  ASSERT_FLOAT_EQ(123.4, f);
+  ASSERT_TRUE(android::base::ParseFloat("-123.4", &f));
+  ASSERT_FLOAT_EQ(-123.4, f);
+
+  ASSERT_TRUE(android::base::ParseFloat("0", &f, 0.0));
+  ASSERT_FLOAT_EQ(0.0, f);
+  ASSERT_FALSE(android::base::ParseFloat("0", &f, 1e-9));
+  ASSERT_FALSE(android::base::ParseFloat("3.0", &f, -1.0, 2.0));
+  ASSERT_TRUE(android::base::ParseFloat("1.0", &f, 0.0, 2.0));
+  ASSERT_FLOAT_EQ(1.0, f);
+
+  ASSERT_FALSE(android::base::ParseFloat("123.4x", nullptr));
+  ASSERT_TRUE(android::base::ParseFloat("-123.4", nullptr));
+  ASSERT_FALSE(android::base::ParseFloat("3.0", nullptr, -1.0, 2.0));
+  ASSERT_TRUE(android::base::ParseFloat("1.0", nullptr, 0.0, 2.0));
 }
diff --git a/base/unique_fd_test.cpp b/base/unique_fd_test.cpp
deleted file mode 100644
index 3fdf12a..0000000
--- a/base/unique_fd_test.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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 "android-base/unique_fd.h"
-
-#include <gtest/gtest.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-using android::base::unique_fd;
-
-TEST(unique_fd, unowned_close) {
-#if defined(__BIONIC__)
-  unique_fd fd(open("/dev/null", O_RDONLY));
-  EXPECT_DEATH(close(fd.get()), "incorrect tag");
-#endif
-}
-
-TEST(unique_fd, untag_on_release) {
-  unique_fd fd(open("/dev/null", O_RDONLY));
-  close(fd.release());
-}
-
-TEST(unique_fd, move) {
-  unique_fd fd(open("/dev/null", O_RDONLY));
-  unique_fd fd_moved = std::move(fd);
-  ASSERT_EQ(-1, fd.get());
-  ASSERT_GT(fd_moved.get(), -1);
-}
-
-TEST(unique_fd, unowned_close_after_move) {
-#if defined(__BIONIC__)
-  unique_fd fd(open("/dev/null", O_RDONLY));
-  unique_fd fd_moved = std::move(fd);
-  ASSERT_EQ(-1, fd.get());
-  ASSERT_GT(fd_moved.get(), -1);
-  EXPECT_DEATH(close(fd_moved.get()), "incorrect tag");
-#endif
-}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 8fc2171..5e2d171 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,6 +63,9 @@
     name: "bootstat",
     defaults: ["bootstat_defaults"],
     static_libs: ["libbootstat"],
+    shared_libs: [
+        "libstatslog"
+    ],
     init_rc: ["bootstat.rc"],
     product_variables: {
         pdk: {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 4d9f1ac..c17e00f 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -45,6 +45,7 @@
 #include <cutils/android_reboot.h>
 #include <cutils/properties.h>
 #include <metricslogger/metrics_logger.h>
+#include <statslog.h>
 
 #include "boot_event_record_store.h"
 
@@ -185,7 +186,7 @@
     {"wdog_bark", 42},
     {"wdog_bite", 43},
     {"wdog_reset", 44},
-    {"shutdown,", 45},  // Trailing comma is intentional.
+    {"shutdown,", 45},  // Trailing comma is intentional. Do NOT use.
     {"shutdown,userrequested", 46},
     {"reboot,bootloader", 47},
     {"reboot,cold", 48},
@@ -261,8 +262,8 @@
     {"oem_rpm_undef_error", 116},
     {"oem_crash_on_the_lk", 117},
     {"oem_rpm_reset", 118},
-    {"oem_lpass_cfg", 119},
-    {"oem_xpu_ns_error", 120},
+    {"REUSE1", 119},  // Former dupe, can be re-used
+    {"REUSE2", 120},  // Former dupe, can be re-used
     {"factory_cable", 121},
     {"oem_ar6320_failed_to_powerup", 122},
     {"watchdog_rpm_bite", 123},
@@ -1026,6 +1027,16 @@
   return timings;
 }
 
+// Returns the total bootloader boot time from the ro.boot.boottime system property.
+int32_t GetBootloaderTime(const BootloaderTimingMap& bootloader_timings) {
+  int32_t total_time = 0;
+  for (const auto& timing : bootloader_timings) {
+    total_time += timing.second;
+  }
+
+  return total_time;
+}
+
 // Parses and records the set of bootloader stages and associated boot times
 // from the ro.boot.boottime system property.
 void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
@@ -1039,11 +1050,10 @@
   boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
 }
 
-// Records the closest estimation to the absolute device boot time, i.e.,
+// Returns the closest estimation to the absolute device boot time, i.e.,
 // from power on to boot_complete, including bootloader times.
-void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
-                            const BootloaderTimingMap& bootloader_timings,
-                            std::chrono::milliseconds uptime) {
+std::chrono::milliseconds GetAbsoluteBootTime(const BootloaderTimingMap& bootloader_timings,
+                                              std::chrono::milliseconds uptime) {
   int32_t bootloader_time_ms = 0;
 
   for (const auto& timing : bootloader_timings) {
@@ -1053,23 +1063,36 @@
   }
 
   auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
-  auto absolute_total =
-      std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
-  boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
+  return bootloader_duration + uptime;
 }
 
-// Gets the boot time offset. This is useful when Android is running in a
-// container, because the boot_clock is not reset when Android reboots.
-std::chrono::nanoseconds GetBootTimeOffset() {
-  static const int64_t boottime_offset =
-      android::base::GetIntProperty<int64_t>("ro.boot.boottime_offset", 0);
-  return std::chrono::nanoseconds(boottime_offset);
+// Records the closest estimation to the absolute device boot time in seconds.
+// i.e. from power on to boot_complete, including bootloader times.
+void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
+                            std::chrono::milliseconds absolute_total) {
+  auto absolute_total_sec = std::chrono::duration_cast<std::chrono::seconds>(absolute_total);
+  boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total_sec.count());
 }
 
-// Returns the current uptime, accounting for any offset in the CLOCK_BOOTTIME
-// clock.
-android::base::boot_clock::duration GetUptime() {
-  return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
+// Logs the total boot time and reason to statsd.
+void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
+                         std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
+                         double time_since_last_boot_sec) {
+  const std::string reason(GetProperty(bootloader_reboot_reason_property));
+
+  if (reason.empty()) {
+    android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, "<EMPTY>", "<EMPTY>",
+                               end_time.count(), total_duration.count(),
+                               (int64_t)bootloader_duration_ms,
+                               (int64_t)time_since_last_boot_sec * 1000);
+    return;
+  }
+
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
+  android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
+                             system_reason.c_str(), end_time.count(), total_duration.count(),
+                             (int64_t)bootloader_duration_ms,
+                             (int64_t)time_since_last_boot_sec * 1000);
 }
 
 void SetSystemBootReason() {
@@ -1088,6 +1111,20 @@
   SetProperty(last_reboot_reason_property, "");
 }
 
+// Gets the boot time offset. This is useful when Android is running in a
+// container, because the boot_clock is not reset when Android reboots.
+std::chrono::nanoseconds GetBootTimeOffset() {
+  static const int64_t boottime_offset =
+      android::base::GetIntProperty<int64_t>("ro.boot.boottime_offset", 0);
+  return std::chrono::nanoseconds(boottime_offset);
+}
+
+// Returns the current uptime, accounting for any offset in the CLOCK_BOOTTIME
+// clock.
+android::base::boot_clock::duration GetUptime() {
+  return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
+}
+
 // Records several metrics related to the time it takes to boot the device,
 // including disambiguating boot time on encrypted or non-encrypted devices.
 void RecordBootComplete() {
@@ -1097,10 +1134,11 @@
   auto uptime_ns = GetUptime();
   auto uptime_s = std::chrono::duration_cast<std::chrono::seconds>(uptime_ns);
   time_t current_time_utc = time(nullptr);
+  time_t time_since_last_boot = 0;
 
   if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
     time_t last_boot_time_utc = record.second;
-    time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
+    time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
     boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot);
   }
 
@@ -1140,10 +1178,18 @@
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
 
   const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
+  int32_t bootloader_boot_duration = GetBootloaderTime(bootloader_timings);
   RecordBootloaderTimings(&boot_event_store, bootloader_timings);
 
   auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(uptime_ns);
-  RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
+  auto absolute_boot_time = GetAbsoluteBootTime(bootloader_timings, uptime_ms);
+  RecordAbsoluteBootTime(&boot_event_store, absolute_boot_time);
+
+  auto boot_end_time_point = std::chrono::system_clock::now().time_since_epoch();
+  auto boot_end_time = std::chrono::duration_cast<std::chrono::milliseconds>(boot_end_time_point);
+
+  LogBootInfoToStatsd(boot_end_time, absolute_boot_time, bootloader_boot_duration,
+                      time_since_last_boot);
 }
 
 // Records the boot_reason metric by querying the ro.boot.bootreason system
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 1b20157..03b3287 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -43,7 +43,7 @@
     export_include_dirs: ["tombstoned/include"],
 }
 
-// Utility library to tombstoned and get an output fd.
+// Utility library to talk to tombstoned and get an output fd.
 cc_library_static {
     name: "libtombstoned_client_static",
     defaults: ["debuggerd_defaults"],
@@ -166,6 +166,9 @@
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
+    // Needed for private/bionic_fdsan.h
+    include_dirs: ["bionic/libc"],
+
     static_libs: [
         "libbacktrace",
         "libunwindstack",
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 40cf6c3..93f7572 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -249,24 +249,48 @@
 }
 
 static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
-                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_address) {
+                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
+                          uintptr_t* fdsan_table_address) {
   std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
+  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
   ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
   if (rc == -1) {
     PLOG(FATAL) << "failed to read target ucontext";
-  } else if (rc != sizeof(CrashInfo)) {
-    LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
-               << sizeof(CrashInfo);
+  } else {
+    ssize_t expected_size = 0;
+    switch (crash_info->header.version) {
+      case 1:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV1);
+        break;
+
+      case 2:
+        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+        break;
+
+      default:
+        LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
+        break;
+    };
+
+    if (rc != expected_size) {
+      LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
+                 << expected_size;
+    }
   }
 
-  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
-  if (crash_info->version != 1) {
-    LOG(FATAL) << "version mismatch, expected 1, received " << crash_info->version;
-  }
+  *fdsan_table_address = 0;
+  switch (crash_info->header.version) {
+    case 2:
+      *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+    case 1:
+      *abort_msg_address = crash_info->data.v1.abort_msg_address;
+      *siginfo = crash_info->data.v1.siginfo;
+      regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->data.v1.ucontext));
+      break;
 
-  *siginfo = crash_info->siginfo;
-  regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->ucontext));
-  *abort_address = crash_info->abort_msg_address;
+    default:
+      __builtin_unreachable();
+  }
 }
 
 // Wait for a process to clone and return the child's pid.
@@ -369,7 +393,8 @@
   ATRACE_NAME("after reparent");
   pid_t pseudothread_tid;
   DebuggerdDumpType dump_type;
-  uintptr_t abort_address = 0;
+  uintptr_t abort_msg_address = 0;
+  uintptr_t fdsan_table_address = 0;
 
   Initialize(argv);
   ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -387,7 +412,7 @@
   OpenFilesList open_files;
   {
     ATRACE_NAME("open files");
-    populate_open_files_list(g_target_thread, &open_files);
+    populate_open_files_list(&open_files, g_target_thread);
   }
 
   // In order to reduce the duration that we pause the process for, we ptrace
@@ -429,7 +454,8 @@
 
       if (thread == g_target_thread) {
         // Read the thread's registers along with the rest of the crash info out of the pipe.
-        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_address);
+        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
+                      &fdsan_table_address);
         info.siginfo = &siginfo;
         info.signo = info.siginfo->si_signo;
       } else {
@@ -504,8 +530,8 @@
     g_output_fd = std::move(devnull);
   }
 
-  LOG(INFO) << "performing dump of process " << target_process << " (target tid = " << g_target_thread
-            << ")";
+  LOG(INFO) << "performing dump of process " << target_process
+            << " (target tid = " << g_target_thread << ")";
 
   int signo = siginfo.si_signo;
   bool fatal_signal = signo != DEBUGGER_SIGNAL;
@@ -541,9 +567,16 @@
     ATRACE_NAME("dump_backtrace");
     dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
   } else {
-    ATRACE_NAME("engrave_tombstone");
-    engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
-                      g_target_thread, abort_address, &open_files, &amfd_data);
+    {
+      ATRACE_NAME("fdsan table dump");
+      populate_fdsan_table(&open_files, process_memory, fdsan_table_address);
+    }
+
+    {
+      ATRACE_NAME("engrave_tombstone");
+      engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
+                        g_target_thread, abort_msg_address, &open_files, &amfd_data);
+    }
   }
 
   if (fatal_signal) {
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 615fb46..91e6f71 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -108,6 +108,7 @@
   int saved_errno_;
 };
 
+extern "C" void* android_fdsan_get_fd_table();
 extern "C" void debuggerd_fallback_handler(siginfo_t*, ucontext_t*, void*);
 
 static debuggerd_callbacks_t g_callbacks;
@@ -286,6 +287,7 @@
   siginfo_t* siginfo;
   void* ucontext;
   uintptr_t abort_msg;
+  uintptr_t fdsan_table;
 };
 
 // Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -330,23 +332,23 @@
   }
 
   // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
-  uint32_t version = 1;
-  constexpr size_t expected =
-      sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(uintptr_t);
+  uint32_t version = 2;
+  constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
 
   errno = 0;
   if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
-    fatal_errno("failed to set pipe bufer size");
+    fatal_errno("failed to set pipe buffer size");
   }
 
-  struct iovec iovs[4] = {
+  struct iovec iovs[5] = {
       {.iov_base = &version, .iov_len = sizeof(version)},
       {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
       {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
       {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
+      {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
   };
 
-  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4));
+  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 5));
   if (rc == -1) {
     fatal_errno("failed to write crash info");
   } else if (rc != expected) {
@@ -504,6 +506,7 @@
       .siginfo = info,
       .ucontext = context,
       .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
+      .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
   };
 
   // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
index 4727ca4..d47f2dd 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
@@ -14,23 +14,31 @@
  * limitations under the License.
  */
 
-#ifndef _DEBUGGERD_OPEN_FILES_LIST_H
-#define _DEBUGGERD_OPEN_FILES_LIST_H
+#pragma once
 
+#include <stdint.h>
 #include <sys/types.h>
 
+#include <map>
+#include <optional>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "utility.h"
 
-typedef std::vector<std::pair<int, std::string>> OpenFilesList;
+struct FDInfo {
+  std::optional<std::string> path;
+  std::optional<uint64_t> fdsan_owner;
+};
 
-/* Populates the given list with open files for the given process. */
-void populate_open_files_list(pid_t pid, OpenFilesList* list);
+using OpenFilesList = std::map<int, FDInfo>;
 
-/* Dumps the open files list to the log. */
+// Populates the given list with open files for the given process.
+void populate_open_files_list(OpenFilesList* list, pid_t pid);
+
+// Populates the given list with the target process's fdsan table.
+void populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,
+                          uint64_t fdsan_table_address);
+
+// Dumps the open files list to the log.
 void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix);
-
-#endif // _DEBUGGERD_OPEN_FILES_LIST_H
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index b12703e..1fdf236 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -32,10 +32,12 @@
 
 #include <android-base/file.h>
 #include <log/log.h>
+#include <unwindstack/Memory.h>
 
 #include "libdebuggerd/utility.h"
+#include "private/bionic_fdsan.h"
 
-void populate_open_files_list(pid_t pid, OpenFilesList* list) {
+void populate_open_files_list(OpenFilesList* list, pid_t pid) {
   std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
   std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fd_dir_name.c_str()), closedir);
   if (dir == nullptr) {
@@ -53,17 +55,84 @@
     std::string path = fd_dir_name + "/" + std::string(de->d_name);
     std::string target;
     if (android::base::Readlink(path, &target)) {
-      list->emplace_back(fd, target);
+      (*list)[fd].path = target;
     } else {
+      (*list)[fd].path = "???";
       ALOGE("failed to readlink %s: %s", path.c_str(), strerror(errno));
-      list->emplace_back(fd, "???");
     }
   }
 }
 
+void populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,
+                          uint64_t fdsan_table_address) {
+  constexpr size_t inline_fds = sizeof(FdTable::entries) / sizeof(*FdTable::entries);
+  static_assert(inline_fds == 128);
+  size_t entry_offset = offsetof(FdTable, entries);
+  for (size_t i = 0; i < inline_fds; ++i) {
+    uint64_t address = fdsan_table_address + entry_offset + sizeof(FdEntry) * i;
+    FdEntry entry;
+    if (!memory->Read(address, &entry, sizeof(entry))) {
+      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();
+    }
+  }
+
+  size_t overflow_offset = offsetof(FdTable, overflow);
+  uintptr_t overflow = 0;
+  if (!memory->Read(fdsan_table_address + overflow_offset, &overflow, sizeof(overflow))) {
+    ALOGE("failed to read fdsan table overflow pointer: %s", strerror(errno));
+    return;
+  }
+
+  if (!overflow) {
+    return;
+  }
+
+  size_t overflow_length;
+  if (!memory->Read(overflow, &overflow_length, sizeof(overflow_length))) {
+    ALOGE("failed to read fdsan overflow table length: %s", strerror(errno));
+    return;
+  }
+
+  if (overflow_length > 131072) {
+    ALOGE("unreasonable large fdsan overflow table size %zu, bailing out", overflow_length);
+    return;
+  }
+
+  for (size_t i = 0; i < overflow_length; ++i) {
+    int fd = i + inline_fds;
+    uint64_t address = overflow + offsetof(FdTableOverflow, entries) + i * sizeof(FdEntry);
+    FdEntry entry;
+    if (!memory->Read(address, &entry, sizeof(entry))) {
+      ALOGE("failed to read fdsan overflow entry for fd %d: %s", fd, strerror(errno));
+      return;
+    }
+    if (entry.close_tag) {
+      (*list)[fd].fdsan_owner = entry.close_tag;
+    }
+  }
+  return;
+}
+
 void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) {
-  for (auto& file : files) {
-    _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str());
+  for (auto& [fd, entry] : files) {
+    const std::optional<std::string>& path = entry.path;
+    const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;
+    if (path && fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %#" PRIx64 ")\n", prefix, fd,
+           path->c_str(), *fdsan_owner);
+    } else if (path && !fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (unowned)\n", prefix, fd, path->c_str());
+    } else if (!path && fdsan_owner) {
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: <MISSING> (owned by %#" PRIx64 ")\n", prefix, fd,
+           *fdsan_owner);
+    } else {
+      ALOGE("OpenFilesList contains an entry (fd %d) with no path or owner", fd);
+    }
   }
 }
 
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index acac72c..d7036fd 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -34,13 +34,13 @@
 
   // Get the list of open files for this process.
   OpenFilesList list;
-  populate_open_files_list(getpid(), &list);
+  populate_open_files_list(&list, getpid());
 
   // Verify our open file is in the list.
   bool found = false;
-  for (auto&  file : list) {
+  for (auto& file : list) {
     if (file.first == tf.fd) {
-      EXPECT_EQ(file.second, std::string(tf.path));
+      EXPECT_EQ(file.second.path.value_or(""), std::string(tf.path));
       found = true;
       break;
     }
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 6903b0e..bfd0fbb 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -81,9 +81,24 @@
 };
 
 // Sent from handler to crash_dump via pipe.
-struct __attribute__((__packed__)) CrashInfo {
-  uint32_t version;  // must be 1.
+struct __attribute__((__packed__)) CrashInfoHeader {
+  uint32_t version;
+};
+
+struct __attribute__((__packed__)) CrashInfoDataV1 {
   siginfo_t siginfo;
   ucontext_t ucontext;
   uintptr_t abort_msg_address;
 };
+
+struct __attribute__((__packed__)) CrashInfoDataV2 : public CrashInfoDataV1 {
+  uintptr_t fdsan_table_address;
+};
+
+struct __attribute__((__packed__)) CrashInfo {
+  CrashInfoHeader header;
+  union {
+    CrashInfoDataV1 v1;
+    CrashInfoDataV2 v2;
+  } data;
+};
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 2b4a954..3414d53 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -1,3 +1,17 @@
+// 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.
+
 cc_library_host_static {
     name: "libfastboot2",
 
@@ -54,3 +68,57 @@
     export_include_dirs: ["."],
 
 }
+
+cc_defaults {
+    name: "fastboot_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wvla",
+    ],
+    rtti: true,
+
+    clang_cflags: [
+        "-Wthread-safety",
+    ],
+}
+
+cc_binary {
+    name: "fastbootd",
+    defaults: ["fastboot_defaults"],
+
+    recovery: true,
+
+    srcs: [
+        "device/commands.cpp",
+        "device/fastboot_device.cpp",
+        "device/flashing.cpp",
+        "device/main.cpp",
+        "device/usb_client.cpp",
+        "device/utility.cpp",
+        "device/variables.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.boot@1.0",
+        "libadbd",
+        "libasyncio",
+        "libbase",
+        "libbootloader_message",
+        "libcutils",
+        "libext2_uuid",
+        "libext4_utils",
+        "libfs_mgr",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "liblp",
+        "libsparse",
+        "libutils",
+    ],
+
+    cpp_std: "c++17",
+}
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 5e7e955..43daab0 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -27,7 +27,12 @@
 #define FB_CMD_REBOOT "reboot"
 #define FB_CMD_SHUTDOWN "shutdown"
 #define FB_CMD_REBOOT_BOOTLOADER "reboot-bootloader"
+#define FB_CMD_REBOOT_RECOVERY "reboot-recovery"
+#define FB_CMD_REBOOT_FASTBOOT "reboot-fastboot"
 #define FB_CMD_POWERDOWN "powerdown"
+#define FB_CMD_CREATE_PARTITION "create-logical-partition"
+#define FB_CMD_DELETE_PARTITION "delete-logical-partition"
+#define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
@@ -49,3 +54,7 @@
 #define FB_VAR_HAS_SLOT "has-slot"
 #define FB_VAR_SLOT_COUNT "slot-count"
 #define FB_VAR_PARTITION_SIZE "partition-size"
+#define FB_VAR_SLOT_SUCCESSFUL "slot-successful"
+#define FB_VAR_SLOT_UNBOOTABLE "slot-unbootable"
+#define FB_VAR_IS_LOGICAL "is-logical"
+#define FB_VAR_IS_USERSPACE "is-userspace"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
new file mode 100644
index 0000000..690bfa8
--- /dev/null
+++ b/fastboot/device/commands.cpp
@@ -0,0 +1,338 @@
+/*
+ * 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 "commands.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
+#include <ext4_utils/wipe.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <uuid/uuid.h>
+
+#include "constants.h"
+#include "fastboot_device.h"
+#include "flashing.h"
+#include "utility.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_0::CommandResult;
+using ::android::hardware::boot::V1_0::Slot;
+using namespace android::fs_mgr;
+
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    using VariableHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
+    const std::unordered_map<std::string, VariableHandler> kVariableMap = {
+            {FB_VAR_VERSION, GetVersion},
+            {FB_VAR_VERSION_BOOTLOADER, GetBootloaderVersion},
+            {FB_VAR_VERSION_BASEBAND, GetBasebandVersion},
+            {FB_VAR_PRODUCT, GetProduct},
+            {FB_VAR_SERIALNO, GetSerial},
+            {FB_VAR_SECURE, GetSecure},
+            {FB_VAR_UNLOCKED, GetUnlocked},
+            {FB_VAR_MAX_DOWNLOAD_SIZE, GetMaxDownloadSize},
+            {FB_VAR_CURRENT_SLOT, ::GetCurrentSlot},
+            {FB_VAR_SLOT_COUNT, GetSlotCount},
+            {FB_VAR_HAS_SLOT, GetHasSlot},
+            {FB_VAR_SLOT_SUCCESSFUL, GetSlotSuccessful},
+            {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable},
+            {FB_VAR_PARTITION_SIZE, GetPartitionSize},
+            {FB_VAR_IS_LOGICAL, GetPartitionIsLogical},
+            {FB_VAR_IS_USERSPACE, GetIsUserspace}};
+
+    // args[0] is command name, args[1] is variable.
+    auto found_variable = kVariableMap.find(args[1]);
+    if (found_variable == kVariableMap.end()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unknown variable");
+    }
+
+    std::vector<std::string> getvar_args(args.begin() + 2, args.end());
+    return found_variable->second(device, getvar_args);
+}
+
+bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
+    }
+    PartitionHandle handle;
+    if (!OpenPartition(device, args[1], &handle)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
+    }
+    if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
+        return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
+    }
+    return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
+}
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
+    }
+    // arg[0] is the command name, arg[1] contains size of data to be downloaded
+    unsigned int size;
+    if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
+    }
+    device->download_data().resize(size);
+    if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
+        return false;
+    }
+
+    if (device->HandleData(true, &device->download_data())) {
+        return device->WriteStatus(FastbootResult::OKAY, "");
+    }
+
+    PLOG(ERROR) << "Couldn't download data";
+    return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
+}
+
+bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
+    }
+    int ret = Flash(device, args[1]);
+    if (ret < 0) {
+        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
+}
+
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
+    }
+
+    // Slot suffix needs to be between 'a' and 'z'.
+    Slot slot;
+    if (!GetSlotNumber(args[1], &slot)) {
+        return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
+    }
+
+    // Non-A/B devices will not have a boot control HAL.
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Cannot set slot: boot control HAL absent");
+    }
+    if (slot >= boot_control_hal->getNumberSlots()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
+    }
+    CommandResult ret;
+    auto cb = [&ret](CommandResult result) { ret = result; };
+    auto result = boot_control_hal->setActiveBootSlot(slot, cb);
+    if (result.isOk() && ret.success) return device->WriteStatus(FastbootResult::OKAY, "");
+    return device->WriteStatus(FastbootResult::FAIL, "Unable to set slot");
+}
+
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Shutting down");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,from_fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting bootloader");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting fastboot");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return result;
+}
+
+static bool EnterRecovery() {
+    const char msg_switch_to_recovery = 'r';
+
+    android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+    if (sock < 0) {
+        PLOG(ERROR) << "Couldn't create sock";
+        return false;
+    }
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        PLOG(ERROR) << "Couldn't connect to recovery";
+        return false;
+    }
+    // Switch to recovery will not update the boot reason since it does not
+    // require a reboot.
+    auto ret = write(sock, &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
+    if (ret != sizeof(msg_switch_to_recovery)) {
+        PLOG(ERROR) << "Couldn't write message to switch to recovery";
+        return false;
+    }
+
+    return true;
+}
+
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto status = true;
+    if (EnterRecovery()) {
+        status = device->WriteStatus(FastbootResult::OKAY, "Rebooting to recovery");
+    } else {
+        status = device->WriteStatus(FastbootResult::FAIL, "Unable to reboot to recovery");
+    }
+    device->CloseDevice();
+    TEMP_FAILURE_RETRY(pause());
+    return status;
+}
+
+// Helper class for opening a handle to a MetadataBuilder and writing the new
+// partition table to the same place it was read.
+class PartitionBuilder {
+  public:
+    explicit PartitionBuilder(FastbootDevice* device);
+
+    bool Write();
+    bool Valid() const { return !!builder_; }
+    MetadataBuilder* operator->() const { return builder_.get(); }
+
+  private:
+    std::string super_device_;
+    uint32_t slot_number_;
+    std::unique_ptr<MetadataBuilder> builder_;
+};
+
+PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
+    auto super_device = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    if (!super_device) {
+        return;
+    }
+    super_device_ = *super_device;
+
+    std::string slot = device->GetCurrentSlot();
+    slot_number_ = SlotNumberForSlotSuffix(slot);
+    builder_ = MetadataBuilder::New(super_device_, slot_number_);
+}
+
+bool PartitionBuilder::Write() {
+    std::unique_ptr<LpMetadata> metadata = builder_->Export();
+    if (!metadata) {
+        return false;
+    }
+    return UpdatePartitionTable(super_device_, *metadata.get(), slot_number_);
+}
+
+bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 3) {
+        return device->WriteFail("Invalid partition name and size");
+    }
+
+    uint64_t partition_size;
+    std::string partition_name = args[1];
+    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
+        return device->WriteFail("Invalid partition size");
+    }
+
+    PartitionBuilder builder(device);
+    if (!builder.Valid()) {
+        return device->WriteFail("Could not open super partition");
+    }
+    // TODO(112433293) Disallow if the name is in the physical table as well.
+    if (builder->FindPartition(partition_name)) {
+        return device->WriteFail("Partition already exists");
+    }
+
+    // Make a random UUID, since they're not currently used.
+    uuid_t uuid;
+    char uuid_str[37];
+    uuid_generate_random(uuid);
+    uuid_unparse(uuid, uuid_str);
+
+    Partition* partition = builder->AddPartition(partition_name, uuid_str, 0);
+    if (!partition) {
+        return device->WriteFail("Failed to add partition");
+    }
+    if (!builder->ResizePartition(partition, partition_size)) {
+        builder->RemovePartition(partition_name);
+        return device->WriteFail("Not enough space for partition");
+    }
+    if (!builder.Write()) {
+        return device->WriteFail("Failed to write partition table");
+    }
+    return device->WriteOkay("Partition created");
+}
+
+bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteFail("Invalid partition name and size");
+    }
+
+    PartitionBuilder builder(device);
+    if (!builder.Valid()) {
+        return device->WriteFail("Could not open super partition");
+    }
+    builder->RemovePartition(args[1]);
+    if (!builder.Write()) {
+        return device->WriteFail("Failed to write partition table");
+    }
+    return device->WriteOkay("Partition deleted");
+}
+
+bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 3) {
+        return device->WriteFail("Invalid partition name and size");
+    }
+
+    uint64_t partition_size;
+    std::string partition_name = args[1];
+    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
+        return device->WriteFail("Invalid partition size");
+    }
+
+    PartitionBuilder builder(device);
+    if (!builder.Valid()) {
+        return device->WriteFail("Could not open super partition");
+    }
+
+    Partition* partition = builder->FindPartition(partition_name);
+    if (!partition) {
+        return device->WriteFail("Partition does not exist");
+    }
+    if (!builder->ResizePartition(partition, partition_size)) {
+        return device->WriteFail("Not enough space to resize partition");
+    }
+    if (!builder.Write()) {
+        return device->WriteFail("Failed to write partition table");
+    }
+    return device->WriteOkay("Partition resized");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
new file mode 100644
index 0000000..f67df91
--- /dev/null
+++ b/fastboot/device/commands.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <functional>
+#include <string>
+#include <vector>
+
+class FastbootDevice;
+
+enum class FastbootResult {
+    OKAY,
+    FAIL,
+    INFO,
+    DATA,
+};
+
+// Execute a command with the given arguments (possibly empty).
+using CommandHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
+
+bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
new file mode 100644
index 0000000..c306e67
--- /dev/null
+++ b/fastboot/device/fastboot_device.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 "fastboot_device.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
+
+#include <algorithm>
+
+#include "constants.h"
+#include "flashing.h"
+#include "usb_client.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::boot::V1_0::IBootControl;
+using ::android::hardware::boot::V1_0::Slot;
+namespace sph = std::placeholders;
+
+FastbootDevice::FastbootDevice()
+    : kCommandMap({
+              {FB_CMD_SET_ACTIVE, SetActiveHandler},
+              {FB_CMD_DOWNLOAD, DownloadHandler},
+              {FB_CMD_GETVAR, GetVarHandler},
+              {FB_CMD_SHUTDOWN, ShutDownHandler},
+              {FB_CMD_REBOOT, RebootHandler},
+              {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler},
+              {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler},
+              {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler},
+              {FB_CMD_ERASE, EraseHandler},
+              {FB_CMD_FLASH, FlashHandler},
+              {FB_CMD_CREATE_PARTITION, CreatePartitionHandler},
+              {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
+              {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
+      }),
+      transport_(std::make_unique<ClientUsbTransport>()),
+      boot_control_hal_(IBootControl::getService()) {}
+
+FastbootDevice::~FastbootDevice() {
+    CloseDevice();
+}
+
+void FastbootDevice::CloseDevice() {
+    transport_->Close();
+}
+
+std::string FastbootDevice::GetCurrentSlot() {
+    // Non-A/B devices must not have boot control HALs.
+    if (!boot_control_hal_) {
+        return "";
+    }
+    std::string suffix;
+    auto cb = [&suffix](hidl_string s) { suffix = s; };
+    boot_control_hal_->getSuffix(boot_control_hal_->getCurrentSlot(), cb);
+    return suffix;
+}
+
+bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
+    constexpr size_t kResponseReasonSize = 4;
+    constexpr size_t kNumResponseTypes = 4;  // "FAIL", "OKAY", "INFO", "DATA"
+
+    char buf[FB_RESPONSE_SZ];
+    constexpr size_t kMaxMessageSize = sizeof(buf) - kResponseReasonSize;
+    size_t msg_len = std::min(kMaxMessageSize, message.size());
+
+    constexpr const char* kResultStrings[kNumResponseTypes] = {RESPONSE_OKAY, RESPONSE_FAIL,
+                                                               RESPONSE_INFO, RESPONSE_DATA};
+
+    if (static_cast<size_t>(result) >= kNumResponseTypes) {
+        return false;
+    }
+
+    memcpy(buf, kResultStrings[static_cast<size_t>(result)], kResponseReasonSize);
+    memcpy(buf + kResponseReasonSize, message.c_str(), msg_len);
+
+    size_t response_len = kResponseReasonSize + msg_len;
+    auto write_ret = this->get_transport()->Write(buf, response_len);
+    if (write_ret != static_cast<ssize_t>(response_len)) {
+        PLOG(ERROR) << "Failed to write " << message;
+        return false;
+    }
+
+    return true;
+}
+
+bool FastbootDevice::HandleData(bool read, std::vector<char>* data) {
+    auto read_write_data_size = read ? this->get_transport()->Read(data->data(), data->size())
+                                     : this->get_transport()->Write(data->data(), data->size());
+    if (read_write_data_size == -1 || static_cast<size_t>(read_write_data_size) != data->size()) {
+        return false;
+    }
+    return true;
+}
+
+void FastbootDevice::ExecuteCommands() {
+    char command[FB_RESPONSE_SZ + 1];
+    for (;;) {
+        auto bytes_read = transport_->Read(command, FB_RESPONSE_SZ);
+        if (bytes_read == -1) {
+            PLOG(ERROR) << "Couldn't read command";
+            return;
+        }
+        command[bytes_read] = '\0';
+
+        LOG(INFO) << "Fastboot command: " << command;
+        auto args = android::base::Split(command, ":");
+        auto found_command = kCommandMap.find(args[0]);
+        if (found_command == kCommandMap.end()) {
+            WriteStatus(FastbootResult::FAIL, "Unrecognized command");
+            continue;
+        }
+        if (!found_command->second(this, args)) {
+            return;
+        }
+    }
+}
+
+bool FastbootDevice::WriteOkay(const std::string& message) {
+    return WriteStatus(FastbootResult::OKAY, message);
+}
+
+bool FastbootDevice::WriteFail(const std::string& message) {
+    return WriteStatus(FastbootResult::FAIL, message);
+}
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
new file mode 100644
index 0000000..addc2ef
--- /dev/null
+++ b/fastboot/device/fastboot_device.h
@@ -0,0 +1,58 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <android/hardware/boot/1.0/IBootControl.h>
+
+#include "commands.h"
+#include "transport.h"
+#include "variables.h"
+
+class FastbootDevice {
+  public:
+    FastbootDevice();
+    ~FastbootDevice();
+
+    void CloseDevice();
+    void ExecuteCommands();
+    bool WriteStatus(FastbootResult result, const std::string& message);
+    bool HandleData(bool read, std::vector<char>* data);
+    std::string GetCurrentSlot();
+
+    // Shortcuts for writing OKAY and FAIL status results.
+    bool WriteOkay(const std::string& message);
+    bool WriteFail(const std::string& message);
+
+    std::vector<char>& download_data() { return download_data_; }
+    Transport* get_transport() { return transport_.get(); }
+    android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
+        return boot_control_hal_;
+    }
+
+  private:
+    const std::unordered_map<std::string, CommandHandler> kCommandMap;
+
+    std::unique_ptr<Transport> transport_;
+    android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+    std::vector<char> download_data_;
+};
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
new file mode 100644
index 0000000..b5ed170
--- /dev/null
+++ b/fastboot/device/flashing.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "flashing.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <ext4_utils/ext4_utils.h>
+#include <sparse/sparse.h>
+
+#include "fastboot_device.h"
+#include "utility.h"
+
+namespace {
+
+constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a;
+
+}  // namespace
+
+int FlashRawDataChunk(int fd, const char* data, size_t len) {
+    size_t ret = 0;
+    while (ret < len) {
+        int this_len = std::min(static_cast<size_t>(1048576UL * 8), len - ret);
+        int this_ret = write(fd, data, this_len);
+        if (this_ret < 0) {
+            PLOG(ERROR) << "Failed to flash data of len " << len;
+            return -1;
+        }
+        data += this_ret;
+        ret += this_ret;
+    }
+    return 0;
+}
+
+int FlashRawData(int fd, const std::vector<char>& downloaded_data) {
+    int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size());
+    if (ret < 0) {
+        return -errno;
+    }
+    return ret;
+}
+
+int WriteCallback(void* priv, const void* data, size_t len) {
+    int fd = reinterpret_cast<long long>(priv);
+    if (!data) {
+        return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno;
+    }
+    return FlashRawDataChunk(fd, reinterpret_cast<const char*>(data), len);
+}
+
+int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
+    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
+    if (!file) {
+        return -ENOENT;
+    }
+    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd));
+}
+
+int FlashBlockDevice(int fd, std::vector<char>& downloaded_data) {
+    lseek64(fd, 0, SEEK_SET);
+    if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&
+        *reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {
+        return FlashSparseData(fd, downloaded_data);
+    } else {
+        return FlashRawData(fd, downloaded_data);
+    }
+}
+
+int Flash(FastbootDevice* device, const std::string& partition_name) {
+    PartitionHandle handle;
+    if (!OpenPartition(device, partition_name, &handle)) {
+        return -ENOENT;
+    }
+
+    std::vector<char> data = std::move(device->download_data());
+    if (data.size() == 0) {
+        return -EINVAL;
+    } else if (data.size() > get_block_device_size(handle.fd())) {
+        return -EOVERFLOW;
+    }
+    return FlashBlockDevice(handle.fd(), data);
+}
diff --git a/init/watchdogd.h b/fastboot/device/flashing.h
similarity index 69%
copy from init/watchdogd.h
copy to fastboot/device/flashing.h
index 73f77d5..206a407 100644
--- a/init/watchdogd.h
+++ b/fastboot/device/flashing.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+#pragma once
 
-namespace android {
-namespace init {
+#include <string>
+#include <vector>
 
-int watchdogd_main(int argc, char **argv);
+class FastbootDevice;
 
-}  // namespace init
-}  // namespace android
-
-#endif
+int Flash(FastbootDevice* device, const std::string& partition_name);
diff --git a/init/watchdogd.h b/fastboot/device/main.cpp
similarity index 63%
copy from init/watchdogd.h
copy to fastboot/device/main.cpp
index 73f77d5..df9c900 100644
--- a/init/watchdogd.h
+++ b/fastboot/device/main.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+#include <android-base/logging.h>
 
-namespace android {
-namespace init {
+#include "fastboot_device.h"
 
-int watchdogd_main(int argc, char **argv);
+int main(int /*argc*/, char* argv[]) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
 
-}  // namespace init
-}  // namespace android
-
-#endif
+    while (true) {
+        FastbootDevice device;
+        device.ExecuteCommands();
+    }
+}
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
new file mode 100644
index 0000000..215f99a
--- /dev/null
+++ b/fastboot/device/usb_client.cpp
@@ -0,0 +1,307 @@
+/*
+ * 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 "usb_client.h"
+
+#include <endian.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+constexpr int kMaxPacketSizeFs = 64;
+constexpr int kMaxPacketSizeHs = 512;
+constexpr int kMaxPacketsizeSs = 1024;
+
+constexpr size_t kFbFfsNumBufs = 16;
+constexpr size_t kFbFfsBufSize = 32768;
+
+constexpr const char* kUsbFfsFastbootEp0 = "/dev/usb-ffs/fastboot/ep0";
+constexpr const char* kUsbFfsFastbootOut = "/dev/usb-ffs/fastboot/ep1";
+constexpr const char* kUsbFfsFastbootIn = "/dev/usb-ffs/fastboot/ep2";
+
+struct FuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct SsFuncDesc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_ss_ep_comp_descriptor source_comp;
+    struct usb_endpoint_descriptor_no_audio sink;
+    struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct DescV2 {
+    struct usb_functionfs_descs_head_v2 header;
+    // The rest of the structure depends on the flags in the header.
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
+    struct FuncDesc fs_descs, hs_descs;
+    struct SsFuncDesc ss_descs;
+} __attribute__((packed));
+
+struct usb_interface_descriptor fastboot_interface = {
+        .bLength = USB_DT_INTERFACE_SIZE,
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+        .bInterfaceSubClass = 66,
+        .bInterfaceProtocol = 3,
+        .iInterface = 1, /* first string from the provided table */
+};
+
+static struct FuncDesc fs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(fs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(fs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeFs,
+                },
+};
+
+static struct FuncDesc hs_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(hs_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(hs_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketSizeHs,
+                },
+};
+
+static struct SsFuncDesc ss_descriptors = {
+        .intf = fastboot_interface,
+        .source =
+                {
+                        .bLength = sizeof(ss_descriptors.source),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_OUT,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .source_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.source_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+        .sink =
+                {
+                        .bLength = sizeof(ss_descriptors.sink),
+                        .bDescriptorType = USB_DT_ENDPOINT,
+                        .bEndpointAddress = 1 | USB_DIR_IN,
+                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                        .wMaxPacketSize = kMaxPacketsizeSs,
+                },
+        .sink_comp =
+                {
+                        .bLength = sizeof(ss_descriptors.sink_comp),
+                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+                        .bMaxBurst = 15,
+                },
+};
+
+#define STR_INTERFACE_ "fastboot"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+                        .length = htole32(sizeof(strings)),
+                        .str_count = htole32(1),
+                        .lang_count = htole32(1),
+                },
+        .lang0 =
+                {
+                        htole16(0x0409), /* en-us */
+                        STR_INTERFACE_,
+                },
+};
+
+static struct DescV2 v2_descriptor = {
+        .header =
+                {
+                        .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+                        .length = htole32(sizeof(v2_descriptor)),
+                        .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                                 FUNCTIONFS_HAS_SS_DESC,
+                },
+        .fs_count = 3,
+        .hs_count = 3,
+        .ss_count = 5,
+        .fs_descs = fs_descriptors,
+        .hs_descs = hs_descriptors,
+        .ss_descs = ss_descriptors,
+};
+
+// Reimplementing since usb_ffs_close() does not close the control FD.
+static void CloseFunctionFs(usb_handle* h) {
+    if (h->bulk_in > 0) {
+        close(h->bulk_in);
+        h->bulk_in = -1;
+    }
+    if (h->bulk_out > 0) {
+        close(h->bulk_out);
+        h->bulk_out = -1;
+    }
+    if (h->control > 0) {
+        close(h->control);
+        h->control = -1;
+    }
+}
+
+static bool InitFunctionFs(usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+
+    if (h->control < 0) {  // might have already done this before
+        LOG(INFO) << "opening control endpoint " << kUsbFfsFastbootEp0;
+        h->control = open(kUsbFfsFastbootEp0, O_RDWR);
+        if (h->control < 0) {
+            PLOG(ERROR) << "cannot open control endpoint " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        auto ret = write(h->control, &v2_descriptor, sizeof(v2_descriptor));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write descriptors " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+
+        ret = write(h->control, &strings, sizeof(strings));
+        if (ret < 0) {
+            PLOG(ERROR) << "cannot write strings " << kUsbFfsFastbootEp0;
+            goto err;
+        }
+        // Signal only when writing the descriptors to ffs
+        android::base::SetProperty("sys.usb.ffs.ready", "1");
+    }
+
+    h->bulk_out = open(kUsbFfsFastbootOut, O_RDONLY);
+    if (h->bulk_out < 0) {
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << kUsbFfsFastbootOut;
+        goto err;
+    }
+
+    h->bulk_in = open(kUsbFfsFastbootIn, O_WRONLY);
+    if (h->bulk_in < 0) {
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << kUsbFfsFastbootIn;
+        goto err;
+    }
+
+    h->read_aiob.fd = h->bulk_out;
+    h->write_aiob.fd = h->bulk_in;
+    h->reads_zero_packets = false;
+    return true;
+
+err:
+    CloseFunctionFs(h);
+    return false;
+}
+
+ClientUsbTransport::ClientUsbTransport()
+    : handle_(std::unique_ptr<usb_handle>(create_usb_handle(kFbFfsNumBufs, kFbFfsBufSize))) {
+    if (!InitFunctionFs(handle_.get())) {
+        handle_.reset(nullptr);
+    }
+}
+
+ssize_t ClientUsbTransport::Read(void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    char* char_data = static_cast<char*>(data);
+    size_t bytes_read_total = 0;
+    while (bytes_read_total < len) {
+        auto bytes_to_read = std::min(len - bytes_read_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_read_now = handle_->read(handle_.get(), char_data, bytes_to_read);
+        if (bytes_read_now < 0) {
+            return bytes_read_total;
+        }
+        bytes_read_total += bytes_read_now;
+        char_data += bytes_read_now;
+        if (static_cast<size_t>(bytes_read_now) < bytes_to_read) {
+            break;
+        }
+    }
+    return bytes_read_total;
+}
+
+ssize_t ClientUsbTransport::Write(const void* data, size_t len) {
+    if (handle_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+    const char* char_data = reinterpret_cast<const char*>(data);
+    size_t bytes_written_total = 0;
+    while (bytes_written_total < len) {
+        auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
+        auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
+        if (bytes_written_now < 0) {
+            return bytes_written_total;
+        }
+        bytes_written_total += bytes_written_now;
+        char_data += bytes_written_now;
+        if (static_cast<size_t>(bytes_written_now) < bytes_to_write) {
+            break;
+        }
+    }
+    return bytes_written_total;
+}
+
+int ClientUsbTransport::Close() {
+    if (handle_ == nullptr) {
+        return -1;
+    }
+    CloseFunctionFs(handle_.get());
+    return 0;
+}
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
new file mode 100644
index 0000000..3694f9a
--- /dev/null
+++ b/fastboot/device/usb_client.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 <memory>
+
+#include <adbd/usb.h>
+
+#include "transport.h"
+
+class ClientUsbTransport : public Transport {
+  public:
+    ClientUsbTransport();
+    ~ClientUsbTransport() override = default;
+
+    ssize_t Read(void* data, size_t len) override;
+    ssize_t Write(const void* data, size_t len) override;
+    int Close() override;
+
+  private:
+    std::unique_ptr<usb_handle> handle_;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientUsbTransport);
+};
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
new file mode 100644
index 0000000..ec84576
--- /dev/null
+++ b/fastboot/device/utility.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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 "utility.h"
+
+#include <android-base/logging.h>
+#include <fs_mgr_dm_linear.h>
+#include <liblp/liblp.h>
+
+#include "fastboot_device.h"
+
+using namespace android::fs_mgr;
+using android::base::unique_fd;
+using android::hardware::boot::V1_0::Slot;
+
+static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
+    std::optional<std::string> path = FindPhysicalPartition(name);
+    if (!path) {
+        return false;
+    }
+    *handle = PartitionHandle(*path);
+    return true;
+}
+
+static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
+                                 PartitionHandle* handle) {
+    std::optional<std::string> path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    if (!path) {
+        return false;
+    }
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot);
+    std::string dm_path;
+    if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, &dm_path)) {
+        LOG(ERROR) << "Could not map partition: " << name;
+        return false;
+    }
+    auto closer = [name]() -> void { DestroyLogicalPartition(name); };
+    *handle = PartitionHandle(dm_path, std::move(closer));
+    return true;
+}
+
+bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) {
+    // We prioritize logical partitions over physical ones, and do this
+    // consistently for other partition operations (like getvar:partition-size).
+    if (LogicalPartitionExists(name, device->GetCurrentSlot())) {
+        if (!OpenLogicalPartition(name, device->GetCurrentSlot(), handle)) {
+            return false;
+        }
+    } else if (!OpenPhysicalPartition(name, handle)) {
+        LOG(ERROR) << "No such partition: " << name;
+        return false;
+    }
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), O_WRONLY | O_EXCL)));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open block device: " << handle->path();
+        return false;
+    }
+    handle->set_fd(std::move(fd));
+    return true;
+}
+
+std::optional<std::string> FindPhysicalPartition(const std::string& name) {
+    std::string path = "/dev/block/by-name/" + name;
+    if (access(path.c_str(), R_OK | W_OK) < 0) {
+        return {};
+    }
+    return path;
+}
+
+static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata,
+                                                       const std::string& name) {
+    for (const auto& partition : metadata.partitions) {
+        if (GetPartitionName(partition) == name) {
+            return &partition;
+        }
+    }
+    return nullptr;
+}
+
+bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
+                            bool* is_zero_length) {
+    auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    if (!path) {
+        return false;
+    }
+
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);
+    if (!metadata) {
+        return false;
+    }
+    const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name);
+    if (!partition) {
+        return false;
+    }
+    if (is_zero_length) {
+        *is_zero_length = (partition->num_extents == 0);
+    }
+    return true;
+}
+
+bool GetSlotNumber(const std::string& slot, Slot* number) {
+    if (slot.size() != 1) {
+        return false;
+    }
+    if (slot[0] < 'a' || slot[0] > 'z') {
+        return false;
+    }
+    *number = slot[0] - 'a';
+    return true;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
new file mode 100644
index 0000000..0931fc3
--- /dev/null
+++ b/fastboot/device/utility.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <optional>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
+
+// Logical partitions are only mapped to a block device as needed, and
+// immediately unmapped when no longer needed. In order to enforce this we
+// require accessing partitions through a Handle abstraction, which may perform
+// additional operations after closing its file descriptor.
+class PartitionHandle {
+  public:
+    PartitionHandle() {}
+    explicit PartitionHandle(const std::string& path) : path_(path) {}
+    PartitionHandle(const std::string& path, std::function<void()>&& closer)
+        : path_(path), closer_(std::move(closer)) {}
+    PartitionHandle(PartitionHandle&& other) = default;
+    PartitionHandle& operator=(PartitionHandle&& other) = default;
+    ~PartitionHandle() {
+        if (closer_) {
+            // Make sure the device is closed first.
+            fd_ = {};
+            closer_();
+        }
+    }
+    const std::string& path() const { return path_; }
+    int fd() const { return fd_.get(); }
+    void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); }
+
+  private:
+    std::string path_;
+    android::base::unique_fd fd_;
+    std::function<void()> closer_;
+};
+
+class FastbootDevice;
+
+std::optional<std::string> FindPhysicalPartition(const std::string& name);
+bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
+                            bool* is_zero_length = nullptr);
+bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
+
+bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
new file mode 100644
index 0000000..a5dead2
--- /dev/null
+++ b/fastboot/device/variables.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 "variables.h"
+
+#include <inttypes.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <ext4_utils/ext4_utils.h>
+
+#include "fastboot_device.h"
+#include "flashing.h"
+#include "utility.h"
+
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_0::Slot;
+
+constexpr int kMaxDownloadSizeDefault = 0x20000000;
+constexpr char kFastbootProtocolVersion[] = "0.4";
+
+bool GetVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay(kFastbootProtocolVersion);
+}
+
+bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay(android::base::GetProperty("ro.bootloader", ""));
+}
+
+bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay(android::base::GetProperty("ro.build.expect.baseband", ""));
+}
+
+bool GetProduct(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay(android::base::GetProperty("ro.product.device", ""));
+}
+
+bool GetSerial(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay(android::base::GetProperty("ro.serialno", ""));
+}
+
+bool GetSecure(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay(android::base::GetBoolProperty("ro.secure", "") ? "yes" : "no");
+}
+
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    std::string suffix = device->GetCurrentSlot();
+    std::string slot = suffix.size() == 2 ? suffix.substr(1) : suffix;
+    return device->WriteOkay(slot);
+}
+
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        return "0";
+    }
+    return device->WriteOkay(std::to_string(boot_control_hal->getNumberSlots()));
+}
+
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.empty()) {
+        return device->WriteFail("Missing argument");
+    }
+    Slot slot;
+    if (!GetSlotNumber(args[0], &slot)) {
+        return device->WriteFail("Invalid slot");
+    }
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        return device->WriteFail("Device has no slots");
+    }
+    if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) {
+        return device->WriteOkay("no");
+    }
+    return device->WriteOkay("yes");
+}
+
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.empty()) {
+        return device->WriteFail("Missing argument");
+    }
+    Slot slot;
+    if (!GetSlotNumber(args[0], &slot)) {
+        return device->WriteFail("Invalid slot");
+    }
+    auto boot_control_hal = device->boot_control_hal();
+    if (!boot_control_hal) {
+        return device->WriteFail("Device has no slots");
+    }
+    if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) {
+        return device->WriteOkay("yes");
+    }
+    return device->WriteOkay("no");
+}
+
+bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay(std::to_string(kMaxDownloadSizeDefault));
+}
+
+bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay("yes");
+}
+
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.empty()) {
+        return device->WriteFail("Missing argument");
+    }
+    std::string slot_suffix = device->GetCurrentSlot();
+    if (slot_suffix.empty()) {
+        return device->WriteFail("Invalid slot");
+    }
+    std::string result = (args[0] == "userdata" ? "no" : "yes");
+    return device->WriteOkay(result);
+}
+
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 1) {
+        return device->WriteFail("Missing argument");
+    }
+    // Zero-length partitions cannot be created through device-mapper, so we
+    // special case them here.
+    bool is_zero_length;
+    if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
+        is_zero_length) {
+        return device->WriteOkay("0");
+    }
+    // Otherwise, open the partition as normal.
+    PartitionHandle handle;
+    if (!OpenPartition(device, args[0], &handle)) {
+        return device->WriteFail("Could not open partition");
+    }
+    uint64_t size = get_block_device_size(handle.fd());
+    return device->WriteOkay(android::base::StringPrintf("%" PRIX64, size));
+}
+
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 1) {
+        return device->WriteFail("Missing argument");
+    }
+    // Note: if a partition name is in both the GPT and the super partition, we
+    // return "true", to be consistent with prefering to flash logical partitions
+    // over physical ones.
+    std::string partition_name = args[0];
+    if (LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+        return device->WriteOkay("yes");
+    }
+    if (FindPhysicalPartition(partition_name)) {
+        return device->WriteOkay("no");
+    }
+    return device->WriteFail("Partition not found");
+}
+
+bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+    return device->WriteOkay("yes");
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
new file mode 100644
index 0000000..554a080
--- /dev/null
+++ b/fastboot/device/variables.h
@@ -0,0 +1,39 @@
+/*
+ * 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 <functional>
+#include <string>
+#include <vector>
+
+class FastbootDevice;
+
+bool GetVersion(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 63ee2af..6890643 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -155,6 +155,21 @@
                                         total);
 }
 
+void fb_queue_create_partition(const std::string& partition, const std::string& size) {
+    Action& a = queue_action(OP_COMMAND, FB_CMD_CREATE_PARTITION ":" + partition + ":" + size);
+    a.msg = "Creating '" + partition + "'";
+}
+
+void fb_queue_delete_partition(const std::string& partition) {
+    Action& a = queue_action(OP_COMMAND, FB_CMD_DELETE_PARTITION ":" + partition);
+    a.msg = "Deleting '" + partition + "'";
+}
+
+void fb_queue_resize_partition(const std::string& partition, const std::string& size) {
+    Action& a = queue_action(OP_COMMAND, FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size);
+    a.msg = "Resizing '" + partition + "'";
+}
+
 static int match(const char* str, const char** value, unsigned count) {
     unsigned n;
 
diff --git a/fastboot/engine.h b/fastboot/engine.h
index 74aaa6a..8aebdd7 100644
--- a/fastboot/engine.h
+++ b/fastboot/engine.h
@@ -69,6 +69,9 @@
 void fb_queue_upload(const std::string& outfile);
 void fb_queue_notice(const std::string& notice);
 void fb_queue_wait_for_disconnect(void);
+void fb_queue_create_partition(const std::string& partition, const std::string& size);
+void fb_queue_delete_partition(const std::string& partition);
+void fb_queue_resize_partition(const std::string& partition, const std::string& size);
 int64_t fb_execute_queue();
 void fb_set_active(const std::string& slot);
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index c830321..dc94952 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -111,12 +111,13 @@
     const char* img_name;
     const char* sig_name;
     const char* part_name;
-    bool is_optional;
-    bool is_secondary;
+    bool optional_if_no_image;
+    bool optional_if_no_partition;
+    bool IsSecondary() const { return nickname == nullptr; }
 } images[] = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, false },
-    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  true  },
+    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  false },
     { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  false },
     { "dts",      "dt.img",           "dt.sig",       "dts",      true,  false },
     { "odm",      "odm.img",          "odm.sig",      "odm",      true,  false },
@@ -124,14 +125,15 @@
     { "product-services",
                   "product-services.img",
                                       "product-services.sig",
-                                                      "product-services",
-                                                                  true,  false },
+                                                      "product_services",
+                                                                  true,  true  },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
-    { "system",   "system.img",       "system.sig",   "system",   false, false },
-    { nullptr,    "system_other.img", "system.sig",   "system",   true,  true  },
+    { "super",    "super.img",        "super.sig",    "super",    true,  true  },
+    { "system",   "system.img",       "system.sig",   "system",   false, true  },
+    { nullptr,    "system_other.img", "system.sig",   "system",   true,  false },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  false },
-    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  false },
-    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  true  },
+    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  true  },
+    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  false },
         // clang-format on
 };
 
@@ -636,7 +638,8 @@
     // "require partition-exists=x" is a special case, added because of the trouble we had when
     // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
     // missing out new partitions. A device with new partitions can use "partition-exists" to
-    // override the `is_optional` field in the `images` array.
+    // override the fields `optional_if_no_image` and 'optional_if_no_partition' in the `images`
+    // array.
     if (!strcmp(name, "partition-exists")) {
         const char* partition_name = val[0];
         std::string has_slot;
@@ -647,7 +650,8 @@
         bool known_partition = false;
         for (size_t i = 0; i < arraysize(images); ++i) {
             if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
-                images[i].is_optional = false;
+                images[i].optional_if_no_image = false;
+                images[i].optional_if_no_partition = false;
                 known_partition = true;
             }
         }
@@ -1040,6 +1044,25 @@
     }
 }
 
+static bool if_partition_exists(const std::string& partition, const std::string& slot) {
+    std::string has_slot;
+    std::string partition_name = partition;
+
+    if (fb_getvar("has-slot:" + partition, &has_slot) && has_slot == "yes") {
+        if (slot == "") {
+            std::string current_slot = get_current_slot();
+            if (current_slot == "") {
+                die("Failed to identify current slot");
+            }
+            partition_name += "_" + current_slot;
+        } else {
+            partition_name += "_" + slot;
+        }
+    }
+    std::string partition_size;
+    return fb_getvar("partition-size:" + partition_name, &partition_size);
+}
+
 static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     queue_info_dump();
 
@@ -1075,7 +1098,7 @@
     }
     for (size_t i = 0; i < arraysize(images); ++i) {
         const char* slot = slot_override.c_str();
-        if (images[i].is_secondary) {
+        if (images[i].IsSecondary()) {
             if (!skip_secondary) {
                 slot = secondary.c_str();
             } else {
@@ -1085,12 +1108,17 @@
 
         int fd = unzip_to_file(zip, images[i].img_name);
         if (fd == -1) {
-            if (images[i].is_optional) {
+            if (images[i].optional_if_no_image) {
                 continue; // An optional file is missing, so ignore it.
             }
             die("non-optional file %s missing", images[i].img_name);
         }
 
+        if (images[i].optional_if_no_partition &&
+            !if_partition_exists(images[i].part_name, slot)) {
+            continue;
+        }
+
         fastboot_buffer buf;
         if (!load_buf_fd(fd, &buf)) {
             die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
@@ -1162,7 +1190,7 @@
 
     for (size_t i = 0; i < arraysize(images); i++) {
         const char* slot = NULL;
-        if (images[i].is_secondary) {
+        if (images[i].IsSecondary()) {
             if (!skip_secondary) slot = secondary.c_str();
         } else {
             slot = slot_override.c_str();
@@ -1171,10 +1199,13 @@
         fname = find_item_given_name(images[i].img_name);
         fastboot_buffer buf;
         if (!load_buf(fname.c_str(), &buf)) {
-            if (images[i].is_optional) continue;
+            if (images[i].optional_if_no_image) continue;
             die("could not load '%s': %s", images[i].img_name, strerror(errno));
         }
-
+        if (images[i].optional_if_no_partition &&
+            !if_partition_exists(images[i].part_name, slot)) {
+            continue;
+        }
         auto flashall = [&](const std::string &partition) {
             do_send_signature(fname.c_str());
             flash_buf(partition.c_str(), &buf);
@@ -1332,6 +1363,8 @@
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
+    bool wants_reboot_recovery = false;
+    bool wants_reboot_fastboot = false;
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
@@ -1554,6 +1587,12 @@
                 if (what == "bootloader") {
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
+                } else if (what == "recovery") {
+                    wants_reboot = false;
+                    wants_reboot_recovery = true;
+                } else if (what == "fastboot") {
+                    wants_reboot = false;
+                    wants_reboot_fastboot = true;
                 } else {
                     syntax_error("unknown reboot target %s", what.c_str());
                 }
@@ -1562,6 +1601,10 @@
             if (!args.empty()) syntax_error("junk after reboot command");
         } else if (command == "reboot-bootloader") {
             wants_reboot_bootloader = true;
+        } else if (command == "reboot-recovery") {
+            wants_reboot_recovery = true;
+        } else if (command == "reboot-fastboot") {
+            wants_reboot_fastboot = true;
         } else if (command == "continue") {
             fb_queue_command("continue", "resuming boot");
         } else if (command == "boot") {
@@ -1648,6 +1691,17 @@
             } else {
                 syntax_error("unknown 'flashing' command %s", args[0].c_str());
             }
+        } else if (command == "create-logical-partition") {
+            std::string partition = next_arg(&args);
+            std::string size = next_arg(&args);
+            fb_queue_create_partition(partition, size);
+        } else if (command == "delete-logical-partition") {
+            std::string partition = next_arg(&args);
+            fb_queue_delete_partition(partition);
+        } else if (command == "resize-logical-partition") {
+            std::string partition = next_arg(&args);
+            std::string size = next_arg(&args);
+            fb_queue_resize_partition(partition, size);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
@@ -1679,6 +1733,12 @@
     } else if (wants_reboot_bootloader) {
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
         fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_recovery) {
+        fb_queue_command("reboot-recovery", "rebooting into recovery");
+        fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_fastboot) {
+        fb_queue_command("reboot-fastboot", "rebooting into fastboot");
+        fb_queue_wait_for_disconnect();
     }
 
     int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index aabc620..55ca65d 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -134,7 +134,7 @@
         return ret;
     }
 
-    std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:d:]]+)");
+    std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:xdigit:]]+)");
     std::smatch sm;
 
     for (auto& s : all) {
@@ -264,11 +264,16 @@
                                std::vector<std::string>* info) {
     RetCode ret;
     int dsize;
-    if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize)) || dsize == 0) {
-        error_ = "Upload request failed";
+    if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize))) {
+        error_ = "Upload request failed: " + error_;
         return ret;
     }
 
+    if (!dsize) {
+        error_ = "Upload request failed, device reports 0 bytes available";
+        return BAD_DEV_RESP;
+    }
+
     std::vector<char> data;
     data.resize(dsize);
 
@@ -462,10 +467,10 @@
 }
 
 RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
+    // ioctl on 0-length buffer causes freezing
     if (!size) {
-        return SUCCESS;
+        return BAD_ARG;
     }
-
     // Write the buffer
     ssize_t tmp = transport->Write(buf, size);
 
@@ -521,7 +526,7 @@
     // Now we need to send a multiple of chunk size
     size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
     size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
-    if (SendBuffer(data + total, nbytes)) {
+    if (nbytes && SendBuffer(data + total, nbytes)) {  // Don't send a ZLP
         error_ = ErrnoStr("Send failed in SparseWriteCallback()");
         return -1;
     }
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 8af1743..dd199c0 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -103,7 +103,7 @@
 
     /* HELPERS */
     void SetInfoCallback(std::function<void(std::string&)> info);
-    const std::string RCString(RetCode rc);
+    static const std::string RCString(RetCode rc);
     std::string Error();
     RetCode WaitForDisconnect();
 
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index cc1714f..b0ac2ef 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -177,6 +177,8 @@
     mkf2fs_args.push_back("encrypt");
     mkf2fs_args.push_back("-O");
     mkf2fs_args.push_back("quota");
+    mkf2fs_args.push_back("-O");
+    mkf2fs_args.push_back("verity");
     mkf2fs_args.push_back(fileName);
     mkf2fs_args.push_back(nullptr);
 
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 196321c..6329d54 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -43,6 +43,7 @@
         "fs_mgr_avb.cpp",
         "fs_mgr_avb_ops.cpp",
         "fs_mgr_dm_linear.cpp",
+        "fs_mgr_overlayfs.cpp",
     ],
     shared_libs: [
         "libfec",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index b3df811..99f2df8 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "fs_mgr.h"
+
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
@@ -51,6 +53,7 @@
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
+#include <fs_mgr_overlayfs.h>
 #include <libdm/dm.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
@@ -58,7 +61,6 @@
 #include <log/log_properties.h>
 #include <logwrap/logwrap.h>
 
-#include "fs_mgr.h"
 #include "fs_mgr_avb.h"
 #include "fs_mgr_priv.h"
 
@@ -1035,6 +1037,10 @@
         }
     }
 
+#if ALLOW_ADBD_DISABLE_VERITY == 1  // "userdebug" build
+    fs_mgr_overlayfs_mount_all();
+#endif
+
     if (error_count) {
         return FS_MGR_MNTALL_FAIL;
     } else {
@@ -1052,6 +1058,9 @@
         return FS_MGR_DOMNT_FAILED;
     }
 
+    // Run fsck if needed
+    prepare_fs_for_mount(rec->blk_device, rec);
+
     int ret = __mount(rec->blk_device, rec->mount_point, rec);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
@@ -1177,8 +1186,8 @@
 {
     int ret;
 
-    ret = mount("tmpfs", n_name, "tmpfs",
-                MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
+    ret = mount("tmpfs", n_name, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV | MS_NOEXEC,
+                CRYPTO_TMPFS_OPTIONS);
     if (ret < 0) {
         LERROR << "Cannot mount tmpfs filesystem at " << n_name;
         return -1;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 05e03e1..a91e92e 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -80,13 +80,17 @@
 }
 
 static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
-                                   const LpMetadataPartition& partition, std::string* path) {
+                                   const LpMetadataPartition& partition, bool force_writable,
+                                   std::string* path) {
     DeviceMapper& dm = DeviceMapper::Instance();
 
     DmTable table;
     if (!CreateDmTable(block_device, metadata, partition, &table)) {
         return false;
     }
+    if (force_writable) {
+        table.set_readonly(false);
+    }
     std::string name = GetPartitionName(partition);
     if (!dm.CreateDevice(name, table)) {
         return false;
@@ -106,8 +110,12 @@
         return true;
     }
     for (const auto& partition : metadata->partitions) {
+        if (!partition.num_extents) {
+            LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
+            continue;
+        }
         std::string path;
-        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, &path)) {
+        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -116,7 +124,8 @@
 }
 
 bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
-                            const std::string& partition_name, std::string* path) {
+                            const std::string& partition_name, bool force_writable,
+                            std::string* path) {
     auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
     if (!metadata) {
         LOG(ERROR) << "Could not read partition table.";
@@ -124,7 +133,8 @@
     }
     for (const auto& partition : metadata->partitions) {
         if (GetPartitionName(partition) == partition_name) {
-            return CreateLogicalPartition(block_device, *metadata.get(), partition, path);
+            return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
+                                          path);
         }
     }
     LERROR << "Could not find any partition with name: " << partition_name;
@@ -133,7 +143,11 @@
 
 bool DestroyLogicalPartition(const std::string& name) {
     DeviceMapper& dm = DeviceMapper::Instance();
-    return dm.DeleteDevice(name);
+    if (!dm.DeleteDevice(name)) {
+        return false;
+    }
+    LINFO << "Unmapped logical partition " << name;
+    return true;
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 63a6839..845cca9 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -121,6 +121,7 @@
         "-f",
         "-O", "encrypt",
         "-O", "quota",
+        "-O", "verity",
         "-w", "4096",
         fs_blkdev,
         size_str.c_str(),
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 8b46d64..f87a3b1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -117,21 +117,17 @@
 #define EM_ICE          2
 #define EM_AES_256_CTS  3
 #define EM_AES_256_HEH  4
-#define EM_SPECK_128_256_XTS 5
-#define EM_SPECK_128_256_CTS 6
 
 static const struct flag_list file_contents_encryption_modes[] = {
     {"aes-256-xts", EM_AES_256_XTS},
-    {"speck128/256-xts", EM_SPECK_128_256_XTS},
     {"software", EM_AES_256_XTS}, /* alias for backwards compatibility */
-    {"ice", EM_ICE},              /* hardware-specific inline cryptographic engine */
+    {"ice", EM_ICE}, /* hardware-specific inline cryptographic engine */
     {0, 0},
 };
 
 static const struct flag_list file_names_encryption_modes[] = {
     {"aes-256-cts", EM_AES_256_CTS},
     {"aes-256-heh", EM_AES_256_HEH},
-    {"speck128/256-cts", EM_SPECK_128_256_CTS},
     {0, 0},
 };
 
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
new file mode 100644
index 0000000..74dfc00
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -0,0 +1,478 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <linux/fs.h>
+#include <selinux/selinux.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+
+#if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
+
+bool fs_mgr_overlayfs_mount_all() {
+    return false;
+}
+
+bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change) {
+    if (change) *change = false;
+    return false;
+}
+
+bool fs_mgr_overlayfs_teardown(const char*, bool* change) {
+    if (change) *change = false;
+    return false;
+}
+
+#else  // ALLOW_ADBD_DISABLE_VERITY == 0
+
+namespace {
+
+// 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'.
+bool fs_mgr_boot_completed() {
+    return !android::base::GetProperty("ro.boottime.init", "").empty() &&
+           !!access("/dev/.booting", F_OK);
+}
+
+bool fs_mgr_is_dir(const std::string& path) {
+    struct stat st;
+    return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
+}
+
+// Similar test as overlayfs workdir= validation in the kernel for read-write
+// validation, except we use fs_mgr_work.  Covers space and storage issues.
+bool fs_mgr_dir_is_writable(const std::string& path) {
+    auto test_directory = path + "/fs_mgr_work";
+    rmdir(test_directory.c_str());
+    auto ret = !mkdir(test_directory.c_str(), 0700);
+    return ret | !rmdir(test_directory.c_str());
+}
+
+std::string fs_mgr_get_context(const std::string& mount_point) {
+    char* ctx = nullptr;
+    auto len = getfilecon(mount_point.c_str(), &ctx);
+    if ((len > 0) && ctx) {
+        std::string context(ctx, len);
+        free(ctx);
+        return context;
+    }
+    return "";
+}
+
+bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
+    // readonly filesystem, can not be mount -o remount,rw
+    return "squashfs"s == fsrec->fs_type;
+}
+
+constexpr char upper_name[] = "upper";
+constexpr char work_name[] = "work";
+
+std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
+    if (!fs_mgr_is_dir(mount_point)) return "";
+    auto dir = kOverlayMountPoint + "/overlay/" + android::base::Basename(mount_point) + "/";
+    auto upper = dir + upper_name;
+    if (!fs_mgr_is_dir(upper)) return "";
+    auto work = dir + work_name;
+    if (!fs_mgr_is_dir(work)) return "";
+    if (!fs_mgr_dir_is_writable(work)) return "";
+    return dir;
+}
+
+constexpr char lowerdir_option[] = "lowerdir=";
+constexpr char upperdir_option[] = "upperdir=";
+
+// default options for mount_point, returns empty string for none available.
+std::string fs_mgr_get_overlayfs_options(const char* mount_point) {
+    auto fsrec_mount_point = std::string(mount_point);
+    auto candidate = fs_mgr_get_overlayfs_candidate(fsrec_mount_point);
+    if (candidate.empty()) return "";
+
+    auto context = fs_mgr_get_context(fsrec_mount_point);
+    if (!context.empty()) context = ",rootcontext="s + context;
+    return "override_creds=off,"s + lowerdir_option + fsrec_mount_point + "," + upperdir_option +
+           candidate + upper_name + ",workdir=" + candidate + work_name + context;
+}
+
+bool fs_mgr_system_root_image(const fstab* fstab) {
+    if (!fstab) {  // can not happen?
+        // This will return empty on init first_stage_mount,
+        // hence why we prefer checking the fstab instead.
+        return android::base::GetBoolProperty("ro.build.system_root_image", false);
+    }
+    for (auto i = 0; i < fstab->num_entries; i++) {
+        const auto fsrec = &fstab->recs[i];
+        auto fsrec_mount_point = fsrec->mount_point;
+        if (!fsrec_mount_point) continue;
+        if ("/system"s == fsrec_mount_point) return false;
+    }
+    return true;
+}
+
+std::string fs_mgr_get_overlayfs_options(const fstab* fstab, const char* mount_point) {
+    if (fs_mgr_system_root_image(fstab) && ("/"s == mount_point)) mount_point = "/system";
+
+    return fs_mgr_get_overlayfs_options(mount_point);
+}
+
+// return true if system supports overlayfs
+bool fs_mgr_wants_overlayfs() {
+    // This will return empty on init first_stage_mount, so speculative
+    // determination, empty (unset) _or_ "1" is true which differs from the
+    // official ro.debuggable policy.  ALLOW_ADBD_DISABLE_VERITY == 0 should
+    // protect us from false in any case, so this is insurance.
+    auto debuggable = android::base::GetProperty("ro.debuggable", "1");
+    if (debuggable != "1") return false;
+
+    // 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) {
+        overlayfs_in_kernel = !access("/sys/module/overlay/parameters/override_creds", F_OK);
+    }
+    return overlayfs_in_kernel;
+}
+
+bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
+    if (!fsrec) return false;
+
+    auto fsrec_mount_point = fsrec->mount_point;
+    if (!fsrec_mount_point) return false;
+
+    if (!fsrec->fs_type) return false;
+
+    // Don't check entries that are managed by vold.
+    if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
+
+    // Only concerned with readonly partitions.
+    if (!(fsrec->flags & MS_RDONLY)) return false;
+
+    // If unbindable, do not allow overlayfs as this could expose us to
+    // security issues.  On Android, this could also be used to turn off
+    // the ability to overlay an otherwise acceptable filesystem since
+    // /system and /vendor are never bound(sic) to.
+    if (fsrec->flags & MS_UNBINDABLE) return false;
+
+    if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
+
+    // Verity enabled?
+    const auto basename_mount_point(android::base::Basename(fsrec_mount_point));
+    auto found = false;
+    fs_mgr_update_verity_state(
+            [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
+                if (mount_point && (basename_mount_point == mount_point)) found = true;
+            });
+    return !found;
+}
+
+bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr) {
+    auto save_errno = errno;
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+    if (!dir) {
+        if (errno == ENOENT) {
+            errno = save_errno;
+            return true;
+        }
+        PERROR << "overlayfs open " << path;
+        return false;
+    }
+    dirent* entry;
+    auto ret = true;
+    while ((entry = readdir(dir.get()))) {
+        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+        auto file = path + "/" + entry->d_name;
+        if (entry->d_type == DT_UNKNOWN) {
+            struct stat st;
+            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+        }
+        if (entry->d_type == DT_DIR) {
+            ret &= fs_mgr_rm_all(file, change);
+            if (!rmdir(file.c_str())) {
+                if (change) *change = true;
+            } else {
+                ret = false;
+                PERROR << "overlayfs rmdir " << file;
+            }
+            continue;
+        }
+        if (!unlink(file.c_str())) {
+            if (change) *change = true;
+        } else {
+            ret = false;
+            PERROR << "overlayfs rm " << file;
+        }
+    }
+    return ret;
+}
+
+bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
+                                bool* change) {
+    auto ret = true;
+    auto fsrec_mount_point = overlay + android::base::Basename(mount_point) + "/";
+    auto save_errno = errno;
+    if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "overlayfs mkdir " << fsrec_mount_point;
+    } else {
+        errno = save_errno;
+    }
+
+    save_errno = errno;
+    if (!mkdir((fsrec_mount_point + work_name).c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "overlayfs mkdir " << fsrec_mount_point << work_name;
+    } else {
+        errno = save_errno;
+    }
+
+    auto new_context = fs_mgr_get_context(mount_point);
+    if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
+        ret = false;
+        PERROR << "overlayfs setfscreatecon " << new_context;
+    }
+    auto upper = fsrec_mount_point + upper_name;
+    save_errno = errno;
+    if (!mkdir(upper.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "overlayfs mkdir " << upper;
+    } else {
+        errno = save_errno;
+    }
+    if (!new_context.empty()) setfscreatecon(nullptr);
+
+    return ret;
+}
+
+bool fs_mgr_overlayfs_mount(const fstab* fstab, const fstab_rec* fsrec) {
+    if (!fs_mgr_wants_overlayfs(fsrec)) return false;
+    auto fsrec_mount_point = fsrec->mount_point;
+    if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
+    auto options = fs_mgr_get_overlayfs_options(fstab, fsrec_mount_point);
+    if (options.empty()) return false;
+
+    // hijack __mount() report format to help triage
+    auto report = "__mount(source=overlay,target="s + fsrec_mount_point + ",type=overlay";
+    const auto opt_list = android::base::Split(options, ",");
+    for (const auto opt : opt_list) {
+        if (android::base::StartsWith(opt, upperdir_option)) {
+            report = report + "," + opt;
+            break;
+        }
+    }
+    report = report + ")=";
+
+    auto ret = mount("overlay", fsrec_mount_point, "overlay", MS_RDONLY | MS_RELATIME,
+                     options.c_str());
+    if (ret) {
+        PERROR << report << ret;
+        return false;
+    } else {
+        LINFO << report << ret;
+        return true;
+    }
+}
+
+bool fs_mgr_overlayfs_already_mounted(const char* mount_point) {
+    if (!mount_point) return false;
+    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
+            fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab);
+    if (!fstab) return false;
+    const auto lowerdir = std::string(lowerdir_option) + mount_point;
+    for (auto i = 0; i < fstab->num_entries; ++i) {
+        const auto fsrec = &fstab->recs[i];
+        const auto fs_type = fsrec->fs_type;
+        if (!fs_type) continue;
+        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+        auto fsrec_mount_point = fsrec->mount_point;
+        if (!fsrec_mount_point) continue;
+        if (strcmp(fsrec_mount_point, mount_point)) continue;
+        const auto fs_options = fsrec->fs_options;
+        if (!fs_options) continue;
+        const auto options = android::base::Split(fs_options, ",");
+        for (const auto opt : options) {
+            if (opt == lowerdir) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+}  // namespace
+
+bool fs_mgr_overlayfs_mount_all() {
+    auto ret = false;
+
+    if (!fs_mgr_wants_overlayfs()) return ret;
+
+    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                                      fs_mgr_free_fstab);
+    if (!fstab) return ret;
+
+    for (auto i = 0; i < fstab->num_entries; i++) {
+        const auto fsrec = &fstab->recs[i];
+        auto fsrec_mount_point = fsrec->mount_point;
+        if (!fsrec_mount_point) continue;
+        if (fs_mgr_overlayfs_already_mounted(fsrec_mount_point)) continue;
+
+        if (fs_mgr_overlayfs_mount(fstab.get(), fsrec)) ret = true;
+    }
+    return ret;
+}
+
+// Returns false if setup not permitted, errno set to last error.
+// If something is altered, set *change.
+bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
+    if (change) *change = false;
+    auto ret = false;
+    if (backing && (kOverlayMountPoint != backing)) {
+        errno = EINVAL;
+        return ret;
+    }
+    if (!fs_mgr_wants_overlayfs()) return ret;
+    if (!fs_mgr_boot_completed()) {
+        errno = EBUSY;
+        PERROR << "overlayfs setup";
+        return ret;
+    }
+
+    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                                      fs_mgr_free_fstab);
+    std::vector<std::string> mounts;
+    if (fstab) {
+        if (!fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
+        for (auto i = 0; i < fstab->num_entries; i++) {
+            const auto fsrec = &fstab->recs[i];
+            auto fsrec_mount_point = fsrec->mount_point;
+            if (!fsrec_mount_point) continue;
+            if (mount_point && strcmp(fsrec_mount_point, mount_point)) continue;
+            if (!fs_mgr_wants_overlayfs(fsrec)) continue;
+            mounts.emplace_back(fsrec_mount_point);
+        }
+        if (mounts.empty()) return ret;
+    }
+
+    if (mount_point && ("/"s == mount_point) && fs_mgr_system_root_image(fstab.get())) {
+        mount_point = "/system";
+    }
+    auto overlay = kOverlayMountPoint + "/overlay/";
+    auto save_errno = errno;
+    if (!mkdir(overlay.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        PERROR << "overlayfs mkdir " << overlay;
+    } else {
+        errno = save_errno;
+    }
+    if (!fstab && mount_point && fs_mgr_overlayfs_setup_one(overlay, mount_point, change)) {
+        ret = true;
+    }
+    for (const auto& fsrec_mount_point : mounts) {
+        ret |= fs_mgr_overlayfs_setup_one(overlay, fsrec_mount_point, change);
+    }
+    return ret;
+}
+
+// Returns false if teardown not permitted, errno set to last error.
+// If something is altered, set *change.
+bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
+    if (change) *change = false;
+    if (mount_point && ("/"s == mount_point)) {
+        std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
+                fs_mgr_read_fstab_default(), fs_mgr_free_fstab);
+        if (fs_mgr_system_root_image(fstab.get())) mount_point = "/system";
+    }
+    auto ret = true;
+    const auto overlay = kOverlayMountPoint + "/overlay";
+    const auto oldpath = overlay + (mount_point ?: "");
+    const auto newpath = oldpath + ".teardown";
+    ret &= fs_mgr_rm_all(newpath);
+    auto save_errno = errno;
+    if (rename(oldpath.c_str(), newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "overlayfs mv " << oldpath << " " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    ret &= fs_mgr_rm_all(newpath, change);
+    save_errno = errno;
+    if (!rmdir(newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "overlayfs rmdir " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    if (mount_point) {
+        save_errno = errno;
+        if (!rmdir(overlay.c_str())) {
+            if (change) *change = true;
+        } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
+            ret = false;
+            PERROR << "overlayfs rmdir " << overlay;
+        } else {
+            errno = save_errno;
+        }
+    }
+    if (!fs_mgr_wants_overlayfs()) {
+        // After obligatory teardown to make sure everything is clean, but if
+        // we didn't want overlayfs in the the first place, we do not want to
+        // waste time on a reboot (or reboot request message).
+        if (change) *change = false;
+    }
+    // And now that we did what we could, lets inform
+    // caller that there may still be more to do.
+    if (!fs_mgr_boot_completed()) {
+        errno = EBUSY;
+        PERROR << "overlayfs teardown";
+        ret = false;
+    }
+    return ret;
+}
+
+#endif  // ALLOW_ADBD_DISABLE_VERITY != 0
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index afd7227..a347faf 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -45,7 +45,7 @@
 #define PWARNING PLOG(WARNING) << FS_MGR_TAG
 #define PERROR   PLOG(ERROR) << FS_MGR_TAG
 
-#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
+#define CRYPTO_TMPFS_OPTIONS "size=512m,mode=0771,uid=1000,gid=1000"
 
 /* fstab has the following format:
  *
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index cac475c..f15c450 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -41,9 +41,11 @@
 
 // Create a block device for a single logical partition, given metadata and
 // the partition name. On success, a path to the partition's block device is
-// returned.
+// returned. If |force_writable| is true, the "readonly" flag will be ignored
+// so the partition can be flashed.
 bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
-                            const std::string& partition_name, std::string* path);
+                            const std::string& partition_name, bool force_writable,
+                            std::string* path);
 
 // Destroy the block device for a logical partition, by name.
 bool DestroyLogicalPartition(const std::string& name);
diff --git a/init/watchdogd.h b/fs_mgr/include/fs_mgr_overlayfs.h
similarity index 60%
copy from init/watchdogd.h
copy to fs_mgr/include/fs_mgr_overlayfs.h
index 73f77d5..1d2ff03 100644
--- a/init/watchdogd.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+#pragma once
 
-namespace android {
-namespace init {
+#include <fstab/fstab.h>
 
-int watchdogd_main(int argc, char **argv);
-
-}  // namespace init
-}  // namespace android
-
-#endif
+bool fs_mgr_overlayfs_mount_all();
+bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
+                            bool* change = nullptr);
+bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 9d710f9..2015e4d 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -48,10 +48,20 @@
         PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
         return false;
     }
-    if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
+
+    int alignment_offset;
+    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
         return false;
     }
+    int logical_block_size;
+    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+        return false;
+    }
+
+    device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
+    device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
     return true;
 #else
     (void)block_device;
@@ -74,6 +84,19 @@
 
 void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
     size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+
+    if (LinearExtent* new_extent = extent->AsLinearExtent()) {
+        if (!extents_.empty() && extents_.back()->AsLinearExtent() &&
+            extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) {
+            // If the previous extent can be merged into this new one, do so
+            // to avoid creating unnecessary extents.
+            LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
+            extent = std::make_unique<LinearExtent>(
+                    prev_extent->num_sectors() + new_extent->num_sectors(),
+                    prev_extent->physical_sector());
+            extents_.pop_back();
+        }
+    }
     extents_.push_back(std::move(extent));
 }
 
@@ -82,11 +105,7 @@
     extents_.clear();
 }
 
-void Partition::ShrinkTo(uint64_t requested_size) {
-    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
-    if (size_ <= aligned_size) {
-        return;
-    }
+void Partition::ShrinkTo(uint64_t aligned_size) {
     if (aligned_size == 0) {
         RemoveExtents();
         return;
@@ -106,7 +125,7 @@
         sectors_to_remove -= extent->num_sectors();
         extents_.pop_back();
     }
-    DCHECK(size_ == requested_size);
+    DCHECK(size_ == aligned_size);
 }
 
 std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
@@ -182,6 +201,7 @@
 
     device_info_.alignment = geometry_.alignment;
     device_info_.alignment_offset = geometry_.alignment_offset;
+    device_info_.logical_block_size = geometry_.logical_block_size;
     return true;
 }
 
@@ -205,6 +225,10 @@
         LERROR << "Block device size must be a multiple of 512.";
         return false;
     }
+    if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Logical block size must be a multiple of 512.";
+        return false;
+    }
     if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
         LERROR << "Alignment offset is not sector-aligned.";
         return false;
@@ -248,6 +272,18 @@
         return false;
     }
 
+    // Finally, the size of the allocatable space must be a multiple of the
+    // logical block size. If we have no more free space after this
+    // computation, then we abort. Note that the last sector is inclusive,
+    // so we have to account for that.
+    uint64_t num_free_sectors = last_sector - first_sector + 1;
+    uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    if (num_free_sectors < sectors_per_block) {
+        LERROR << "Not enough space to allocate any partition tables.";
+        return false;
+    }
+    last_sector = first_sector + (num_free_sectors / sectors_per_block) * sectors_per_block - 1;
+
     geometry_.first_logical_sector = first_sector;
     geometry_.last_logical_sector = last_sector;
     geometry_.metadata_max_size = metadata_max_size;
@@ -255,6 +291,7 @@
     geometry_.alignment = device_info_.alignment;
     geometry_.alignment_offset = device_info_.alignment_offset;
     geometry_.block_device_size = device_info_.size;
+    geometry_.logical_block_size = device_info.logical_block_size;
     return true;
 }
 
@@ -290,13 +327,7 @@
     }
 }
 
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
-    // Align the space needed up to the nearest sector.
-    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
-    if (partition->size() >= aligned_size) {
-        return true;
-    }
-
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
     // Figure out how much we need to allocate.
     uint64_t space_needed = aligned_size - partition->size();
     uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
@@ -307,95 +338,101 @@
         uint64_t end;
 
         Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+        uint64_t length() const { return end - start; }
         bool operator<(const Interval& other) const { return start < other.start; }
     };
-    std::vector<Interval> intervals;
 
-    // Collect all extents in the partition table.
+    // Collect all extents in the partition table, then sort them by starting
+    // sector.
+    std::vector<Interval> extents;
     for (const auto& partition : partitions_) {
         for (const auto& extent : partition->extents()) {
             LinearExtent* linear = extent->AsLinearExtent();
             if (!linear) {
                 continue;
             }
-            intervals.emplace_back(linear->physical_sector(),
-                                   linear->physical_sector() + extent->num_sectors());
+            extents.emplace_back(linear->physical_sector(),
+                                 linear->physical_sector() + extent->num_sectors());
         }
     }
+    std::sort(extents.begin(), extents.end());
 
-    // Sort extents by starting sector.
-    std::sort(intervals.begin(), intervals.end());
+    // Convert the extent list into a list of gaps between the extents; i.e.,
+    // the list of ranges that are free on the disk.
+    std::vector<Interval> free_regions;
+    for (size_t i = 1; i < extents.size(); i++) {
+        const Interval& previous = extents[i - 1];
+        const Interval& current = extents[i];
+
+        uint64_t aligned = AlignSector(previous.end);
+        if (aligned >= current.start) {
+            // There is no gap between these two extents, try the next one.
+            // Note that we check with >= instead of >, since alignment may
+            // bump the ending sector past the beginning of the next extent.
+            continue;
+        }
+
+        // The new interval represents the free space starting at the end of
+        // the previous interval, and ending at the start of the next interval.
+        free_regions.emplace_back(aligned, current.start);
+    }
+
+    // Add a final interval representing the remainder of the free space.
+    uint64_t last_free_extent_start =
+            extents.empty() ? geometry_.first_logical_sector : extents.back().end;
+    last_free_extent_start = AlignSector(last_free_extent_start);
+    if (last_free_extent_start <= geometry_.last_logical_sector) {
+        free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1);
+    }
+
+    const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    CHECK(sectors_needed % sectors_per_block == 0);
 
     // Find gaps that we can use for new extents. Note we store new extents in a
     // temporary vector, and only commit them if we are guaranteed enough free
     // space.
     std::vector<std::unique_ptr<LinearExtent>> new_extents;
-    for (size_t i = 1; i < intervals.size(); i++) {
-        const Interval& previous = intervals[i - 1];
-        const Interval& current = intervals[i];
+    for (auto& region : free_regions) {
+        if (region.length() % sectors_per_block != 0) {
+            // This should never happen, because it would imply that we
+            // once allocated an extent that was not a multiple of the
+            // block size. That extent would be rejected by DM_TABLE_LOAD.
+            LERROR << "Region " << region.start << ".." << region.end
+                   << " is not a multiple of the block size, " << sectors_per_block;
 
-        if (previous.end >= current.start) {
-            // There is no gap between these two extents, try the next one. Note that
-            // extents may never overlap, but just for safety, we ignore them if they
-            // do.
-            DCHECK(previous.end == current.start);
-            continue;
+            // If for some reason the final region is mis-sized we still want
+            // to be able to grow partitions. So just to be safe, round the
+            // region down to the nearest block.
+            region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block;
+            if (!region.length()) {
+                continue;
+            }
         }
 
-        uint64_t aligned = AlignSector(previous.end);
-        if (aligned >= current.start) {
-            // After alignment, this extent is not usable.
-            continue;
-        }
+        uint64_t sectors = std::min(sectors_needed, region.length());
+        CHECK(sectors % sectors_per_block == 0);
 
-        // This gap is enough to hold the remainder of the space requested, so we
-        // can allocate what we need and return.
-        if (current.start - aligned >= sectors_needed) {
-            auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
-            sectors_needed -= extent->num_sectors();
-            new_extents.push_back(std::move(extent));
+        auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+        new_extents.push_back(std::move(extent));
+        sectors_needed -= sectors;
+        if (!sectors_needed) {
             break;
         }
-
-        // This gap is not big enough to fit the remainder of the space requested,
-        // so consume the whole thing and keep looking for more.
-        auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
-        sectors_needed -= extent->num_sectors();
-        new_extents.push_back(std::move(extent));
     }
-
-    // If we still have more to allocate, take it from the remaining free space
-    // in the allocatable region.
     if (sectors_needed) {
-        uint64_t first_sector;
-        if (intervals.empty()) {
-            first_sector = geometry_.first_logical_sector;
-        } else {
-            first_sector = intervals.back().end;
-        }
-        DCHECK(first_sector <= geometry_.last_logical_sector);
-
-        // Note: After alignment, |first_sector| may be > the last usable sector.
-        first_sector = AlignSector(first_sector);
-
-        // Note: the last usable sector is inclusive.
-        if (first_sector > geometry_.last_logical_sector ||
-            geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
-            LERROR << "Not enough free space to expand partition: " << partition->name();
-            return false;
-        }
-        auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
-        new_extents.push_back(std::move(extent));
+        LERROR << "Not enough free space to expand partition: " << partition->name();
+        return false;
     }
 
+    // Everything succeeded, so commit the new extents.
     for (auto& extent : new_extents) {
         partition->AddExtent(std::move(extent));
     }
     return true;
 }
 
-void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
-    partition->ShrinkTo(requested_size);
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
+    partition->ShrinkTo(aligned_size);
 }
 
 std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
@@ -455,6 +492,12 @@
 void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
     device_info_.size = device_info.size;
 
+    // Note that if the logical block size changes, we're probably in trouble:
+    // we could have already built extents that will only work on the previous
+    // size.
+    DCHECK(partitions_.empty() ||
+           device_info_.logical_block_size == device_info.logical_block_size);
+
     // The kernel does not guarantee these values are present, so we only
     // replace existing values if the new values are non-zero.
     if (device_info.alignment) {
@@ -465,5 +508,23 @@
     }
 }
 
+bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
+    // Align the space needed up to the nearest sector.
+    uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size);
+    uint64_t old_size = partition->size();
+
+    if (aligned_size > old_size) {
+        if (!GrowPartition(partition, aligned_size)) {
+            return false;
+        }
+    } else if (aligned_size < partition->size()) {
+        ShrinkPartition(partition, aligned_size);
+    }
+
+    LINFO << "Partition " << partition->name() << " will resize from " << old_size << " bytes to "
+          << aligned_size << " bytes";
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index b610fd4..da9c8f3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -45,7 +45,7 @@
 
     Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
-    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
     EXPECT_EQ(system->size(), 65536);
     ASSERT_EQ(system->extents().size(), 1);
 
@@ -55,24 +55,28 @@
     // The first logical sector will be (4096+1024*2)/512 = 12.
     EXPECT_EQ(extent->physical_sector(), 12);
 
-    // Test growing to the same size.
-    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    // Test resizing to the same size.
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
     EXPECT_EQ(system->size(), 65536);
     EXPECT_EQ(system->extents().size(), 1);
     EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    // Test growing to a smaller size.
-    EXPECT_EQ(builder->GrowPartition(system, 0), true);
-    EXPECT_EQ(system->size(), 65536);
+    // Test resizing to a smaller size.
+    EXPECT_EQ(builder->ResizePartition(system, 0), true);
+    EXPECT_EQ(system->size(), 0);
+    EXPECT_EQ(system->extents().size(), 0);
+    // Test resizing to a greater size.
+    builder->ResizePartition(system, 131072);
+    EXPECT_EQ(system->size(), 131072);
     EXPECT_EQ(system->extents().size(), 1);
-    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    // Test shrinking to a greater size.
-    builder->ShrinkPartition(system, 131072);
-    EXPECT_EQ(system->size(), 65536);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 131072 / LP_SECTOR_SIZE);
+    // Test resizing again, that the extents are merged together.
+    builder->ResizePartition(system, 1024 * 256);
+    EXPECT_EQ(system->size(), 1024 * 256);
     EXPECT_EQ(system->extents().size(), 1);
-    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), (1024 * 256) / LP_SECTOR_SIZE);
 
     // Test shrinking within the same extent.
-    builder->ShrinkPartition(system, 32768);
+    builder->ResizePartition(system, 32768);
     EXPECT_EQ(system->size(), 32768);
     EXPECT_EQ(system->extents().size(), 1);
     extent = system->extents()[0]->AsLinearExtent();
@@ -81,7 +85,7 @@
     EXPECT_EQ(extent->physical_sector(), 12);
 
     // Test shrinking to 0.
-    builder->ShrinkPartition(system, 0);
+    builder->ResizePartition(system, 0);
     EXPECT_EQ(system->size(), 0);
     EXPECT_EQ(system->extents().size(), 0);
 }
@@ -92,12 +96,12 @@
     // Test that we align up to one sector.
     Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
-    EXPECT_EQ(builder->GrowPartition(system, 10000), true);
-    EXPECT_EQ(system->size(), 10240);
+    EXPECT_EQ(builder->ResizePartition(system, 10000), true);
+    EXPECT_EQ(system->size(), 12288);
     EXPECT_EQ(system->extents().size(), 1);
 
-    builder->ShrinkPartition(system, 9000);
-    EXPECT_EQ(system->size(), 9216);
+    builder->ResizePartition(system, 7000);
+    EXPECT_EQ(system->size(), 8192);
     EXPECT_EQ(system->extents().size(), 1);
 }
 
@@ -121,13 +125,13 @@
 
 TEST(liblp, InternalAlignment) {
     // Test the metadata fitting within alignment.
-    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
+    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
 
     // Test a large alignment offset thrown in.
     device_info.alignment_offset = 753664;
@@ -136,7 +140,7 @@
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
 
     // Alignment offset without alignment doesn't mean anything.
     device_info.alignment = 0;
@@ -151,7 +155,7 @@
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 78);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1973);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -164,7 +168,7 @@
 }
 
 TEST(liblp, InternalPartitionAlignment) {
-    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
+    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
 
     Partition* a = builder->AddPartition("a", TEST_GUID, 0);
@@ -174,8 +178,8 @@
 
     // Add a bunch of small extents to each, interleaving.
     for (size_t i = 0; i < 10; i++) {
-        ASSERT_TRUE(builder->GrowPartition(a, a->size() + 4096));
-        ASSERT_TRUE(builder->GrowPartition(b, b->size() + 4096));
+        ASSERT_TRUE(builder->ResizePartition(a, a->size() + 4096));
+        ASSERT_TRUE(builder->ResizePartition(b, b->size() + 4096));
     }
     EXPECT_EQ(a->size(), 40960);
     EXPECT_EQ(b->size(), 40960);
@@ -203,9 +207,9 @@
 
     Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
-    EXPECT_EQ(builder->GrowPartition(system, 1036288), true);
+    EXPECT_EQ(builder->ResizePartition(system, 1036288), true);
     EXPECT_EQ(system->size(), 1036288);
-    EXPECT_EQ(builder->GrowPartition(system, 1036289), false);
+    EXPECT_EQ(builder->ResizePartition(system, 1036289), false);
 }
 
 TEST(liblp, BuildComplex) {
@@ -215,9 +219,9 @@
     Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
-    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
-    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
-    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
     EXPECT_EQ(system->size(), 98304);
     EXPECT_EQ(vendor->size(), 32768);
 
@@ -268,9 +272,9 @@
     Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
-    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
-    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
-    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
 
     unique_ptr<LpMetadata> exported = builder->Export();
     EXPECT_NE(exported, nullptr);
@@ -323,9 +327,9 @@
     Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
-    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
-    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
-    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
 
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -382,7 +386,7 @@
     static const size_t kMetadataSize = 64 * 1024;
 
     // No space to store metadata + geometry.
-    BlockDeviceInfo device_info(kDiskSize, 0, 0);
+    BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
@@ -391,8 +395,8 @@
     builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
-    // Space for metadata + geometry + one free sector.
-    device_info.size += LP_SECTOR_SIZE;
+    // Space for metadata + geometry + one free block.
+    device_info.size += device_info.logical_block_size;
     builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_NE(builder, nullptr);
 
@@ -425,19 +429,21 @@
     ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
     ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
     ASSERT_LE(device_info.alignment_offset, INT_MAX);
+    ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
 
     // Having an alignment offset > alignment doesn't really make sense.
     ASSERT_LT(device_info.alignment_offset, device_info.alignment);
 }
 
 TEST(liblp, UpdateBlockDeviceInfo) {
-    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
+    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
     EXPECT_EQ(builder->block_device_info().size, device_info.size);
     EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
     EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+    EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size);
 
     device_info.alignment = 0;
     device_info.alignment_offset = 2048;
@@ -451,3 +457,27 @@
     EXPECT_EQ(builder->block_device_info().alignment, 8192);
     EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
 }
+
+TEST(liblp, InvalidBlockSize) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    EXPECT_EQ(builder, nullptr);
+}
+
+TEST(liblp, AlignedExtentSize) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(partition, 512));
+    EXPECT_EQ(partition->size(), 4096);
+}
+
+TEST(liblp, AlignedFreeSpace) {
+    // Only one sector free - at least one block is required.
+    BlockDeviceInfo device_info(10240, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
+    ASSERT_EQ(builder, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 8bde313..a35cf8e 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -32,11 +32,16 @@
 
 // By default, partitions are aligned on a 1MiB boundary.
 static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+static const uint32_t kDefaultBlockSize = 4096;
 
 struct BlockDeviceInfo {
-    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
-    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
-        : size(size), alignment(alignment), alignment_offset(alignment_offset) {}
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
+                    uint32_t logical_block_size)
+        : size(size),
+          alignment(alignment),
+          alignment_offset(alignment_offset),
+          logical_block_size(logical_block_size) {}
     // Size of the block device, in bytes.
     uint64_t size;
     // Optimal target alignment, in bytes. Partition extents will be aligned to
@@ -46,6 +51,8 @@
     // |alignment_offset| on the target device is correctly aligned on its
     // parent device. This value must be 0 or a multiple of 512.
     uint32_t alignment_offset;
+    // Block size, for aligning extent sizes and partition sizes.
+    uint32_t logical_block_size;
 };
 
 // Abstraction around dm-targets that can be encoded into logical partition tables.
@@ -74,6 +81,7 @@
     LinearExtent* AsLinearExtent() override { return this; }
 
     uint64_t physical_sector() const { return physical_sector_; }
+    uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
 
   private:
     uint64_t physical_sector_;
@@ -88,6 +96,8 @@
 };
 
 class Partition final {
+    friend class MetadataBuilder;
+
   public:
     Partition(const std::string& name, const std::string& guid, uint32_t attributes);
 
@@ -97,10 +107,6 @@
     // Remove all extents from this partition.
     void RemoveExtents();
 
-    // Remove and/or shrink extents until the partition is the requested size.
-    // See MetadataBuilder::ShrinkPartition for more information.
-    void ShrinkTo(uint64_t requested_size);
-
     const std::string& name() const { return name_; }
     uint32_t attributes() const { return attributes_; }
     const std::string& guid() const { return guid_; }
@@ -108,6 +114,8 @@
     uint64_t size() const { return size_; }
 
   private:
+    void ShrinkTo(uint64_t aligned_size);
+
     std::string name_;
     std::string guid_;
     std::vector<std::unique_ptr<Extent>> extents_;
@@ -144,7 +152,7 @@
     // size. This is a convenience method for tests.
     static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
                                                 uint32_t metadata_slot_count) {
-        BlockDeviceInfo device_info(blockdev_size, 0, 0);
+        BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
         return New(device_info, metadata_max_size, metadata_slot_count);
     }
 
@@ -162,29 +170,17 @@
     // Find a partition by name. If no partition is found, nullptr is returned.
     Partition* FindPartition(const std::string& name);
 
-    // Grow a partition to the requested size. If the partition's size is already
-    // greater or equal to the requested size, this will return true and the
-    // partition table will not be changed. Otherwise, a greedy algorithm is
-    // used to find free gaps in the partition table and allocate them for this
-    // partition. If not enough space can be allocated, false is returned, and
-    // the parition table will not be modified.
+    // Grow or shrink a partition to the requested size. This size will be
+    // rounded UP to the nearest block (512 bytes).
     //
-    // The size will be rounded UP to the nearest sector.
+    // When growing a partition, a greedy algorithm is used to find free gaps
+    // in the partition table and allocate them. If not enough space can be
+    // allocated, false is returned, and the parition table will not be
+    // modified.
     //
     // Note, this is an in-memory operation, and it does not alter the
     // underlying filesystem or contents of the partition on disk.
-    bool GrowPartition(Partition* partition, uint64_t requested_size);
-
-    // Shrink a partition to the requested size. If the partition is already
-    // smaller than the given size, this will return and the partition table
-    // will not be changed. Otherwise, extents will be removed and/or shrunk
-    // from the end of the partition until it is the requested size.
-    //
-    // The size will be rounded UP to the nearest sector.
-    //
-    // Note, this is an in-memory operation, and it does not alter the
-    // underlying filesystem or contents of the partition on disk.
-    void ShrinkPartition(Partition* partition, uint64_t requested_size);
+    bool ResizePartition(Partition* partition, uint64_t requested_size);
 
     // Amount of space that can be allocated to logical partitions.
     uint64_t AllocatableSpace() const;
@@ -198,7 +194,8 @@
     MetadataBuilder();
     bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
     bool Init(const LpMetadata& metadata);
-
+    bool GrowPartition(Partition* partition, uint64_t aligned_size);
+    void ShrinkPartition(Partition* partition, uint64_t aligned_size);
     uint64_t AlignSector(uint64_t sector);
 
     LpMetadataGeometry geometry_;
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index e1323e1..52c80f7 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -136,6 +136,12 @@
      * can be used to verify the geometry against a target device.
      */
     uint64_t block_device_size;
+
+    /* 76: Logical block size of the super partition block device. This is the
+     * minimal alignment for partition and extent sizes, and it must be a
+     * multiple of LP_SECTOR_SIZE.
+     */
+    uint32_t logical_block_size;
 } __attribute__((packed)) LpMetadataGeometry;
 
 /* The logical partition metadata has a number of tables; they are described
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index bbbedc7..638f4b3 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -85,7 +85,7 @@
     if (!system) {
         return false;
     }
-    return builder->GrowPartition(system, 24 * 1024);
+    return builder->ResizePartition(system, 24 * 1024);
 }
 
 // Create a temporary disk and flash it with the default partition setup.
@@ -345,7 +345,7 @@
     ASSERT_NE(partition, nullptr);
     // Add one extent to any partition to fill up more space - we're at 508
     // bytes after this, out of 512.
-    ASSERT_TRUE(builder->GrowPartition(partition, 1024));
+    ASSERT_TRUE(builder->ResizePartition(partition, 1024));
 
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 4522275..6ef5124 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -26,6 +26,8 @@
 #include "liblp/metadata_format.h"
 
 #define LP_TAG "[liblp]"
+#define LWARN LOG(WARNING) << LP_TAG
+#define LINFO LOG(INFO) << LP_TAG
 #define LERROR LOG(ERROR) << LP_TAG
 #define PERROR PLOG(ERROR) << LP_TAG
 
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 092dbf1..7bf42ae 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -40,7 +40,8 @@
                                    80000,
                                    0,
                                    0,
-                                   1024 * 1024};
+                                   1024 * 1024,
+                                   4096};
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 156319b..fc9d83f 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -304,7 +304,11 @@
         PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
         return false;
     }
-    return FlashPartitionTable(fd, metadata, slot_number);
+    if (!FlashPartitionTable(fd, metadata, slot_number)) {
+        return false;
+    }
+    LWARN << "Flashed new logical partition geometry to " << block_device;
+    return true;
 }
 
 bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
@@ -314,7 +318,12 @@
         PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
         return false;
     }
-    return UpdatePartitionTable(fd, metadata, slot_number);
+    if (!UpdatePartitionTable(fd, metadata, slot_number)) {
+        return false;
+    }
+    LINFO << "Updated logical partition table at slot " << slot_number << " on device "
+          << block_device;
+    return true;
 }
 
 bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
index 28f0b07..6d5d1ea 100644
--- a/gatekeeperd/Android.mk
+++ b/gatekeeperd/Android.mk
@@ -32,6 +32,7 @@
 	libbase \
 	libutils \
 	libcrypto \
+	libkeystore_aidl \
 	libkeystore_binder \
 	libhidlbase \
 	libhidltransport \
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index abb387c..5f3ce36 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -25,14 +25,15 @@
 #include <unistd.h>
 #include <memory>
 
+#include <android/security/IKeystoreService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
 #include <gatekeeper/password_handle.h> // for password_handle_t
 #include <hardware/gatekeeper.h>
 #include <hardware/hw_auth_token.h>
-#include <keystore/IKeystoreService.h>
 #include <keystore/keystore.h> // For error code
+#include <keystore/keystore_return_types.h>
 #include <log/log.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
@@ -317,11 +318,15 @@
             // TODO: cache service?
             sp<IServiceManager> sm = defaultServiceManager();
             sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-            sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+            sp<security::IKeystoreService> service =
+                interface_cast<security::IKeystoreService>(binder);
             if (service != NULL) {
-                auto ret = service->addAuthToken(*auth_token, *auth_token_length);
-                if (!ret.isOk()) {
-                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, int32_t(ret));
+                std::vector<uint8_t> auth_token_vector(*auth_token,
+                                                       (*auth_token) + *auth_token_length);
+                int result = 0;
+                auto binder_result = service->addAuthToken(auth_token_vector, &result);
+                if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, result);
                 }
             } else {
                 ALOGE("Unable to communicate with KeyStore");
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 7269b62..6b00f81 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -1,6 +1,7 @@
 cc_library_headers {
     name: "libhealthd_headers",
     vendor_available: true,
+    recovery_available: true,
     export_include_dirs: ["include"],
     header_libs: ["libbatteryservice_headers"],
     export_header_lib_headers: ["libbatteryservice_headers"],
@@ -9,7 +10,9 @@
 cc_library_static {
     name: "libbatterymonitor",
     srcs: ["BatteryMonitor.cpp"],
+    cflags: ["-Wall", "-Werror"],
     vendor_available: true,
+    recovery_available: true,
     export_include_dirs: ["include"],
     shared_libs: [
         "libutils",
@@ -18,3 +21,66 @@
     header_libs: ["libhealthd_headers"],
     export_header_lib_headers: ["libhealthd_headers"],
 }
+
+cc_defaults {
+    name: "android.hardware.health@2.0-service_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+        "libhealthservice",
+        "libhealthstoragedefault",
+        "libbatterymonitor",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "android.hardware.health@2.0",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.health@2.0-service",
+    defaults: ["android.hardware.health@2.0-service_defaults"],
+
+    vendor: true,
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.health@2.0-service.rc"],
+    srcs: [
+        "HealthServiceDefault.cpp",
+    ],
+
+    overrides: [
+        "healthd",
+    ]
+}
+
+cc_binary {
+    name: "healthd",
+    defaults: ["android.hardware.health@2.0-service_defaults"],
+
+    init_rc: ["healthd.rc"],
+    srcs: [
+        "HealthServiceHealthd.cpp",
+    ],
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "android.hardware.health@1.0",
+    ],
+
+    vintf_fragments: [
+        "manifest_healthd.xml"
+    ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 1244903..f7214c6 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,27 +3,6 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    healthd_mode_android.cpp \
-    BatteryPropertiesRegistrar.cpp
-
-LOCAL_MODULE := libhealthd_android
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
-    $(LOCAL_PATH) \
-    $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
-    libbatterymonitor \
-    libbatteryservice \
-    libutils \
-    libbase \
-    libcutils \
-    liblog \
-    libc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
 
 LOCAL_MODULE := libhealthd_draw
 
@@ -45,6 +24,8 @@
 LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
 endif
 
+LOCAL_HEADER_LIBRARIES := libbatteryservice_headers
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -68,6 +49,11 @@
     $(LOCAL_PATH)/include
 
 LOCAL_STATIC_LIBRARIES := \
+    android.hardware.health@2.0 \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@1.0 \
+    android.hardware.health@1.0-convert \
+    libhealthstoragedefault \
     libminui \
     libpng \
     libz \
@@ -92,7 +78,6 @@
 endif
 
 LOCAL_SRC_FILES := \
-    healthd_common.cpp \
     charger.cpp \
 
 LOCAL_MODULE := charger
@@ -106,14 +91,17 @@
 ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 LOCAL_CFLAGS += -DCHARGER_NO_UI
 endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-endif
 
-LOCAL_STATIC_LIBRARIES := \
+CHARGER_STATIC_LIBRARIES := \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@2.0 \
+    android.hardware.health@1.0 \
+    android.hardware.health@1.0-convert \
+    libhidltransport \
+    libhidlbase \
+    libhwbinder_noltopgo \
+    libhealthstoragedefault \
+    libvndksupport \
     libhealthd_charger \
     libhealthd_draw \
     libbatterymonitor \
@@ -124,6 +112,8 @@
     libm \
     libc \
 
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 LOCAL_STATIC_LIBRARIES += \
     libminui \
@@ -144,6 +134,21 @@
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SRC_FILES := \
+    charger.cpp \
+    charger_test.cpp \
+
+include $(BUILD_EXECUTABLE)
+
+CHARGER_STATIC_LIBRARIES :=
+
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 define _add-charger-image
 include $$(CLEAR_VARS)
@@ -171,41 +176,3 @@
 _add-charger-image :=
 _img_modules :=
 endif # LOCAL_CHARGER_NO_UI
-
-### healthd ###
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    healthd_common.cpp \
-    healthd.cpp \
-
-LOCAL_MODULE := healthd
-LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-endif
-
-LOCAL_STATIC_LIBRARIES := \
-    libhealthd_android \
-    libbatterymonitor \
-    libbatteryservice \
-    android.hardware.health@1.0-convert \
-
-LOCAL_SHARED_LIBRARIES := \
-    libbinder \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libm \
-    libc \
-    libhidlbase \
-    libhidltransport \
-    android.hardware.health@1.0 \
-
-include $(BUILD_EXECUTABLE)
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index fa79d0b..80c5afe 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -88,6 +88,10 @@
     initBatteryProperties(&props);
 }
 
+struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
+    return batteryMonitor->props;
+}
+
 int BatteryMonitor::getBatteryStatus(const char* status) {
     int ret;
     struct sysfsStringEnumMap batteryStatusMap[] = {
@@ -531,12 +535,6 @@
                                       POWER_SUPPLY_SYSFS_PATH, name);
                     if (access(path, R_OK) == 0) {
                         mHealthdConfig->batteryVoltagePath = path;
-                    } else {
-                        path.clear();
-                        path.appendFormat("%s/%s/batt_vol",
-                                          POWER_SUPPLY_SYSFS_PATH, name);
-                        if (access(path, R_OK) == 0)
-                            mHealthdConfig->batteryVoltagePath = path;
                     }
                 }
 
@@ -586,12 +584,6 @@
                                       name);
                     if (access(path, R_OK) == 0) {
                         mHealthdConfig->batteryTemperaturePath = path;
-                    } else {
-                        path.clear();
-                        path.appendFormat("%s/%s/batt_temp",
-                                          POWER_SUPPLY_SYSFS_PATH, name);
-                        if (access(path, R_OK) == 0)
-                            mHealthdConfig->batteryTemperaturePath = path;
                     }
                 }
 
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
deleted file mode 100644
index e51a06d..0000000
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BatteryPropertiesRegistrar.h"
-#include <batteryservice/BatteryService.h>
-#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.h>
-#include <private/android_filesystem_config.h>
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-
-#include <healthd/healthd.h>
-
-namespace android {
-
-void BatteryPropertiesRegistrar::publish(
-    const sp<BatteryPropertiesRegistrar>& service) {
-    defaultServiceManager()->addService(String16("batteryproperties"), service);
-}
-
-void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
-    Vector<sp<IBatteryPropertiesListener> > listenersCopy;
-
-    // Binder currently may service an incoming oneway transaction whenever an
-    // outbound oneway call is made (if there is already a pending incoming
-    // oneway call waiting).  This is considered a bug and may change in the
-    // future.  For now, avoid recursive mutex lock while making outbound
-    // calls by making a local copy of the current list of listeners.
-    {
-        Mutex::Autolock _l(mRegistrationLock);
-        listenersCopy = mListeners;
-    }
-    for (size_t i = 0; i < listenersCopy.size(); i++) {
-        listenersCopy[i]->batteryPropertiesChanged(props);
-    }
-}
-
-void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {
-    {
-        if (listener == NULL)
-            return;
-        Mutex::Autolock _l(mRegistrationLock);
-        // check whether this is a duplicate
-        for (size_t i = 0; i < mListeners.size(); i++) {
-            if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
-                return;
-            }
-        }
-
-        mListeners.add(listener);
-        IInterface::asBinder(listener)->linkToDeath(this);
-    }
-    healthd_battery_update();
-}
-
-void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
-    if (listener == NULL)
-        return;
-    Mutex::Autolock _l(mRegistrationLock);
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
-            IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-}
-
-status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
-    return healthd_get_property(id, val);
-}
-
-void BatteryPropertiesRegistrar::scheduleUpdate() {
-    healthd_battery_update();
-}
-
-status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) {
-    IPCThreadState* self = IPCThreadState::self();
-    const int pid = self->getCallingPid();
-    const int uid = self->getCallingUid();
-    if ((uid != AID_SHELL) &&
-        !PermissionCache::checkPermission(
-                String16("android.permission.DUMP"), pid, uid))
-        return PERMISSION_DENIED;
-
-    healthd_dump_battery_state(fd);
-    return OK;
-}
-
-void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
-    Mutex::Autolock _l(mRegistrationLock);
-
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) == who) {
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-}
-
-}  // namespace android
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
deleted file mode 100644
index 14e9145..0000000
--- a/healthd/BatteryPropertiesRegistrar.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-
-#include <binder/IBinder.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-#include <batteryservice/BatteryService.h>
-#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-
-namespace android {
-
-class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
-                                   public IBinder::DeathRecipient {
-public:
-    void publish(const sp<BatteryPropertiesRegistrar>& service);
-    void notifyListeners(const struct BatteryProperties& props);
-    void scheduleUpdate();
-
-private:
-    Mutex mRegistrationLock;
-    Vector<sp<IBatteryPropertiesListener> > mListeners;
-
-    void registerListener(const sp<IBatteryPropertiesListener>& listener);
-    void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
-    status_t getProperty(int id, struct BatteryProperty *val);
-    status_t dump(int fd, const Vector<String16>& args);
-    void binderDied(const wp<IBinder>& who);
-};
-
-};  // namespace android
-
-#endif // HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
diff --git a/healthd/HealthServiceDefault.cpp b/healthd/HealthServiceDefault.cpp
new file mode 100644
index 0000000..89ecc2f
--- /dev/null
+++ b/healthd/HealthServiceDefault.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <health2/service.h>
+#include <healthd/healthd.h>
+
+void healthd_board_init(struct healthd_config*) {
+    // Implementation-defined init logic goes here.
+    // 1. config->periodic_chores_interval_* variables
+    // 2. config->battery*Path variables
+    // 3. config->energyCounter. In this implementation, energyCounter is not defined.
+
+    // use defaults
+}
+
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+    // Implementation-defined update logic goes here. An implementation
+    // can make modifications to prop before broadcasting it to all callbacks.
+
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+int main() {
+    return health_service_main();
+}
diff --git a/healthd/HealthServiceHealthd.cpp b/healthd/HealthServiceHealthd.cpp
new file mode 100644
index 0000000..5fd2597
--- /dev/null
+++ b/healthd/HealthServiceHealthd.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "healthd"
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <health2/service.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::OK;
+using android::NAME_NOT_FOUND;
+using android::hardware::health::V1_0::HealthConfig;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V1_0::Result;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+
+using IHealthLegacy = android::hardware::health::V1_0::IHealth;
+
+static android::sp<IHealthLegacy> gHealth_1_0;
+
+static int healthd_board_get_energy_counter(int64_t* energy) {
+    if (gHealth_1_0 == nullptr) {
+        return NAME_NOT_FOUND;
+    }
+
+    Result result = Result::NOT_SUPPORTED;
+    gHealth_1_0->energyCounter([energy, &result](Result ret, int64_t energyOut) {
+        result = ret;
+        *energy = energyOut;
+    });
+
+    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
+}
+
+void healthd_board_init(struct healthd_config* config) {
+    gHealth_1_0 = IHealthLegacy::getService();
+
+    if (gHealth_1_0 == nullptr) {
+        return;
+    }
+
+    HealthConfig halConfig{};
+    convertToHealthConfig(config, halConfig);
+    gHealth_1_0->init(halConfig, [config](const auto& halConfigOut) {
+        convertFromHealthConfig(halConfigOut, config);
+        // always redirect energy counter queries
+        config->energyCounter = healthd_board_get_energy_counter;
+    });
+    LOG(INFO) << LOG_TAG << ": redirecting calls to 1.0 health HAL";
+}
+
+// TODO(b/68724651): Move this function into healthd_mode_service_2_0_battery_update
+// with logthis returned.
+int healthd_board_battery_update(struct android::BatteryProperties* props) {
+    int logthis = 0;
+
+    if (gHealth_1_0 == nullptr) {
+        return logthis;
+    }
+
+    HealthInfo info;
+    convertToHealthInfo(props, info);
+    gHealth_1_0->update(info, [props, &logthis](int32_t ret, const auto& infoOut) {
+        logthis = ret;
+        convertFromHealthInfo(infoOut, props);
+    });
+
+    return logthis;
+}
+
+int main() {
+    return health_service_main("backup");
+}
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
new file mode 100644
index 0000000..dca0ccc
--- /dev/null
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -0,0 +1,5 @@
+service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service
+    class hal
+    user system
+    group system
+    file /dev/kmsg w
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 5a8fe1a..43e7fd5 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "charger"
 #define KLOG_LEVEL 6
 
+#include <health2/Health.h>
 #include <healthd/healthd.h>
 
 #include <stdlib.h>
@@ -62,7 +63,9 @@
 };
 #endif
 
-static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
+static void healthd_mode_nop_init(struct healthd_config* config) {
+    using android::hardware::health::V2_0::implementation::Health;
+    Health::initInstance(config);
 }
 
 static int healthd_mode_nop_preparetowait(void) {
@@ -76,7 +79,7 @@
     struct android::BatteryProperties* /*props*/) {
 }
 
-int main(int argc, char **argv) {
+int healthd_charger_main(int argc, char** argv) {
     int ch;
 
     healthd_mode_ops = &charger_ops;
@@ -100,3 +103,9 @@
 
     return healthd_main();
 }
+
+#ifndef CHARGER_TEST
+int main(int argc, char** argv) {
+    return healthd_charger_main(argc, argv);
+}
+#endif
diff --git a/healthd/charger_test.cpp b/healthd/charger_test.cpp
new file mode 100644
index 0000000..a7e2161
--- /dev/null
+++ b/healthd/charger_test.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "charger_test"
+#include <android/log.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <fstream>
+#include <iostream>
+#include <mutex>
+#include <streambuf>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <health2/Health.h>
+
+#define LOG_THIS(fmt, ...)     \
+    ALOGE(fmt, ##__VA_ARGS__); \
+    printf(fmt "\n", ##__VA_ARGS__);
+
+template <typename T>
+class Atomic {
+  public:
+    Atomic(T&& init) : mValue(std::move(init)) {}
+    void set(T&& newVal) {
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            mValue = std::move(newVal);
+        }
+        mChanged.notify_all();
+    }
+    bool waitFor(long ms, const T& expectVal) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        return mChanged.wait_for(lock, std::chrono::milliseconds(ms),
+                                 [this, &expectVal] { return mValue == expectVal; });
+    }
+  private:
+    std::mutex mMutex;
+    std::condition_variable mChanged;
+    T mValue;
+};
+
+Atomic<bool>& getUpdateNotifier() {
+    static Atomic<bool> val(false);
+    return val;
+}
+
+int energyCounter(int64_t* counter) {
+    *counter = 0xEC12345;
+    return 0;
+}
+
+const char* createFile(const char* path, const char* content) {
+    std::ofstream stream(path);
+    if (!stream.is_open()) {
+        LOG_THIS("Cannot create file %s", path);
+        return NULL;
+    }
+    stream << content << std::endl;
+    stream.close();
+    return path;
+}
+
+std::string openToString(const char* path) {
+    std::ifstream stream(path);
+    if (!stream.is_open()) {
+        LOG_THIS("Cannot open file %s", path);
+        return "";
+    }
+    return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
+}
+
+int expectContains(const std::string& content, const std::vector<std::string>& fields) {
+    int status = 0;
+    for (const auto& field : fields) {
+        auto pos = content.find(field);
+        if (pos == std::string::npos) {
+            LOG_THIS("Cannot find substr '%s'", field.c_str());
+            status = 1;
+        }
+    }
+    return status;
+}
+
+::android::hardware::hidl_handle createHidlHandle(const char* filepath) {
+    int fd = creat(filepath, S_IRUSR | S_IWUSR);
+    if (fd < 0) return {};
+    native_handle_t* nativeHandle = native_handle_create(1, 0);
+    nativeHandle->data[0] = fd;
+    ::android::hardware::hidl_handle handle;
+    handle.setTo(nativeHandle, true /* shouldOwn */);
+    return handle;
+}
+
+void healthd_board_init(struct healthd_config* config) {
+    config->periodic_chores_interval_fast = 60;
+    config->periodic_chores_interval_slow = 600;
+
+    config->batteryStatusPath = createFile("/data/local/tmp/batteryStatus", "Not charging");
+    config->batteryHealthPath = createFile("/data/local/tmp/batteryHealth", "Unspecified failure");
+    config->batteryPresentPath = createFile("/data/local/tmp/batteryPresent", "1");
+    config->batteryCapacityPath = createFile("/data/local/tmp/batteryCapacity", "47");
+    config->batteryVoltagePath = createFile("/data/local/tmp/batteryVoltage", "45000");
+    config->batteryTemperaturePath = createFile("/data/local/tmp/batteryTemperature", "987");
+    config->batteryTechnologyPath = createFile("/data/local/tmp/batteryTechnology", "NiCd");
+    config->batteryCurrentNowPath = createFile("/data/local/tmp/batteryCurrentNow", "99000");
+    config->batteryCurrentAvgPath = createFile("/data/local/tmp/batteryCurrentAvg", "98000");
+    config->batteryChargeCounterPath = createFile("/data/local/tmp/batteryChargeCounter", "600");
+    config->batteryFullChargePath = createFile("/data/local/tmp/batteryFullCharge", "3515547");
+    config->batteryCycleCountPath = createFile("/data/local/tmp/batteryCycleCount", "77");
+
+    config->energyCounter = energyCounter;
+    config->boot_min_cap = 50;
+    config->screen_on = NULL;
+}
+
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+    getUpdateNotifier().set(true /* updated */);
+
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+extern int healthd_charger_main(int argc, char** argv);
+
+int main(int argc, char** argv) {
+    using android::hardware::health::V2_0::implementation::Health;
+
+    const char* dumpFile = "/data/local/tmp/dump.txt";
+
+    std::thread bgThread([=] {
+        healthd_charger_main(argc, argv);
+    });
+
+    // wait for healthd_init to finish
+    if (!getUpdateNotifier().waitFor(1000 /* wait ms */, true /* updated */)) {
+        LOG_THIS("Time out.");
+        exit(1);
+    }
+
+    Health::getImplementation()->debug(createHidlHandle(dumpFile), {} /* options */);
+
+    std::string content = openToString(dumpFile);
+    int status = expectContains(content, {
+        "status: 4",
+        "health: 6",
+        "present: 1",
+        "level: 47",
+        "voltage: 45",
+        "temp: 987",
+        "current now: 99000",
+        "current avg: 98000",
+        "charge counter: 600",
+        "current now: 99",
+        "cycle count: 77",
+        "Full charge: 3515547"
+    });
+
+    if (status == 0) {
+        LOG_THIS("Test success.");
+    } else {
+        LOG_THIS("Actual dump:\n%s", content.c_str());
+    }
+
+    exit(status);  // force bgThread to exit
+}
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
deleted file mode 100644
index ed1971a..0000000
--- a/healthd/healthd.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2016 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 "healthd"
-#define KLOG_LEVEL 6
-
-#include <healthd/healthd.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <cutils/klog.h>
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <android/hardware/health/1.0/types.h>
-#include <hal_conversion.h>
-
-using namespace android;
-
-using IHealth = ::android::hardware::health::V1_0::IHealth;
-using Result = ::android::hardware::health::V1_0::Result;
-using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
-using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
-
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-
-// device specific hal interface;
-static sp<IHealth> gHealth;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// Android mode
-extern void healthd_mode_android_init(struct healthd_config *config);
-extern int healthd_mode_android_preparetowait(void);
-extern void healthd_mode_android_heartbeat(void);
-extern void healthd_mode_android_battery_update(
-    struct android::BatteryProperties *props);
-
-static struct healthd_mode_ops android_ops = {
-    .init = healthd_mode_android_init,
-    .preparetowait = healthd_mode_android_preparetowait,
-    .heartbeat = healthd_mode_android_heartbeat,
-    .battery_update = healthd_mode_android_battery_update,
-};
-
-// default energy counter property redirect to talk to device
-// HAL
-static int healthd_board_get_energy_counter(int64_t *energy) {
-
-    if (gHealth == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    Result result = Result::NOT_SUPPORTED;
-    gHealth->energyCounter([=, &result] (Result ret, int64_t energyOut) {
-                result = ret;
-                *energy = energyOut;
-            });
-
-    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
-}
-
-void healthd_board_init(struct healthd_config *config) {
-
-    // Initialize the board HAL - Equivalent of healthd_board_init(config)
-    // in charger/recovery mode.
-
-    gHealth = IHealth::getService();
-    if (gHealth == nullptr) {
-        KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
-        return;
-    }
-
-    HealthConfig halConfig;
-    convertToHealthConfig(config, halConfig);
-    gHealth->init(halConfig, [=] (const auto &halConfigOut) {
-            convertFromHealthConfig(halConfigOut, config);
-            // always redirect energy counter queries
-            config->energyCounter = healthd_board_get_energy_counter;
-            });
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties *props) {
-    int logthis = 0;
-
-    if (gHealth == nullptr) {
-        return logthis;
-    }
-
-    HealthInfo info;
-    convertToHealthInfo(props, info);
-    gHealth->update(info,
-            [=, &logthis] (int32_t ret, const auto &infoOut) {
-                logthis = ret;
-                convertFromHealthInfo(infoOut, props);
-            });
-
-    return logthis;
-}
-
-int main(int /*argc*/, char ** /*argv*/) {
-
-    healthd_mode_ops = &android_ops;
-
-    return healthd_main();
-}
diff --git a/healthd/healthd.rc b/healthd/healthd.rc
new file mode 100644
index 0000000..8e2ebb6
--- /dev/null
+++ b/healthd/healthd.rc
@@ -0,0 +1,4 @@
+service healthd /system/bin/healthd
+    class hal
+    critical
+    group root system wakelock
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
deleted file mode 100644
index 6599919..0000000
--- a/healthd/healthd_common.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "healthd-common"
-#define KLOG_LEVEL 6
-
-#include <healthd/healthd.h>
-#include <healthd/BatteryMonitor.h>
-
-#include <errno.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <batteryservice/BatteryService.h>
-#include <cutils/klog.h>
-#include <cutils/uevent.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <utils/Errors.h>
-
-using namespace android;
-
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
-  // Periodic chores fast interval in seconds
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-#else
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-#endif
-
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
-  // Periodic chores fast interval in seconds
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
-#else
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-#endif
-
-static struct healthd_config healthd_config = {
-    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
-    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
-    .batteryStatusPath = String8(String8::kEmptyString),
-    .batteryHealthPath = String8(String8::kEmptyString),
-    .batteryPresentPath = String8(String8::kEmptyString),
-    .batteryCapacityPath = String8(String8::kEmptyString),
-    .batteryVoltagePath = String8(String8::kEmptyString),
-    .batteryTemperaturePath = String8(String8::kEmptyString),
-    .batteryTechnologyPath = String8(String8::kEmptyString),
-    .batteryCurrentNowPath = String8(String8::kEmptyString),
-    .batteryCurrentAvgPath = String8(String8::kEmptyString),
-    .batteryChargeCounterPath = String8(String8::kEmptyString),
-    .batteryFullChargePath = String8(String8::kEmptyString),
-    .batteryCycleCountPath = String8(String8::kEmptyString),
-    .energyCounter = NULL,
-    .boot_min_cap = 0,
-    .screen_on = NULL,
-};
-
-static int eventct;
-static int epollfd;
-
-#define POWER_SUPPLY_SUBSYSTEM "power_supply"
-
-// epoll_create() parameter is actually unused
-#define MAX_EPOLL_EVENTS 40
-static int uevent_fd;
-static int wakealarm_fd;
-
-// -1 for no epoll timeout
-static int awake_poll_interval = -1;
-
-static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-
-static BatteryMonitor* gBatteryMonitor;
-
-struct healthd_mode_ops *healthd_mode_ops;
-
-int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
-    struct epoll_event ev;
-
-    ev.events = EPOLLIN;
-
-    if (wakeup == EVENT_WAKEUP_FD)
-        ev.events |= EPOLLWAKEUP;
-
-    ev.data.ptr = (void *)handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-        KLOG_ERROR(LOG_TAG,
-                   "epoll_ctl failed; errno=%d\n", errno);
-        return -1;
-    }
-
-    eventct++;
-    return 0;
-}
-
-static void wakealarm_set_interval(int interval) {
-    struct itimerspec itval;
-
-    if (wakealarm_fd == -1)
-            return;
-
-    wakealarm_wake_interval = interval;
-
-    if (interval == -1)
-        interval = 0;
-
-    itval.it_interval.tv_sec = interval;
-    itval.it_interval.tv_nsec = 0;
-    itval.it_value.tv_sec = interval;
-    itval.it_value.tv_nsec = 0;
-
-    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
-        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
-}
-
-status_t healthd_get_property(int id, struct BatteryProperty *val) {
-    return gBatteryMonitor->getProperty(id, val);
-}
-
-void healthd_battery_update(void) {
-    // Fast wake interval when on charger (watch for overheat);
-    // slow wake interval when on battery (watch for drained battery).
-
-   int new_wake_interval = gBatteryMonitor->update() ?
-       healthd_config.periodic_chores_interval_fast :
-           healthd_config.periodic_chores_interval_slow;
-
-    if (new_wake_interval != wakealarm_wake_interval)
-            wakealarm_set_interval(new_wake_interval);
-
-    // During awake periods poll at fast rate.  If wake alarm is set at fast
-    // rate then just use the alarm; if wake alarm is set at slow rate then
-    // poll at fast rate while awake and let alarm wake up at slow rate when
-    // asleep.
-
-    if (healthd_config.periodic_chores_interval_fast == -1)
-        awake_poll_interval = -1;
-    else
-        awake_poll_interval =
-            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
-                -1 : healthd_config.periodic_chores_interval_fast * 1000;
-}
-
-void healthd_dump_battery_state(int fd) {
-    gBatteryMonitor->dumpState(fd);
-    fsync(fd);
-}
-
-static void periodic_chores() {
-    healthd_battery_update();
-}
-
-#define UEVENT_MSG_LEN 2048
-static void uevent_event(uint32_t /*epevents*/) {
-    char msg[UEVENT_MSG_LEN+2];
-    char *cp;
-    int n;
-
-    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
-    if (n <= 0)
-        return;
-    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-        return;
-
-    msg[n] = '\0';
-    msg[n+1] = '\0';
-    cp = msg;
-
-    while (*cp) {
-        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
-            healthd_battery_update();
-            break;
-        }
-
-        /* advance to after the next \0 */
-        while (*cp++)
-            ;
-    }
-}
-
-static void uevent_init(void) {
-    uevent_fd = uevent_open_socket(64*1024, true);
-
-    if (uevent_fd < 0) {
-        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
-        return;
-    }
-
-    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
-    if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
-        KLOG_ERROR(LOG_TAG,
-                   "register for uevent events failed\n");
-}
-
-static void wakealarm_event(uint32_t /*epevents*/) {
-    unsigned long long wakeups;
-
-    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
-        return;
-    }
-
-    periodic_chores();
-}
-
-static void wakealarm_init(void) {
-    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
-    if (wakealarm_fd == -1) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
-        return;
-    }
-
-    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
-        KLOG_ERROR(LOG_TAG,
-                   "Registration of wakealarm event failed\n");
-
-    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
-}
-
-static void healthd_mainloop(void) {
-    int nevents = 0;
-    while (1) {
-        struct epoll_event events[eventct];
-        int timeout = awake_poll_interval;
-        int mode_timeout;
-
-        /* Don't wait for first timer timeout to run periodic chores */
-        if (!nevents)
-            periodic_chores();
-
-        healthd_mode_ops->heartbeat();
-
-        mode_timeout = healthd_mode_ops->preparetowait();
-        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
-            timeout = mode_timeout;
-        nevents = epoll_wait(epollfd, events, eventct, timeout);
-        if (nevents == -1) {
-            if (errno == EINTR)
-                continue;
-            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
-            break;
-        }
-
-        for (int n = 0; n < nevents; ++n) {
-            if (events[n].data.ptr)
-                (*(void (*)(int))events[n].data.ptr)(events[n].events);
-        }
-    }
-
-    return;
-}
-
-static int healthd_init() {
-    epollfd = epoll_create(MAX_EPOLL_EVENTS);
-    if (epollfd == -1) {
-        KLOG_ERROR(LOG_TAG,
-                   "epoll_create failed; errno=%d\n",
-                   errno);
-        return -1;
-    }
-
-    healthd_board_init(&healthd_config);
-    healthd_mode_ops->init(&healthd_config);
-    wakealarm_init();
-    uevent_init();
-    gBatteryMonitor = new BatteryMonitor();
-    gBatteryMonitor->init(&healthd_config);
-    return 0;
-}
-
-int healthd_main() {
-    int ret;
-
-    klog_set_level(KLOG_LEVEL);
-
-    if (!healthd_mode_ops) {
-        KLOG_ERROR("healthd ops not set, exiting\n");
-        exit(1);
-    }
-
-    ret = healthd_init();
-    if (ret) {
-        KLOG_ERROR("Initialization failed, exiting\n");
-        exit(2);
-    }
-
-    healthd_mainloop();
-    KLOG_ERROR("Main loop terminated, exiting\n");
-    return 3;
-}
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
deleted file mode 100644
index c612313..0000000
--- a/healthd/healthd_mode_android.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "healthd-android"
-
-#include <healthd/healthd.h>
-#include "BatteryPropertiesRegistrar.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <cutils/klog.h>
-#include <sys/epoll.h>
-
-using namespace android;
-
-static int gBinderFd;
-static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar;
-
-void healthd_mode_android_battery_update(
-    struct android::BatteryProperties *props) {
-    if (gBatteryPropertiesRegistrar != NULL)
-        gBatteryPropertiesRegistrar->notifyListeners(*props);
-
-    return;
-}
-
-int healthd_mode_android_preparetowait(void) {
-    IPCThreadState::self()->flushCommands();
-    return -1;
-}
-
-void healthd_mode_android_heartbeat(void) {
-}
-
-static void binder_event(uint32_t /*epevents*/) {
-    IPCThreadState::self()->handlePolledCommands();
-}
-
-void healthd_mode_android_init(struct healthd_config* /*config*/) {
-    ProcessState::self()->setThreadPoolMaxThreadCount(0);
-    IPCThreadState::self()->disableBackgroundScheduling(true);
-    IPCThreadState::self()->setupPolling(&gBinderFd);
-
-    if (gBinderFd >= 0) {
-        if (healthd_register_event(gBinderFd, binder_event))
-            KLOG_ERROR(LOG_TAG,
-                       "Register for binder events failed\n");
-    }
-
-    gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
-    gBatteryPropertiesRegistrar->publish(gBatteryPropertiesRegistrar);
-}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index f1fe5cd..56a9f86 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -49,6 +49,7 @@
 #include "AnimationParser.h"
 #include "healthd_draw.h"
 
+#include <health2/Health.h>
 #include <healthd/healthd.h>
 
 using namespace android;
@@ -612,6 +613,8 @@
 }
 
 void healthd_mode_charger_init(struct healthd_config* config) {
+    using android::hardware::health::V2_0::implementation::Health;
+
     int ret;
     charger* charger = &charger_state;
     int i;
@@ -666,6 +669,10 @@
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
     charger->next_pwr_check = -1;
+
+    // Initialize Health implementation (which initializes the internal BatteryMonitor).
+    Health::initInstance(config);
+
     healthd_config = config;
     charger->boot_min_cap = config->boot_min_cap;
 }
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 97435c7..4d1d53f 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -42,6 +42,7 @@
     int getChargeStatus();
     status_t getProperty(int id, struct BatteryProperty *val);
     void dumpState(int fd);
+    friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
 
   private:
     struct healthd_config *mHealthdConfig;
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 17efbd6..c01e8d7 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -81,10 +81,6 @@
 // Global helper functions
 
 int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
-void healthd_battery_update();
-android::status_t healthd_get_property(int id,
-    struct android::BatteryProperty *val);
-void healthd_dump_battery_state(int fd);
 
 struct healthd_mode_ops {
     void (*init)(struct healthd_config *config);
diff --git a/healthd/manifest_healthd.xml b/healthd/manifest_healthd.xml
new file mode 100644
index 0000000..097a7d8
--- /dev/null
+++ b/healthd/manifest_healthd.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.hardware.health</name>
+        <transport>hwbinder</transport>
+        <version>2.0</version>
+        <interface>
+            <name>IHealth</name>
+            <instance>backup</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/init/Android.bp b/init/Android.bp
index 660d586..84a78e2 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -92,6 +92,7 @@
 
 cc_library_static {
     name: "libinit",
+    recovery_available: true,
     defaults: ["init_defaults"],
     srcs: [
         "action.cpp",
@@ -115,6 +116,7 @@
         "property_service.cpp",
         "property_type.cpp",
         "reboot.cpp",
+        "reboot_utils.cpp",
         "security.cpp",
         "selinux.cpp",
         "service.cpp",
@@ -127,7 +129,6 @@
         "ueventd.cpp",
         "ueventd_parser.cpp",
         "util.cpp",
-        "watchdogd.cpp",
     ],
     whole_static_libs: ["libcap"],
     header_libs: ["bootimg_headers"],
@@ -137,30 +138,21 @@
     },
 }
 
-/*
-This is not yet ready, see the below TODOs for what is missing
-
 cc_binary {
-    // TODO: Missing,
-    //LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-    //LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-    name: "init",
+    name: "init_second_stage",
+    recovery_available: true,
+    stem: "init",
     defaults: ["init_defaults"],
+    static_libs: ["libinit"],
     required: [
         "e2fsdroid",
         "mke2fs",
         "sload_f2fs",
         "make_f2fs",
     ],
-    static_executable: true,
     srcs: ["main.cpp"],
-    symlinks: [
-        "sbin/ueventd",
-        "sbin/watchdogd",
-    ],
+    symlinks: ["ueventd"],
 }
-*/
 
 // Tests
 // ------------------------------------------------------------------------------
diff --git a/init/Android.mk b/init/Android.mk
index d0cb820..d20509b 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -41,35 +41,35 @@
 
 include $(CLEAR_VARS)
 LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES := main.cpp
+LOCAL_SRC_FILES := \
+    devices.cpp \
+    first_stage_mount.cpp \
+    init_first_stage.cpp \
+    reboot_utils.cpp \
+    selinux.cpp \
+    uevent_listener.cpp \
+    util.cpp \
 
-LOCAL_MODULE:= init
+LOCAL_MODULE := init
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
 
 LOCAL_STATIC_LIBRARIES := \
-    libinit \
-    libbootloader_message \
     libfs_mgr \
     libfec \
     libfec_rs \
-    libhidl-gen-utils \
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
     libseccomp_policy \
     libcrypto_utils \
     libsparse \
-    libprocessgroup \
     libavb \
     libkeyutils \
-    libprotobuf-cpp-lite \
-    libpropertyinfoserializer \
-    libpropertyinfoparser \
     liblp \
-
-shared_libs := \
     libcutils \
     libbase \
     liblog \
@@ -77,28 +77,11 @@
     libdl \
     libz \
     libselinux \
-
-ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-# init is static executable for non-system-as-root devices, because the dynamic linker
-# and shared libs are not available before /system is mounted, but init has to run
-# before the partition is mounted.
-LOCAL_STATIC_LIBRARIES += $(shared_libs) libc++_static
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-else
-LOCAL_SHARED_LIBRARIES := $(shared_libs) libc++
-endif
-shared_libs :=
+    libcap \
 
 LOCAL_REQUIRED_MODULES := \
-    e2fsdroid \
-    mke2fs \
-    sload_f2fs \
-    make_f2fs \
-
-# Create symlinks.
-LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
-    ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
-    ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
+    init_second_stage \
+    init_second_stage.recovery \
 
 LOCAL_SANITIZE := signed-integer-overflow
 include $(BUILD_EXECUTABLE)
diff --git a/init/devices.cpp b/init/devices.cpp
index ed4a739..58c8b2e 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -34,7 +34,7 @@
 #include "util.h"
 
 #ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#error "Do not include init.h in files used by ueventd; it will expose init's globals"
 #endif
 
 using android::base::Basename;
@@ -372,7 +372,7 @@
     }
 }
 
-void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+void DeviceHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
         FixupSysPermissions(uevent.path, uevent.subsystem);
     }
@@ -418,6 +418,10 @@
     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
 }
 
+void DeviceHandler::ColdbootDone() {
+    skip_restorecon_ = true;
+}
+
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
                              std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
diff --git a/init/devices.h b/init/devices.h
index 0be660f..9d39eaa 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -29,6 +29,7 @@
 #include <selinux/label.h>
 
 #include "uevent.h"
+#include "uevent_handler.h"
 
 namespace android {
 namespace init {
@@ -105,7 +106,7 @@
     std::string dir_name_ = "/dev";
 };
 
-class DeviceHandler {
+class DeviceHandler : public UeventHandler {
   public:
     friend class DeviceHandlerTester;
 
@@ -113,11 +114,12 @@
     DeviceHandler(std::vector<Permissions> dev_permissions,
                   std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
                   std::set<std::string> boot_devices, bool skip_restorecon);
+    virtual ~DeviceHandler() = default;
 
-    void HandleDeviceEvent(const Uevent& uevent);
+    void HandleUevent(const Uevent& uevent) override;
+    void ColdbootDone() override;
 
     std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
-    void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
 
   private:
     bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 28bda34..740e82c 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -35,8 +35,6 @@
 namespace android {
 namespace init {
 
-std::vector<std::string> firmware_directories;
-
 static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
                          int loading_fd, int data_fd) {
     // Start transfer.
@@ -58,7 +56,10 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-static void ProcessFirmwareEvent(const Uevent& uevent) {
+FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories)
+    : firmware_directories_(std::move(firmware_directories)) {}
+
+void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) {
     int booting = IsBooting();
 
     LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
@@ -80,7 +81,7 @@
     }
 
 try_loading_again:
-    for (const auto& firmware_directory : firmware_directories) {
+    for (const auto& firmware_directory : firmware_directories_) {
         std::string file = firmware_directory + uevent.firmware;
         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
         struct stat sb;
@@ -104,7 +105,7 @@
     write(loading_fd, "-1", 2);
 }
 
-void HandleFirmwareEvent(const Uevent& uevent) {
+void FirmwareHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.subsystem != "firmware" || uevent.action != "add") return;
 
     // Loading the firmware in a child means we can do that in parallel...
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 6081511..3996096 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -21,13 +21,23 @@
 #include <vector>
 
 #include "uevent.h"
+#include "uevent_handler.h"
 
 namespace android {
 namespace init {
 
-extern std::vector<std::string> firmware_directories;
+class FirmwareHandler : public UeventHandler {
+  public:
+    explicit FirmwareHandler(std::vector<std::string> firmware_directories);
+    virtual ~FirmwareHandler() = default;
 
-void HandleFirmwareEvent(const Uevent& uevent);
+    void HandleUevent(const Uevent& uevent) override;
+
+  private:
+    void ProcessFirmwareEvent(const Uevent& uevent);
+
+    std::vector<std::string> firmware_directories_;
+};
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 43075b2..0c5cf76 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -35,6 +35,7 @@
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
 #include "fs_mgr_dm_linear.h"
+#include "fs_mgr_overlayfs.h"
 #include "uevent.h"
 #include "uevent_listener.h"
 #include "util.h"
@@ -206,7 +207,7 @@
         bool found = false;
         auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
             if (uevent.path == dm_path) {
-                device_handler_->HandleDeviceEvent(uevent);
+                device_handler_->HandleUevent(uevent);
                 found = true;
                 return ListenerAction::kStop;
             }
@@ -273,7 +274,7 @@
             lp_metadata_partition_ = links[0];
         }
         required_devices_partition_names_.erase(iter);
-        device_handler_->HandleDeviceEvent(uevent);
+        device_handler_->HandleUevent(uevent);
         if (required_devices_partition_names_.empty()) {
             return ListenerAction::kStop;
         } else {
@@ -310,7 +311,7 @@
     auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
         if (uevent.device_name == device_name) {
             LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
-            device_handler_->HandleDeviceEvent(uevent);
+            device_handler_->HandleUevent(uevent);
             found = true;
             return ListenerAction::kStop;
         }
@@ -351,6 +352,7 @@
             return false;
         }
     }
+    fs_mgr_overlayfs_mount_all();
     return true;
 }
 
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 2352fc7..8866bdc 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -41,10 +41,9 @@
 }
 
 // selinux.h
-bool SelinuxHasVendorInit() {
-    return true;
+int SelinuxGetVendorAndroidVersion() {
+    return 10000;
 }
-
 void SelabelInitialize() {}
 
 bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index f0e1f07..0af11f6 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -23,6 +23,9 @@
 
 #include <string>
 
+// android/api-level.h
+#define __ANDROID_API_P__ 28
+
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
 
@@ -41,7 +44,7 @@
                            const std::string& source_context, const ucred& cr, std::string* error);
 
 // selinux.h
-bool SelinuxHasVendorInit();
+int SelinuxGetVendorAndroidVersion();
 void SelabelInitialize();
 bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
 
diff --git a/init/init.cpp b/init/init.cpp
index 73194bd..b550f1b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,15 +18,12 @@
 
 #include <dirent.h>
 #include <fcntl.h>
-#include <paths.h>
 #include <pthread.h>
-#include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/signalfd.h>
-#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -43,8 +40,6 @@
 #include <cutils/android_reboot.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
-#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
 
 #include "action_parser.h"
 #include "epoll.h"
@@ -53,12 +48,12 @@
 #include "keychords.h"
 #include "property_service.h"
 #include "reboot.h"
+#include "reboot_utils.h"
 #include "security.h"
 #include "selinux.h"
 #include "sigchld_handler.h"
 #include "ueventd.h"
 #include "util.h"
-#include "watchdogd.h"
 
 using namespace std::chrono_literals;
 using namespace std::string_literals;
@@ -418,14 +413,6 @@
     return Success();
 }
 
-static void global_seccomp() {
-    import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
-        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
-            LOG(FATAL) << "Failed to globally enable seccomp!";
-        }
-    });
-}
-
 // Set the UDC controller for the ConfigFS USB Gadgets.
 // Read the UDC controller in use from "/sys/class/udc".
 // In case of multiple UDC controllers select the first one.
@@ -442,40 +429,6 @@
     }
 }
 
-static void InstallRebootSignalHandlers() {
-    // Instead of panic'ing the kernel as is the default behavior when init crashes,
-    // we prefer to reboot to bootloader on development builds, as this will prevent
-    // boot looping bad configurations and allow both developers and test farms to easily
-    // recover.
-    struct sigaction action;
-    memset(&action, 0, sizeof(action));
-    sigfillset(&action.sa_mask);
-    action.sa_handler = [](int signal) {
-        // These signal handlers are also caught for processes forked from init, however we do not
-        // want them to trigger reboot, so we directly call _exit() for children processes here.
-        if (getpid() != 1) {
-            _exit(signal);
-        }
-
-        // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
-        // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
-        // and probably good enough given this is already an error case and only enabled for
-        // development builds.
-        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
-    };
-    action.sa_flags = SA_RESTART;
-    sigaction(SIGABRT, &action, nullptr);
-    sigaction(SIGBUS, &action, nullptr);
-    sigaction(SIGFPE, &action, nullptr);
-    sigaction(SIGILL, &action, nullptr);
-    sigaction(SIGSEGV, &action, nullptr);
-#if defined(SIGSTKFLT)
-    sigaction(SIGSTKFLT, &action, nullptr);
-#endif
-    sigaction(SIGSYS, &action, nullptr);
-    sigaction(SIGTRAP, &action, nullptr);
-}
-
 static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
     if (siginfo.ssi_pid != 0) {
         // Drop any userspace SIGTERM requests.
@@ -617,10 +570,6 @@
         return ueventd_main(argc, argv);
     }
 
-    if (!strcmp(basename(argv[0]), "watchdogd")) {
-        return watchdogd_main(argc, argv);
-    }
-
     if (argc > 1 && !strcmp(argv[1], "subcontext")) {
         android::base::InitLogging(argv, &android::base::KernelLogger);
         const BuiltinFunctionMap function_map;
@@ -631,106 +580,6 @@
         InstallRebootSignalHandlers();
     }
 
-    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
-
-    if (is_first_stage) {
-        boot_clock::time_point start_time = boot_clock::now();
-
-        std::vector<std::pair<std::string, int>> errors;
-#define CHECKCALL(x) \
-    if (x != 0) errors.emplace_back(#x " failed", errno);
-
-        // Clear the umask.
-        umask(0);
-
-        CHECKCALL(clearenv());
-        CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
-        // Get the basic filesystem setup we need put together in the initramdisk
-        // on / and then we'll let the rc file figure out the rest.
-        CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
-        CHECKCALL(mkdir("/dev/pts", 0755));
-        CHECKCALL(mkdir("/dev/socket", 0755));
-        CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
-#define MAKE_STR(x) __STRING(x)
-        CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
-#undef MAKE_STR
-        // Don't expose the raw commandline to unprivileged processes.
-        CHECKCALL(chmod("/proc/cmdline", 0440));
-        gid_t groups[] = { AID_READPROC };
-        CHECKCALL(setgroups(arraysize(groups), groups));
-        CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
-        CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
-
-        CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
-
-        if constexpr (WORLD_WRITABLE_KMSG) {
-            CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
-        }
-
-        CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
-        CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
-
-        // Mount staging areas for devices managed by vold
-        // See storage config details at http://source.android.com/devices/storage/
-        CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
-                        "mode=0755,uid=0,gid=1000"));
-        // /mnt/vendor is used to mount vendor-specific partitions that can not be
-        // part of the vendor partition, e.g. because they are mounted read-write.
-        CHECKCALL(mkdir("/mnt/vendor", 0755));
-        // /mnt/product is used to mount product-specific partitions that can not be
-        // part of the product partition, e.g. because they are mounted read-write.
-        CHECKCALL(mkdir("/mnt/product", 0755));
-
-#undef CHECKCALL
-
-        // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
-        // talk to the outside world...
-        InitKernelLogging(argv);
-
-        if (!errors.empty()) {
-            for (const auto& [error_string, error_errno] : errors) {
-                LOG(ERROR) << error_string << " " << strerror(error_errno);
-            }
-            LOG(FATAL) << "Init encountered errors starting first stage, aborting";
-        }
-
-        LOG(INFO) << "init first stage started!";
-
-        if (!DoFirstStageMount()) {
-            LOG(FATAL) << "Failed to mount required partitions early ...";
-        }
-
-        SetInitAvbVersionInRecovery();
-
-        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
-        global_seccomp();
-
-        // Set up SELinux, loading the SELinux policy.
-        SelinuxSetupKernelLogging();
-        SelinuxInitialize();
-
-        // We're in the kernel domain, so re-exec init to transition to the init domain now
-        // that the SELinux policy has been loaded.
-        if (selinux_android_restorecon("/init", 0) == -1) {
-            PLOG(FATAL) << "restorecon failed of /init failed";
-        }
-
-        setenv("INIT_SECOND_STAGE", "true", 1);
-
-        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
-        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
-        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
-
-        char* path = argv[0];
-        char* args[] = { path, nullptr };
-        execv(path, args);
-
-        // execv() only returns if an error happened, in which case we
-        // panic and never fall through this conditional.
-        PLOG(FATAL) << "execv(\"" << path << "\") failed";
-    }
-
-    // At this point we're in the second stage of init.
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
@@ -762,7 +611,6 @@
     if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
     // Clean up our environment.
-    unsetenv("INIT_SECOND_STAGE");
     unsetenv("INIT_STARTED_AT");
     unsetenv("INIT_SELINUX_TOOK");
     unsetenv("INIT_AVB_VERSION");
diff --git a/init/init.h b/init/init.h
index 6c82fa1..f244ad7 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,8 +31,8 @@
 namespace android {
 namespace init {
 
-// Note: These globals are *only* valid in init, so they should not be used in ueventd,
-// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
+// Note: These globals are *only* valid in init, so they should not be used in ueventd
+// or any files that may be included in ueventd, such as devices.cpp and util.cpp.
 // TODO: Have an Init class and remove all globals.
 extern std::string default_console;
 extern std::vector<std::string> late_import_paths;
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
new file mode 100644
index 0000000..466cde3
--- /dev/null
+++ b/init/init_first_stage.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 <paths.h>
+#include <seccomp_policy.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
+#include "first_stage_mount.h"
+#include "reboot_utils.h"
+#include "selinux.h"
+#include "util.h"
+
+using android::base::boot_clock;
+
+namespace android {
+namespace init {
+
+static void GlobalSeccomp() {
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
+                                    bool in_qemu) {
+        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+            LOG(FATAL) << "Failed to globally enable seccomp!";
+        }
+    });
+}
+
+int main(int argc, char** argv) {
+    if (REBOOT_BOOTLOADER_ON_PANIC) {
+        InstallRebootSignalHandlers();
+    }
+
+    boot_clock::time_point start_time = boot_clock::now();
+
+    std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+    if (x != 0) errors.emplace_back(#x " failed", errno);
+
+    // Clear the umask.
+    umask(0);
+
+    CHECKCALL(clearenv());
+    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
+    // Get the basic filesystem setup we need put together in the initramdisk
+    // on / and then we'll let the rc file figure out the rest.
+    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+    CHECKCALL(mkdir("/dev/pts", 0755));
+    CHECKCALL(mkdir("/dev/socket", 0755));
+    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
+    // Don't expose the raw commandline to unprivileged processes.
+    CHECKCALL(chmod("/proc/cmdline", 0440));
+    gid_t groups[] = {AID_READPROC};
+    CHECKCALL(setgroups(arraysize(groups), groups));
+    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
+
+    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
+
+    if constexpr (WORLD_WRITABLE_KMSG) {
+        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
+    }
+
+    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
+
+    // This is needed for log wrapper, which gets called before ueventd runs.
+    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
+    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
+
+    // Mount staging areas for devices managed by vold
+    // See storage config details at http://source.android.com/devices/storage/
+    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=1000"));
+    // /mnt/vendor is used to mount vendor-specific partitions that can not be
+    // part of the vendor partition, e.g. because they are mounted read-write.
+    CHECKCALL(mkdir("/mnt/vendor", 0755));
+    // /mnt/product is used to mount product-specific partitions that can not be
+    // part of the product partition, e.g. because they are mounted read-write.
+    CHECKCALL(mkdir("/mnt/product", 0755));
+
+#undef CHECKCALL
+
+    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
+    // talk to the outside world...
+    android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+    });
+
+    if (!errors.empty()) {
+        for (const auto& [error_string, error_errno] : errors) {
+            LOG(ERROR) << error_string << " " << strerror(error_errno);
+        }
+        LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+    }
+
+    LOG(INFO) << "init first stage started!";
+
+    if (!DoFirstStageMount()) {
+        LOG(FATAL) << "Failed to mount required partitions early ...";
+    }
+
+    SetInitAvbVersionInRecovery();
+
+    // Does this need to be done in first stage init or can it be done later?
+    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+    GlobalSeccomp();
+
+    // Set up SELinux, loading the SELinux policy.
+    SelinuxSetupKernelLogging();
+    SelinuxInitialize();
+
+    // We're in the kernel domain and want to transition to the init domain when we exec second
+    // stage init.  File systems that store SELabels in their xattrs, such as ext4 do not need an
+    // explicit restorecon here, but other file systems do.  In particular, this is needed for
+    // ramdisks such as the recovery image for A/B devices.
+    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+    }
+
+    static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
+    uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
+    setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
+
+    const char* path = "/system/bin/init";
+    const char* args[] = {path, nullptr};
+    execv(path, const_cast<char**>(args));
+
+    // execv() only returns if an error happened, in which case we
+    // panic and never fall through this conditional.
+    PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+    return 1;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    return android::init::main(argc, argv);
+}
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index 1734a7e..1e0db57 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -139,7 +139,7 @@
     return Insmod(dependencies[0], args);
 }
 
-void ModaliasHandler::HandleModaliasEvent(const Uevent& uevent) {
+void ModaliasHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.modalias.empty()) return;
 
     for (const auto& [alias, module] : module_aliases_) {
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
index e79da32..3247c86 100644
--- a/init/modalias_handler.h
+++ b/init/modalias_handler.h
@@ -16,22 +16,23 @@
 
 #pragma once
 
-#include "result.h"
-#include "uevent.h"
-
 #include <string>
 #include <unordered_map>
 #include <vector>
 
+#include "result.h"
+#include "uevent.h"
+#include "uevent_handler.h"
+
 namespace android {
 namespace init {
 
-class ModaliasHandler {
+class ModaliasHandler : public UeventHandler {
   public:
     ModaliasHandler();
-    ~ModaliasHandler(){};
+    virtual ~ModaliasHandler() = default;
 
-    void HandleModaliasEvent(const Uevent& uevent);
+    void HandleUevent(const Uevent& uevent) override;
 
   private:
     Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c0d811f..cd2f630 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -16,6 +16,7 @@
 
 #include "property_service.h"
 
+#include <android/api-level.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -576,7 +577,7 @@
     size_t flen = 0;
 
     const char* context = kInitContext.c_str();
-    if (SelinuxHasVendorInit()) {
+    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
         for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
             if (StartsWith(filename, path_prefix)) {
                 context = secontext;
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 11507f4..7401857 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,11 +20,9 @@
 #include <fcntl.h>
 #include <linux/fs.h>
 #include <mntent.h>
-#include <sys/capability.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
-#include <sys/reboot.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -51,9 +49,9 @@
 #include <selinux/selinux.h>
 
 #include "action_manager.h"
-#include "capabilities.h"
 #include "init.h"
 #include "property_service.h"
+#include "reboot_utils.h"
 #include "service.h"
 #include "sigchld_handler.h"
 
@@ -159,54 +157,6 @@
                  << stat;
 }
 
-bool IsRebootCapable() {
-    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
-        PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
-        return true;
-    }
-
-    ScopedCaps caps(cap_get_proc());
-    if (!caps) {
-        PLOG(WARNING) << "cap_get_proc() failed";
-        return true;
-    }
-
-    cap_flag_value_t value = CAP_SET;
-    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
-        PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
-        return true;
-    }
-    return value == CAP_SET;
-}
-
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
-    LOG(INFO) << "Reboot ending, jumping to kernel";
-
-    if (!IsRebootCapable()) {
-        // On systems where init does not have the capability of rebooting the
-        // device, just exit cleanly.
-        exit(0);
-    }
-
-    switch (cmd) {
-        case ANDROID_RB_POWEROFF:
-            reboot(RB_POWER_OFF);
-            break;
-
-        case ANDROID_RB_RESTART2:
-            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
-                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
-            break;
-
-        case ANDROID_RB_THERMOFF:
-            reboot(RB_POWER_OFF);
-            break;
-    }
-    // In normal case, reboot should not return.
-    PLOG(ERROR) << "reboot call returned";
-    abort();
-}
-
 /* Find all read+write block devices and emulated devices in /proc/mounts
  * and add them to correpsponding list.
  */
@@ -329,8 +279,15 @@
     return stat;
 }
 
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
-              bool runFsck) {
+//* Reboot / shutdown the system.
+// cmd ANDROID_RB_* as defined in android_reboot.h
+// reason Reason string like "reboot", "shutdown,userrequested"
+// rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
+//              empty string.
+// runFsck Whether to run fsck after umount is done.
+//
+static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+                     bool runFsck) {
     Timer t;
     LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
diff --git a/init/reboot.h b/init/reboot.h
index 1c58bd1..07dcb6e 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -22,26 +22,9 @@
 namespace android {
 namespace init {
 
-// This is a wrapper around the actual reboot calls.  DoReboot() should be preferred in most cases.
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);
-
-/* Reboot / shutdown the system.
- * cmd ANDROID_RB_* as defined in android_reboot.h
- * reason Reason string like "reboot", "shutdown,userrequested"
- * rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
- *              empty string.
- * runFsck Whether to run fsck after umount is done.
- */
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
-              bool runFsck) __attribute__((__noreturn__));
-
 // Parses and handles a setprop sys.powerctl message.
 bool HandlePowerctlMessage(const std::string& command);
 
-// Determines whether the system is capable of rebooting. This is conservative,
-// so if any of the attempts to determine this fail, it will still return true.
-bool IsRebootCapable();
-
 }  // namespace init
 }  // namespace android
 
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
new file mode 100644
index 0000000..9610304
--- /dev/null
+++ b/init/reboot_utils.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 <sys/capability.h>
+#include <sys/reboot.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+
+#include "capabilities.h"
+
+namespace android {
+namespace init {
+
+bool IsRebootCapable() {
+    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
+        PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
+        return true;
+    }
+
+    ScopedCaps caps(cap_get_proc());
+    if (!caps) {
+        PLOG(WARNING) << "cap_get_proc() failed";
+        return true;
+    }
+
+    cap_flag_value_t value = CAP_SET;
+    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
+        PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
+        return true;
+    }
+    return value == CAP_SET;
+}
+
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+    LOG(INFO) << "Reboot ending, jumping to kernel";
+
+    if (!IsRebootCapable()) {
+        // On systems where init does not have the capability of rebooting the
+        // device, just exit cleanly.
+        exit(0);
+    }
+
+    switch (cmd) {
+        case ANDROID_RB_POWEROFF:
+            reboot(RB_POWER_OFF);
+            break;
+
+        case ANDROID_RB_RESTART2:
+            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
+            break;
+
+        case ANDROID_RB_THERMOFF:
+            reboot(RB_POWER_OFF);
+            break;
+    }
+    // In normal case, reboot should not return.
+    PLOG(ERROR) << "reboot call returned";
+    abort();
+}
+
+void InstallRebootSignalHandlers() {
+    // Instead of panic'ing the kernel as is the default behavior when init crashes,
+    // we prefer to reboot to bootloader on development builds, as this will prevent
+    // boot looping bad configurations and allow both developers and test farms to easily
+    // recover.
+    struct sigaction action;
+    memset(&action, 0, sizeof(action));
+    sigfillset(&action.sa_mask);
+    action.sa_handler = [](int signal) {
+        // These signal handlers are also caught for processes forked from init, however we do not
+        // want them to trigger reboot, so we directly call _exit() for children processes here.
+        if (getpid() != 1) {
+            _exit(signal);
+        }
+
+        // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
+        // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
+        // and probably good enough given this is already an error case and only enabled for
+        // development builds.
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+    };
+    action.sa_flags = SA_RESTART;
+    sigaction(SIGABRT, &action, nullptr);
+    sigaction(SIGBUS, &action, nullptr);
+    sigaction(SIGFPE, &action, nullptr);
+    sigaction(SIGILL, &action, nullptr);
+    sigaction(SIGSEGV, &action, nullptr);
+#if defined(SIGSTKFLT)
+    sigaction(SIGSTKFLT, &action, nullptr);
+#endif
+    sigaction(SIGSYS, &action, nullptr);
+    sigaction(SIGTRAP, &action, nullptr);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
new file mode 100644
index 0000000..073a16a
--- /dev/null
+++ b/init/reboot_utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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 <string>
+
+namespace android {
+namespace init {
+
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+bool IsRebootCapable();
+// This is a wrapper around the actual reboot calls.
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
+void InstallRebootSignalHandlers();
+
+}  // namespace init
+}  // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 94f206e..fd7e86f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -47,6 +47,7 @@
 
 #include "selinux.h"
 
+#include <android/api-level.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <sys/wait.h>
@@ -414,6 +415,8 @@
     if constexpr (WORLD_WRITABLE_KMSG) {
         selinux_android_restorecon("/dev/kmsg_debug", 0);
     }
+    selinux_android_restorecon("/dev/null", 0);
+    selinux_android_restorecon("/dev/ptmx", 0);
     selinux_android_restorecon("/dev/socket", 0);
     selinux_android_restorecon("/dev/random", 0);
     selinux_android_restorecon("/dev/urandom", 0);
@@ -470,29 +473,27 @@
     selinux_set_callback(SELINUX_CB_LOG, cb);
 }
 
-// This function checks whether the sepolicy supports vendor init.
-bool SelinuxHasVendorInit() {
+// This function returns the Android version with which the vendor SEPolicy was compiled.
+// It is used for version checks such as whether or not vendor_init should be used
+int SelinuxGetVendorAndroidVersion() {
     if (!IsSplitPolicyDevice()) {
-        // If this device does not split sepolicy files, vendor_init will be available in the latest
-        // monolithic sepolicy file.
-        return true;
+        // If this device does not split sepolicy files, it's not a Treble device and therefore,
+        // we assume it's always on the latest platform.
+        return __ANDROID_API_FUTURE__;
     }
 
     std::string version;
     if (!GetVendorMappingVersion(&version)) {
-        // Return true as the default if we failed to load the vendor sepolicy version.
-        return true;
+        LOG(FATAL) << "Could not read vendor SELinux version";
     }
 
     int major_version;
     std::string major_version_str(version, 0, version.find('.'));
     if (!ParseInt(major_version_str, &major_version)) {
-        PLOG(ERROR) << "Failed to parse the vendor sepolicy major version " << major_version_str;
-        // Return true as the default if we failed to parse the major version.
-        return true;
+        PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
     }
 
-    return major_version >= 28;
+    return major_version;
 }
 
 // selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
diff --git a/init/selinux.h b/init/selinux.h
index 30069b5..c41d7f0 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -27,7 +27,7 @@
 void SelinuxRestoreContext();
 
 void SelinuxSetupKernelLogging();
-bool SelinuxHasVendorInit();
+int SelinuxGetVendorAndroidVersion();
 
 void SelabelInitialize();
 bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
diff --git a/init/service.cpp b/init/service.cpp
index 4c2747e..d20e90a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -46,10 +46,12 @@
 #include "util.h"
 
 #if defined(__ANDROID__)
+#include <android/api-level.h>
 #include <sys/system_properties.h>
 
 #include "init.h"
 #include "property_service.h"
+#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
@@ -1211,6 +1213,13 @@
     }
 
     std::vector<std::string> str_args(args.begin() + 2, args.end());
+
+    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
+        if (str_args[0] == "/sbin/watchdogd") {
+            str_args[0] = "/system/bin/watchdogd";
+        }
+    }
+
     service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
     return Success();
 }
diff --git a/init/stable_properties.h b/init/stable_properties.h
index 05b2acb..4972d10 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -36,6 +36,7 @@
     "init.svc.zygote",
     "persist.bluetooth.btsnoopenable",
     "persist.sys.crash_rcu",
+    "persist.sys.usb.usbradio.config",
     "persist.sys.zram_enabled",
     "ro.board.platform",
     "ro.bootmode",
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index ee72513..c2a21d4 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -30,6 +30,7 @@
 #include "util.h"
 
 #if defined(__ANDROID__)
+#include <android/api-level.h>
 #include "property_service.h"
 #include "selinux.h"
 #else
@@ -355,7 +356,7 @@
 static bool shutting_down;
 
 std::vector<Subcontext>* InitializeSubcontexts() {
-    if (SelinuxHasVendorInit()) {
+    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
         for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
             subcontexts.emplace_back(path_prefix, secontext);
         }
diff --git a/init/watchdogd.h b/init/uevent_handler.h
similarity index 71%
rename from init/watchdogd.h
rename to init/uevent_handler.h
index 73f77d5..75d1990 100644
--- a/init/watchdogd.h
+++ b/init/uevent_handler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,15 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+#pragma once
+
+#include "uevent.h"
 
 namespace android {
 namespace init {
 
-int watchdogd_main(int argc, char **argv);
+class UeventHandler {
+  public:
+    virtual ~UeventHandler() = default;
+
+    virtual void HandleUevent(const Uevent& uevent) = 0;
+
+    virtual void ColdbootDone() {}
+};
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index e9d829b..95be6af 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -38,6 +38,7 @@
 #include "firmware_handler.h"
 #include "modalias_handler.h"
 #include "selinux.h"
+#include "uevent_handler.h"
 #include "uevent_listener.h"
 #include "ueventd_parser.h"
 #include "util.h"
@@ -107,11 +108,10 @@
 
 class ColdBoot {
   public:
-    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler,
-             ModaliasHandler& modalias_handler)
+    ColdBoot(UeventListener& uevent_listener,
+             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers)
         : uevent_listener_(uevent_listener),
-          device_handler_(device_handler),
-          modalias_handler_(modalias_handler),
+          uevent_handlers_(uevent_handlers),
           num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
 
     void Run();
@@ -124,8 +124,7 @@
     void WaitForSubProcesses();
 
     UeventListener& uevent_listener_;
-    DeviceHandler& device_handler_;
-    ModaliasHandler& modalias_handler_;
+    std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
 
     unsigned int num_handler_subprocesses_;
     std::vector<Uevent> uevent_queue_;
@@ -136,16 +135,16 @@
 void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
     for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
         auto& uevent = uevent_queue_[i];
-        device_handler_.HandleDeviceEvent(uevent);
-        modalias_handler_.HandleModaliasEvent(uevent);
+
+        for (auto& uevent_handler : uevent_handlers_) {
+            uevent_handler->HandleUevent(uevent);
+        }
     }
     _exit(EXIT_SUCCESS);
 }
 
 void ColdBoot::RegenerateUevents() {
     uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
-        HandleFirmwareEvent(uevent);
-
         uevent_queue_.emplace_back(std::move(uevent));
         return ListenerAction::kContinue;
     });
@@ -168,7 +167,6 @@
 
 void ColdBoot::DoRestoreCon() {
     selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    device_handler_.set_skip_restorecon(false);
 }
 
 void ColdBoot::WaitForSubProcesses() {
@@ -234,8 +232,7 @@
     SelinuxSetupKernelLogging();
     SelabelInitialize();
 
-    DeviceHandler device_handler;
-    ModaliasHandler modalias_handler;
+    std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
     UeventListener uevent_listener;
 
     {
@@ -248,19 +245,27 @@
                 ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
                              "/ueventd." + hardware + ".rc"});
 
-        device_handler = DeviceHandler{std::move(ueventd_configuration.dev_permissions),
-                                       std::move(ueventd_configuration.sysfs_permissions),
-                                       std::move(ueventd_configuration.subsystems),
-                                       fs_mgr_get_boot_devices(), true};
+        uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
+                std::move(ueventd_configuration.dev_permissions),
+                std::move(ueventd_configuration.sysfs_permissions),
+                std::move(ueventd_configuration.subsystems), fs_mgr_get_boot_devices(), true));
+        uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
+                std::move(ueventd_configuration.firmware_directories)));
 
-        firmware_directories = ueventd_configuration.firmware_directories;
+        if (ueventd_configuration.enable_modalias_handling) {
+            uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
+        }
     }
 
     if (access(COLDBOOT_DONE, F_OK) != 0) {
-        ColdBoot cold_boot(uevent_listener, device_handler, modalias_handler);
+        ColdBoot cold_boot(uevent_listener, uevent_handlers);
         cold_boot.Run();
     }
 
+    for (auto& uevent_handler : uevent_handlers) {
+        uevent_handler->ColdbootDone();
+    }
+
     // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
     signal(SIGCHLD, SIG_IGN);
     // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
@@ -268,10 +273,10 @@
     while (waitpid(-1, nullptr, WNOHANG) > 0) {
     }
 
-    uevent_listener.Poll([&device_handler, &modalias_handler](const Uevent& uevent) {
-        HandleFirmwareEvent(uevent);
-        modalias_handler.HandleModaliasEvent(uevent);
-        device_handler.HandleDeviceEvent(uevent);
+    uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
+        for (auto& uevent_handler : uevent_handlers) {
+            uevent_handler->HandleUevent(uevent);
+        }
         return ListenerAction::kContinue;
     });
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 54b0d16..677938e 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -84,6 +84,23 @@
     return Success();
 }
 
+Result<Success> ParseModaliasHandlingLine(std::vector<std::string>&& args,
+                                          bool* enable_modalias_handling) {
+    if (args.size() != 2) {
+        return Error() << "modalias_handling lines take exactly one parameter";
+    }
+
+    if (args[1] == "enabled") {
+        *enable_modalias_handling = true;
+    } else if (args[1] == "disabled") {
+        *enable_modalias_handling = false;
+    } else {
+        return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
+    }
+
+    return Success();
+}
+
 class SubsystemParser : public SectionParser {
   public:
     SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
@@ -182,6 +199,9 @@
     parser.AddSingleLineParser("firmware_directories",
                                std::bind(ParseFirmwareDirectoriesLine, _1,
                                          &ueventd_configuration.firmware_directories));
+    parser.AddSingleLineParser("modalias_handling",
+                               std::bind(ParseModaliasHandlingLine, _1,
+                                         &ueventd_configuration.enable_modalias_handling));
 
     for (const auto& config : configs) {
         parser.ParseConfig(config);
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 343d58b..7d30edf 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -30,6 +30,7 @@
     std::vector<SysfsPermissions> sysfs_permissions;
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
+    bool enable_modalias_handling = false;
 };
 
 UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
diff --git a/init/util.cpp b/init/util.cpp
index 7735717..105ac87 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -47,7 +47,7 @@
 #endif
 
 #ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#error "Do not include init.h in files used by ueventd; it will expose init's globals"
 #endif
 
 using android::base::boot_clock;
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 1eab46c..1915f22 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -149,8 +149,8 @@
     }
 
     constexpr int kMaxMessageSize = sizeof(FuseBuffer);
-    if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0 ||
-        setsockopt(fds[1], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0) {
+    if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0 ||
+        setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0) {
         PLOG(ERROR) << "Failed to update buffer size for socket";
         return false;
     }
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index 7a70bf3..3063815 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -25,7 +25,7 @@
 
 // The numbers came from sdcard.c.
 // Maximum number of bytes to write/read in one request/one reply.
-constexpr size_t kFuseMaxWrite = 256 * 1024;
+constexpr size_t kFuseMaxWrite = 128 * 1024;
 constexpr size_t kFuseMaxRead = 128 * 1024;
 constexpr int32_t kFuseSuccess = 0;
 
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index a9cfce4..c564271 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -31,6 +31,7 @@
 
 #include <deque>
 #include <iterator>
+#include <memory>
 #include <string>
 #include <vector>
 
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 48def6f..af8f0a2 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -62,7 +62,7 @@
 static const struct fs_path_config android_dirs[] = {
     // clang-format off
     { 00770, AID_SYSTEM,       AID_CACHE,        0, "cache" },
-    { 00500, AID_ROOT,         AID_ROOT,         0, "config" },
+    { 00555, AID_ROOT,         AID_ROOT,         0, "config" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data/app" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data/app-private" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data/app-ephemeral" },
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index bb1ce34..1b7c377 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -108,6 +108,13 @@
 android_log_list_element android_log_read_next(android_log_context ctx);
 android_log_list_element android_log_peek_next(android_log_context ctx);
 
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+                             const char* msg, size_t len);
+
 /* Finished with reader or writer context */
 int android_log_destroy(android_log_context* ctx);
 
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
index cbd3091..9f74534 100644
--- a/liblog/include_vndk/log/log_event_list.h
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -63,6 +63,13 @@
 /* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
 int android_log_write_list(android_log_context ctx, log_id_t id);
 
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+                             const char* msg, size_t len);
+
 /* Finished with reader or writer context */
 int android_log_destroy(android_log_context* ctx);
 
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 66670fe..015c9cb 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -53,3 +53,9 @@
     __android_log_is_loggable_len;
     __android_log_is_debuggable; # vndk
 };
+
+LIBLOG_Q {
+  global:
+    android_log_reset; #vndk
+    android_log_parser_reset; #vndk
+};
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index a59cb87..14002ce 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -45,14 +45,9 @@
   uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
 } android_log_context_internal;
 
-LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
-  size_t needed, i;
-  android_log_context_internal* context;
+static void init_context(android_log_context_internal* context, uint32_t tag) {
+  size_t needed;
 
-  context = calloc(1, sizeof(android_log_context_internal));
-  if (!context) {
-    return NULL;
-  }
   context->tag = tag;
   context->read_write_flag = kAndroidLoggerWrite;
   needed = sizeof(uint8_t) + sizeof(uint8_t);
@@ -63,6 +58,24 @@
   context->storage[context->pos + 0] = EVENT_TYPE_LIST;
   context->list[0] = context->pos + 1;
   context->pos += needed;
+}
+
+static void init_parser_context(android_log_context_internal* context,
+                                const char* msg, size_t len) {
+  len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+  context->len = len;
+  memcpy(context->storage, msg, len);
+  context->read_write_flag = kAndroidLoggerRead;
+}
+
+LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
+  android_log_context_internal* context;
+
+  context = calloc(1, sizeof(android_log_context_internal));
+  if (!context) {
+    return NULL;
+  }
+  init_context(context, tag);
 
   return (android_log_context)context;
 }
@@ -76,10 +89,7 @@
   if (!context) {
     return NULL;
   }
-  len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
-  context->len = len;
-  memcpy(context->storage, msg, len);
-  context->read_write_flag = kAndroidLoggerRead;
+  init_parser_context(context, msg, len);
 
   return (android_log_context)context;
 }
@@ -97,6 +107,38 @@
   return 0;
 }
 
+LIBLOG_ABI_PUBLIC int android_log_reset(android_log_context ctx) {
+  android_log_context_internal* context;
+  uint32_t tag;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+    return -EBADF;
+  }
+
+  tag = context->tag;
+  memset(context, 0, sizeof(*context));
+  init_context(context, tag);
+
+  return 0;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_parser_reset(android_log_context ctx,
+                                               const char* msg, size_t len) {
+  android_log_context_internal* context;
+
+  context = (android_log_context_internal*)ctx;
+  if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
+    return -EBADF;
+  }
+
+  memset(context, 0, sizeof(*context));
+  init_parser_context(context, msg, len);
+
+  return 0;
+}
+
+
 LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
   size_t needed;
   android_log_context_internal* context;
diff --git a/libmetricslogger/OWNERS b/libmetricslogger/OWNERS
index 7fe0443..6a6fba2 100644
--- a/libmetricslogger/OWNERS
+++ b/libmetricslogger/OWNERS
@@ -1 +1,2 @@
+cwren@google.com
 jhawkins@google.com
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 2c76869..c305db2 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -47,6 +47,8 @@
   public:
     // Create a complex event with category|category|.
     explicit ComplexEventLogger(int category);
+    // Set the package name that this event originates from.
+    void SetPackageName(const std::string& package_name);
     // Add tagged data to the event, with the given tag and integer value.
     void AddTaggedData(int tag, int32_t value);
     // Add tagged data to the event, with the given tag and string value.
@@ -70,14 +72,49 @@
     LOGBUILDER_VALUE = 802,
     LOGBUILDER_COUNTER = 803,
     LOGBUILDER_HISTOGRAM = 804,
+    LOGBUILDER_PACKAGENAME = 806,
 
     ACTION_BOOT = 1098,
     FIELD_PLATFORM_REASON = 1099,
 
+    FIELD_DURATION_MILLIS = 1304,
+
+    FIELD_END_BATTERY_PERCENT = 1308,
+
     ACTION_HIDDEN_API_ACCESSED = 1391,
     FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
     FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
     FIELD_HIDDEN_API_SIGNATURE = 1394,
+
+    ACTION_USB_CONNECTOR_CONNECTED = 1422,
+    ACTION_USB_CONNECTOR_DISCONNECTED = 1423,
+    ACTION_USB_AUDIO_CONNECTED = 1424,
+    FIELD_USB_AUDIO_VIDPID = 1425,
+    ACTION_USB_AUDIO_DISCONNECTED = 1426,
+    ACTION_HARDWARE_FAILED = 1427,
+    FIELD_HARDWARE_TYPE = 1428,
+    FIELD_HARDWARE_FAILURE_CODE = 1429,
+    ACTION_PHYSICAL_DROP = 1430,
+    FIELD_CONFIDENCE_PERCENT = 1431,
+    FIELD_ACCEL_MILLI_G = 1432,
+    ACTION_BATTERY_HEALTH = 1433,
+    FIELD_BATTERY_HEALTH_SNAPSHOT_TYPE = 1434,
+    FIELD_BATTERY_TEMPERATURE_DECI_C = 1435,
+    FIELD_BATTERY_VOLTAGE_UV = 1436,
+    FIELD_BATTERY_OPEN_CIRCUIT_VOLTAGE_UV = 1437,
+    ACTION_BATTERY_CHARGE_CYCLES = 1438,
+    FIELD_BATTERY_CHARGE_CYCLES = 1439,
+
+    ACTION_SLOW_IO = 1442,
+    FIELD_IO_OPERATION_TYPE = 1443,
+    FIELD_IO_OPERATION_COUNT = 1444,
+    ACTION_SPEAKER_IMPEDANCE = 1445,
+    FIELD_SPEAKER_IMPEDANCE_MILLIOHMS = 1446,
+    FIELD_SPEAKER_LOCATION = 1447,
+    FIELD_BATTERY_RESISTANCE_UOHMS = 1448,
+    FIELD_BATTERY_CURRENT_UA = 1449,
+    FIELD_HARDWARE_LOCATION = 1450,
+    ACTION_BATTERY_CAUSED_SHUTDOWN = 1441,
 };
 
 enum {
@@ -91,5 +128,30 @@
     ACCESS_METHOD_LINKING = 3,
 };
 
+enum HardwareType {
+    HARDWARE_UNKNOWN = 0,
+    HARDWARE_MICROPHONE = 1,
+    HARDWARE_CODEC = 2,
+    HARDWARE_SPEAKER = 3,
+    HARDWARE_FINGERPRINT = 4,
+};
+
+enum HardwareFailureCode {
+    HARDWARE_FAILURE_UNKNOWN = 0,
+    HARDWARE_FAILURE_COMPLETE = 1,
+    HARDWARE_FAILURE_SPEAKER_HIGH_Z = 2,
+    HARDWARE_FAILURE_SPEAKER_SHORT = 3,
+    HARDWARE_FAILURE_FINGERPRINT_SENSOR_BROKEN = 4,
+    HARDWARE_FAILURE_FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5,
+};
+
+enum IoOperation {
+    IOOP_UNKNOWN = 0,
+    IOOP_READ = 1,
+    IOOP_WRITE = 2,
+    IOOP_UNMAP = 3,
+    IOOP_SYNC = 4,
+};
+
 }  // namespace metricslogger
 }  // namespace android
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 912fa12..6a32153 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -62,6 +62,10 @@
     logger << LOGBUILDER_CATEGORY << category;
 }
 
+void ComplexEventLogger::SetPackageName(const std::string& package_name) {
+    logger << LOGBUILDER_PACKAGENAME << package_name;
+}
+
 void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
     logger << tag << value;
 }
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index e53a4c8..9ecdd4f 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -218,6 +218,20 @@
      * to construct the pseudo header used in the checksum calculation.
      */
     dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
+    /*
+     * check validity of dhcp_size.
+     * 1) cannot be negative or zero.
+     * 2) src buffer contains enough bytes to copy
+     * 3) cannot exceed destination buffer
+     */
+    if ((dhcp_size <= 0) ||
+        ((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||
+        ((int)sizeof(struct dhcp_msg) < dhcp_size)) {
+#if VERBOSE
+        ALOGD("Malformed Packet");
+#endif
+        return -1;
+    }
     saddr = packet.ip.saddr;
     daddr = packet.ip.daddr;
     nread = ntohs(packet.ip.tot_len);
diff --git a/libpixelflinger/fixed.cpp b/libpixelflinger/fixed.cpp
index 5094537..de6b479 100644
--- a/libpixelflinger/fixed.cpp
+++ b/libpixelflinger/fixed.cpp
@@ -70,17 +70,6 @@
 
 // ------------------------------------------------------------------------
 
-GGLfixed gglFastDivx(GGLfixed n, GGLfixed d)
-{
-    if ((d>>24) && ((d>>24)+1)) {
-        n >>= 8;
-        d >>= 8;
-    }
-    return gglMulx(n, gglRecip(d));
-}
-
-// ------------------------------------------------------------------------
-
 static const GGLfixed ggl_sqrt_reciproc_approx_tab[8] = {
     // 1/sqrt(x) with x = 1-N/16, N=[8...1]
     0x16A09, 0x15555, 0x143D1, 0x134BF, 0x1279A, 0x11C01, 0x111AC, 0x10865
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 51e9e26..7f39e9b 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -86,7 +86,6 @@
 GGLfixed gglPowx(GGLfixed x, GGLfixed y) CONST;
 GGLfixed gglSqrtx(GGLfixed a) CONST;
 GGLfixed gglSqrtRecipx(GGLfixed x) CONST;
-GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) CONST;
 int32_t gglMulDivi(int32_t a, int32_t b, int32_t c);
 
 int32_t gglRecipQNormalized(int32_t x, int* exponent);
diff --git a/libsync/Android.bp b/libsync/Android.bp
index c95563d..e56f8ba 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -20,8 +20,9 @@
     cflags: ["-Werror"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libsync",
+    recovery_available: true,
     defaults: ["libsync_defaults"],
 }
 
@@ -31,14 +32,6 @@
     export_include_dirs: ["include"],
 }
 
-// libsync_recovery is only intended for the recovery binary.
-// Future versions of the kernel WILL require an updated libsync, and will break
-// anything statically linked against the current libsync.
-cc_library_static {
-    name: "libsync_recovery",
-    defaults: ["libsync_defaults"],
-}
-
 cc_test {
     name: "sync-unit-tests",
     shared_libs: ["libsync"],
diff --git a/libsystem/include/system/camera.h b/libsystem/include/system/camera.h
index 5d0873a..7d79673 100644
--- a/libsystem/include/system/camera.h
+++ b/libsystem/include/system/camera.h
@@ -203,6 +203,15 @@
      * (except disconnect and sending CAMERA_CMD_PING) after getting this.
      */
     CAMERA_ERROR_RELEASED = 2,
+
+    /**
+     * Camera was released because device policy change or the client application
+     * is going to background. The client should call Camera::disconnect
+     * immediately after getting this notification. Otherwise, the camera will be
+     * released by camera service in a short time. The client should not call any
+     * method (except disconnect and sending CAMERA_CMD_PING) after getting this.
+     */
+    CAMERA_ERROR_DISABLED = 3,
     CAMERA_ERROR_SERVER_DIED = 100
 };
 
diff --git a/libsystem/include/system/graphics-base-v1.0.h b/libsystem/include/system/graphics-base-v1.0.h
new file mode 100644
index 0000000..44913cc
--- /dev/null
+++ b/libsystem/include/system/graphics-base-v1.0.h
@@ -0,0 +1,140 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.0
+// Location: hardware/interfaces/graphics/common/1.0/
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    HAL_PIXEL_FORMAT_RGBA_8888 = 1,
+    HAL_PIXEL_FORMAT_RGBX_8888 = 2,
+    HAL_PIXEL_FORMAT_RGB_888 = 3,
+    HAL_PIXEL_FORMAT_RGB_565 = 4,
+    HAL_PIXEL_FORMAT_BGRA_8888 = 5,
+    HAL_PIXEL_FORMAT_YCBCR_422_SP = 16,
+    HAL_PIXEL_FORMAT_YCRCB_420_SP = 17,
+    HAL_PIXEL_FORMAT_YCBCR_422_I = 20,
+    HAL_PIXEL_FORMAT_RGBA_FP16 = 22,
+    HAL_PIXEL_FORMAT_RAW16 = 32,
+    HAL_PIXEL_FORMAT_BLOB = 33,
+    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34,
+    HAL_PIXEL_FORMAT_YCBCR_420_888 = 35,
+    HAL_PIXEL_FORMAT_RAW_OPAQUE = 36,
+    HAL_PIXEL_FORMAT_RAW10 = 37,
+    HAL_PIXEL_FORMAT_RAW12 = 38,
+    HAL_PIXEL_FORMAT_RGBA_1010102 = 43,
+    HAL_PIXEL_FORMAT_Y8 = 538982489,
+    HAL_PIXEL_FORMAT_Y16 = 540422489,
+    HAL_PIXEL_FORMAT_YV12 = 842094169,
+} android_pixel_format_t;
+
+typedef enum {
+    HAL_TRANSFORM_FLIP_H = 1,   // (1 << 0)
+    HAL_TRANSFORM_FLIP_V = 2,   // (1 << 1)
+    HAL_TRANSFORM_ROT_90 = 4,   // (1 << 2)
+    HAL_TRANSFORM_ROT_180 = 3,  // (FLIP_H | FLIP_V)
+    HAL_TRANSFORM_ROT_270 = 7,  // ((FLIP_H | FLIP_V) | ROT_90)
+} android_transform_t;
+
+typedef enum {
+    HAL_DATASPACE_UNKNOWN = 0,
+    HAL_DATASPACE_ARBITRARY = 1,
+    HAL_DATASPACE_STANDARD_SHIFT = 16,
+    HAL_DATASPACE_STANDARD_MASK = 4128768,                      // (63 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_UNSPECIFIED = 0,                     // (0 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT709 = 65536,                       // (1 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_625 = 131072,                  // (2 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608,       // (3 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_525 = 262144,                  // (4 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680,       // (5 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT2020 = 393216,                     // (6 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752,  // (7 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT470M = 524288,                     // (8 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_FILM = 589824,                       // (9 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_DCI_P3 = 655360,                     // (10 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896,                  // (11 << STANDARD_SHIFT)
+    HAL_DATASPACE_TRANSFER_SHIFT = 22,
+    HAL_DATASPACE_TRANSFER_MASK = 130023424,       // (31 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0,        // (0 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_LINEAR = 4194304,       // (1 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_SRGB = 8388608,         // (2 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912,  // (3 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216,    // (4 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520,    // (5 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824,    // (6 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_ST2084 = 29360128,      // (7 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_HLG = 33554432,         // (8 << TRANSFER_SHIFT)
+    HAL_DATASPACE_RANGE_SHIFT = 27,
+    HAL_DATASPACE_RANGE_MASK = 939524096,      // (7 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_UNSPECIFIED = 0,       // (0 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_FULL = 134217728,      // (1 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_LIMITED = 268435456,   // (2 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_EXTENDED = 402653184,  // (3 << RANGE_SHIFT)
+    HAL_DATASPACE_SRGB_LINEAR = 512,
+    HAL_DATASPACE_V0_SRGB_LINEAR = 138477568,  // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_V0_SCRGB_LINEAR =
+        406913024,  // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
+    HAL_DATASPACE_SRGB = 513,
+    HAL_DATASPACE_V0_SRGB = 142671872,   // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
+    HAL_DATASPACE_V0_SCRGB = 411107328,  // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
+    HAL_DATASPACE_JFIF = 257,
+    HAL_DATASPACE_V0_JFIF = 146931712,  // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+    HAL_DATASPACE_BT601_625 = 258,
+    HAL_DATASPACE_V0_BT601_625 =
+        281149440,  // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT601_525 = 259,
+    HAL_DATASPACE_V0_BT601_525 =
+        281280512,  // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT709 = 260,
+    HAL_DATASPACE_V0_BT709 = 281083904,  // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_DCI_P3_LINEAR = 139067392,  // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_DCI_P3 = 155844608,  // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3_LINEAR =
+        139067392,                         // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3 = 143261696,  // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
+    HAL_DATASPACE_ADOBE_RGB = 151715840,  // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
+    HAL_DATASPACE_BT2020_LINEAR = 138805248,  // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_BT2020 = 147193856,     // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+    HAL_DATASPACE_BT2020_PQ = 163971072,  // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
+    HAL_DATASPACE_DEPTH = 4096,
+    HAL_DATASPACE_SENSOR = 4097,
+} android_dataspace_t;
+
+typedef enum {
+    HAL_COLOR_MODE_NATIVE = 0,
+    HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
+    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
+    HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
+    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
+    HAL_COLOR_MODE_STANDARD_BT709 = 5,
+    HAL_COLOR_MODE_DCI_P3 = 6,
+    HAL_COLOR_MODE_SRGB = 7,
+    HAL_COLOR_MODE_ADOBE_RGB = 8,
+    HAL_COLOR_MODE_DISPLAY_P3 = 9,
+} android_color_mode_t;
+
+typedef enum {
+    HAL_COLOR_TRANSFORM_IDENTITY = 0,
+    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
+    HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
+    HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
+    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
+    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
+    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
+} android_color_transform_t;
+
+typedef enum {
+    HAL_HDR_DOLBY_VISION = 1,
+    HAL_HDR_HDR10 = 2,
+    HAL_HDR_HLG = 3,
+} android_hdr_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics-base-v1.1.h b/libsystem/include/system/graphics-base-v1.1.h
new file mode 100644
index 0000000..f95b9ba
--- /dev/null
+++ b/libsystem/include/system/graphics-base-v1.1.h
@@ -0,0 +1,48 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.1
+// Location: hardware/interfaces/graphics/common/1.1/
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    HAL_PIXEL_FORMAT_DEPTH_16 = 48,
+    HAL_PIXEL_FORMAT_DEPTH_24 = 49,
+    HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = 50,
+    HAL_PIXEL_FORMAT_DEPTH_32F = 51,
+    HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 = 52,
+    HAL_PIXEL_FORMAT_STENCIL_8 = 53,
+    HAL_PIXEL_FORMAT_YCBCR_P010 = 54,
+} android_pixel_format_v1_1_t;
+
+typedef enum {
+    HAL_DATASPACE_BT2020_ITU =
+        281411584,  // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT2020_ITU_PQ =
+        298188800,  // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_LIMITED)
+    HAL_DATASPACE_BT2020_ITU_HLG = 302383104,  // ((STANDARD_BT2020 | TRANSFER_HLG) | RANGE_LIMITED)
+    HAL_DATASPACE_BT2020_HLG = 168165376,      // ((STANDARD_BT2020 | TRANSFER_HLG) | RANGE_FULL)
+} android_dataspace_v1_1_t;
+
+typedef enum {
+    HAL_COLOR_MODE_BT2020 = 10,
+    HAL_COLOR_MODE_BT2100_PQ = 11,
+    HAL_COLOR_MODE_BT2100_HLG = 12,
+} android_color_mode_v1_1_t;
+
+typedef enum {
+    HAL_RENDER_INTENT_COLORIMETRIC = 0,
+    HAL_RENDER_INTENT_ENHANCE = 1,
+    HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC = 2,
+    HAL_RENDER_INTENT_TONE_MAP_ENHANCE = 3,
+} android_render_intent_v1_1_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics-base.h b/libsystem/include/system/graphics-base.h
index 2a44faf..ea92007 100644
--- a/libsystem/include/system/graphics-base.h
+++ b/libsystem/include/system/graphics-base.h
@@ -1,141 +1,7 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
-// Source: android.hardware.graphics.common@1.0
-// Root: android.hardware:hardware/interfaces
+#ifndef SYSTEM_CORE_GRAPHICS_BASE_H_
+#define SYSTEM_CORE_GRAPHICS_BASE_H_
 
-#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
-#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#include "graphics-base-v1.0.h"
+#include "graphics-base-v1.1.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
-    HAL_PIXEL_FORMAT_RGBA_8888 = 1,
-    HAL_PIXEL_FORMAT_RGBX_8888 = 2,
-    HAL_PIXEL_FORMAT_RGB_888 = 3,
-    HAL_PIXEL_FORMAT_RGB_565 = 4,
-    HAL_PIXEL_FORMAT_BGRA_8888 = 5,
-    HAL_PIXEL_FORMAT_RGBA_1010102 = 43, // 0x2B
-    HAL_PIXEL_FORMAT_RGBA_FP16 = 22, // 0x16
-    HAL_PIXEL_FORMAT_YV12 = 842094169, // 0x32315659
-    HAL_PIXEL_FORMAT_Y8 = 538982489, // 0x20203859
-    HAL_PIXEL_FORMAT_Y16 = 540422489, // 0x20363159
-    HAL_PIXEL_FORMAT_RAW16 = 32, // 0x20
-    HAL_PIXEL_FORMAT_RAW10 = 37, // 0x25
-    HAL_PIXEL_FORMAT_RAW12 = 38, // 0x26
-    HAL_PIXEL_FORMAT_RAW_OPAQUE = 36, // 0x24
-    HAL_PIXEL_FORMAT_BLOB = 33, // 0x21
-    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34, // 0x22
-    HAL_PIXEL_FORMAT_YCBCR_420_888 = 35, // 0x23
-    HAL_PIXEL_FORMAT_YCBCR_422_888 = 39, // 0x27
-    HAL_PIXEL_FORMAT_YCBCR_444_888 = 40, // 0x28
-    HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41, // 0x29
-    HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42, // 0x2A
-    HAL_PIXEL_FORMAT_YCBCR_422_SP = 16, // 0x10
-    HAL_PIXEL_FORMAT_YCRCB_420_SP = 17, // 0x11
-    HAL_PIXEL_FORMAT_YCBCR_422_I = 20, // 0x14
-    HAL_PIXEL_FORMAT_JPEG = 256, // 0x100
-} android_pixel_format_t;
-
-typedef enum {
-    HAL_TRANSFORM_FLIP_H = 1, // 0x01
-    HAL_TRANSFORM_FLIP_V = 2, // 0x02
-    HAL_TRANSFORM_ROT_90 = 4, // 0x04
-    HAL_TRANSFORM_ROT_180 = 3, // 0x03
-    HAL_TRANSFORM_ROT_270 = 7, // 0x07
-} android_transform_t;
-
-typedef enum {
-    HAL_DATASPACE_UNKNOWN = 0, // 0x0
-    HAL_DATASPACE_ARBITRARY = 1, // 0x1
-    HAL_DATASPACE_STANDARD_SHIFT = 16,
-    HAL_DATASPACE_STANDARD_MASK = 4128768, // (63 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_UNSPECIFIED = 0, // (0 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT709 = 65536, // (1 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_625 = 131072, // (2 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608, // (3 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_525 = 262144, // (4 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680, // (5 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT2020 = 393216, // (6 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752, // (7 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT470M = 524288, // (8 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_FILM = 589824, // (9 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_DCI_P3 = 655360, // (10 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896, // (11 << STANDARD_SHIFT)
-    HAL_DATASPACE_TRANSFER_SHIFT = 22,
-    HAL_DATASPACE_TRANSFER_MASK = 130023424, // (31 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0, // (0 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_LINEAR = 4194304, // (1 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_SRGB = 8388608, // (2 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912, // (3 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216, // (4 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520, // (5 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824, // (6 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_ST2084 = 29360128, // (7 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_HLG = 33554432, // (8 << TRANSFER_SHIFT)
-    HAL_DATASPACE_RANGE_SHIFT = 27,
-    HAL_DATASPACE_RANGE_MASK = 939524096, // (7 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_UNSPECIFIED = 0, // (0 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_FULL = 134217728, // (1 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_LIMITED = 268435456, // (2 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_EXTENDED = 402653184, // (3 << RANGE_SHIFT)
-    HAL_DATASPACE_SRGB_LINEAR = 512, // 0x200
-    HAL_DATASPACE_V0_SRGB_LINEAR = 138477568, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_V0_SCRGB_LINEAR = 406913024, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
-    HAL_DATASPACE_SRGB = 513, // 0x201
-    HAL_DATASPACE_V0_SRGB = 142671872, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
-    HAL_DATASPACE_V0_SCRGB = 411107328, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
-    HAL_DATASPACE_JFIF = 257, // 0x101
-    HAL_DATASPACE_V0_JFIF = 146931712, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
-    HAL_DATASPACE_BT601_625 = 258, // 0x102
-    HAL_DATASPACE_V0_BT601_625 = 281149440, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
-    HAL_DATASPACE_BT601_525 = 259, // 0x103
-    HAL_DATASPACE_V0_BT601_525 = 281280512, // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
-    HAL_DATASPACE_BT709 = 260, // 0x104
-    HAL_DATASPACE_V0_BT709 = 281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
-    HAL_DATASPACE_DCI_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_DCI_P3 = 155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
-    HAL_DATASPACE_DISPLAY_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_DISPLAY_P3 = 143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
-    HAL_DATASPACE_ADOBE_RGB = 151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
-    HAL_DATASPACE_BT2020_LINEAR = 138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_BT2020 = 147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
-    HAL_DATASPACE_BT2020_PQ = 163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
-    HAL_DATASPACE_DEPTH = 4096, // 0x1000
-    HAL_DATASPACE_SENSOR = 4097, // 0x1001
-} android_dataspace_t;
-
-typedef enum {
-    HAL_COLOR_MODE_NATIVE = 0,
-    HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
-    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
-    HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
-    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
-    HAL_COLOR_MODE_STANDARD_BT709 = 5,
-    HAL_COLOR_MODE_DCI_P3 = 6,
-    HAL_COLOR_MODE_SRGB = 7,
-    HAL_COLOR_MODE_ADOBE_RGB = 8,
-    HAL_COLOR_MODE_DISPLAY_P3 = 9,
-} android_color_mode_t;
-
-typedef enum {
-    HAL_COLOR_TRANSFORM_IDENTITY = 0,
-    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
-    HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
-    HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
-    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
-    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
-    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
-} android_color_transform_t;
-
-typedef enum {
-    HAL_HDR_DOLBY_VISION = 1,
-    HAL_HDR_HDR10 = 2,
-    HAL_HDR_HLG = 3,
-} android_hdr_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#endif  // SYSTEM_CORE_GRAPHICS_BASE_H_
diff --git a/libsystem/include/system/graphics-sw.h b/libsystem/include/system/graphics-sw.h
new file mode 100644
index 0000000..9e1a88e
--- /dev/null
+++ b/libsystem/include/system/graphics-sw.h
@@ -0,0 +1,16 @@
+#ifndef SYSTEM_CORE_GRAPHICS_SW_H_
+#define SYSTEM_CORE_GRAPHICS_SW_H_
+
+/* Software formats not in the HAL definitions. */
+typedef enum {
+    HAL_PIXEL_FORMAT_YCBCR_422_888 = 39,   // 0x27
+    HAL_PIXEL_FORMAT_YCBCR_444_888 = 40,   // 0x28
+    HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41,    // 0x29
+    HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42,  // 0x2A
+} android_pixel_format_sw_t;
+
+/* for compatibility */
+#define HAL_PIXEL_FORMAT_YCbCr_422_888 HAL_PIXEL_FORMAT_YCBCR_422_888
+#define HAL_PIXEL_FORMAT_YCbCr_444_888 HAL_PIXEL_FORMAT_YCBCR_444_888
+
+#endif  // SYSTEM_CORE_GRAPHICS_SW_H_
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index 1a99187..1b6060a 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -25,6 +25,7 @@
  * generated.
  */
 #include "graphics-base.h"
+#include "graphics-sw.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -32,8 +33,6 @@
 
 /* for compatibility */
 #define HAL_PIXEL_FORMAT_YCbCr_420_888 HAL_PIXEL_FORMAT_YCBCR_420_888
-#define HAL_PIXEL_FORMAT_YCbCr_422_888 HAL_PIXEL_FORMAT_YCBCR_422_888
-#define HAL_PIXEL_FORMAT_YCbCr_444_888 HAL_PIXEL_FORMAT_YCBCR_444_888
 #define HAL_PIXEL_FORMAT_YCbCr_422_SP HAL_PIXEL_FORMAT_YCBCR_422_SP
 #define HAL_PIXEL_FORMAT_YCrCb_420_SP HAL_PIXEL_FORMAT_YCRCB_420_SP
 #define HAL_PIXEL_FORMAT_YCbCr_422_I HAL_PIXEL_FORMAT_YCBCR_422_I
@@ -257,6 +256,11 @@
     float minLuminance;
 };
 
+struct android_cta861_3_metadata {
+    float maxContentLightLevel;
+    float maxFrameAverageLightLevel;
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index df5da65..be2145d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -190,6 +190,7 @@
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
+        "tests/RegsInfoTest.cpp",
         "tests/RegsIterateTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index 3d982f6..8ec560c 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -24,6 +24,7 @@
 
 #include <android-base/unique_fd.h>
 
+#include <dex/class_accessor-inl.h>
 #include <dex/code_item_accessors-inl.h>
 #include <dex/compact_dex_file.h>
 #include <dex/dex_file-inl.h>
@@ -98,38 +99,20 @@
 
   // Check the methods we haven't cached.
   for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) {
-    const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(class_def_index_);
-    const uint8_t* class_data = dex_file_->GetClassData(class_def);
-    if (class_data == nullptr) {
-      continue;
-    }
+    art::ClassAccessor accessor(*dex_file_, dex_file_->GetClassDef(class_def_index_));
 
-    if (class_it_.get() == nullptr || !class_it_->HasNext()) {
-      class_it_.reset(new art::ClassDataItemIterator(*dex_file_.get(), class_data));
-    }
-
-    for (; class_it_->HasNext(); class_it_->Next()) {
-      if (!class_it_->IsAtMethod()) {
-        continue;
-      }
-      const art::DexFile::CodeItem* code_item = class_it_->GetMethodCodeItem();
-      if (code_item == nullptr) {
-        continue;
-      }
-      art::CodeItemInstructionAccessor code(*dex_file_.get(), code_item);
+    for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
+      art::CodeItemInstructionAccessor code = method.GetInstructions();
       if (!code.HasCodeItem()) {
         continue;
       }
-
       uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
-      uint32_t offset_end = offset + code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
-      uint32_t member_index = class_it_->GetMemberIndex();
+      uint32_t offset_end = offset + code.InsnsSizeInBytes();
+      uint32_t member_index = method.GetIndex();
       method_cache_[offset_end] = std::make_pair(offset, member_index);
       if (offset <= dex_offset && dex_offset < offset_end) {
         *method_name = dex_file_->PrettyMethod(member_index, false);
         *method_offset = dex_offset - offset;
-        // Move past this element.
-        class_it_->Next();
         return true;
       }
     }
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 508692d..c123158 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -45,7 +45,6 @@
   std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_;  // dex offset to method index.
 
   uint32_t class_def_index_ = 0;
-  std::unique_ptr<art::ClassDataItemIterator> class_it_;
 };
 
 class DexFileFromFile : public DexFile {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 2c00456..f59a472 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -167,15 +167,10 @@
     return false;
   }
 
-  if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias)) {
-    return false;
-  }
-
-  // We could still potentially unwind without the section header
-  // information, so ignore any errors.
-  if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
-    log(0, "Malformed section header found, ignoring...");
-  }
+  // If we have enough information that this is an elf file, then allow
+  // malformed program and section headers.
+  ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias);
+  ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
   return true;
 }
 
@@ -200,14 +195,12 @@
 }
 
 template <typename EhdrType, typename PhdrType>
-bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
+void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
     if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
-      last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address = offset;
-      return false;
+      return;
     }
 
     switch (phdr.p_type) {
@@ -242,11 +235,10 @@
       break;
     }
   }
-  return true;
 }
 
 template <typename EhdrType, typename ShdrType>
-bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
+void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
   uint64_t offset = ehdr.e_shoff;
   uint64_t sec_offset = 0;
   uint64_t sec_size = 0;
@@ -267,9 +259,7 @@
   offset += ehdr.e_shentsize;
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
     if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
-      last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address = offset;
-      return false;
+      return;
     }
 
     if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
@@ -277,18 +267,14 @@
       // the string terminated names.
       ShdrType str_shdr;
       if (shdr.sh_link >= ehdr.e_shnum) {
-        last_error_.code = ERROR_UNWIND_INFO;
-        return false;
+        continue;
       }
       uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
       if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = str_offset;
-        return false;
+        continue;
       }
       if (str_shdr.sh_type != SHT_STRTAB) {
-        last_error_.code = ERROR_UNWIND_INFO;
-        return false;
+        continue;
       }
       symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
                                      str_shdr.sh_offset, str_shdr.sh_size));
@@ -324,7 +310,6 @@
                                                             static_cast<uint64_t>(shdr.sh_offset)));
     }
   }
-  return true;
 }
 
 template <typename DynType>
@@ -499,11 +484,13 @@
 template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
 template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
 
-template bool ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, uint64_t*);
-template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, uint64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&,
+                                                                       uint64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&,
+                                                                       uint64_t*);
 
-template bool ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
-template bool ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
+template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
+template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
 
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index 47825f5..e6dd33c 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -25,11 +25,13 @@
 
 template <typename AddressType>
 struct RegsInfo {
+  static constexpr size_t MAX_REGISTERS = 64;
+
   RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}
 
   RegsImpl<AddressType>* regs = nullptr;
   uint64_t saved_reg_map = 0;
-  AddressType saved_regs[64];
+  AddressType saved_regs[MAX_REGISTERS];
 
   inline AddressType Get(uint32_t reg) {
     if (IsSaved(reg)) {
@@ -39,23 +41,23 @@
   }
 
   inline AddressType* Save(uint32_t reg) {
-    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
-      // This should never happen as since all currently supported
-      // architectures have the total number of registers < 64.
+    if (reg > MAX_REGISTERS) {
+      // This should never happen since all currently supported
+      // architectures have < 64 total registers.
       abort();
     }
-    saved_reg_map |= 1 << reg;
+    saved_reg_map |= 1ULL << reg;
     saved_regs[reg] = (*regs)[reg];
     return &(*regs)[reg];
   }
 
   inline bool IsSaved(uint32_t reg) {
-    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
-      // This should never happen as since all currently supported
-      // architectures have the total number of registers < 64.
+    if (reg > MAX_REGISTERS) {
+      // This should never happen since all currently supported
+      // architectures have < 64 total registers.
       abort();
     }
-    return saved_reg_map & (1 << reg);
+    return saved_reg_map & (1ULL << reg);
   }
 
   inline uint16_t Total() { return regs->total_regs(); }
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5c1210d..a45eba8 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -104,10 +104,10 @@
   bool ReadAllHeaders(uint64_t* load_bias);
 
   template <typename EhdrType, typename PhdrType>
-  bool ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
+  void ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
 
   template <typename EhdrType, typename ShdrType>
-  bool ReadSectionHeaders(const EhdrType& ehdr);
+  void ReadSectionHeaders(const EhdrType& ehdr);
 
   template <typename DynType>
   bool GetSonameWithTemplate(std::string* soname);
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index 81c0af3..f0b5e3a 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -33,8 +33,7 @@
 
 #if defined(__arm__)
 
-inline __always_inline void RegsGetLocal(Regs* regs) {
-  void* reg_data = regs->RawData();
+inline __attribute__((__always_inline__)) void AsmGetRegs(void* reg_data) {
   asm volatile(
       ".align 2\n"
       "bx pc\n"
@@ -55,8 +54,7 @@
 
 #elif defined(__aarch64__)
 
-inline __always_inline void RegsGetLocal(Regs* regs) {
-  void* reg_data = regs->RawData();
+inline __attribute__((__always_inline__)) void AsmGetRegs(void* reg_data) {
   asm volatile(
       "1:\n"
       "stp x0, x1, [%[base], #0]\n"
@@ -87,11 +85,12 @@
 
 extern "C" void AsmGetRegs(void* regs);
 
-inline void RegsGetLocal(Regs* regs) {
+#endif
+
+inline __attribute__((__always_inline__)) void RegsGetLocal(Regs* regs) {
   AsmGetRegs(regs->RawData());
 }
 
-#endif
 
 }  // namespace unwindstack
 
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index aa6df84..9326bff 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -97,9 +97,15 @@
   template <typename ElfType>
   void InitHeadersDebugFrameFail();
 
+  template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+  void InitProgramHeadersMalformed();
+
   template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
   void InitSectionHeadersMalformed();
 
+  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+  void InitSectionHeadersMalformedSymData();
+
   template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
   void InitSectionHeaders(uint64_t entry_size);
 
@@ -677,6 +683,29 @@
   InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitProgramHeadersMalformed() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  Ehdr ehdr = {};
+  ehdr.e_phoff = 0x100;
+  ehdr.e_phnum = 3;
+  ehdr.e_phentsize = sizeof(Phdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+}
+
+TEST_F(ElfInterfaceTest, init_program_headers_malformed32) {
+  InitProgramHeadersMalformed<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_program_headers_malformed64) {
+  InitProgramHeadersMalformed<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>();
+}
+
 template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
 void ElfInterfaceTest::InitSectionHeadersMalformed() {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -700,6 +729,80 @@
   InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformedSymData() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x1000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 5;
+  ehdr.e_shentsize = sizeof(Shdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_SYMTAB;
+  shdr.sh_link = 4;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_DYNSYM;
+  shdr.sh_link = 10;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_DYNSYM;
+  shdr.sh_link = 2;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for the entries.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0U, elf->debug_frame_offset());
+  EXPECT_EQ(0U, elf->debug_frame_size());
+  EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+  EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+  std::string name;
+  uint64_t name_offset;
+  ASSERT_FALSE(elf->GetFunctionName(0x90010, &name, &name_offset));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata32) {
+  InitSectionHeadersMalformedSymData<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata64) {
+  InitSectionHeadersMalformedSymData<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
 template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
 void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -708,7 +811,7 @@
 
   Ehdr ehdr = {};
   ehdr.e_shoff = offset;
-  ehdr.e_shnum = 10;
+  ehdr.e_shnum = 5;
   ehdr.e_shentsize = entry_size;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
@@ -795,7 +898,7 @@
 
   Ehdr ehdr = {};
   ehdr.e_shoff = offset;
-  ehdr.e_shnum = 10;
+  ehdr.e_shnum = 6;
   ehdr.e_shentsize = sizeof(Shdr);
   ehdr.e_shstrndx = 2;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index f599503..861b82f 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -319,7 +319,7 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   ehdr.e_shoff = 0x2000;
   ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
-  ehdr.e_shnum = 4;
+  ehdr.e_shnum = 0;
   memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
 
   Elf* elf = info.GetElf(process_memory_, false);
@@ -341,7 +341,7 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
   ehdr.e_shoff = 0x2000;
   ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
-  ehdr.e_shnum = 4;
+  ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
   Elf* elf = info.GetElf(process_memory_, false);
@@ -368,7 +368,7 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
   ehdr.e_shoff = 0x2000;
   ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
-  ehdr.e_shnum = 4;
+  ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
   Elf* elf_in_threads[kNumConcurrentThreads];
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
new file mode 100644
index 0000000..052b5bf
--- /dev/null
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Regs.h>
+
+#include "RegsFake.h"
+#include "RegsInfo.h"
+
+namespace unwindstack {
+
+TEST(RegsInfoTest, single_uint32_t) {
+  RegsImplFake<uint32_t> regs(10);
+  RegsInfo<uint32_t> info(&regs);
+
+  regs[1] = 0x100;
+  ASSERT_FALSE(info.IsSaved(1));
+  ASSERT_EQ(0x100U, info.Get(1));
+  ASSERT_EQ(10, info.Total());
+
+  uint32_t* value = info.Save(1);
+  ASSERT_EQ(value, &regs[1]);
+  regs[1] = 0x200;
+  ASSERT_TRUE(info.IsSaved(1));
+  ASSERT_EQ(0x100U, info.Get(1));
+  ASSERT_EQ(0x200U, regs[1]);
+}
+
+TEST(RegsInfoTest, single_uint64_t) {
+  RegsImplFake<uint64_t> regs(20);
+  RegsInfo<uint64_t> info(&regs);
+
+  regs[3] = 0x300;
+  ASSERT_FALSE(info.IsSaved(3));
+  ASSERT_EQ(0x300U, info.Get(3));
+  ASSERT_EQ(20, info.Total());
+
+  uint64_t* value = info.Save(3);
+  ASSERT_EQ(value, &regs[3]);
+  regs[3] = 0x400;
+  ASSERT_TRUE(info.IsSaved(3));
+  ASSERT_EQ(0x300U, info.Get(3));
+  ASSERT_EQ(0x400U, regs[3]);
+}
+
+TEST(RegsInfoTest, all) {
+  RegsImplFake<uint64_t> regs(64);
+  RegsInfo<uint64_t> info(&regs);
+
+  for (uint32_t i = 0; i < 64; i++) {
+    regs[i] = i * 0x100;
+    ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+  }
+
+  for (uint32_t i = 0; i < 64; i++) {
+    ASSERT_FALSE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+    uint64_t* reg = info.Save(i);
+    ASSERT_EQ(reg, &regs[i]) << "Reg " + std::to_string(i) + " failed.";
+    *reg = i * 0x1000 + 0x100;
+    ASSERT_EQ(i * 0x1000 + 0x100, regs[i]) << "Reg " + std::to_string(i) + " failed.";
+  }
+
+  for (uint32_t i = 0; i < 64; i++) {
+    ASSERT_TRUE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+    ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 9758b18..7e62542 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -137,6 +137,7 @@
 /* Returns the USB product ID from the device descriptor for the USB device */
 uint16_t usb_device_get_product_id(struct usb_device *device);
 
+/* Returns a pointer to device descriptor */
 const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
 
 /* Returns a USB descriptor string for the given string ID.
@@ -156,6 +157,12 @@
 int usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,
                                size_t* response_size);
 
+/* Returns the length in bytes read into the raw descriptors array */
+size_t usb_device_get_descriptors_length(const struct usb_device* device);
+
+/* Returns a pointer to the raw descriptors array */
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device);
+
 /* Returns a USB descriptor string for the given string ID.
  * Used to implement usb_device_get_manufacturer_name,
  * usb_device_get_product_name and usb_device_get_serial.
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index cb8d430..415488f 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -76,9 +76,11 @@
     int                         wddbus;
 };
 
+#define MAX_DESCRIPTORS_LENGTH 4096
+
 struct usb_device {
     char dev_name[64];
-    unsigned char desc[4096];
+    unsigned char desc[MAX_DESCRIPTORS_LENGTH];
     int desc_length;
     int fd;
     int writeable;
@@ -387,6 +389,8 @@
     return device;
 
 failed:
+    // TODO It would be more appropriate to have callers do this
+    // since this function doesn't "own" this file descriptor.
     close(fd);
     free(device);
     return NULL;
@@ -455,11 +459,18 @@
     return __le16_to_cpu(desc->idProduct);
 }
 
-const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device)
-{
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device* device) {
     return (struct usb_device_descriptor*)device->desc;
 }
 
+size_t usb_device_get_descriptors_length(const struct usb_device* device) {
+    return device->desc_length;
+}
+
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device) {
+    return device->desc;
+}
+
 /* Returns a USB descriptor string for the given string ID.
  * Return value: < 0 on error.  0 on success.
  * The string is returned in ucs2_out in USB-native UCS-2 encoding.
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index bd6015e..fe6f33d 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,16 +16,15 @@
 
 #define LOG_TAG "CallStack"
 
-#include <utils/CallStack.h>
-
-#include <memory>
-
 #include <utils/Printer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
 #include <backtrace/Backtrace.h>
 
+#define CALLSTACK_WEAK  // Don't generate weak definitions.
+#include <utils/CallStack.h>
+
 namespace android {
 
 CallStack::CallStack() {
@@ -76,4 +75,30 @@
     }
 }
 
+// The following four functions may be used via weak symbol references from libutils.
+// Clients assume that if any of these symbols are available, then deleteStack() is.
+
+#ifdef WEAKS_AVAILABLE
+
+CallStack::CallStackUPtr CallStack::getCurrentInternal(int ignoreDepth) {
+    CallStack::CallStackUPtr stack(new CallStack());
+    stack->update(ignoreDepth + 1);
+    return stack;
+}
+
+void CallStack::logStackInternal(const char* logtag, const CallStack* stack,
+                                 android_LogPriority priority) {
+    stack->log(logtag, priority);
+}
+
+String8 CallStack::stackToStringInternal(const char* prefix, const CallStack* stack) {
+    return stack->toString(prefix);
+}
+
+void CallStack::deleteStack(CallStack* stack) {
+    delete stack;
+}
+
+#endif // WEAKS_AVAILABLE
+
 }; // namespace android
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 9074850..3f1e79a 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -17,30 +17,41 @@
 #define LOG_TAG "RefBase"
 // #define LOG_NDEBUG 0
 
+#include <memory>
+
 #include <utils/RefBase.h>
 
 #include <utils/CallStack.h>
 
+#include <utils/Mutex.h>
+
 #ifndef __unused
 #define __unused __attribute__((__unused__))
 #endif
 
-// compile with refcounting debugging enabled
-#define DEBUG_REFS                      0
+// Compile with refcounting debugging enabled.
+#define DEBUG_REFS 0
+
+// The following three are ignored unless DEBUG_REFS is set.
 
 // whether ref-tracking is enabled by default, if not, trackMe(true, false)
 // needs to be called explicitly
-#define DEBUG_REFS_ENABLED_BY_DEFAULT   0
+#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
 
 // whether callstack are collected (significantly slows things down)
-#define DEBUG_REFS_CALLSTACK_ENABLED    1
+#define DEBUG_REFS_CALLSTACK_ENABLED 1
 
 // folder where stack traces are saved when DEBUG_REFS is enabled
 // this folder needs to exist and be writable
-#define DEBUG_REFS_CALLSTACK_PATH       "/data/debug"
+#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
 
 // log all reference counting operations
-#define PRINT_REFS                      0
+#define PRINT_REFS 0
+
+// Continue after logging a stack trace if ~RefBase discovers that reference
+// count has never been incremented. Normally we conspicuously crash in that
+// case.
+#define DEBUG_REFBASE_DESTRUCTION 1
 
 // ---------------------------------------------------------------------------
 
@@ -184,7 +195,7 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                refs->stack.log(LOG_TAG);
+                CallStack::logStack(LOG_TAG, refs->stack.get());
 #endif
                 refs = refs->next;
             }
@@ -198,14 +209,14 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                refs->stack.log(LOG_TAG);
+                CallStack::logStack(LOG_TAG, refs->stack.get());
 #endif
                 refs = refs->next;
             }
         }
         if (dumpStack) {
             ALOGE("above errors at:");
-            CallStack stack(LOG_TAG);
+            CallStack::logStack(LOG_TAG);
         }
     }
 
@@ -279,7 +290,7 @@
                      this);
             int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
             if (rc >= 0) {
-                write(rc, text.string(), text.length());
+                (void)write(rc, text.string(), text.length());
                 close(rc);
                 ALOGD("STACK TRACE for %p saved in %s", this, name);
             }
@@ -294,7 +305,7 @@
         ref_entry* next;
         const void* id;
 #if DEBUG_REFS_CALLSTACK_ENABLED
-        CallStack stack;
+        CallStack::CallStackUPtr stack;
 #endif
         int32_t ref;
     };
@@ -311,7 +322,7 @@
             ref->ref = mRef;
             ref->id = id;
 #if DEBUG_REFS_CALLSTACK_ENABLED
-            ref->stack.update(2);
+            ref->stack = CallStack::getCurrent(2);
 #endif
             ref->next = *refs;
             *refs = ref;
@@ -346,7 +357,7 @@
                 ref = ref->next;
             }
 
-            CallStack stack(LOG_TAG);
+            CallStack::logStack(LOG_TAG);
         }
     }
 
@@ -373,7 +384,7 @@
                      inc, refs->id, refs->ref);
             out->append(buf);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-            out->append(refs->stack.toString("\t\t"));
+            out->append(CallStack::stackToString("\t\t", refs->stack.get()));
 #else
             out->append("\t\t(call stacks disabled)");
 #endif
@@ -700,16 +711,16 @@
         if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
             delete mRefs;
         }
-    } else if (mRefs->mStrong.load(std::memory_order_relaxed)
-            == INITIAL_STRONG_VALUE) {
+    } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
         // We never acquired a strong reference on this object.
-        LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0,
-                "RefBase: Explicit destruction with non-zero weak "
-                "reference count");
-        // TODO: Always report if we get here. Currently MediaMetadataRetriever
-        // C++ objects are inconsistently managed and sometimes get here.
-        // There may be other cases, but we believe they should all be fixed.
-        delete mRefs;
+#if DEBUG_REFBASE_DESTRUCTION
+        // Treating this as fatal is prone to causing boot loops. For debugging, it's
+        // better to treat as non-fatal.
+        ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
+        CallStack::logStack(LOG_TAG);
+#else
+        LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
+#endif
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
     const_cast<weakref_impl*&>(mRefs) = nullptr;
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index f820b8b..5c0b406 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -66,6 +66,23 @@
     return getEmptyString();
 }
 
+static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
+    if (u16len >= SIZE_MAX / sizeof(char16_t)) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
+    SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
+    ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+    if (buf) {
+        char16_t* str = (char16_t*)buf->data();
+        memcpy(str, u16str, u16len * sizeof(char16_t));
+        str[u16len] = 0;
+        return str;
+    }
+    return getEmptyString();
+}
+
 // ---------------------------------------------------------------------------
 
 String16::String16()
@@ -98,35 +115,9 @@
     setTo(o, len, begin);
 }
 
-String16::String16(const char16_t* o)
-{
-    size_t len = strlen16(o);
-    SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
-    ALOG_ASSERT(buf, "Unable to allocate shared buffer");
-    if (buf) {
-        char16_t* str = (char16_t*)buf->data();
-        strcpy16(str, o);
-        mString = str;
-        return;
-    }
+String16::String16(const char16_t* o) : mString(allocFromUTF16(o, strlen16(o))) {}
 
-    mString = getEmptyString();
-}
-
-String16::String16(const char16_t* o, size_t len)
-{
-    SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
-    ALOG_ASSERT(buf, "Unable to allocate shared buffer");
-    if (buf) {
-        char16_t* str = (char16_t*)buf->data();
-        memcpy(str, o, len*sizeof(char16_t));
-        str[len] = 0;
-        mString = str;
-        return;
-    }
-
-    mString = getEmptyString();
-}
+String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}
 
 String16::String16(const String8& o)
     : mString(allocFromUTF8(o.string(), o.size()))
@@ -188,6 +179,11 @@
 
 status_t String16::setTo(const char16_t* other, size_t len)
 {
+    if (len >= SIZE_MAX / sizeof(char16_t)) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((len+1)*sizeof(char16_t));
     if (buf) {
@@ -211,6 +207,11 @@
         return NO_ERROR;
     }
 
+    if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
@@ -232,6 +233,11 @@
         return NO_ERROR;
     }
 
+    if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 0c1b875..56004fe 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_CALLSTACK_H
 #define ANDROID_CALLSTACK_H
 
+#include <memory>
+
 #include <android/log.h>
 #include <backtrace/backtrace_constants.h>
 #include <utils/String8.h>
@@ -25,6 +27,19 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#if !defined(__APPLE__) && !defined(_WIN32)
+# define WEAKS_AVAILABLE 1
+#endif
+#ifndef CALLSTACK_WEAK
+# ifdef WEAKS_AVAILABLE
+#   define CALLSTACK_WEAK __attribute__((weak))
+# else // !WEAKS_AVAILABLE
+#   define CALLSTACK_WEAK
+# endif // !WEAKS_AVAILABLE
+#endif // CALLSTACK_WEAK predefined
+
+#define ALWAYS_INLINE __attribute__((always_inline))
+
 namespace android {
 
 class Printer;
@@ -36,7 +51,7 @@
     CallStack();
     // Create a callstack with the current thread's stack trace.
     // Immediately dump it to logcat using the given logtag.
-    CallStack(const char* logtag, int32_t ignoreDepth=1);
+    CallStack(const char* logtag, int32_t ignoreDepth = 1);
     ~CallStack();
 
     // Reset the stack frames (same as creating an empty call stack).
@@ -44,7 +59,7 @@
 
     // Immediately collect the stack traces for the specified thread.
     // The default is to dump the stack of the current call.
-    void update(int32_t ignoreDepth=1, pid_t tid=BACKTRACE_CURRENT_THREAD);
+    void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
 
     // Dump a stack trace to the log using the supplied logtag.
     void log(const char* logtag,
@@ -63,7 +78,88 @@
     // Get the count of stack frames that are in this call stack.
     size_t size() const { return mFrameLines.size(); }
 
-private:
+    // DO NOT USE ANYTHING BELOW HERE. The following public members are expected
+    // to disappear again shortly, once a better replacement facility exists.
+    // The replacement facility will be incompatible!
+
+    // Debugging accesses to some basic functionality. These use weak symbols to
+    // avoid introducing a dependency on libutilscallstack. Such a dependency from
+    // libutils results in a cyclic build dependency. These routines can be called
+    // from within libutils. But if the actual library is unavailable, they do
+    // nothing.
+    //
+    // DO NOT USE THESE. They will disappear.
+    struct StackDeleter {
+#ifdef WEAKS_AVAILABLE
+        void operator()(CallStack* stack) {
+            deleteStack(stack);
+        }
+#else
+        void operator()(CallStack*) {}
+#endif
+    };
+
+    typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;
+
+    // Return current call stack if possible, nullptr otherwise.
+#ifdef WEAKS_AVAILABLE
+    static CallStackUPtr ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
+        if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
+            ALOGW("CallStack::getCurrentInternal not linked, returning null");
+            return CallStackUPtr(nullptr);
+        } else {
+            return getCurrentInternal(ignoreDepth);
+        }
+    }
+#else // !WEAKS_AVAILABLE
+    static CallStackUPtr ALWAYS_INLINE getCurrent(int32_t = 1) {
+        return CallStackUPtr(nullptr);
+    }
+#endif // !WEAKS_AVAILABLE
+
+#ifdef WEAKS_AVAILABLE
+    static void ALWAYS_INLINE logStack(const char* logtag, CallStack* stack = getCurrent().get(),
+                                       android_LogPriority priority = ANDROID_LOG_DEBUG) {
+        if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
+            logStackInternal(logtag, stack, priority);
+        } else {
+            ALOGW("CallStack::logStackInternal not linked");
+        }
+    }
+
+#else
+    static void ALWAYS_INLINE logStack(const char*, CallStack* = getCurrent().get(),
+                                       android_LogPriority = ANDROID_LOG_DEBUG) {
+    }
+#endif // !WEAKS_AVAILABLE
+
+#ifdef WEAKS_AVAILABLE
+    static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
+                                               const CallStack* stack = getCurrent().get()) {
+        if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
+            return stackToStringInternal(prefix, stack);
+        } else {
+            return String8("<CallStack package not linked>");
+        }
+    }
+#else // !WEAKS_AVAILABLE
+    static String8 ALWAYS_INLINE stackToString(const char* = nullptr,
+                                               const CallStack* = getCurrent().get()) {
+        return String8("<CallStack package not linked>");
+    }
+#endif // !WEAKS_AVAILABLE
+
+  private:
+#ifdef WEAKS_AVAILABLE
+    static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
+    static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
+                                                android_LogPriority priority);
+    static String8 CALLSTACK_WEAK stackToStringInternal(const char* prefix, const CallStack* stack);
+    // The deleter is only invoked on non-null pointers. Hence it will never be
+    // invoked if CallStack is not linked.
+    static void CALLSTACK_WEAK deleteStack(CallStack* stack);
+#endif // WEAKS_AVAILABLE
+
     Vector<String8> mFrameLines;
 };
 
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 377479f..23dd5dc 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -643,6 +643,55 @@
   ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
 }
 
+// A zip file whose local file header at offset zero is corrupted.
+//
+// ---------------
+// cat foo > a.txt
+// zip a.zip a.txt
+// cat a.zip | xxd -i
+//
+// Manual changes :
+// [2] = 0xff  // Corrupt the LFH signature of entry 0.
+// [3] = 0xff  // Corrupt the LFH signature of entry 0.
+static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
+    //[lfh-sig-----------], [lfh contents---------------------------------
+    0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
+    //--------------------------------------------------------------------
+    0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
+    //-------------------------------]  [file-name-----------------], [---
+    0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
+    // entry-contents------------------------------------------------------
+    0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
+    //--------------------------------------------------------------------
+    0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
+    //-------------------------------------], [cd-record-sig-------], [---
+    0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
+    // cd-record-----------------------------------------------------------
+    0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
+    //--------------------------------------------------------------------
+    0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
+    //--------------------------------------------------------------------
+    0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
+    //-]  [lfh-file-header-off-], [file-name-----------------], [extra----
+    0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
+    //--------------------------------------------------------------------
+    0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
+    //-------------------------------------------------------], [eocd-sig-
+    0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
+    //-------], [---------------------------------------------------------
+    0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
+    //-------------------------------------------]
+    0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+TEST(ziparchive, BrokenLfhSignature) {
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
+                                        kZipFileWithBrokenLfhSignature.size()));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle));
+}
+
 class VectorReader : public zip_archive::Reader {
  public:
   VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index f357cc2..3b28775 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -55,6 +55,7 @@
 
 using namespace std::chrono_literals;
 using namespace std::chrono;
+using namespace std::literals;
 
 namespace {
 
@@ -497,8 +498,7 @@
         }
         ::usleep(200000);  // let everything settle
     }
-    llkWriteStringToFile(std::string("SysRq : Trigger a crash : 'livelock,") + state + "'\n",
-                         "/dev/kmsg");
+    llkWriteStringToFile("SysRq : Trigger a crash : 'livelock,"s + state + "'\n", "/dev/kmsg");
     android::base::WriteStringToFd("c", sysrqTriggerFd);
     // NOTREACHED
     // DYB
@@ -1070,7 +1070,7 @@
         std::to_string(kthreaddPid) + "," + std::to_string(::getpid()) + "," +
         std::to_string(::gettid()) + "," LLK_BLACKLIST_PROCESS_DEFAULT);
     if (threadname) {
-        defaultBlacklistProcess += std::string(",") + threadname;
+        defaultBlacklistProcess += ","s + threadname;
     }
     for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
         defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 2efda86..903d0e2 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -6,18 +6,34 @@
         "libcutils",
         "liblog",
     ],
+    static_libs: [
+        "libstatslogc",
+        "libstatssocket",
+    ],
     local_include_dirs: ["include"],
-    cflags: ["-Werror"],
-
+    cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
     init_rc: ["lmkd.rc"],
-
     product_variables: {
-        debuggable: {
+        use_lmkd_stats_log: {
             cflags: [
-                "-DLMKD_TRACE_KILLS"
+                "-DLMKD_LOG_STATS"
             ],
         },
     },
+    logtags: ["event.logtags"],
+}
+
+cc_library_static {
+    name: "libstatslogc",
+    srcs: ["statslog.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: ["libstatssocket",],
 }
 
 cc_library_static {
diff --git a/lmkd/event.logtags b/lmkd/event.logtags
new file mode 100644
index 0000000..7c2cd18
--- /dev/null
+++ b/lmkd/event.logtags
@@ -0,0 +1,38 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace.  Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+#    (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# s: Number of seconds (monotonic time)
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+# for meminfo logs
+10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index c2487d6..59f17f2 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -37,6 +37,11 @@
 #include <cutils/sockets.h>
 #include <lmkd.h>
 #include <log/log.h>
+#include <log/log_event_list.h>
+
+#ifdef LMKD_LOG_STATS
+#include "statslog.h"
+#endif
 
 /*
  * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
@@ -52,8 +57,8 @@
 
 #else /* LMKD_TRACE_KILLS */
 
-#define TRACE_KILL_START(pid)
-#define TRACE_KILL_END()
+#define TRACE_KILL_START(pid) ((void)(pid))
+#define TRACE_KILL_END() ((void)0)
 
 #endif /* LMKD_TRACE_KILLS */
 
@@ -68,6 +73,9 @@
 #define MEMINFO_PATH "/proc/meminfo"
 #define LINE_MAX 128
 
+/* Android Logger event logtags (see event.logtags) */
+#define MEMINFO_LOG_TAG 10195355
+
 /* gid containing AID_SYSTEM required */
 #define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
 #define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
@@ -78,6 +86,11 @@
 /* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
 #define SYSTEM_ADJ (-900)
 
+#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#define STRINGIFY_INTERNAL(x) #x
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+
 /* default to old in-kernel interface if no memory pressure events */
 static bool use_inkernel_interface = true;
 static bool has_inkernel_module;
@@ -111,6 +124,9 @@
 static bool kill_heaviest_task;
 static unsigned long kill_timeout_ms;
 static bool use_minfree_levels;
+static bool per_app_memcg;
+
+static android_log_context ctx;
 
 /* data required to handle events */
 struct event_handler_info {
@@ -190,7 +206,17 @@
     MI_SHMEM,
     MI_UNEVICTABLE,
     MI_FREE_SWAP,
-    MI_DIRTY,
+    MI_ACTIVE_ANON,
+    MI_INACTIVE_ANON,
+    MI_ACTIVE_FILE,
+    MI_INACTIVE_FILE,
+    MI_SRECLAIMABLE,
+    MI_SUNRECLAIM,
+    MI_KERNEL_STACK,
+    MI_PAGE_TABLES,
+    MI_ION_HELP,
+    MI_ION_HELP_POOL,
+    MI_CMA_FREE,
     MI_FIELD_COUNT
 };
 
@@ -202,7 +228,17 @@
     "Shmem:",
     "Unevictable:",
     "SwapFree:",
-    "Dirty:",
+    "Active(anon):",
+    "Inactive(anon):",
+    "Active(file):",
+    "Inactive(file):",
+    "SReclaimable:",
+    "SUnreclaim:",
+    "KernelStack:",
+    "PageTables:",
+    "ION_heap:",
+    "ION_heap_pool:",
+    "CmaFree:",
 };
 
 union meminfo {
@@ -214,7 +250,17 @@
         int64_t shmem;
         int64_t unevictable;
         int64_t free_swap;
-        int64_t dirty;
+        int64_t active_anon;
+        int64_t inactive_anon;
+        int64_t active_file;
+        int64_t inactive_file;
+        int64_t sreclaimable;
+        int64_t sunreclaimable;
+        int64_t kernel_stack;
+        int64_t page_tables;
+        int64_t ion_heap;
+        int64_t ion_heap_pool;
+        int64_t cma_free;
         /* fields below are calculated rather than read from the file */
         int64_t nr_file_pages;
     } field;
@@ -245,6 +291,11 @@
     int fd;
 };
 
+#ifdef LMKD_LOG_STATS
+static bool enable_stats_log;
+static android_log_context log_ctx;
+#endif
+
 #define PIDHASH_SZ 1024
 static struct proc *pidhash[PIDHASH_SZ];
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
@@ -472,7 +523,7 @@
         return;
     }
 
-    if (low_ram_device) {
+    if (per_app_memcg) {
         if (params.oomadj >= 900) {
             soft_limit_mult = 0;
         } else if (params.oomadj >= 800) {
@@ -718,6 +769,58 @@
     maxevents++;
 }
 
+#ifdef LMKD_LOG_STATS
+static void memory_stat_parse_line(char *line, struct memory_stat *mem_st) {
+    char key[LINE_MAX + 1];
+    int64_t value;
+
+    sscanf(line, "%" STRINGIFY(LINE_MAX) "s  %" SCNd64 "", key, &value);
+
+    if (strcmp(key, "total_") < 0) {
+        return;
+    }
+
+    if (!strcmp(key, "total_pgfault"))
+        mem_st->pgfault = value;
+    else if (!strcmp(key, "total_pgmajfault"))
+        mem_st->pgmajfault = value;
+    else if (!strcmp(key, "total_rss"))
+        mem_st->rss_in_bytes = value;
+    else if (!strcmp(key, "total_cache"))
+        mem_st->cache_in_bytes = value;
+    else if (!strcmp(key, "total_swap"))
+        mem_st->swap_in_bytes = value;
+}
+
+static int memory_stat_parse(struct memory_stat *mem_st,  int pid, uid_t uid) {
+    FILE *fp;
+    char buf[PATH_MAX];
+
+    /*
+     * Per-application memory.stat files are available only when
+     * per-application memcgs are enabled.
+     */
+    if (!per_app_memcg)
+        return -1;
+
+    snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
+
+    fp = fopen(buf, "r");
+
+    if (fp == NULL) {
+        ALOGE("%s open failed: %s", buf, strerror(errno));
+        return -1;
+    }
+
+    while (fgets(buf, PAGE_SIZE, fp) != NULL ) {
+        memory_stat_parse_line(buf, mem_st);
+    }
+    fclose(fp);
+
+    return 0;
+}
+#endif
+
 /* /prop/zoneinfo parsing routines */
 static int64_t zoneinfo_parse_protection(char *cp) {
     int64_t max = 0;
@@ -857,6 +960,15 @@
     return 0;
 }
 
+static void meminfo_log(union meminfo *mi) {
+    for (int field_idx = 0; field_idx < MI_FIELD_COUNT; field_idx++) {
+        android_log_write_int32(ctx, (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX));
+    }
+
+    android_log_write_list(ctx, LOG_ID_EVENTS);
+    android_log_reset(ctx);
+}
+
 static int proc_get_size(int pid) {
     char path[PATH_MAX];
     char line[LINE_MAX];
@@ -943,6 +1055,11 @@
     int tasksize;
     int r;
 
+#ifdef LMKD_LOG_STATS
+    struct memory_stat mem_st = {};
+    int memory_stat_parse_result = -1;
+#endif
+
     taskname = proc_get_name(pid);
     if (!taskname) {
         pid_remove(pid);
@@ -955,6 +1072,12 @@
         return -1;
     }
 
+#ifdef LMKD_LOG_STATS
+    if (enable_stats_log) {
+        memory_stat_parse_result = memory_stat_parse(&mem_st, pid, uid);
+    }
+#endif
+
     TRACE_KILL_START(pid);
 
     /* CAP_KILL required */
@@ -971,6 +1094,15 @@
     if (r) {
         ALOGE("kill(%d): errno=%d", pid, errno);
         return -1;
+    } else {
+#ifdef LMKD_LOG_STATS
+        if (memory_stat_parse_result == 0) {
+            stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
+                    procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
+                    mem_st.cache_in_bytes, mem_st.swap_in_bytes);
+        }
+#endif
+        return tasksize;
     }
 
     return tasksize;
@@ -987,6 +1119,10 @@
     int killed_size;
     int pages_freed = 0;
 
+#ifdef LMKD_LOG_STATS
+    bool lmk_state_change_start = false;
+#endif
+
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
 
@@ -999,14 +1135,35 @@
 
             killed_size = kill_one_process(procp, min_score_adj, level);
             if (killed_size >= 0) {
+#ifdef LMKD_LOG_STATS
+                if (enable_stats_log && !lmk_state_change_start) {
+                    lmk_state_change_start = true;
+                    stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
+                                                  LMK_STATE_CHANGE_START);
+                }
+#endif
+
                 pages_freed += killed_size;
                 if (pages_freed >= pages_to_free) {
+
+#ifdef LMKD_LOG_STATS
+                    if (enable_stats_log && lmk_state_change_start) {
+                        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
+                                LMK_STATE_CHANGE_STOP);
+                    }
+#endif
                     return pages_freed;
                 }
             }
         }
     }
 
+#ifdef LMKD_LOG_STATS
+    if (enable_stats_log && lmk_state_change_start) {
+        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
+    }
+#endif
+
     return pages_freed;
 }
 
@@ -1121,10 +1278,8 @@
     }
 
     if (skip_count > 0) {
-        if (debug_process_killing) {
-            ALOGI("%lu memory pressure events were skipped after a kill!",
-                skip_count);
-        }
+        ALOGI("%lu memory pressure events were skipped after a kill!",
+              skip_count);
         skip_count = 0;
     }
 
@@ -1221,6 +1376,8 @@
             if (debug_process_killing) {
                 ALOGI("Nothing to kill");
             }
+        } else {
+            meminfo_log(&mi);
         }
     } else {
         int pages_freed;
@@ -1249,27 +1406,29 @@
                 return;
             }
             min_score_adj = level_oomadj[level];
-        } else {
-            if (debug_process_killing) {
-                ALOGI("Killing because cache %ldkB is below "
-                      "limit %ldkB for oom_adj %d\n"
-                      "   Free memory is %ldkB %s reserved",
-                      other_file * page_k, minfree * page_k, min_score_adj,
-                      other_free * page_k, other_free >= 0 ? "above" : "below");
-            }
         }
 
-        if (debug_process_killing) {
-            ALOGI("Trying to free %d pages", pages_to_free);
-        }
         pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free);
+
+        if (use_minfree_levels) {
+            ALOGI("Killing because cache %ldkB is below "
+                  "limit %ldkB for oom_adj %d\n"
+                  "   Free memory is %ldkB %s reserved",
+                  other_file * page_k, minfree * page_k, min_score_adj,
+                  other_free * page_k, other_free >= 0 ? "above" : "below");
+        }
+
         if (pages_freed < pages_to_free) {
-            if (debug_process_killing) {
-                ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed);
-            }
+            ALOGI("Unable to free enough memory (pages to free=%d, pages freed=%d)",
+                  pages_to_free, pages_freed);
         } else {
+            ALOGI("Reclaimed enough memory (pages to free=%d, pages freed=%d)",
+                  pages_to_free, pages_freed);
             gettimeofday(&last_report_tm, NULL);
         }
+        if (pages_freed > 0) {
+            meminfo_log(&mi);
+        }
     }
 }
 
@@ -1481,6 +1640,14 @@
         (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
     use_minfree_levels =
         property_get_bool("ro.lmk.use_minfree_levels", false);
+    per_app_memcg =
+        property_get_bool("ro.config.per_app_memcg", low_ram_device);
+
+    ctx = create_android_logger(MEMINFO_LOG_TAG);
+
+#ifdef LMKD_LOG_STATS
+    statslog_init(&log_ctx, &enable_stats_log);
+#endif
 
     if (!init()) {
         if (!use_inkernel_interface) {
@@ -1509,6 +1676,12 @@
         mainloop();
     }
 
+#ifdef LMKD_LOG_STATS
+    statslog_destroy(&log_ctx);
+#endif
+
+    android_log_destroy(&ctx);
+
     ALOGI("exiting");
     return 0;
 }
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
new file mode 100644
index 0000000..66d1164
--- /dev/null
+++ b/lmkd/statslog.c
@@ -0,0 +1,117 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 <assert.h>
+#include <errno.h>
+#include <log/log_id.h>
+#include <stats_event_list.h>
+#include <time.h>
+
+static int64_t getElapsedRealTimeNs() {
+    struct timespec t;
+    t.tv_sec = t.tv_nsec = 0;
+    clock_gettime(CLOCK_BOOTTIME, &t);
+    return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
+}
+
+/**
+ * Logs the change in LMKD state which is used as start/stop boundaries for logging
+ * LMK_KILL_OCCURRED event.
+ * Code: LMK_STATE_CHANGED = 54
+ */
+int
+stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state) {
+    assert(ctx != NULL);
+    int ret = -EINVAL;
+    if (!ctx) {
+        return ret;
+    }
+
+    reset_log_context(ctx);
+
+    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, state)) < 0) {
+        return ret;
+    }
+
+    return write_to_logger(ctx, LOG_ID_STATS);
+}
+
+/**
+ * Logs the event when LMKD kills a process to reduce memory pressure.
+ * Code: LMK_KILL_OCCURRED = 51
+ */
+int
+stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
+                              char const* process_name, int32_t oom_score, int64_t pgfault,
+                              int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
+                              int64_t swap_in_bytes) {
+    assert(ctx != NULL);
+    int ret = -EINVAL;
+    if (!ctx) {
+        return ret;
+    }
+    reset_log_context(ctx);
+
+    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, uid)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
+        return ret;
+    }
+
+    return write_to_logger(ctx, LOG_ID_STATS);
+}
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
new file mode 100644
index 0000000..edebb19
--- /dev/null
+++ b/lmkd/statslog.h
@@ -0,0 +1,91 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 _STATSLOG_H_
+#define _STATSLOG_H_
+
+#include <assert.h>
+#include <stats_event_list.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/properties.h>
+
+__BEGIN_DECLS
+
+/*
+ * These are defined in
+ * http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
+ */
+#define LMK_KILL_OCCURRED 51
+#define LMK_STATE_CHANGED 54
+#define LMK_STATE_CHANGE_START 1
+#define LMK_STATE_CHANGE_STOP 2
+
+/*
+ * The single event tag id for all stats logs.
+ * Keep this in sync with system/core/logcat/event.logtags
+ */
+const static int kStatsEventTag = 1937006964;
+
+static inline void statslog_init(android_log_context* log_ctx, bool* enable_stats_log) {
+    assert(log_ctx != NULL);
+    assert(enable_stats_log != NULL);
+    *enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
+
+    if (*enable_stats_log) {
+        *log_ctx = create_android_logger(kStatsEventTag);
+    }
+}
+
+static inline void statslog_destroy(android_log_context* log_ctx) {
+    assert(log_ctx != NULL);
+    if (*log_ctx) {
+        android_log_destroy(log_ctx);
+    }
+}
+
+struct memory_stat {
+    int64_t pgfault;
+    int64_t pgmajfault;
+    int64_t rss_in_bytes;
+    int64_t cache_in_bytes;
+    int64_t swap_in_bytes;
+};
+
+#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
+
+/**
+ * Logs the change in LMKD state which is used as start/stop boundaries for logging
+ * LMK_KILL_OCCURRED event.
+ * Code: LMK_STATE_CHANGED = 54
+ */
+int
+stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
+
+/**
+ * Logs the event when LMKD kills a process to reduce memory pressure.
+ * Code: LMK_KILL_OCCURRED = 51
+ */
+int
+stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
+                              char const* process_name, int32_t oom_score, int64_t pgfault,
+                              int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
+                              int64_t swap_in_bytes);
+
+__END_DECLS
+
+#endif /* _STATSLOG_H_ */
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
index 8c7a75f..1996bae 100644
--- a/lmkd/tests/lmkd_test.cpp
+++ b/lmkd/tests/lmkd_test.cpp
@@ -110,9 +110,14 @@
 }
 
 bool getExecPath(std::string &path) {
-    // exec path as utf8z c_str().
-    // std::string contains _all_ nul terminated argv[] strings.
-    return android::base::ReadFileToString("/proc/self/cmdline", &path);
+    char buf[PATH_MAX + 1];
+    int ret = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+    if (ret < 0) {
+        return false;
+    }
+    buf[ret] = '\0';
+    path = buf;
+    return true;
 }
 
 /* Child synchronization primitives */
@@ -310,8 +315,8 @@
     if (getuid() != static_cast<unsigned>(AID_ROOT)) {
         // if not root respawn itself as root and capture output
         std::string command = StringPrintf(
-            "%s=true su root %s --gtest_filter=lmkd.check_for_oom 2>&1",
-            LMKDTEST_RESPAWN_FLAG, test_path.c_str());
+            "%s=true su root %s 2>&1", LMKDTEST_RESPAWN_FLAG,
+            test_path.c_str());
         std::string test_output = readCommand(command);
         GTEST_LOG_(INFO) << test_output;
     } else {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index a78319f..9b04363 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -1115,9 +1115,6 @@
         // client wants to start from the beginning
         it = mLogElements.begin();
     } else {
-        // 3 second limit to continue search for out-of-order entries.
-        log_time min = start - pruneMargin;
-
         // Cap to 300 iterations we look back for out-of-order entries.
         size_t count = 300;
 
@@ -1133,7 +1130,7 @@
             } else if (element->getRealTime() == start) {
                 last = ++it;
                 break;
-            } else if (!--count || (element->getRealTime() < min)) {
+            } else if (!--count) {
                 break;
             }
         }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 763dc79..3a6a5e8 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -98,6 +98,9 @@
 else
   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
+endif
 
 # For /odm partition.
 LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 486d096..bf22951 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -84,7 +84,7 @@
     restorecon_recursive /mnt
 
     mount configfs none /config nodev noexec nosuid
-    chmod 0775 /config/sdcardfs
+    chmod 0770 /config/sdcardfs
     chown system package_info /config/sdcardfs
 
     mkdir /mnt/secure 0700 root root
@@ -180,6 +180,12 @@
     copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
     copy /dev/cpuset/mems /dev/cpuset/system-background/mems
 
+    # restricted is for system tasks that are being throttled
+    # due to screen off.
+    mkdir /dev/cpuset/restricted
+    copy /dev/cpuset/cpus /dev/cpuset/restricted/cpus
+    copy /dev/cpuset/mems /dev/cpuset/restricted/mems
+
     mkdir /dev/cpuset/top-app
     copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
     copy /dev/cpuset/mems /dev/cpuset/top-app/mems
@@ -190,11 +196,13 @@
     chown system system /dev/cpuset/background
     chown system system /dev/cpuset/system-background
     chown system system /dev/cpuset/top-app
+    chown system system /dev/cpuset/restricted
     chown system system /dev/cpuset/tasks
     chown system system /dev/cpuset/foreground/tasks
     chown system system /dev/cpuset/background/tasks
     chown system system /dev/cpuset/system-background/tasks
     chown system system /dev/cpuset/top-app/tasks
+    chown system system /dev/cpuset/restricted/tasks
 
     # set system-background to 0775 so SurfaceFlinger can touch it
     chmod 0775 /dev/cpuset/system-background
@@ -203,6 +211,7 @@
     chmod 0664 /dev/cpuset/background/tasks
     chmod 0664 /dev/cpuset/system-background/tasks
     chmod 0664 /dev/cpuset/top-app/tasks
+    chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
 
 
@@ -366,6 +375,10 @@
     # create the lost+found directories, so as to enforce our permissions
     mkdir /cache/lost+found 0770 root root
 
+    restorecon_recursive /metadata
+    mkdir /metadata/vold
+    chmod 0700 /metadata/vold
+
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
@@ -424,6 +437,7 @@
     mkdir /data/misc/carrierid 0770 system radio
     mkdir /data/misc/apns 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
+    mkdir /data/misc/network_watchlist 0774 system system
     mkdir /data/misc/textclassifier 0771 system system
     mkdir /data/misc/vpn 0770 system vpn
     mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
@@ -456,6 +470,8 @@
     mkdir /data/misc/gcov 0770 root root
 
     mkdir /data/vendor 0771 root root
+    mkdir /data/vendor_ce 0771 root root
+    mkdir /data/vendor_de 0771 root root
     mkdir /data/vendor/hardware 0771 root root
 
     # For security reasons, /data/local/tmp should always be empty.
@@ -688,6 +704,7 @@
 
 on property:vold.decrypt=trigger_post_fs_data
     trigger post-fs-data
+    trigger zygote-start
 
 on property:vold.decrypt=trigger_restart_min_framework
     # A/B update verifier that marks a successful boot.
@@ -695,6 +712,8 @@
     class_start main
 
 on property:vold.decrypt=trigger_restart_framework
+    stop surfaceflinger
+    start surfaceflinger
     # A/B update verifier that marks a successful boot.
     exec_start update_verifier
     class_start main
@@ -732,17 +751,12 @@
 
 ## Daemon processes to be run by init.
 ##
-service ueventd /sbin/ueventd
+service ueventd /system/bin/ueventd
     class core
     critical
     seclabel u:r:ueventd:s0
     shutdown critical
 
-service healthd /system/bin/healthd
-    class core
-    critical
-    group root system wakelock
-
 service console /system/bin/sh
     class core
     console
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
index de1aab3..3a33c94 100644
--- a/rootdir/init.usb.configfs.rc
+++ b/rootdir/init.usb.configfs.rc
@@ -2,7 +2,6 @@
     write /config/usb_gadget/g1/UDC "none"
     stop adbd
     setprop sys.usb.ffs.ready 0
-    setprop sys.usb.ffs.mtp.ready 0
     write /config/usb_gadget/g1/bDeviceClass 0
     write /config/usb_gadget/g1/bDeviceSubClass 0
     write /config/usb_gadget/g1/bDeviceProtocol 0
@@ -24,7 +23,7 @@
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
-on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=mtp && property:sys.usb.configfs=1
+on property:sys.usb.config=mtp && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
     symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -33,15 +32,14 @@
 on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
     start adbd
 
-on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
-property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
     symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
     symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
-on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=ptp && property:sys.usb.configfs=1
+on property:sys.usb.config=ptp && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
     symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -50,8 +48,7 @@
 on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
     start adbd
 
-on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
-property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
     symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
     symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 574bbfe..dc36596 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -43,6 +43,44 @@
 
 #include <private/android_filesystem_config.h>
 
+#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
+#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
+
+static bool supports_esdfs(void) {
+    std::string filesystems;
+    if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) {
+        PLOG(ERROR) << "Could not read /proc/filesystems";
+        return false;
+    }
+    for (const auto& fs : android::base::Split(filesystems, "\n")) {
+        if (fs.find("esdfs") != std::string::npos) return true;
+    }
+    return false;
+}
+
+static bool should_use_sdcardfs(void) {
+    char property[PROPERTY_VALUE_MAX];
+
+    // Allow user to have a strong opinion about state
+    property_get(PROP_SDCARDFS_USER, property, "");
+    if (!strcmp(property, "force_on")) {
+        LOG(WARNING) << "User explicitly enabled sdcardfs";
+        return true;
+    } else if (!strcmp(property, "force_off")) {
+        LOG(WARNING) << "User explicitly disabled sdcardfs";
+        return !supports_esdfs();
+    }
+
+    // Fall back to device opinion about state
+    if (property_get_bool(PROP_SDCARDFS_DEVICE, true)) {
+        LOG(WARNING) << "Device explicitly enabled sdcardfs";
+        return true;
+    } else {
+        LOG(WARNING) << "Device explicitly disabled sdcardfs";
+        return !supports_esdfs();
+    }
+}
+
 // NOTE: This is a vestigial program that simply exists to mount the in-kernel
 // sdcardfs filesystem.  The older FUSE-based design that used to live here has
 // been completely removed to avoid confusion.
@@ -61,7 +99,7 @@
 
 static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
                            uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
-                           mode_t mask, bool derive_gid, bool default_normal) {
+                           mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
     // Try several attempts, each time with one less option, to gracefully
     // handle older kernels that aren't updated yet.
     for (int i = 0; i < 4; i++) {
@@ -72,7 +110,7 @@
 
         auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
                                                 fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
-        if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
+        if (mount(source_path.c_str(), dest_path.c_str(), use_esdfs ? "esdfs" : "sdcardfs",
                   MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
             PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
         } else {
@@ -104,9 +142,21 @@
     return true;
 }
 
+static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
+                                     const std::string& dest_path, uid_t fsuid, gid_t fsgid,
+                                     bool multi_user, userid_t userid, gid_t gid, mode_t mask,
+                                     bool derive_gid, bool default_normal, bool use_esdfs) {
+    if (use_esdfs) {
+        return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
+                              derive_gid, default_normal, use_esdfs);
+    } else {
+        return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
+    }
+}
+
 static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
                          gid_t gid, userid_t userid, bool multi_user, bool full_write,
-                         bool derive_gid, bool default_normal) {
+                         bool derive_gid, bool default_normal, bool use_esdfs) {
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -116,10 +166,13 @@
         // Multi-user storage is fully isolated per user, so "other"
         // permissions are completely masked off.
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
-                                         full_write ? 0007 : 0027)) {
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
+                                      default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
+                                      derive_gid, default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -127,11 +180,13 @@
         // the Android directories are masked off to a single user
         // deep inside attr_from_stat().
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY,
-                                         full_write ? 0027 : 0022) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
-                                         full_write ? 0007 : 0022)) {
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
+                                      derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
+                                      derive_gid, default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
@@ -242,6 +297,6 @@
     }
 
     run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
-                 default_normal);
+                 default_normal, !should_use_sdcardfs());
     return 1;
 }
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 0586381..3d7521c 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -27,6 +27,7 @@
 phony {
     name: "shell_and_utilities_recovery",
     required: [
+        "grep.recovery",
         "sh.recovery",
         "toolbox.recovery",
         "toybox.recovery",
diff --git a/storaged/Android.bp b/storaged/Android.bp
new file mode 100644
index 0000000..7466728
--- /dev/null
+++ b/storaged/Android.bp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_defaults {
+    name: "storaged_defaults",
+
+    shared_libs: [
+        "android.hardware.health@1.0",
+        "android.hardware.health@2.0",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libsysutils",
+        "libutils",
+        "libz",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wno-unused-parameter"
+    ],
+}
+
+cc_library_static {
+    name: "libstoraged",
+
+    defaults: ["storaged_defaults"],
+
+    aidl: {
+        export_aidl_headers: true,
+        local_include_dirs: ["binder"],
+        include_dirs: ["frameworks/native/aidl/binder"],
+    },
+
+    srcs: [
+        "storaged.cpp",
+        "storaged_diskstats.cpp",
+        "storaged_info.cpp",
+        "storaged_service.cpp",
+        "storaged_utils.cpp",
+        "storaged_uid_monitor.cpp",
+        "uid_info.cpp",
+        "storaged.proto",
+        ":storaged_aidl",
+        "binder/android/os/storaged/IStoragedPrivate.aidl",
+    ],
+
+    static_libs: ["libhealthhalutils"],
+    header_libs: ["libbatteryservice_headers"],
+
+    logtags: ["EventLogTags.logtags"],
+
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
+
+    export_include_dirs: ["include"],
+}
+
+cc_binary {
+    name: "storaged",
+
+    defaults: ["storaged_defaults"],
+
+    init_rc: ["storaged.rc"],
+
+    srcs: ["main.cpp"],
+
+    static_libs: [
+        "libhealthhalutils",
+        "libstoraged",
+    ],
+}
+
+/*
+ * Run with:
+ *  adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
+ */
+cc_test {
+    name: "storaged-unit-tests",
+
+    defaults: ["storaged_defaults"],
+
+    srcs: ["tests/storaged_test.cpp"],
+
+    static_libs: [
+        "libhealthhalutils",
+        "libstoraged",
+    ],
+}
+
+// AIDL interface between storaged and framework.jar
+filegroup {
+    name: "storaged_aidl",
+    srcs: [
+        "binder/android/os/IStoraged.aidl",
+    ],
+}
diff --git a/storaged/Android.mk b/storaged/Android.mk
deleted file mode 100644
index a1abe0f..0000000
--- a/storaged/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2016 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-LIBSTORAGED_SHARED_LIBRARIES := \
-    libbinder \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libsysutils \
-    libbatteryservice \
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    storaged.cpp \
-    storaged_info.cpp \
-    storaged_service.cpp \
-    storaged_utils.cpp \
-    storaged_uid_monitor.cpp \
-    EventLogTags.logtags
-
-LOCAL_MODULE := libstoraged
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := storaged
-LOCAL_INIT_RC := storaged.rc
-LOCAL_SRC_FILES := main.cpp
-# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
-LOCAL_STATIC_LIBRARIES := libstoraged
-LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
-LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
-LOCAL_C_INCLUDES := external/googletest/googletest/include
-
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/init/watchdogd.h b/storaged/binder/android/os/IStoraged.aidl
similarity index 69%
copy from init/watchdogd.h
copy to storaged/binder/android/os/IStoraged.aidl
index 73f77d5..0bcc70c 100644
--- a/init/watchdogd.h
+++ b/storaged/binder/android/os/IStoraged.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+package android.os;
 
-namespace android {
-namespace init {
-
-int watchdogd_main(int argc, char **argv);
-
-}  // namespace init
-}  // namespace android
-
-#endif
+/** {@hide} */
+interface IStoraged {
+    void onUserStarted(int userId);
+    void onUserStopped(int userId);
+    int getRecentPerf();
+}
\ No newline at end of file
diff --git a/init/watchdogd.h b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
similarity index 69%
copy from init/watchdogd.h
copy to storaged/binder/android/os/storaged/IStoragedPrivate.aidl
index 73f77d5..9c888e3 100644
--- a/init/watchdogd.h
+++ b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+package android.os.storaged;
 
-namespace android {
-namespace init {
+import android.os.storaged.UidInfo;
 
-int watchdogd_main(int argc, char **argv);
-
-}  // namespace init
-}  // namespace android
-
-#endif
+/** {@hide} */
+interface IStoragedPrivate {
+    UidInfo[] dumpUids();
+    int[] dumpPerfHistory();
+}
\ No newline at end of file
diff --git a/init/watchdogd.h b/storaged/binder/android/os/storaged/UidInfo.aidl
similarity index 69%
copy from init/watchdogd.h
copy to storaged/binder/android/os/storaged/UidInfo.aidl
index 73f77d5..440f386 100644
--- a/init/watchdogd.h
+++ b/storaged/binder/android/os/storaged/UidInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,6 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+package android.os.storaged;
 
-namespace android {
-namespace init {
-
-int watchdogd_main(int argc, char **argv);
-
-}  // namespace init
-}  // namespace android
-
-#endif
+parcelable UidInfo cpp_header "include/uid_info.h";
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index fa68406..6f92048 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -26,28 +26,18 @@
 #include <unordered_map>
 #include <vector>
 
-#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <utils/Mutex.h>
 
-#include "storaged_info.h"
-#include "storaged_uid_monitor.h"
-
-using namespace android;
+#include <android/hardware/health/2.0/IHealth.h>
 
 #define FRIEND_TEST(test_case_name, test_name) \
 friend class test_case_name##_##test_name##_Test
 
-/* For debug */
-#ifdef DEBUG
-#define debuginfo(fmt, ...) \
- do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
- while(0)
-#else
-#define debuginfo(...)
-#endif
-
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof((x)[0]))
 
+#define IS_ALIGNED(x, align)   (!((x) & ((align) - 1)))
+#define ROUND_UP(x, align)     (((x) + ((align) - 1)) & ~((align) - 1))
+
 #define SECTOR_SIZE ( 512 )
 #define SEC_TO_MSEC ( 1000 )
 #define MSEC_TO_USEC ( 1000 )
@@ -55,184 +45,24 @@
 #define SEC_TO_USEC ( 1000000 )
 #define HOUR_TO_SEC ( 3600 )
 #define DAY_TO_SEC ( 3600 * 24 )
+#define WEEK_TO_DAYS ( 7 )
+#define YEAR_TO_WEEKS ( 52 )
 
-// number of attributes diskstats has
-#define DISK_STATS_SIZE ( 11 )
-// maximum size limit of a stats file
-#define DISK_STATS_FILE_MAX_SIZE ( 256 )
-#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
-struct disk_stats {
-    /* It will be extremely unlikely for any of the following entries to overflow.
-     * For read_bytes(which will be greater than any of the following entries), it
-     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
-     * is the peak memory transfer rate for current memory.
-     * The diskstats entries (first 11) need to be at top in this structure _after_
-     * compiler's optimization.
-     */
-    uint64_t read_ios;       // number of read I/Os processed
-    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os
-    uint64_t read_sectors;   // number of sectors read
-    uint64_t read_ticks;     // total wait time for read requests
-    uint64_t write_ios;      // number of write I/Os processed
-    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os
-    uint64_t write_sectors;  // number of sectors written
-    uint64_t write_ticks;    // total wait time for write requests
-    uint64_t io_in_flight;   // number of I/Os currently in flight
-    uint64_t io_ticks;       // total time this block device has been active
-    uint64_t io_in_queue;    // total wait time for all requests
+#include "storaged_diskstats.h"
+#include "storaged_info.h"
+#include "storaged_uid_monitor.h"
+#include "storaged.pb.h"
+#include "uid_info.h"
 
-    uint64_t start_time;     // monotonic time accounting starts
-    uint64_t end_time;       // monotonic time accounting ends
-    uint32_t counter;        // private counter for accumulate calculations
-    double   io_avg;         // average io_in_flight for accumulate calculations
-};
-
-
-
-struct disk_perf {
-    uint32_t read_perf;         // read speed (kbytes/s)
-    uint32_t read_ios;          // read I/Os per second
-    uint32_t write_perf;        // write speed (kbytes/s)
-    uint32_t write_ios;         // write I/Os per second
-    uint32_t queue;             // I/Os in queue
-};
-
-#define CMD_MAX_LEN ( 64 )
-struct task_info {
-    uint32_t pid;                   // task id
-    uint64_t rchar;                 // characters read
-    uint64_t wchar;                 // characters written
-    uint64_t syscr;                 // read syscalls
-    uint64_t syscw;                 // write syscalls
-    uint64_t read_bytes;            // bytes read (from storage layer)
-    uint64_t write_bytes;           // bytes written (to storage layer)
-    uint64_t cancelled_write_bytes; // cancelled write byte by truncate
-
-    uint64_t starttime;             // start time of task
-
-    char cmd[CMD_MAX_LEN];          // filename of the executable
-};
-
-class lock_t {
-    sem_t* mSem;
-public:
-    lock_t(sem_t* sem) {
-        mSem = sem;
-        sem_wait(mSem);
-    }
-    ~lock_t() {
-        sem_post(mSem);
-    }
-};
-
-class stream_stats {
-private:
-    double mSum;
-    double mSquareSum;
-    uint32_t mCnt;
-public:
-    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
-    ~stream_stats() {};
-    double get_mean() {
-        return mSum / mCnt;
-    }
-    double get_std() {
-        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
-    }
-    void add(uint32_t num) {
-        mSum += (double)num;
-        mSquareSum += (double)num * (double)num;
-        mCnt++;
-    }
-    void evict(uint32_t num) {
-        if (mSum < num || mSquareSum < (double)num * (double)num) return;
-        mSum -= (double)num;
-        mSquareSum -= (double)num * (double)num;
-        mCnt--;
-    }
-};
-
-#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
-#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
-#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
-#define UID_IO_STATS_PATH "/proc/uid_io/stats"
-
-class disk_stats_monitor {
-private:
-    FRIEND_TEST(storaged_test, disk_stats_monitor);
-    const char* DISK_STATS_PATH;
-    struct disk_stats mPrevious;
-    struct disk_stats mAccumulate;
-    bool mStall;
-    std::queue<struct disk_perf> mBuffer;
-    struct {
-        stream_stats read_perf;           // read speed (bytes/s)
-        stream_stats read_ios;            // read I/Os per second
-        stream_stats write_perf;          // write speed (bytes/s)
-        stream_stats write_ios;           // write I/O per second
-        stream_stats queue;               // I/Os in queue
-    } mStats;
-    bool mValid;
-    const uint32_t mWindow;
-    const double mSigma;
-    struct disk_perf mMean;
-    struct disk_perf mStd;
-
-    void update_mean();
-    void update_std();
-    void add(struct disk_perf* perf);
-    void evict(struct disk_perf* perf);
-    bool detect(struct disk_perf* perf);
-
-    void update(struct disk_stats* stats);
-
-public:
-    disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
-            mStall(false),
-            mValid(false),
-            mWindow(window_size),
-            mSigma(sigma) {
-        memset(&mPrevious, 0, sizeof(mPrevious));
-        memset(&mMean, 0, sizeof(mMean));
-        memset(&mStd, 0, sizeof(mStd));
-
-        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
-            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
-        } else {
-            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
-        }
-    }
-    void update(void);
-};
-
-class disk_stats_publisher {
-private:
-    FRIEND_TEST(storaged_test, disk_stats_publisher);
-    const char* DISK_STATS_PATH;
-    struct disk_stats mAccumulate;
-    struct disk_stats mPrevious;
-public:
-    disk_stats_publisher(void) {
-        memset(&mAccumulate, 0, sizeof(struct disk_stats));
-        memset(&mPrevious, 0, sizeof(struct disk_stats));
-
-        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
-            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
-        } else {
-            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
-        }
-    }
-
-    ~disk_stats_publisher(void) {}
-    void publish(void);
-    void update(void);
-};
+using namespace std;
+using namespace android;
 
 // Periodic chores intervals in seconds
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT ( 300 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO ( 3600 )
 
 // UID IO threshold in bytes
 #define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
@@ -241,25 +71,35 @@
     int periodic_chores_interval_unit;
     int periodic_chores_interval_disk_stats_publish;
     int periodic_chores_interval_uid_io;
-    bool proc_uid_io_available;      // whether uid_io is accessible
-    bool diskstats_available;   // whether diskstats is accessible
+    int periodic_chores_interval_flush_proto;
     int event_time_check_usec;  // check how much cputime spent in event loop
 };
 
-class storaged_t : public BnBatteryPropertiesListener,
-                   public IBinder::DeathRecipient {
-private:
+class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
+                   public android::hardware::hidl_death_recipient {
+  private:
     time_t mTimer;
     storaged_config mConfig;
-    disk_stats_publisher mDiskStats;
-    disk_stats_monitor mDsm;
+    unique_ptr<disk_stats_monitor> mDsm;
     uid_monitor mUidm;
     time_t mStarttime;
-    sp<IBatteryPropertiesRegistrar> battery_properties;
-    std::unique_ptr<storage_info_t> storage_info;
-public:
+    sp<android::hardware::health::V2_0::IHealth> health;
+    unique_ptr<storage_info_t> storage_info;
+    static const uint32_t current_version;
+    unordered_map<userid_t, bool> proto_loaded;
+    void load_proto(userid_t user_id);
+    char* prepare_proto(userid_t user_id, StoragedProto* proto);
+    void flush_proto(userid_t user_id, StoragedProto* proto);
+    void flush_proto_data(userid_t user_id, const char* data, ssize_t size);
+    string proto_path(userid_t user_id) {
+        return string("/data/misc_ce/") + to_string(user_id) +
+               "/storaged/storaged.proto";
+    }
+    void init_health_service();
+
+  public:
     storaged_t(void);
-    ~storaged_t() {}
+    void init(void);
     void event(void);
     void event_checked(void);
     void pause(void) {
@@ -270,24 +110,37 @@
         return mStarttime;
     }
 
-    std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+    unordered_map<uint32_t, uid_info> get_uids(void) {
         return mUidm.get_uid_io_stats();
     }
-    std::map<uint64_t, struct uid_records> get_uid_records(
+
+    vector<int> get_perf_history(void) {
+        return storage_info->get_perf_history();
+    }
+
+    uint32_t get_recent_perf(void) { return storage_info->get_recent_perf(); }
+
+    map<uint64_t, struct uid_records> get_uid_records(
             double hours, uint64_t threshold, bool force_report) {
         return mUidm.dump(hours, threshold, force_report);
     }
+
     void update_uid_io_interval(int interval) {
         if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
             mConfig.periodic_chores_interval_uid_io = interval;
         }
     }
 
-    void init_battery_service();
-    virtual void batteryPropertiesChanged(struct BatteryProperties props);
-    void binderDied(const wp<IBinder>& who);
+    void add_user_ce(userid_t user_id);
+    void remove_user_ce(userid_t user_id);
+
+    virtual ::android::hardware::Return<void> healthInfoChanged(
+        const ::android::hardware::health::V2_0::HealthInfo& info);
+    void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
 
     void report_storage_info();
+
+    void flush_protos(unordered_map<int, StoragedProto>* protos);
 };
 
 // Eventlog tag
diff --git a/storaged/include/storaged_diskstats.h b/storaged/include/storaged_diskstats.h
new file mode 100644
index 0000000..0b93ba6
--- /dev/null
+++ b/storaged/include/storaged_diskstats.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _STORAGED_DISKSTATS_H_
+#define _STORAGED_DISKSTATS_H_
+
+#include <stdint.h>
+
+#include <android/hardware/health/2.0/IHealth.h>
+
+// number of attributes diskstats has
+#define DISK_STATS_SIZE ( 11 )
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+
+struct disk_stats {
+    /* It will be extremely unlikely for any of the following entries to overflow.
+     * For read_bytes(which will be greater than any of the following entries), it
+     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
+     * is the peak memory transfer rate for current memory.
+     * The diskstats entries (first 11) need to be at top in this structure _after_
+     * compiler's optimization.
+     */
+    uint64_t read_ios;       // number of read I/Os processed
+    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os
+    uint64_t read_sectors;   // number of sectors read
+    uint64_t read_ticks;     // total wait time for read requests
+    uint64_t write_ios;      // number of write I/Os processed
+    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os
+    uint64_t write_sectors;  // number of sectors written
+    uint64_t write_ticks;    // total wait time for write requests
+    uint64_t io_in_flight;   // number of I/Os currently in flight
+    uint64_t io_ticks;       // total time this block device has been active
+    uint64_t io_in_queue;    // total wait time for all requests
+
+    uint64_t start_time;     // monotonic time accounting starts
+    uint64_t end_time;       // monotonic time accounting ends
+    uint32_t counter;        // private counter for accumulate calculations
+    double   io_avg;         // average io_in_flight for accumulate calculations
+
+    bool is_zero() {
+        return read_ios == 0 && write_ios == 0 &&
+               io_in_flight == 0 && io_ticks == 0 && io_in_queue == 0;
+    }
+
+    friend disk_stats operator- (disk_stats curr, const disk_stats& prev) {
+        curr.read_ios -= prev.read_ios;
+        curr.read_merges -= prev.read_merges;
+        curr.read_sectors -= prev.read_sectors;
+        curr.read_ticks -= prev.read_ticks;
+        curr.write_ios -= prev.write_ios;
+        curr.write_merges -= prev.write_merges;
+        curr.write_sectors -= prev.write_sectors;
+        curr.write_ticks -= prev.write_ticks;
+        /* skips io_in_flight, use current value */
+        curr.io_ticks -= prev.io_ticks;
+        curr.io_in_queue -= prev.io_in_queue;
+        return curr;
+    }
+
+    friend bool operator== (const disk_stats& a, const disk_stats& b) {
+        return a.read_ios == b.read_ios &&
+               a.read_merges == b.read_merges &&
+               a.read_sectors == b.read_sectors &&
+               a.read_ticks == b.read_ticks &&
+               a.write_ios == b.write_ios &&
+               a.write_merges == b.write_merges &&
+               a.write_sectors == b.write_sectors &&
+               a.write_ticks == b.write_ticks &&
+               /* skips io_in_flight */
+               a.io_ticks == b.io_ticks &&
+               a.io_in_queue == b.io_in_queue;
+    }
+
+    disk_stats& operator+= (const disk_stats& stats) {
+        read_ios += stats.read_ios;
+        read_merges += stats.read_merges;
+        read_sectors += stats.read_sectors;
+        read_ticks += stats.read_ticks;
+        write_ios += stats.write_ios;
+        write_merges += stats.write_merges;
+        write_sectors += stats.write_sectors;
+        write_ticks += stats.write_ticks;
+        /* skips io_in_flight, use current value */
+        io_ticks += stats.io_ticks;
+        io_in_queue += stats.io_in_queue;
+        return *this;
+    }
+};
+
+struct disk_perf {
+    uint32_t read_perf;         // read speed (kbytes/s)
+    uint32_t read_ios;          // read I/Os per second
+    uint32_t write_perf;        // write speed (kbytes/s)
+    uint32_t write_ios;         // write I/Os per second
+    uint32_t queue;             // I/Os in queue
+    bool is_zero() {
+        return read_perf == 0 && read_ios == 0 &&
+               write_perf == 0 && write_ios == 0 && queue == 0;
+    }
+};
+
+class stream_stats {
+private:
+    double mSum;
+    double mSquareSum;
+    uint32_t mCnt;
+public:
+    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
+    ~stream_stats() {};
+    double get_mean() {
+        return mSum / mCnt;
+    }
+    double get_std() {
+        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
+    }
+    void add(uint32_t num) {
+        mSum += (double)num;
+        mSquareSum += (double)num * (double)num;
+        mCnt++;
+    }
+    void evict(uint32_t num) {
+        if (mSum < num || mSquareSum < (double)num * (double)num) return;
+        mSum -= (double)num;
+        mSquareSum -= (double)num * (double)num;
+        mCnt--;
+    }
+};
+
+class disk_stats_monitor {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_monitor);
+    const char* const DISK_STATS_PATH;
+    struct disk_stats mPrevious;
+    struct disk_stats mAccumulate;      /* reset after stall */
+    struct disk_stats mAccumulate_pub;  /* reset after publish */
+    bool mStall;
+    std::queue<struct disk_perf> mBuffer;
+    struct {
+        stream_stats read_perf;           // read speed (bytes/s)
+        stream_stats read_ios;            // read I/Os per second
+        stream_stats write_perf;          // write speed (bytes/s)
+        stream_stats write_ios;           // write I/O per second
+        stream_stats queue;               // I/Os in queue
+    } mStats;
+    bool mValid;
+    const uint32_t mWindow;
+    const double mSigma;
+    struct disk_perf mMean;
+    struct disk_perf mStd;
+    android::sp<android::hardware::health::V2_0::IHealth> mHealth;
+
+    void update_mean();
+    void update_std();
+    void add(struct disk_perf* perf);
+    void evict(struct disk_perf* perf);
+    bool detect(struct disk_perf* perf);
+
+    void update(struct disk_stats* stats);
+
+public:
+  disk_stats_monitor(const android::sp<android::hardware::health::V2_0::IHealth>& healthService,
+                     uint32_t window_size = 5, double sigma = 1.0)
+      : DISK_STATS_PATH(
+            healthService != nullptr
+                ? nullptr
+                : (access(MMC_DISK_STATS_PATH, R_OK) == 0
+                       ? MMC_DISK_STATS_PATH
+                       : (access(SDA_DISK_STATS_PATH, R_OK) == 0 ? SDA_DISK_STATS_PATH : nullptr))),
+        mPrevious(),
+        mAccumulate(),
+        mAccumulate_pub(),
+        mStall(false),
+        mValid(false),
+        mWindow(window_size),
+        mSigma(sigma),
+        mMean(),
+        mStd(),
+        mHealth(healthService) {}
+  bool enabled() { return mHealth != nullptr || DISK_STATS_PATH != nullptr; }
+  void update(void);
+  void publish(void);
+};
+
+#endif /* _STORAGED_DISKSTATS_H_ */
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 7d04c7a..9c3d0e7 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -19,14 +19,26 @@
 
 #include <string.h>
 
+#include <chrono>
+
+#include <android/hardware/health/2.0/IHealth.h>
+#include <utils/Mutex.h>
+
+#include "storaged.h"
+#include "storaged.pb.h"
+
 #define FRIEND_TEST(test_case_name, test_name) \
 friend class test_case_name##_##test_name##_Test
 
 using namespace std;
+using namespace android;
+using namespace chrono;
+using namespace storaged_proto;
 
 class storage_info_t {
-protected:
+  protected:
     FRIEND_TEST(storaged_test, storage_info_t);
+    FRIEND_TEST(storaged_test, storage_info_t_proto);
     // emmc lifetime
     uint16_t eol;                   // pre-eol (end of life) information
     uint16_t lifetime_a;            // device life time estimation (type A)
@@ -36,16 +48,38 @@
     const string userdata_path = "/data";
     uint64_t userdata_total_kb;
     uint64_t userdata_free_kb;
+    // io perf history
+    time_point<system_clock> day_start_tp;
+    vector<uint32_t> recent_perf;
+    uint32_t nr_samples;
+    vector<uint32_t> daily_perf;
+    uint32_t nr_days;
+    vector<uint32_t> weekly_perf;
+    uint32_t nr_weeks;
+    Mutex si_mutex;
 
     storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0),
-        userdata_total_kb(0), userdata_free_kb(0) {}
+        userdata_total_kb(0), userdata_free_kb(0), nr_samples(0),
+        daily_perf(WEEK_TO_DAYS, 0), nr_days(0),
+        weekly_perf(YEAR_TO_WEEKS, 0), nr_weeks(0) {
+            day_start_tp = system_clock::now();
+            day_start_tp -= chrono::seconds(duration_cast<chrono::seconds>(
+                day_start_tp.time_since_epoch()).count() % DAY_TO_SEC);
+    }
     void publish();
     storage_info_t* s_info;
-public:
-    static storage_info_t* get_storage_info();
-    virtual ~storage_info_t() {}
+
+  public:
+    static storage_info_t* get_storage_info(
+        const sp<android::hardware::health::V2_0::IHealth>& healthService);
+    virtual ~storage_info_t() {};
     virtual void report() {};
-    void refresh();
+    void load_perf_history_proto(const IOPerfHistory& perf_history);
+    void refresh(IOPerfHistory* perf_history);
+    void update_perf_history(uint32_t bw,
+                             const time_point<system_clock>& tp);
+    vector<int> get_perf_history();
+    uint32_t get_recent_perf();
 };
 
 class emmc_info_t : public storage_info_t {
@@ -69,4 +103,18 @@
     virtual void report();
 };
 
+class health_storage_info_t : public storage_info_t {
+  private:
+    using IHealth = hardware::health::V2_0::IHealth;
+    using StorageInfo = hardware::health::V2_0::StorageInfo;
+
+    sp<IHealth> mHealth;
+    void set_values_from_hal_storage_info(const StorageInfo& halInfo);
+
+  public:
+    health_storage_info_t(const sp<IHealth>& service) : mHealth(service){};
+    virtual ~health_storage_info_t() {}
+    virtual void report();
+};
+
 #endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index a8ddf4c..7ec6864 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -19,42 +19,38 @@
 
 #include <vector>
 
-#include <binder/IInterface.h>
-#include <binder/IBinder.h>
+#include <binder/BinderService.h>
 
-#include "storaged.h"
+#include "android/os/BnStoraged.h"
+#include "android/os/storaged/BnStoragedPrivate.h"
 
-using namespace android;
+using namespace std;
+using namespace android::os;
+using namespace android::os::storaged;
 
-// Interface
-class IStoraged : public IInterface {
+class StoragedService : public BinderService<StoragedService>, public BnStoraged {
+private:
+    void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);
+    void dumpUidRecords(int fd, const vector<struct uid_record>& entries);
 public:
-    enum {
-        DUMPUIDS  = IBinder::FIRST_CALL_TRANSACTION,
-    };
-    // Request the service to run the test function
-    virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
+    static status_t start();
+    static char const* getServiceName() { return "storaged"; }
+    virtual status_t dump(int fd, const Vector<String16> &args) override;
 
-    DECLARE_META_INTERFACE(Storaged);
+    binder::Status onUserStarted(int32_t userId);
+    binder::Status onUserStopped(int32_t userId);
+    binder::Status getRecentPerf(int32_t* _aidl_return);
 };
 
-// Client
-class BpStoraged : public BpInterface<IStoraged> {
+class StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {
 public:
-    BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
-    virtual std::vector<struct uid_info> dump_uids(const char* option);
+    static status_t start();
+    static char const* getServiceName() { return "storaged_pri"; }
+
+    binder::Status dumpUids(vector<UidInfo>* _aidl_return);
+    binder::Status dumpPerfHistory(vector<int32_t>* _aidl_return);
 };
 
-// Server
-class BnStoraged : public BnInterface<IStoraged> {
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
-};
-
-class Storaged : public BnStoraged {
-    virtual std::vector<struct uid_info> dump_uids(const char* option);
-    virtual status_t dump(int fd, const Vector<String16>& args);
-};
-
-sp<IStoraged> get_storaged_service();
+sp<IStoragedPrivate> get_storaged_pri_service();
 
 #endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 901a872..3a718fa 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -23,88 +23,103 @@
 #include <unordered_map>
 #include <vector>
 
-enum uid_stat_t {
-    FOREGROUND = 0,
-    BACKGROUND = 1,
-    UID_STATS = 2
+#include <cutils/multiuser.h>
+#include <utils/Mutex.h>
+
+#include "storaged.pb.h"
+#include "uid_info.h"
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+using namespace std;
+using namespace storaged_proto;
+using namespace android;
+using namespace android::os::storaged;
+
+class uid_info : public UidInfo {
+public:
+    bool parse_uid_io_stats(string&& s);
 };
 
-enum charger_stat_t {
-    CHARGER_OFF = 0,
-    CHARGER_ON = 1,
-    CHARGER_STATS = 2
-};
-
-enum io_type_t {
-    READ = 0,
-    WRITE = 1,
-    IO_TYPES = 2
-};
-
-struct uid_io_stats {
-    uint64_t rchar;                 // characters read
-    uint64_t wchar;                 // characters written
-    uint64_t read_bytes;            // bytes read (from storage layer)
-    uint64_t write_bytes;           // bytes written (to storage layer)
-    uint64_t fsync;                 // number of fsync syscalls
-};
-
-struct uid_info {
-    uint32_t uid;                   // user id
-    std::string name;               // package name
-    struct uid_io_stats io[UID_STATS];    // [0]:foreground [1]:background
+class io_usage {
+public:
+    io_usage() : bytes{{{0}}} {};
+    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+    bool is_zero() const;
+    io_usage& operator+= (const io_usage& stats) {
+        for (int i = 0; i < IO_TYPES; i++) {
+            for (int j = 0; j < UID_STATS; j++) {
+                for (int k = 0; k < CHARGER_STATS; k++) {
+                    bytes[i][j][k] += stats.bytes[i][j][k];
+                }
+            }
+        }
+        return *this;
+    }
 };
 
 struct uid_io_usage {
-    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+    userid_t user_id;
+    io_usage uid_ios;
+    // mapped from task comm to task io usage
+    map<string, io_usage> task_ios;
 };
 
 struct uid_record {
-    std::string name;
+    string name;
     struct uid_io_usage ios;
 };
 
 struct uid_records {
     uint64_t start_ts;
-    std::vector<struct uid_record> entries;
+    vector<struct uid_record> entries;
 };
 
 class uid_monitor {
 private:
+    FRIEND_TEST(storaged_test, uid_monitor);
     // last dump from /proc/uid_io/stats, uid -> uid_info
-    std::unordered_map<uint32_t, struct 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
-    std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
+    unordered_map<string, struct uid_io_usage> curr_io_stats;
     // io usage records, end timestamp -> {start timestamp, vector of records}
-    std::map<uint64_t, struct uid_records> records;
+    map<uint64_t, struct uid_records> io_history;
     // charger ON/OFF
     charger_stat_t charger_stat;
     // protects curr_io_stats, last_uid_io_stats, records and charger_stat
-    sem_t um_lock;
+    Mutex uidm_mutex;
     // start time for IO records
     uint64_t start_ts;
+    // true if UID_IO_STATS_PATH is accessible
+    const bool enable;
 
     // reads from /proc/uid_io/stats
-    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
+    unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();
     // flushes curr_io_stats to records
     void add_records_locked(uint64_t curr_ts);
     // updates curr_io_stats and set last_uid_io_stats
     void update_curr_io_stats_locked();
+    // writes io_history to protobuf
+    void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
 
 public:
     uid_monitor();
-    ~uid_monitor();
     // called by storaged main thread
     void init(charger_stat_t stat);
     // called by storaged -u
-    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
+    unordered_map<uint32_t, uid_info> get_uid_io_stats();
     // called by dumpsys
-    std::map<uint64_t, struct uid_records> dump(
+    map<uint64_t, struct 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
-    void report();
+    bool enabled() { return enable; };
+    void report(unordered_map<int, StoragedProto>* protos);
+    // restores io_history from protobuf
+    void load_uid_io_proto(const UidIOUsage& proto);
+    void clear_user_history(userid_t user_id);
 };
 
 #endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 2161c40..62cb12d 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -24,21 +24,20 @@
 
 #include "storaged.h"
 
+using namespace android::os::storaged;
+
 // Diskstats
 bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
 struct disk_perf get_disk_perf(struct disk_stats* stats);
-struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
+void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr, struct disk_stats* inc);
 void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
-bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
 
 // UID I/O
-void sort_running_uids_info(std::vector<struct uid_info> &uids);
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries);
+void sort_running_uids_info(std::vector<UidInfo> &uids);
 
 // Logging
-void log_console_running_uids_info(std::vector<struct uid_info> uids);
+void log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task);
+void log_console_perf_history(const vector<int>& perf_history);
 
-void log_debug_disk_perf(struct disk_perf* perf, const char* type);
-
-void log_event_disk_stats(struct disk_stats* stats, const char* type);
-void log_event_emmc_info(struct emmc_info* info_);
 #endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/include/uid_info.h b/storaged/include/uid_info.h
new file mode 100644
index 0000000..c5533ac
--- /dev/null
+++ b/storaged/include/uid_info.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _UID_INFO_H_
+#define _UID_INFO_H_
+
+#include <string>
+#include <unordered_map>
+
+#include <binder/Parcelable.h>
+
+namespace android {
+namespace os {
+namespace storaged {
+
+enum uid_stat_t {
+    FOREGROUND = 0,
+    BACKGROUND = 1,
+    UID_STATS = 2
+};
+
+enum charger_stat_t {
+    CHARGER_OFF = 0,
+    CHARGER_ON = 1,
+    CHARGER_STATS = 2
+};
+
+enum io_type_t {
+    READ = 0,
+    WRITE = 1,
+    IO_TYPES = 2
+};
+
+struct io_stats {
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t fsync;                 // number of fsync syscalls
+};
+
+class task_info {
+public:
+    std::string comm;
+    pid_t pid;
+    io_stats io[UID_STATS];
+    bool parse_task_io_stats(std::string&& s);
+};
+
+class UidInfo : public Parcelable {
+public:
+    uint32_t uid;                     // user id
+    std::string name;                 // package name
+    io_stats io[UID_STATS];           // [0]:foreground [1]:background
+    std::unordered_map<uint32_t, task_info> tasks; // mapped from pid
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+};
+
+} // namespace storaged
+} // namespace os
+} // namespace android
+
+#endif /*  _UID_INFO_H_ */
\ No newline at end of file
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 72ec58f..3817fb5 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -39,72 +39,81 @@
 #include <storaged_service.h>
 #include <storaged_utils.h>
 
-sp<storaged_t> storaged;
+using namespace std;
+using namespace android;
+
+sp<storaged_t> storaged_sp;
 
 // Function of storaged's main thread
 void* storaged_main(void* /* unused */) {
-    storaged = new storaged_t();
+    storaged_sp = new storaged_t();
 
-    storaged->init_battery_service();
-    storaged->report_storage_info();
+    storaged_sp->init();
+    storaged_sp->report_storage_info();
 
     LOG_TO(SYSTEM, INFO) << "storaged: Start";
 
     for (;;) {
-        storaged->event_checked();
-        storaged->pause();
+        storaged_sp->event_checked();
+        storaged_sp->pause();
     }
     return NULL;
 }
 
-static void help_message(void) {
+void help_message(void) {
     printf("usage: storaged [OPTION]\n");
     printf("  -u    --uid                   Dump uid I/O usage to stdout\n");
+    printf("  -t    --task                  Dump task I/O usage to stdout\n");
+    printf("  -p    --perf                  Dump I/O perf history to stdout\n");
     printf("  -s    --start                 Start storaged (default)\n");
     fflush(stdout);
 }
 
 int main(int argc, char** argv) {
-    int flag_main_service = 0;
-    int flag_dump_uid = 0;
+    bool flag_main_service = false;
+    bool flag_dump_uid = false;
+    bool flag_dump_task = false;
+    bool flag_dump_perf = false;
     int opt;
 
     for (;;) {
         int opt_idx = 0;
         static struct option long_options[] = {
-            {"start",       no_argument,        0, 's'},
-            {"kill",        no_argument,        0, 'k'},
-            {"uid",         no_argument,        0, 'u'},
-            {"help",        no_argument,        0, 'h'}
+            {"perf",        no_argument,    nullptr, 'p'},
+            {"start",       no_argument,    nullptr, 's'},
+            {"task",        no_argument,    nullptr, 't'},
+            {"uid",         no_argument,    nullptr, 'u'},
+            {nullptr,       0,              nullptr,  0}
         };
-        opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+        opt = getopt_long(argc, argv, ":pstu", long_options, &opt_idx);
         if (opt == -1) {
             break;
         }
 
         switch (opt) {
+        case 'p':
+            flag_dump_perf = true;
+            break;
         case 's':
-            flag_main_service = 1;
+            flag_main_service = true;
+            break;
+        case 't':
+            flag_dump_task = true;
             break;
         case 'u':
-            flag_dump_uid = 1;
+            flag_dump_uid = true;
             break;
-        case 'h':
+        default:
             help_message();
             return 0;
-        case '?':
-        default:
-            fprintf(stderr, "no supported option\n");
-            help_message();
-            return -1;
         }
     }
 
     if (argc == 1) {
-        flag_main_service = 1;
+        flag_main_service = true;
     }
 
-    if (flag_main_service && flag_dump_uid) {
+    if (flag_main_service && (flag_dump_uid || flag_dump_task)) {
         fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
         help_message();
         return -1;
@@ -119,7 +128,12 @@
             return -1;
         }
 
-        defaultServiceManager()->addService(String16("storaged"), new Storaged());
+        if (StoragedService::start() != android::OK ||
+            StoragedPrivateService::start() != android::OK) {
+            PLOG_TO(SYSTEM, ERROR) << "Failed to start storaged service";
+            return -1;
+        }
+
         android::ProcessState::self()->startThreadPool();
         IPCThreadState::self()->joinThreadPool();
         pthread_join(storaged_main_thread, NULL);
@@ -127,23 +141,33 @@
         return 0;
     }
 
-    if (flag_dump_uid) {
-        sp<IStoraged> storaged_service = get_storaged_service();
-        if (storaged_service == NULL) {
-            fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
-            return -1;
-        }
-        std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
+    sp<IStoragedPrivate> storaged_service = get_storaged_pri_service();
+    if (storaged_service == NULL) {
+        fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+        return -1;
+    }
 
-        if (res.size() == 0) {
-            fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+    if (flag_dump_uid || flag_dump_task) {
+        vector<UidInfo> uid_io;
+        binder::Status status = storaged_service->dumpUids(&uid_io);
+        if (!status.isOk() || uid_io.size() == 0) {
+            fprintf(stderr, "UID I/O info is not available.\n");
             return 0;
         }
 
-        sort_running_uids_info(res);
-        log_console_running_uids_info(res);
+        sort_running_uids_info(uid_io);
+        log_console_running_uids_info(uid_io, flag_dump_task);
+    }
 
-        return 0;
+    if (flag_dump_perf) {
+        vector<int> perf_history;
+        binder::Status status = storaged_service->dumpPerfHistory(&perf_history);
+        if (!status.isOk() || perf_history.size() == 0) {
+            fprintf(stderr, "I/O perf history is not available.\n");
+            return 0;
+        }
+
+        log_console_perf_history(perf_history);
     }
 
     return 0;
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 06afea6..f346c38 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -16,184 +16,118 @@
 
 #define LOG_TAG "storaged"
 
+#include <dirent.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <time.h>
 #include <unistd.h>
+#include <zlib.h>
 
+#include <chrono>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
 #include <batteryservice/BatteryServiceConstants.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
 #include <cutils/properties.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
 #include <log/log.h>
 
 #include <storaged.h>
 #include <storaged_utils.h>
 
-/* disk_stats_publisher */
-void disk_stats_publisher::publish(void) {
-    // Logging
-    struct disk_perf perf = get_disk_perf(&mAccumulate);
-    log_debug_disk_perf(&perf, "regular");
-    log_event_disk_stats(&mAccumulate, "regular");
-    // Reset global structures
-    memset(&mAccumulate, 0, sizeof(struct disk_stats));
-}
+using namespace android::base;
+using namespace chrono;
+using namespace google::protobuf::io;
+using namespace storaged_proto;
 
-void disk_stats_publisher::update(void) {
-    struct disk_stats curr;
-    if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
-        struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
-        add_disk_stats(&inc, &mAccumulate);
-#ifdef DEBUG
-//            log_kernel_disk_stats(&mPrevious, "prev stats");
-//            log_kernel_disk_stats(&curr, "curr stats");
-//            log_kernel_disk_stats(&inc, "inc stats");
-//            log_kernel_disk_stats(&mAccumulate, "accumulated stats");
-#endif
-        mPrevious = curr;
-    }
-}
+namespace {
 
-/* disk_stats_monitor */
-void disk_stats_monitor::update_mean() {
-    CHECK(mValid);
-    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
-    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
-    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
-    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
-    mMean.queue = (uint32_t)mStats.queue.get_mean();
-}
+/*
+ * The system user is the initial user that is implicitly created on first boot
+ * and hosts most of the system services. Keep this in sync with
+ * frameworks/base/core/java/android/os/UserManager.java
+ */
+constexpr int USER_SYSTEM = 0;
 
-void disk_stats_monitor::update_std() {
-    CHECK(mValid);
-    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
-    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
-    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
-    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
-    mStd.queue = (uint32_t)mStats.queue.get_std();
-}
+constexpr ssize_t benchmark_unit_size = 16 * 1024;  // 16KB
 
-void disk_stats_monitor::add(struct disk_perf* perf) {
-    mStats.read_perf.add(perf->read_perf);
-    mStats.read_ios.add(perf->read_ios);
-    mStats.write_perf.add(perf->write_perf);
-    mStats.write_ios.add(perf->write_ios);
-    mStats.queue.add(perf->queue);
-}
+constexpr ssize_t min_benchmark_size = 128 * 1024;  // 128KB
 
-void disk_stats_monitor::evict(struct disk_perf* perf) {
-    mStats.read_perf.evict(perf->read_perf);
-    mStats.read_ios.evict(perf->read_ios);
-    mStats.write_perf.evict(perf->write_perf);
-    mStats.write_ios.evict(perf->write_ios);
-    mStats.queue.evict(perf->queue);
-}
+}  // namespace
 
-bool disk_stats_monitor::detect(struct disk_perf* perf) {
-    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
-            ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
-            ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
-}
+const uint32_t storaged_t::current_version = 4;
 
-void disk_stats_monitor::update(struct disk_stats* stats) {
-    struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
-    struct disk_perf perf = get_disk_perf(&inc);
-    // Update internal data structures
-    if (LIKELY(mValid)) {
-        CHECK_EQ(mBuffer.size(), mWindow);
+using android::hardware::interfacesEqual;
+using android::hardware::Return;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V1_0::toString;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hidl::manager::V1_0::IServiceManager;
 
-        if (UNLIKELY(detect(&perf))) {
-            mStall = true;
-            add_disk_stats(&inc, &mAccumulate);
-            log_debug_disk_perf(&mMean, "stalled_mean");
-            log_debug_disk_perf(&mStd, "stalled_std");
-        } else {
-            if (mStall) {
-                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
-                log_debug_disk_perf(&acc_perf, "stalled");
-                log_event_disk_stats(&mAccumulate, "stalled");
-                mStall = false;
-                memset(&mAccumulate, 0, sizeof(mAccumulate));
-            }
-        }
 
-        evict(&mBuffer.front());
-        mBuffer.pop();
-        add(&perf);
-        mBuffer.push(perf);
-
-        update_mean();
-        update_std();
-
-    } else { /* mValid == false */
-        CHECK_LT(mBuffer.size(), mWindow);
-        add(&perf);
-        mBuffer.push(perf);
-        if (mBuffer.size() == mWindow) {
-            mValid = true;
-            update_mean();
-            update_std();
-        }
-    }
-
-    mPrevious = *stats;
-}
-
-void disk_stats_monitor::update(void) {
-    struct disk_stats curr;
-    if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
-        update(&curr);
-    }
-}
-
-static sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
-    sp<IServiceManager> sm = defaultServiceManager();
-    if (sm == NULL) return NULL;
-
-    sp<IBinder> binder = sm->getService(String16("batteryproperties"));
-    if (binder == NULL) return NULL;
-
-    sp<IBatteryPropertiesRegistrar> battery_properties =
-        interface_cast<IBatteryPropertiesRegistrar>(binder);
-
-    return battery_properties;
-}
-
-static inline charger_stat_t is_charger_on(int64_t prop) {
-    return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
+inline charger_stat_t is_charger_on(BatteryStatus prop) {
+    return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
         CHARGER_ON : CHARGER_OFF;
 }
 
-void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
-    mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
+    mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
+    return android::hardware::Void();
 }
 
-void storaged_t::init_battery_service() {
-    if (!mConfig.proc_uid_io_available)
+void storaged_t::init() {
+    init_health_service();
+    mDsm = std::make_unique<disk_stats_monitor>(health);
+    storage_info.reset(storage_info_t::get_storage_info(health));
+}
+
+void storaged_t::init_health_service() {
+    if (!mUidm.enabled())
         return;
 
-    battery_properties = get_battery_properties_service();
-    if (battery_properties == NULL) {
-        LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
+    health = get_health_service();
+    if (health == NULL) {
+        LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
         return;
     }
 
-    struct BatteryProperty val;
-    battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
-    mUidm.init(is_charger_on(val.valueInt64));
+    BatteryStatus status = BatteryStatus::UNKNOWN;
+    auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
+        if (r != Result::SUCCESS) {
+            LOG_TO(SYSTEM, WARNING)
+                << "health: cannot get battery status " << toString(r);
+            return;
+        }
+        if (v == BatteryStatus::UNKNOWN) {
+            LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
+        }
+        status = v;
+    });
+    if (!ret.isOk()) {
+        LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
+            << ret.description();
+    }
 
+    mUidm.init(is_charger_on(status));
     // register listener after init uid_monitor
-    battery_properties->registerListener(this);
-    IInterface::asBinder(battery_properties)->linkToDeath(this);
+    health->registerCallback(this);
+    health->linkToDeath(this, 0 /* cookie */);
 }
 
-void storaged_t::binderDied(const wp<IBinder>& who) {
-    if (battery_properties != NULL &&
-        IInterface::asBinder(battery_properties) == who) {
-        LOG_TO(SYSTEM, ERROR) << "batteryproperties service died, exiting";
-        IPCThreadState::self()->stopProcess();
+void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
+    if (health != NULL && interfacesEqual(health, who.promote())) {
+        LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
+        android::hardware::IPCThreadState::self()->stopProcess();
         exit(1);
     } else {
         LOG_TO(SYSTEM, ERROR) << "unknown service died";
@@ -206,44 +140,195 @@
 
 /* storaged_t */
 storaged_t::storaged_t(void) {
-    if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
-        mConfig.diskstats_available = false;
-    } else {
-        mConfig.diskstats_available = true;
-    }
-
-    mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
-
     mConfig.periodic_chores_interval_unit =
-        property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
+        property_get_int32("ro.storaged.event.interval",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
 
     mConfig.event_time_check_usec =
         property_get_int32("ro.storaged.event.perf_check", 0);
 
     mConfig.periodic_chores_interval_disk_stats_publish =
-        property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
+        property_get_int32("ro.storaged.disk_stats_pub",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
 
     mConfig.periodic_chores_interval_uid_io =
-        property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
+        property_get_int32("ro.storaged.uid_io.interval",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
 
-    storage_info.reset(storage_info_t::get_storage_info());
+    mConfig.periodic_chores_interval_flush_proto =
+        property_get_int32("ro.storaged.flush_proto.interval",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
 
     mStarttime = time(NULL);
+    mTimer = 0;
 }
 
-void storaged_t::event(void) {
-    if (mConfig.diskstats_available) {
-        mDiskStats.update();
-        mDsm.update();
-        storage_info->refresh();
-        if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
-            mDiskStats.publish();
+void storaged_t::add_user_ce(userid_t user_id) {
+    load_proto(user_id);
+    proto_loaded[user_id] = true;
+}
+
+void storaged_t::remove_user_ce(userid_t user_id) {
+    proto_loaded[user_id] = false;
+    mUidm.clear_user_history(user_id);
+    RemoveFileIfExists(proto_path(user_id), nullptr);
+}
+
+void storaged_t::load_proto(userid_t user_id) {
+    string proto_file = proto_path(user_id);
+    ifstream in(proto_file, ofstream::in | ofstream::binary);
+
+    if (!in.good()) return;
+
+    stringstream ss;
+    ss << in.rdbuf();
+    StoragedProto proto;
+    proto.ParseFromString(ss.str());
+
+    const UidIOUsage& uid_io_usage = proto.uid_io_usage();
+    uint32_t computed_crc = crc32(current_version,
+        reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+        uid_io_usage.ByteSize());
+    if (proto.crc() != computed_crc) {
+        LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
+        return;
+    }
+
+    mUidm.load_uid_io_proto(proto.uid_io_usage());
+
+    if (user_id == USER_SYSTEM) {
+        storage_info->load_perf_history_proto(proto.perf_history());
+    }
+}
+
+char* storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
+    proto->set_version(current_version);
+
+    const UidIOUsage& uid_io_usage = proto->uid_io_usage();
+    proto->set_crc(crc32(current_version,
+        reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+        uid_io_usage.ByteSize()));
+
+    uint32_t pagesize = sysconf(_SC_PAGESIZE);
+    if (user_id == USER_SYSTEM) {
+        proto->set_padding("", 1);
+        vector<char> padding;
+        ssize_t size = ROUND_UP(MAX(min_benchmark_size, proto->ByteSize()),
+                                pagesize);
+        padding = vector<char>(size - proto->ByteSize(), 0xFD);
+        proto->set_padding(padding.data(), padding.size());
+        while (!IS_ALIGNED(proto->ByteSize(), pagesize)) {
+            padding.push_back(0xFD);
+            proto->set_padding(padding.data(), padding.size());
         }
     }
 
-    if (mConfig.proc_uid_io_available && mTimer &&
-            (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
-         mUidm.report();
+    char* data = nullptr;
+    if (posix_memalign(reinterpret_cast<void**>(&data),
+                       pagesize, proto->ByteSize())) {
+        PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
+                               << proto->ByteSize() << ")";
+        return data;
+    }
+
+    proto->SerializeToArray(data, proto->ByteSize());
+    return data;
+}
+
+void storaged_t::flush_proto_data(userid_t user_id,
+                                  const char* data, ssize_t size) {
+    string proto_file = proto_path(user_id);
+    string tmp_file = proto_file + "_tmp";
+    unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
+                 O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC |
+                    (user_id == USER_SYSTEM ? O_DIRECT : 0),
+                 S_IRUSR | S_IWUSR)));
+    if (fd == -1) {
+        PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
+        return;
+    }
+
+    if (user_id == USER_SYSTEM) {
+        time_point<steady_clock> start, end;
+        uint32_t benchmark_size = 0;
+        uint64_t benchmark_time_ns = 0;
+        ssize_t ret;
+        bool first_write = true;
+
+        while (size > 0) {
+            start = steady_clock::now();
+            ret = write(fd, data, MIN(benchmark_unit_size, size));
+            if (ret <= 0) {
+                PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+                return;
+            }
+            end = steady_clock::now();
+            /*
+            * compute bandwidth after the first write and if write returns
+            * exactly unit size.
+            */
+            if (!first_write && ret == benchmark_unit_size) {
+                benchmark_size += benchmark_unit_size;
+                benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
+            }
+            size -= ret;
+            data += ret;
+            first_write = false;
+        }
+
+        if (benchmark_size) {
+            int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
+            storage_info->update_perf_history(perf, system_clock::now());
+        }
+    } else {
+        if (!WriteFully(fd, data, size)) {
+            PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+            return;
+        }
+    }
+
+    fd.reset(-1);
+    rename(tmp_file.c_str(), proto_file.c_str());
+}
+
+void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
+    unique_ptr<char> proto_data(prepare_proto(user_id, proto));
+    if (proto_data == nullptr) return;
+
+    flush_proto_data(user_id, proto_data.get(), proto->ByteSize());
+}
+
+void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
+    for (auto& it : *protos) {
+        /*
+         * Don't flush proto if we haven't attempted to load it from file.
+         */
+        if (proto_loaded[it.first]) {
+            flush_proto(it.first, &it.second);
+        }
+    }
+}
+
+void storaged_t::event(void) {
+    unordered_map<int, StoragedProto> protos;
+
+    if (mDsm->enabled()) {
+        mDsm->update();
+        if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
+            mDsm->publish();
+        }
+    }
+
+    if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {
+        mUidm.report(&protos);
+    }
+
+    if (storage_info) {
+        storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());
+    }
+
+    if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
+        flush_protos(&protos);
     }
 
     mTimer += mConfig.periodic_chores_interval_unit;
diff --git a/storaged/storaged.proto b/storaged/storaged.proto
new file mode 100644
index 0000000..2000c0b
--- /dev/null
+++ b/storaged/storaged.proto
@@ -0,0 +1,60 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package storaged_proto;
+option java_package = "com.android.storaged.proto";
+option java_outer_classname = "Storaged";
+
+message IOUsage {
+  optional uint64 rd_fg_chg_on  = 1;
+  optional uint64 rd_fg_chg_off = 2;
+  optional uint64 rd_bg_chg_on  = 3;
+  optional uint64 rd_bg_chg_off = 4;
+  optional uint64 wr_fg_chg_on  = 5;
+  optional uint64 wr_fg_chg_off = 6;
+  optional uint64 wr_bg_chg_on  = 7;
+  optional uint64 wr_bg_chg_off = 8;
+}
+
+message TaskIOUsage {
+  optional string task_name = 1;
+  optional IOUsage ios = 2;
+}
+
+message UidRecord {
+  optional string uid_name = 1;
+  optional uint32 user_id = 2;
+  optional IOUsage uid_io = 3;
+  repeated TaskIOUsage task_io = 4;
+}
+
+message UidIORecords {
+  optional uint64 start_ts = 1;
+  repeated UidRecord entries = 2;
+}
+
+message UidIOItem {
+  optional uint64 end_ts = 1;
+  optional UidIORecords records = 2;
+}
+
+message UidIOUsage {
+  repeated UidIOItem uid_io_items = 2;
+}
+
+message IOPerfHistory {
+  optional uint64 day_start_sec = 1;
+  repeated uint32 recent_perf = 2;
+  optional uint32 nr_samples = 3;
+  repeated uint32 daily_perf = 4;
+  optional uint32 nr_days = 5;
+  repeated uint32 weekly_perf = 6;
+  optional uint32 nr_weeks = 7;
+}
+
+message StoragedProto {
+  optional uint32 crc = 1;
+  optional uint32 version = 2;
+  optional UidIOUsage uid_io_usage = 3;
+  optional IOPerfHistory perf_history = 4;
+  optional bytes padding = 5;
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index ed2cf14..0614fad 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -5,4 +5,4 @@
     file /d/mmc0/mmc0:0001/ext_csd r
     writepid /dev/cpuset/system-background/tasks
     user root
-    group package_info
\ No newline at end of file
+    group package_info
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
new file mode 100644
index 0000000..1050033
--- /dev/null
+++ b/storaged/storaged_diskstats.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <log/log_event_list.h>
+
+#include "storaged.h"
+#include "storaged_diskstats.h"
+
+namespace {
+
+using android::sp;
+using android::hardware::health::V2_0::DiskStats;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::toString;
+
+#ifdef DEBUG
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
+    // skip if the input structure are all zeros
+    if (perf == NULL || perf->is_zero()) return;
+
+    LOG_TO(SYSTEM, INFO) << "disk_perf " << type
+              << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops"
+              << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
+              << " q: " << perf->queue;
+}
+#else
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {}
+#endif
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type) {
+    // skip if the input structure are all zeros
+    if (stats == NULL || stats->is_zero()) return;
+
+    android_log_event_list(EVENTLOGTAG_DISKSTATS)
+        << type << stats->start_time << stats->end_time
+        << stats->read_ios << stats->read_merges
+        << stats->read_sectors << stats->read_ticks
+        << stats->write_ios << stats->write_merges
+        << stats->write_sectors << stats->write_ticks
+        << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
+        << LOG_ID_EVENTS;
+}
+
+} // namespace
+
+bool get_time(struct timespec* ts) {
+    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+    // when system is running.
+    int ret = clock_gettime(CLOCK_MONOTONIC, ts);
+    if (ret < 0) {
+        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        return false;
+    }
+    return true;
+}
+
+void init_disk_stats_other(const struct timespec& ts, struct disk_stats* stats) {
+    stats->start_time = 0;
+    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+    stats->counter = 1;
+    stats->io_avg = (double)stats->io_in_flight;
+}
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
+    // Get time
+    struct timespec ts;
+    if (!get_time(&ts)) {
+        return false;
+    }
+
+    std::string buffer;
+    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
+        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+        return false;
+    }
+
+    // Regular diskstats entries
+    std::stringstream ss(buffer);
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        ss >> *((uint64_t*)stats + i);
+    }
+    // Other entries
+    init_disk_stats_other(ts, stats);
+    return true;
+}
+
+void convert_hal_disk_stats(struct disk_stats* dst, const DiskStats& src) {
+    dst->read_ios = src.reads;
+    dst->read_merges = src.readMerges;
+    dst->read_sectors = src.readSectors;
+    dst->read_ticks = src.readTicks;
+    dst->write_ios = src.writes;
+    dst->write_merges = src.writeMerges;
+    dst->write_sectors = src.writeSectors;
+    dst->write_ticks = src.writeTicks;
+    dst->io_in_flight = src.ioInFlight;
+    dst->io_ticks = src.ioTicks;
+    dst->io_in_queue = src.ioInQueue;
+}
+
+bool get_disk_stats_from_health_hal(const sp<IHealth>& service, struct disk_stats* stats) {
+    struct timespec ts;
+    if (!get_time(&ts)) {
+        return false;
+    }
+
+    bool success = false;
+    auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+        if (result != Result::SUCCESS || halStats.size() == 0) {
+            LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
+                                  << " and size " << halStats.size();
+            return;
+        }
+
+        convert_hal_disk_stats(stats, halStats[0]);
+        success = true;
+    });
+
+    if (!ret.isOk()) {
+        LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with " << ret.description();
+        return false;
+    }
+
+    if (!success) {
+        return false;
+    }
+
+    init_disk_stats_other(ts, stats);
+    return true;
+}
+
+struct disk_perf get_disk_perf(struct disk_stats* stats)
+{
+    struct disk_perf perf = {};
+
+    if (stats->io_ticks) {
+        if (stats->read_ticks) {
+            unsigned long long divisor = stats->read_ticks * stats->io_ticks;
+            perf.read_perf = ((unsigned long long)SECTOR_SIZE *
+                              stats->read_sectors * stats->io_in_queue +
+                              (divisor >> 1)) / divisor;
+            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
+                             stats->read_ios * stats->io_in_queue +
+                             (divisor >> 1)) / divisor;
+        }
+        if (stats->write_ticks) {
+            unsigned long long divisor = stats->write_ticks * stats->io_ticks;
+            perf.write_perf = ((unsigned long long)SECTOR_SIZE *
+                               stats->write_sectors * stats->io_in_queue +
+                               (divisor >> 1)) / divisor;
+            perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
+                              stats->write_ios * stats->io_in_queue +
+                              (divisor >> 1)) / divisor;
+        }
+        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
+                     stats->io_ticks;
+    }
+    return perf;
+}
+
+void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr,
+                        struct disk_stats* inc)
+{
+    *inc = *curr - *prev;
+    inc->start_time = prev->end_time;
+    inc->end_time = curr->end_time;
+    inc->io_avg = curr->io_avg;
+    inc->counter = 1;
+}
+
+// Add src to dst
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
+{
+    if (dst->end_time != 0 && dst->end_time != src->start_time) {
+        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
+            << " are added. dst end with " << dst->end_time
+            << ", src start with " << src->start_time;
+    }
+
+    *dst += *src;
+
+    dst->io_in_flight = src->io_in_flight;
+    if (dst->counter + src->counter) {
+        dst->io_avg =
+            ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
+            (dst->counter + src->counter);
+    }
+    dst->counter += src->counter;
+    dst->end_time = src->end_time;
+    if (dst->start_time == 0) {
+        dst->start_time = src->start_time;
+    }
+}
+
+/* disk_stats_monitor */
+void disk_stats_monitor::update_mean()
+{
+    CHECK(mValid);
+    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
+    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
+    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
+    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
+    mMean.queue = (uint32_t)mStats.queue.get_mean();
+}
+
+void disk_stats_monitor::update_std()
+{
+    CHECK(mValid);
+    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
+    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
+    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
+    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
+    mStd.queue = (uint32_t)mStats.queue.get_std();
+}
+
+void disk_stats_monitor::add(struct disk_perf* perf)
+{
+    mStats.read_perf.add(perf->read_perf);
+    mStats.read_ios.add(perf->read_ios);
+    mStats.write_perf.add(perf->write_perf);
+    mStats.write_ios.add(perf->write_ios);
+    mStats.queue.add(perf->queue);
+}
+
+void disk_stats_monitor::evict(struct disk_perf* perf) {
+    mStats.read_perf.evict(perf->read_perf);
+    mStats.read_ios.evict(perf->read_ios);
+    mStats.write_perf.evict(perf->write_perf);
+    mStats.write_ios.evict(perf->write_ios);
+    mStats.queue.evict(perf->queue);
+}
+
+bool disk_stats_monitor::detect(struct disk_perf* perf)
+{
+    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
+        ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
+        ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
+}
+
+void disk_stats_monitor::update(struct disk_stats* curr)
+{
+    disk_stats inc;
+    get_inc_disk_stats(&mPrevious, curr, &inc);
+    add_disk_stats(&inc, &mAccumulate_pub);
+
+    struct disk_perf perf = get_disk_perf(&inc);
+    log_debug_disk_perf(&perf, "regular");
+
+    add(&perf);
+    mBuffer.push(perf);
+    if (mBuffer.size() > mWindow) {
+        evict(&mBuffer.front());
+        mBuffer.pop();
+        mValid = true;
+    }
+
+    // Update internal data structures
+    if (LIKELY(mValid)) {
+        CHECK_EQ(mBuffer.size(), mWindow);
+        update_mean();
+        update_std();
+        if (UNLIKELY(detect(&perf))) {
+            mStall = true;
+            add_disk_stats(&inc, &mAccumulate);
+            log_debug_disk_perf(&mMean, "stalled_mean");
+            log_debug_disk_perf(&mStd, "stalled_std");
+        } else {
+            if (mStall) {
+                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
+                log_debug_disk_perf(&acc_perf, "stalled");
+                log_event_disk_stats(&mAccumulate, "stalled");
+                mStall = false;
+                memset(&mAccumulate, 0, sizeof(mAccumulate));
+            }
+        }
+    }
+
+    mPrevious = *curr;
+}
+
+void disk_stats_monitor::update() {
+    disk_stats curr;
+    if (mHealth != nullptr) {
+        if (!get_disk_stats_from_health_hal(mHealth, &curr)) {
+            return;
+        }
+    } else {
+        if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {
+            return;
+        }
+    }
+
+    update(&curr);
+}
+
+void disk_stats_monitor::publish(void)
+{
+    struct disk_perf perf = get_disk_perf(&mAccumulate_pub);
+    log_debug_disk_perf(&perf, "regular");
+    log_event_disk_stats(&mAccumulate, "regular");
+    // Reset global structures
+    memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));
+}
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index b5fb13e..5605f66 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -20,6 +20,8 @@
 #include <string.h>
 #include <sys/statvfs.h>
 
+#include <numeric>
+
 #include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/logging.h>
@@ -27,9 +29,16 @@
 #include <log/log_event_list.h>
 
 #include "storaged.h"
+#include "storaged_info.h"
 
 using namespace std;
+using namespace chrono;
 using namespace android::base;
+using namespace storaged_proto;
+
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::StorageInfo;
 
 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
 const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
@@ -39,14 +48,20 @@
 
 const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
 
-static bool FileExists(const std::string& filename)
+namespace {
+
+bool FileExists(const std::string& filename)
 {
   struct stat buffer;
   return stat(filename.c_str(), &buffer) == 0;
 }
 
-storage_info_t* storage_info_t::get_storage_info()
-{
+} // namespace
+
+storage_info_t* storage_info_t::get_storage_info(const sp<IHealth>& healthService) {
+    if (healthService != nullptr) {
+        return new health_storage_info_t(healthService);
+    }
     if (FileExists(emmc_info_t::emmc_sysfs) ||
         FileExists(emmc_info_t::emmc_debugfs)) {
         return new emmc_info_t;
@@ -57,7 +72,39 @@
     return new storage_info_t;
 }
 
-void storage_info_t::refresh()
+void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
+{
+    Mutex::Autolock _l(si_mutex);
+
+    if (!perf_history.has_day_start_sec() ||
+        perf_history.daily_perf_size() > (int)daily_perf.size() ||
+        perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
+        LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
+        return;
+    }
+
+    day_start_tp = {};
+    day_start_tp += chrono::seconds(perf_history.day_start_sec());
+
+    nr_samples = perf_history.nr_samples();
+    for (auto bw : perf_history.recent_perf()) {
+        recent_perf.push_back(bw);
+    }
+
+    nr_days = perf_history.nr_days();
+    int i = 0;
+    for (auto bw : perf_history.daily_perf()) {
+        daily_perf[i++] = bw;
+    }
+
+    nr_weeks = perf_history.nr_weeks();
+    i = 0;
+    for (auto bw : perf_history.weekly_perf()) {
+        weekly_perf[i++] = bw;
+    }
+}
+
+void storage_info_t::refresh(IOPerfHistory* perf_history)
 {
     struct statvfs buf;
     if (statvfs(userdata_path.c_str(), &buf) != 0) {
@@ -67,6 +114,24 @@
 
     userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
     userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
+
+    Mutex::Autolock _l(si_mutex);
+
+    perf_history->Clear();
+    perf_history->set_day_start_sec(
+        duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
+    for (const uint32_t& bw : recent_perf) {
+        perf_history->add_recent_perf(bw);
+    }
+    perf_history->set_nr_samples(nr_samples);
+    for (const uint32_t& bw : daily_perf) {
+        perf_history->add_daily_perf(bw);
+    }
+    perf_history->set_nr_days(nr_days);
+    for (const uint32_t& bw : weekly_perf) {
+        perf_history->add_weekly_perf(bw);
+    }
+    perf_history->set_nr_weeks(nr_weeks);
 }
 
 void storage_info_t::publish()
@@ -76,6 +141,95 @@
         << LOG_ID_EVENTS;
 }
 
+void storage_info_t::update_perf_history(uint32_t bw,
+                                         const time_point<system_clock>& tp)
+{
+    Mutex::Autolock _l(si_mutex);
+
+    if (tp > day_start_tp &&
+        duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
+        if (nr_samples >= recent_perf.size()) {
+            recent_perf.push_back(bw);
+        } else {
+            recent_perf[nr_samples] = bw;
+        }
+        nr_samples++;
+        return;
+    }
+
+    if (nr_samples < recent_perf.size()) {
+        recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
+    }
+
+    uint32_t daily_avg_bw = 0;
+    if (!recent_perf.empty()) {
+        daily_avg_bw = accumulate(recent_perf.begin(), recent_perf.end(), 0) / recent_perf.size();
+    }
+
+    day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
+        tp.time_since_epoch()).count() % DAY_TO_SEC);
+
+    nr_samples = 0;
+    if (recent_perf.empty())
+        recent_perf.resize(1);
+    recent_perf[nr_samples++] = bw;
+
+    if (nr_days < WEEK_TO_DAYS) {
+        daily_perf[nr_days++] = daily_avg_bw;
+        return;
+    }
+
+    DCHECK(nr_days > 0);
+    uint32_t week_avg_bw = accumulate(daily_perf.begin(),
+        daily_perf.begin() + nr_days, 0) / nr_days;
+
+    nr_days = 0;
+    daily_perf[nr_days++] = daily_avg_bw;
+
+    if (nr_weeks >= YEAR_TO_WEEKS) {
+        nr_weeks = 0;
+    }
+    weekly_perf[nr_weeks++] = week_avg_bw;
+}
+
+vector<int> storage_info_t::get_perf_history()
+{
+    Mutex::Autolock _l(si_mutex);
+
+    vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
+
+    ret[0] = recent_perf.size();
+    ret[1] = daily_perf.size();
+    ret[2] = weekly_perf.size();
+
+    int start = 3;
+    for (size_t i = 0; i < recent_perf.size(); i++) {
+        int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
+        ret[start + i] = recent_perf[idx];
+    }
+
+    start += recent_perf.size();
+    for (size_t i = 0; i < daily_perf.size(); i++) {
+        int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
+        ret[start + i] = daily_perf[idx];
+    }
+
+    start += daily_perf.size();
+    for (size_t i = 0; i < weekly_perf.size(); i++) {
+        int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
+        ret[start + i] = weekly_perf[idx];
+    }
+
+    return ret;
+}
+
+uint32_t storage_info_t::get_recent_perf() {
+    Mutex::Autolock _l(si_mutex);
+    if (recent_perf.size() == 0) return 0;
+    return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
+           recent_perf.size();
+}
+
 void emmc_info_t::report()
 {
     if (!report_sysfs() && !report_debugfs())
@@ -121,6 +275,8 @@
     return true;
 }
 
+namespace {
+
 const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
 /* 2 characters in string for each byte */
 const size_t EXT_CSD_REV_IDX = 192 * 2;
@@ -128,6 +284,8 @@
 const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
 const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
 
+} // namespace
+
 bool emmc_info_t::report_debugfs()
 {
     string buffer;
@@ -210,3 +368,25 @@
     publish();
 }
 
+void health_storage_info_t::report() {
+    auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+        if (result != Result::SUCCESS || halInfos.size() == 0) {
+            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
+                                  << " and size " << halInfos.size();
+            return;
+        }
+        set_values_from_hal_storage_info(halInfos[0]);
+        publish();
+    });
+
+    if (!ret.isOk()) {
+        LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
+    }
+}
+
+void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
+    eol = halInfo.eol;
+    lifetime_a = halInfo.lifetimeA;
+    lifetime_b = halInfo.lifetimeB;
+    version = halInfo.version;
+}
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index b1d3bfd..17ea25b 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <stdint.h>
 
 #include <vector>
@@ -29,60 +30,69 @@
 #include <private/android_filesystem_config.h>
 
 #include <storaged.h>
+#include <storaged_utils.h>
 #include <storaged_service.h>
 
+using namespace std;
 using namespace android::base;
 
-extern sp<storaged_t> storaged;
+extern sp<storaged_t> storaged_sp;
 
-std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
-    Parcel data, reply;
-    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
-
-    remote()->transact(DUMPUIDS, data, &reply);
-
-    uint32_t res_size = reply.readInt32();
-    std::vector<struct uid_info> res(res_size);
-    for (auto&& uid : res) {
-        uid.uid = reply.readInt32();
-        uid.name = reply.readCString();
-        reply.read(&uid.io, sizeof(uid.io));
-    }
-    return res;
+status_t StoragedService::start() {
+    return BinderService<StoragedService>::publish();
 }
-IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
 
-status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    switch(code) {
-        case DUMPUIDS: {
-                if (!data.checkInterface(this))
-                    return BAD_TYPE;
-                std::vector<struct uid_info> res = dump_uids(NULL);
-                reply->writeInt32(res.size());
-                for (auto uid : res) {
-                    reply->writeInt32(uid.uid);
-                    reply->writeCString(uid.name.c_str());
-                    reply->write(&uid.io, sizeof(uid.io));
-                }
-                return NO_ERROR;
-            }
-            break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
+void StoragedService::dumpUidRecords(int fd, const vector<uid_record>& entries) {
+    map<string, io_usage> merged_entries = merge_io_usage(entries);
+    for (const auto& rec : merged_entries) {
+        dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                rec.first.c_str(),
+                rec.second.bytes[READ][FOREGROUND][CHARGER_OFF],
+                rec.second.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                rec.second.bytes[READ][BACKGROUND][CHARGER_OFF],
+                rec.second.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                rec.second.bytes[READ][FOREGROUND][CHARGER_ON],
+                rec.second.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                rec.second.bytes[READ][BACKGROUND][CHARGER_ON],
+                rec.second.bytes[WRITE][BACKGROUND][CHARGER_ON]);
     }
 }
 
-std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
-    std::vector<struct uid_info> uids_v;
-    std::unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
+void StoragedService::dumpUidRecordsDebug(int fd, const vector<uid_record>& entries) {
+    for (const auto& record : entries) {
+        const io_usage& uid_usage = record.ios.uid_ios;
+        dprintf(fd, "%s_%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                record.name.c_str(), record.ios.user_id,
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
 
-    for (const auto& it : uids_m) {
-        uids_v.push_back(it.second);
+        for (const auto& task_it : record.ios.task_ios) {
+            const io_usage& task_usage = task_it.second;
+            const string& comm = task_it.first;
+            dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                    " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                    comm.c_str(),
+                    task_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                    task_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                    task_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                    task_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+        }
     }
-    return uids_v;
 }
 
-status_t Storaged::dump(int fd, const Vector<String16>& args) {
+status_t StoragedService::dump(int fd, const Vector<String16>& args) {
     IPCThreadState* self = IPCThreadState::self();
     const int pid = self->getCallingPid();
     const int uid = self->getCallingUid();
@@ -96,6 +106,7 @@
     int time_window = 0;
     uint64_t threshold = 0;
     bool force_report = false;
+    bool debug = false;
     for (size_t i = 0; i < args.size(); i++) {
         const auto& arg = args[i];
         if (arg == String16("--hours")) {
@@ -123,47 +134,87 @@
             force_report = true;
             continue;
         }
+        if (arg == String16("--debug")) {
+            debug = true;
+            continue;
+        }
     }
 
     uint64_t last_ts = 0;
-    const std::map<uint64_t, struct uid_records>& records =
-                storaged->get_uid_records(hours, threshold, force_report);
+    map<uint64_t, struct uid_records> records =
+                storaged_sp->get_uid_records(hours, threshold, force_report);
     for (const auto& it : records) {
         if (last_ts != it.second.start_ts) {
-            dprintf(fd, "%llu", (unsigned long long)it.second.start_ts);
+            dprintf(fd, "%" PRIu64, it.second.start_ts);
         }
-        dprintf(fd, ",%llu\n", (unsigned long long)it.first);
+        dprintf(fd, ",%" PRIu64 "\n", it.first);
         last_ts = it.first;
 
-        for (const auto& record : it.second.entries) {
-            dprintf(fd, "%s %ju %ju %ju %ju %ju %ju %ju %ju\n",
-                record.name.c_str(),
-                record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
-                record.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF],
-                record.ios.bytes[READ][BACKGROUND][CHARGER_OFF],
-                record.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF],
-                record.ios.bytes[READ][FOREGROUND][CHARGER_ON],
-                record.ios.bytes[WRITE][FOREGROUND][CHARGER_ON],
-                record.ios.bytes[READ][BACKGROUND][CHARGER_ON],
-                record.ios.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+        if (!debug) {
+            dumpUidRecords(fd, it.second.entries);
+        } else {
+            dumpUidRecordsDebug(fd, it.second.entries);
         }
     }
 
     if (time_window) {
-        storaged->update_uid_io_interval(time_window);
+        storaged_sp->update_uid_io_interval(time_window);
     }
 
     return NO_ERROR;
 }
 
-sp<IStoraged> get_storaged_service() {
+binder::Status StoragedService::onUserStarted(int32_t userId) {
+    storaged_sp->add_user_ce(userId);
+    return binder::Status::ok();
+}
+
+binder::Status StoragedService::onUserStopped(int32_t userId) {
+    storaged_sp->remove_user_ce(userId);
+    return binder::Status::ok();
+}
+
+binder::Status StoragedService::getRecentPerf(int32_t* _aidl_return) {
+    uint32_t recent_perf = storaged_sp->get_recent_perf();
+    if (recent_perf > INT32_MAX) {
+        *_aidl_return = INT32_MAX;
+    } else {
+        *_aidl_return = static_cast<int32_t>(recent_perf);
+    }
+    return binder::Status::ok();
+}
+
+status_t StoragedPrivateService::start() {
+    return BinderService<StoragedPrivateService>::publish();
+}
+
+binder::Status StoragedPrivateService::dumpUids(
+        vector<::android::os::storaged::UidInfo>* _aidl_return) {
+    unordered_map<uint32_t, uid_info> uids_m = storaged_sp->get_uids();
+
+    for (const auto& it : uids_m) {
+        UidInfo uinfo;
+        uinfo.uid = it.second.uid;
+        uinfo.name = it.second.name;
+        uinfo.tasks = it.second.tasks;
+        memcpy(&uinfo.io, &it.second.io, sizeof(uinfo.io));
+        _aidl_return->push_back(uinfo);
+    }
+    return binder::Status::ok();
+}
+
+binder::Status StoragedPrivateService::dumpPerfHistory(
+        vector<int32_t>* _aidl_return) {
+    *_aidl_return = storaged_sp->get_perf_history();
+    return binder::Status::ok();
+}
+
+sp<IStoragedPrivate> get_storaged_pri_service() {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) return NULL;
 
-    sp<IBinder> binder = sm->getService(String16("storaged"));
+    sp<IBinder> binder = sm->getService(String16("storaged_pri"));
     if (binder == NULL) return NULL;
 
-    sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
-
-    return storaged;
+    return interface_cast<IStoragedPrivate>(binder);
 }
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index dd8bdd6..5745782 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -38,16 +38,87 @@
 using namespace android;
 using namespace android::base;
 using namespace android::content::pm;
+using namespace android::os::storaged;
+using namespace storaged_proto;
 
-static bool refresh_uid_names;
+namespace {
 
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
+bool refresh_uid_names;
+const char* UID_IO_STATS_PATH = "/proc/uid_io/stats";
+
+} // namepsace
+
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
     return get_uid_io_stats_locked();
 };
 
-static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
+/* return true on parse success and false on failure */
+bool uid_info::parse_uid_io_stats(std::string&& s)
+{
+    std::vector<std::string> fields = Split(s, " ");
+    if (fields.size() < 11 ||
+        !ParseUint(fields[0],  &uid) ||
+        !ParseUint(fields[1],  &io[FOREGROUND].rchar) ||
+        !ParseUint(fields[2],  &io[FOREGROUND].wchar) ||
+        !ParseUint(fields[3],  &io[FOREGROUND].read_bytes) ||
+        !ParseUint(fields[4],  &io[FOREGROUND].write_bytes) ||
+        !ParseUint(fields[5],  &io[BACKGROUND].rchar) ||
+        !ParseUint(fields[6],  &io[BACKGROUND].wchar) ||
+        !ParseUint(fields[7],  &io[BACKGROUND].read_bytes) ||
+        !ParseUint(fields[8],  &io[BACKGROUND].write_bytes) ||
+        !ParseUint(fields[9],  &io[FOREGROUND].fsync) ||
+        !ParseUint(fields[10], &io[BACKGROUND].fsync)) {
+        LOG_TO(SYSTEM, WARNING) << "Invalid uid I/O stats: \""
+                                << s << "\"";
+        return false;
+    }
+    return true;
+}
+
+/* return true on parse success and false on failure */
+bool task_info::parse_task_io_stats(std::string&& s)
+{
+    std::vector<std::string> fields = Split(s, ",");
+    size_t size = fields.size();
+    if (size < 13 ||
+        !ParseInt(fields[size - 11],  &pid) ||
+        !ParseUint(fields[size - 10],  &io[FOREGROUND].rchar) ||
+        !ParseUint(fields[size - 9],  &io[FOREGROUND].wchar) ||
+        !ParseUint(fields[size - 8],  &io[FOREGROUND].read_bytes) ||
+        !ParseUint(fields[size - 7],  &io[FOREGROUND].write_bytes) ||
+        !ParseUint(fields[size - 6],  &io[BACKGROUND].rchar) ||
+        !ParseUint(fields[size - 5],  &io[BACKGROUND].wchar) ||
+        !ParseUint(fields[size - 4],  &io[BACKGROUND].read_bytes) ||
+        !ParseUint(fields[size - 3], &io[BACKGROUND].write_bytes) ||
+        !ParseUint(fields[size - 2], &io[FOREGROUND].fsync) ||
+        !ParseUint(fields[size - 1], &io[BACKGROUND].fsync)) {
+        LOG_TO(SYSTEM, WARNING) << "Invalid task I/O stats: \""
+                                << s << "\"";
+        return false;
+    }
+    comm = Join(std::vector<std::string>(
+                fields.begin() + 1, fields.end() - 11), ',');
+    return true;
+}
+
+bool io_usage::is_zero() const
+{
+    for (int i = 0; i < IO_TYPES; i++) {
+        for (int j = 0; j < UID_STATS; j++) {
+            for (int k = 0; k < CHARGER_STATS; k++) {
+                if (bytes[i][j][k])
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+namespace {
+
+void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
 {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) {
@@ -79,17 +150,19 @@
     refresh_uid_names = false;
 }
 
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
+} // namespace
+
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats_locked()
 {
-    std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
+    std::unordered_map<uint32_t, uid_info> uid_io_stats;
     std::string buffer;
     if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
         PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
         return uid_io_stats;
     }
 
-    std::vector<std::string> io_stats = Split(buffer, "\n");
-    struct uid_info u;
+    std::vector<std::string> io_stats = Split(std::move(buffer), "\n");
+    uid_info u;
     vector<int> uids;
     vector<std::string*> uid_names;
 
@@ -97,32 +170,24 @@
         if (io_stats[i].empty()) {
             continue;
         }
-        std::vector<std::string> fields = Split(io_stats[i], " ");
-        if (fields.size() < 11 ||
-            !ParseUint(fields[0],  &u.uid) ||
-            !ParseUint(fields[1],  &u.io[FOREGROUND].rchar) ||
-            !ParseUint(fields[2],  &u.io[FOREGROUND].wchar) ||
-            !ParseUint(fields[3],  &u.io[FOREGROUND].read_bytes) ||
-            !ParseUint(fields[4],  &u.io[FOREGROUND].write_bytes) ||
-            !ParseUint(fields[5],  &u.io[BACKGROUND].rchar) ||
-            !ParseUint(fields[6],  &u.io[BACKGROUND].wchar) ||
-            !ParseUint(fields[7],  &u.io[BACKGROUND].read_bytes) ||
-            !ParseUint(fields[8],  &u.io[BACKGROUND].write_bytes) ||
-            !ParseUint(fields[9],  &u.io[FOREGROUND].fsync) ||
-            !ParseUint(fields[10], &u.io[BACKGROUND].fsync)) {
-            LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
-                                    << io_stats[i] << "\"";
-            continue;
-        }
 
-        uid_io_stats[u.uid] = u;
-        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()) {
-            refresh_uid_names = true;
+        if (io_stats[i].compare(0, 4, "task")) {
+            if (!u.parse_uid_io_stats(std::move(io_stats[i])))
+                continue;
+            uid_io_stats[u.uid] = u;
+            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()) {
+                refresh_uid_names = true;
+            } else {
+                uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+            }
         } else {
-            uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+            task_info t;
+            if (!t.parse_task_io_stats(std::move(io_stats[i])))
+                continue;
+            uid_io_stats[u.uid].tasks[t.pid] = t;
         }
     }
 
@@ -133,34 +198,41 @@
     return uid_io_stats;
 }
 
-static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+namespace {
 
-static inline int records_size(
-    const std::map<uint64_t, struct uid_records>& curr_records)
+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)
 {
-    int count = 0;
-    for (auto const& it : curr_records) {
+    size_t count = 0;
+    for (auto const& it : history) {
         count += it.second.entries.size();
     }
     return count;
 }
 
-static struct uid_io_usage zero_io_usage;
+} // namespace
 
 void uid_monitor::add_records_locked(uint64_t curr_ts)
 {
     // remove records more than 5 days old
     if (curr_ts > 5 * DAY_TO_SEC) {
-        auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
-        records.erase(records.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) {
         struct uid_record record = {};
         record.name = p.first;
-        record.ios = p.second;
-        if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+        if (!p.second.uid_ios.is_zero()) {
+            record.ios.user_id = p.second.user_id;
+            record.ios.uid_ios = p.second.uid_ios;
+            for (const auto& p_task : p.second.task_ios) {
+                if (!p_task.second.is_zero())
+                    record.ios.task_ios[p_task.first] = p_task.second;
+            }
             new_records.entries.push_back(record);
         }
     }
@@ -173,25 +245,25 @@
       return;
 
     // make some room for new records
-    int overflow = records_size(records) +
+    ssize_t overflow = history_size(io_history) +
         new_records.entries.size() - MAX_UID_RECORDS_SIZE;
-    while (overflow > 0 && records.size() > 0) {
-        auto del_it = records.begin();
+    while (overflow > 0 && io_history.size() > 0) {
+        auto del_it = io_history.begin();
         overflow -= del_it->second.entries.size();
-        records.erase(records.begin());
+        io_history.erase(io_history.begin());
     }
 
-    records[curr_ts] = new_records;
+    io_history[curr_ts] = new_records;
 }
 
 std::map<uint64_t, struct uid_records> uid_monitor::dump(
     double hours, uint64_t threshold, bool force_report)
 {
     if (force_report) {
-        report();
+        report(nullptr);
     }
 
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
 
     std::map<uint64_t, struct uid_records> dump_records;
     uint64_t first_ts = 0;
@@ -200,19 +272,20 @@
         first_ts = time(NULL) - hours * HOUR_TO_SEC;
     }
 
-    for (auto it = records.lower_bound(first_ts); it != records.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;
 
         for (const auto& rec : recs) {
-            if (rec.ios.bytes[READ][FOREGROUND][CHARGER_ON] +
-                rec.ios.bytes[READ][FOREGROUND][CHARGER_OFF] +
-                rec.ios.bytes[READ][BACKGROUND][CHARGER_ON] +
-                rec.ios.bytes[READ][BACKGROUND][CHARGER_OFF] +
-                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_ON] +
-                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
-                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_ON] +
-                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
+            const io_usage& uid_usage = rec.ios.uid_ios;
+            if (uid_usage.bytes[READ][FOREGROUND][CHARGER_ON] +
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF] +
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON] +
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF] +
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON] +
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON] +
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
                 filtered.entries.push_back(rec);
             }
         }
@@ -230,20 +303,21 @@
 
 void uid_monitor::update_curr_io_stats_locked()
 {
-    std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+    std::unordered_map<uint32_t, uid_info> uid_io_stats =
         get_uid_io_stats_locked();
     if (uid_io_stats.empty()) {
         return;
     }
 
     for (const auto& it : uid_io_stats) {
-        const struct uid_info& uid = it.second;
-
+        const uid_info& uid = it.second;
         if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
-          curr_io_stats[uid.name] = {};
+            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;
         int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
@@ -253,30 +327,177 @@
         int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
             last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
 
-        usage.bytes[READ][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat] +=
             (fg_rd_delta < 0) ? 0 : fg_rd_delta;
-        usage.bytes[READ][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat] +=
             (bg_rd_delta < 0) ? 0 : bg_rd_delta;
-        usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat] +=
             (fg_wr_delta < 0) ? 0 : fg_wr_delta;
-        usage.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) {
+            const task_info& task = task_it.second;
+            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;
+            int64_t task_bg_rd_delta = task.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;
+            int64_t task_bg_wr_delta = task.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_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
+            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_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;
+            task_usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+                (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;
+        }
     }
 
     last_uid_io_stats = uid_io_stats;
 }
 
-void uid_monitor::report()
+void uid_monitor::report(unordered_map<int, StoragedProto>* protos)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    if (!enabled()) return;
+
+    Mutex::Autolock _l(uidm_mutex);
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
+
+    if (protos) {
+        update_uid_io_proto(protos);
+    }
+}
+
+namespace {
+
+void set_io_usage_proto(IOUsage* usage_proto, const io_usage& usage)
+{
+    usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]);
+    usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]);
+    usage_proto->set_rd_bg_chg_on(usage.bytes[READ][BACKGROUND][CHARGER_ON]);
+    usage_proto->set_rd_bg_chg_off(usage.bytes[READ][BACKGROUND][CHARGER_OFF]);
+    usage_proto->set_wr_fg_chg_on(usage.bytes[WRITE][FOREGROUND][CHARGER_ON]);
+    usage_proto->set_wr_fg_chg_off(usage.bytes[WRITE][FOREGROUND][CHARGER_OFF]);
+    usage_proto->set_wr_bg_chg_on(usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+    usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]);
+}
+
+void get_io_usage_proto(io_usage* usage, const IOUsage& io_proto)
+{
+    usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on();
+    usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off();
+    usage->bytes[READ][BACKGROUND][CHARGER_ON] = io_proto.rd_bg_chg_on();
+    usage->bytes[READ][BACKGROUND][CHARGER_OFF] = io_proto.rd_bg_chg_off();
+    usage->bytes[WRITE][FOREGROUND][CHARGER_ON] = io_proto.wr_fg_chg_on();
+    usage->bytes[WRITE][FOREGROUND][CHARGER_OFF] = io_proto.wr_fg_chg_off();
+    usage->bytes[WRITE][BACKGROUND][CHARGER_ON] = io_proto.wr_bg_chg_on();
+    usage->bytes[WRITE][BACKGROUND][CHARGER_OFF] = io_proto.wr_bg_chg_off();
+}
+
+} // namespace
+
+void uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)
+{
+    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;
+
+        for (const auto& entry : recs.entries) {
+            userid_t user_id = entry.ios.user_id;
+            UidIOItem* item_proto = user_items[user_id];
+            if (item_proto == nullptr) {
+                item_proto = (*protos)[user_id].mutable_uid_io_usage()
+                             ->add_uid_io_items();
+                user_items[user_id] = item_proto;
+            }
+            item_proto->set_end_ts(end_ts);
+
+            UidIORecords* recs_proto = item_proto->mutable_records();
+            recs_proto->set_start_ts(recs.start_ts);
+
+            UidRecord* rec_proto = recs_proto->add_entries();
+            rec_proto->set_uid_name(entry.name);
+            rec_proto->set_user_id(user_id);
+
+            IOUsage* uid_io_proto = rec_proto->mutable_uid_io();
+            const io_usage& uio_ios = entry.ios.uid_ios;
+            set_io_usage_proto(uid_io_proto, uio_ios);
+
+            for (const auto& task_io : entry.ios.task_ios) {
+                const std::string& task_name = task_io.first;
+                const io_usage& task_ios = task_io.second;
+
+                TaskIOUsage* task_io_proto = rec_proto->add_task_io();
+                task_io_proto->set_task_name(task_name);
+                set_io_usage_proto(task_io_proto->mutable_ios(), task_ios);
+            }
+        }
+    }
+}
+
+void uid_monitor::clear_user_history(userid_t user_id)
+{
+    Mutex::Autolock _l(uidm_mutex);
+
+    for (auto& item : io_history) {
+        vector<uid_record>* entries = &item.second.entries;
+        entries->erase(
+            remove_if(entries->begin(), entries->end(),
+                [user_id](const uid_record& rec) {
+                    return rec.ios.user_id == user_id;}),
+            entries->end());
+    }
+
+    for (auto it = io_history.begin(); it != io_history.end(); ) {
+        if (it->second.entries.empty()) {
+            it = io_history.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
+void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto)
+{
+    if (!enabled()) return;
+
+    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()];
+
+        recs->start_ts = records_proto.start_ts();
+        for (const auto& rec_proto : records_proto.entries()) {
+            struct uid_record record;
+            record.name = rec_proto.uid_name();
+            record.ios.user_id = rec_proto.user_id();
+            get_io_usage_proto(&record.ios.uid_ios, rec_proto.uid_io());
+
+            for (const auto& task_io_proto : rec_proto.task_io()) {
+                get_io_usage_proto(
+                    &record.ios.task_ios[task_io_proto.task_name()],
+                    task_io_proto.ios());
+            }
+            recs->entries.push_back(record);
+        }
+    }
 }
 
 void uid_monitor::set_charger_state(charger_stat_t stat)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
 
     if (charger_stat == stat) {
         return;
@@ -289,16 +510,11 @@
 void uid_monitor::init(charger_stat_t stat)
 {
     charger_stat = stat;
+
     start_ts = time(NULL);
     last_uid_io_stats = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
-{
-    sem_init(&um_lock, 0, 1);
-}
-
-uid_monitor::~uid_monitor()
-{
-    sem_destroy(&um_lock);
+    : enable(!access(UID_IO_STATS_PATH, R_OK)) {
 }
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 74b7436..4fd4bc9 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -18,6 +18,7 @@
 
 #include <dirent.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <linux/time.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -41,124 +42,7 @@
 #include <storaged.h>
 #include <storaged_utils.h>
 
-bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
-    // Get time
-    struct timespec ts;
-    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
-    // when system is running.
-    int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
-    if (ret < 0) {
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
-        return false;
-    }
-
-    std::string buffer;
-    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
-        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
-        return false;
-    }
-
-    // Regular diskstats entries
-    std::stringstream ss(buffer);
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        ss >> *((uint64_t*)stats + i);
-    }
-    // Other entries
-    stats->start_time = 0;
-    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
-        ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
-    stats->counter = 1;
-    stats->io_avg = (double)stats->io_in_flight;
-    return true;
-}
-
-struct disk_perf get_disk_perf(struct disk_stats* stats) {
-    struct disk_perf perf;
-    memset(&perf, 0, sizeof(struct disk_perf));  // initialize
-
-    if (stats->io_ticks) {
-        if (stats->read_ticks) {
-            unsigned long long divisor = stats->read_ticks * stats->io_ticks;
-            perf.read_perf = ((unsigned long long)SECTOR_SIZE *
-                                        stats->read_sectors *
-                                        stats->io_in_queue +
-                                        (divisor >> 1)) /
-                                            divisor;
-            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
-                                        stats->read_ios *
-                                        stats->io_in_queue +
-                                        (divisor >> 1)) /
-                                            divisor;
-        }
-        if (stats->write_ticks) {
-            unsigned long long divisor = stats->write_ticks * stats->io_ticks;
-                        perf.write_perf = ((unsigned long long)SECTOR_SIZE *
-                                                    stats->write_sectors *
-                                                    stats->io_in_queue +
-                                                    (divisor >> 1)) /
-                                                        divisor;
-                        perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
-                                                    stats->write_ios *
-                                                    stats->io_in_queue +
-                                                    (divisor >> 1)) /
-                                                        divisor;
-        }
-        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
-                                stats->io_ticks;
-    }
-    return perf;
-}
-
-struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
-    struct disk_stats inc;
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
-            continue;
-        }
-
-        *((uint64_t*)&inc + i) =
-                *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
-    }
-    // io_in_flight is exception
-    inc.io_in_flight = curr->io_in_flight;
-
-    inc.start_time = prev->end_time;
-    inc.end_time = curr->end_time;
-    inc.io_avg = curr->io_avg;
-    inc.counter = 1;
-
-    return inc;
-}
-
-// Add src to dst
-void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
-    if (dst->end_time != 0 && dst->end_time != src->start_time) {
-        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
-            << " are added. dst end with " << dst->end_time
-            << ", src start with " << src->start_time;
-    }
-
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
-            continue;
-        }
-
-        *((uint64_t*)dst + i) += *((uint64_t*)src + i);
-    }
-
-    dst->io_in_flight = src->io_in_flight;
-    if (dst->counter + src->counter) {
-        dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
-                        (dst->counter + src->counter);
-    }
-    dst->counter += src->counter;
-    dst->end_time = src->end_time;
-    if (dst->start_time == 0) {
-        dst->start_time = src->start_time;
-    }
-}
-
-static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+bool cmp_uid_info(const UidInfo& l, const UidInfo& r) {
     // Compare background I/O first.
     for (int i = UID_STATS - 1; i >= 0; i--) {
         uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
@@ -177,56 +61,72 @@
     return l.name < r.name;
 }
 
-void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+void sort_running_uids_info(std::vector<UidInfo> &uids) {
     std::sort(uids.begin(), uids.end(), cmp_uid_info);
 }
 
 // Logging functions
-void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+void log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task) {
     printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes "
            "bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
 
     for (const auto& uid : uids) {
-        printf("%s %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju\n", uid.name.c_str(),
+        printf("%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+            uid.name.c_str(),
             uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
             uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes,
             uid.io[0].fsync, uid.io[1].fsync);
+        if (flag_dump_task) {
+            for (const auto& task_it : uid.tasks) {
+                const task_info& task = task_it.second;
+                printf("-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                        " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                    task.comm.c_str(),
+                    task.io[0].rchar, task.io[0].wchar, task.io[0].read_bytes, task.io[0].write_bytes,
+                    task.io[1].rchar, task.io[1].wchar, task.io[1].read_bytes, task.io[1].write_bytes,
+                    task.io[0].fsync, task.io[1].fsync);
+            }
+        }
     }
     fflush(stdout);
 }
 
-#if DEBUG
-void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
-    // skip if the input structure are all zeros
-    if (perf == NULL) return;
-    struct disk_perf zero_cmp;
-    memset(&zero_cmp, 0, sizeof(zero_cmp));
-    if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
+void log_console_perf_history(const vector<int>& perf_history) {
+    if (perf_history.size() < 3 ||
+        perf_history.size() != perf_history[0] +
+                               perf_history[1] +
+                               perf_history[2] + (size_t)3) {
+        return;
+    }
 
-    LOG_TO(SYSTEM, INFO) << "perf(ios) " << type
-              << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)"
-              << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)"
-              << " q:" << perf->queue;
-}
-#else
-void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {}
-#endif
+    printf("\nI/O perf history (KB/s) :  most_recent  <---------  least_recent \n");
 
-void log_event_disk_stats(struct disk_stats* stats, const char* type) {
-    // skip if the input structure are all zeros
-    if (stats == NULL) return;
-    struct disk_stats zero_cmp;
-    memset(&zero_cmp, 0, sizeof(zero_cmp));
-    // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
-    if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
+    std::stringstream line;
+    int start = 3;
+    int end = 3 + perf_history[0];
+    std::copy(perf_history.begin() + start, perf_history.begin() + end,
+              std::ostream_iterator<int>(line, " "));
+    printf("last 24 hours : %s\n", line.str().c_str());
 
-    android_log_event_list(EVENTLOGTAG_DISKSTATS)
-        << type << stats->start_time << stats->end_time
-        << stats->read_ios << stats->read_merges
-        << stats->read_sectors << stats->read_ticks
-        << stats->write_ios << stats->write_merges
-        << stats->write_sectors << stats->write_ticks
-        << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
-        << LOG_ID_EVENTS;
+    line.str("");
+    start = end;
+    end += perf_history[1];
+    std::copy(perf_history.begin() + start, perf_history.begin() + end,
+              std::ostream_iterator<int>(line, " "));
+    printf("last 7 days   : %s\n", line.str().c_str());
+
+    line.str("");
+    start = end;
+    std::copy(perf_history.begin() + start, perf_history.end(),
+              std::ostream_iterator<int>(line, " "));
+    printf("last 52 weeks : %s\n", line.str().c_str());
 }
 
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries) {
+    map<string, io_usage> merged_entries;
+    for (const auto& record : entries) {
+        merged_entries[record.name] += record.ios.uid_ios;
+    }
+    return merged_entries;
+}
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
deleted file mode 100644
index 26d04b1..0000000
--- a/storaged/tests/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-test_module_prefix := storaged-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-
-test_src_files := \
-    storaged_test.cpp \
-
-# Build tests for the logger. Run with:
-#   adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_STATIC_LIBRARIES := libstoraged
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index b103ac1..ec47b65 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <chrono>
 #include <deque>
 #include <fcntl.h>
 #include <random>
@@ -24,13 +25,20 @@
 
 #include <gtest/gtest.h>
 
+#include <healthhalutils/HealthHalUtils.h>
 #include <storaged.h>               // data structures
 #include <storaged_utils.h>         // functions to test
 
 #define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
 #define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
 
-static void pause(uint32_t sec) {
+using namespace std;
+using namespace chrono;
+using namespace storaged_proto;
+
+namespace {
+
+void write_and_pause(uint32_t sec) {
     const char* path = "/cache/test";
     int fd = open(path, O_WRONLY | O_CREAT, 0600);
     ASSERT_LT(-1, fd);
@@ -53,6 +61,8 @@
     sleep(sec);
 }
 
+} // namespace
+
 // the return values of the tested functions should be the expected ones
 const char* DISK_STATS_PATH;
 TEST(storaged_test, retvals) {
@@ -77,13 +87,11 @@
     EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
 
     // reading a wrong path should not damage the output structure
-    EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
+    EXPECT_EQ(stats, old_stats);
 }
 
 TEST(storaged_test, disk_stats) {
-    struct disk_stats stats;
-    memset(&stats, 0, sizeof(struct disk_stats));
-
+    struct disk_stats stats = {};
     ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
 
     // every entry of stats (except io_in_flight) should all be greater than 0
@@ -93,11 +101,7 @@
     }
 
     // accumulation of the increments should be the same with the overall increment
-    struct disk_stats base, tmp, curr, acc, inc[5];
-    memset(&base, 0, sizeof(struct disk_stats));
-    memset(&tmp, 0, sizeof(struct disk_stats));
-    memset(&acc, 0, sizeof(struct disk_stats));
-
+    struct disk_stats base = {}, tmp = {}, curr, acc = {}, inc[5];
     for (uint i = 0; i < 5; ++i) {
         ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
         if (i == 0) {
@@ -106,22 +110,18 @@
             sleep(5);
             continue;
         }
-        inc[i] = get_inc_disk_stats(&tmp, &curr);
+        get_inc_disk_stats(&tmp, &curr, &inc[i]);
         add_disk_stats(&inc[i], &acc);
         tmp = curr;
-        pause(5);
+        write_and_pause(5);
     }
-    struct disk_stats overall_inc;
-    memset(&overall_inc, 0, sizeof(disk_stats));
-    overall_inc= get_inc_disk_stats(&base, &curr);
+    struct disk_stats overall_inc = {};
+    get_inc_disk_stats(&base, &curr, &overall_inc);
 
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        if (i == 8) continue; // skip io_in_flight which can be 0
-        EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
-    }
+    EXPECT_EQ(overall_inc, acc);
 }
 
-static double mean(std::deque<uint32_t> nums) {
+double mean(std::deque<uint32_t> nums) {
     double sum = 0.0;
     for (uint32_t i : nums) {
     sum += i;
@@ -129,7 +129,7 @@
     return sum / nums.size();
 }
 
-static double standard_deviation(std::deque<uint32_t> nums) {
+double standard_deviation(std::deque<uint32_t> nums) {
     double sum = 0.0;
     double avg = mean(nums);
     for (uint32_t i : nums) {
@@ -181,7 +181,7 @@
     }
 }
 
-static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
+struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
     struct disk_perf retval;
     retval.read_perf = (double)perf.read_perf * mul;
     retval.read_ios = (double)perf.read_ios * mul;
@@ -192,7 +192,7 @@
     return retval;
 }
 
-static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
+struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
     struct disk_stats retval;
     retval.read_ios = stats1.read_ios + stats2.read_ios;
     retval.read_merges = stats1.read_merges + stats2.read_merges;
@@ -210,11 +210,42 @@
     return retval;
 }
 
+void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
+    EXPECT_LE(stats1.read_ios, stats2.read_ios);
+    EXPECT_LE(stats1.read_merges, stats2.read_merges);
+    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
+    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
+    EXPECT_LE(stats1.write_ios, stats2.write_ios);
+    EXPECT_LE(stats1.write_merges, stats2.write_merges);
+    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
+    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
+    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
+    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
+
+    EXPECT_TRUE(stats1.read_ios < stats2.read_ios ||
+        stats1.read_merges < stats2.read_merges ||
+        stats1.read_sectors < stats2.read_sectors ||
+        stats1.read_ticks < stats2.read_ticks ||
+        stats1.write_ios < stats2.write_ios ||
+        stats1.write_merges < stats2.write_merges ||
+        stats1.write_sectors < stats2.write_sectors ||
+        stats1.write_ticks < stats2.write_ticks ||
+        stats1.io_ticks < stats2.io_ticks ||
+        stats1.io_in_queue < stats2.io_in_queue);
+}
+
 TEST(storaged_test, disk_stats_monitor) {
+    using android::hardware::health::V2_0::get_health_service;
+
+    auto healthService = get_health_service();
+
     // asserting that there is one file for diskstats
-    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    ASSERT_TRUE(healthService != nullptr || access(MMC_DISK_STATS_PATH, R_OK) >= 0 ||
+                access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+
     // testing if detect() will return the right value
-    disk_stats_monitor dsm_detect;
+    disk_stats_monitor dsm_detect{healthService};
+    ASSERT_TRUE(dsm_detect.enabled());
     // feed monitor with constant perf data for io perf baseline
     // using constant perf is reasonable since the functionality of stream_stats
     // has already been tested
@@ -257,7 +288,7 @@
     }
 
     // testing if stalled disk_stats can be correctly accumulated in the monitor
-    disk_stats_monitor dsm_acc;
+    disk_stats_monitor dsm_acc{healthService};
     struct disk_stats norm_inc = {
         .read_ios = 200,
         .read_merges = 0,
@@ -294,14 +325,12 @@
         .io_avg = 0
     };
 
-    struct disk_stats stats_base;
-    memset(&stats_base, 0, sizeof(stats_base));
-
+    struct disk_stats stats_base = {};
     int loop_size = 100;
     for (int i = 0; i < loop_size; ++i) {
         stats_base = disk_stats_add(stats_base, norm_inc);
         dsm_acc.update(&stats_base);
-        EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
+        EXPECT_EQ(dsm_acc.mValid, (uint32_t)i >= dsm_acc.mWindow);
         EXPECT_FALSE(dsm_acc.mStall);
     }
 
@@ -316,36 +345,284 @@
         EXPECT_TRUE(dsm_acc.mValid);
         EXPECT_FALSE(dsm_acc.mStall);
     }
-}
 
-static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
-    EXPECT_LE(stats1.read_ios, stats2.read_ios);
-    EXPECT_LE(stats1.read_merges, stats2.read_merges);
-    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
-    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
-
-    EXPECT_LE(stats1.write_ios, stats2.write_ios);
-    EXPECT_LE(stats1.write_merges, stats2.write_merges);
-    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
-    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
-
-    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
-    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
-}
-
-#define TEST_LOOPS 20
-TEST(storaged_test, disk_stats_publisher) {
-    // asserting that there is one file for diskstats
-    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
-    disk_stats_publisher dsp;
-    struct disk_stats prev;
-    memset(&prev, 0, sizeof(prev));
-
-    for (int i = 0; i < TEST_LOOPS; ++i) {
-        dsp.update();
-        expect_increasing(prev, dsp.mPrevious);
-        prev = dsp.mPrevious;
-        pause(10);
+    struct disk_stats stats_prev = {};
+    loop_size = 10;
+    write_and_pause(5);
+    for (int i = 0; i < loop_size; ++i) {
+        dsm_detect.update();
+        expect_increasing(stats_prev, dsm_detect.mPrevious);
+        stats_prev = dsm_detect.mPrevious;
+        write_and_pause(5);
     }
 }
 
+TEST(storaged_test, storage_info_t) {
+    storage_info_t si;
+    time_point<steady_clock> tp;
+    time_point<system_clock> stp;
+
+    // generate perf history [least_recent  ------> most recent]
+    // day 1:   5,  10,  15,  20            | daily average 12
+    // day 2:  25,  30,  35,  40,  45       | daily average 35
+    // day 3:  50,  55,  60,  65,  70       | daily average 60
+    // day 4:  75,  80,  85,  90,  95       | daily average 85
+    // day 5: 100, 105, 110, 115,           | daily average 107
+    // day 6: 120, 125, 130, 135, 140       | daily average 130
+    // day 7: 145, 150, 155, 160, 165       | daily average 155
+    // end of week 1:                       | weekly average 83
+    // day 1: 170, 175, 180, 185, 190       | daily average 180
+    // day 2: 195, 200, 205, 210, 215       | daily average 205
+    // day 3: 220, 225, 230, 235            | daily average 227
+    // day 4: 240, 245, 250, 255, 260       | daily average 250
+    // day 5: 265, 270, 275, 280, 285       | daily average 275
+    // day 6: 290, 295, 300, 305, 310       | daily average 300
+    // day 7: 315, 320, 325, 330, 335       | daily average 325
+    // end of week 2:                       | weekly average 251
+    // day 1: 340, 345, 350, 355            | daily average 347
+    // day 2: 360, 365, 370, 375
+    si.day_start_tp = {};
+    for (int i = 0; i < 75; i++) {
+        tp += hours(5);
+        stp = {};
+        stp += duration_cast<chrono::seconds>(tp.time_since_epoch());
+        si.update_perf_history((i + 1) * 5, stp);
+    }
+
+    vector<int> history = si.get_perf_history();
+    EXPECT_EQ(history.size(), 66UL);
+    size_t i = 0;
+    EXPECT_EQ(history[i++], 4);
+    EXPECT_EQ(history[i++], 7);    // 7 days
+    EXPECT_EQ(history[i++], 52);   // 52 weeks
+    // last 24 hours
+    EXPECT_EQ(history[i++], 375);
+    EXPECT_EQ(history[i++], 370);
+    EXPECT_EQ(history[i++], 365);
+    EXPECT_EQ(history[i++], 360);
+    // daily average of last 7 days
+    EXPECT_EQ(history[i++], 347);
+    EXPECT_EQ(history[i++], 325);
+    EXPECT_EQ(history[i++], 300);
+    EXPECT_EQ(history[i++], 275);
+    EXPECT_EQ(history[i++], 250);
+    EXPECT_EQ(history[i++], 227);
+    EXPECT_EQ(history[i++], 205);
+    // weekly average of last 52 weeks
+    EXPECT_EQ(history[i++], 251);
+    EXPECT_EQ(history[i++], 83);
+    for (; i < history.size(); i++) {
+        EXPECT_EQ(history[i], 0);
+    }
+}
+
+TEST(storaged_test, storage_info_t_proto) {
+    storage_info_t si;
+    si.day_start_tp = {};
+
+    IOPerfHistory proto;
+    proto.set_nr_samples(10);
+    proto.set_day_start_sec(0);
+    si.load_perf_history_proto(proto);
+
+    // Skip ahead > 1 day, with no data points in the previous day.
+    time_point<system_clock> stp;
+    stp += hours(36);
+    si.update_perf_history(100, stp);
+
+    vector<int> history = si.get_perf_history();
+    EXPECT_EQ(history.size(), 63UL);
+    EXPECT_EQ(history[0], 1);
+    EXPECT_EQ(history[1], 7);
+    EXPECT_EQ(history[2], 52);
+    EXPECT_EQ(history[3], 100);
+    for (size_t i = 4; i < history.size(); i++) {
+        EXPECT_EQ(history[i], 0);
+    }
+}
+
+TEST(storaged_test, uid_monitor) {
+    uid_monitor uidm;
+
+    uidm.io_history[200] = {
+        .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] = 1000,
+              }
+            },
+            { "app1", {
+                .user_id = 1,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.io_history[300] = {
+        .start_ts = 200,
+        .entries = {
+            { "app1", {
+                .user_id = 1,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+            { "app3", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+        },
+    };
+
+    unordered_map<int, StoragedProto> protos;
+
+    uidm.update_uid_io_proto(&protos);
+
+    EXPECT_EQ(protos.size(), 2U);
+    EXPECT_EQ(protos.count(0), 1UL);
+    EXPECT_EQ(protos.count(1), 1UL);
+
+    EXPECT_EQ(protos[0].uid_io_usage().uid_io_items_size(), 2);
+    const UidIOItem& user_0_item_0 = protos[0].uid_io_usage().uid_io_items(0);
+    EXPECT_EQ(user_0_item_0.end_ts(), 200UL);
+    EXPECT_EQ(user_0_item_0.records().start_ts(), 100UL);
+    EXPECT_EQ(user_0_item_0.records().entries_size(), 2);
+    EXPECT_EQ(user_0_item_0.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_0_item_0.records().entries(0).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+    EXPECT_EQ(user_0_item_0.records().entries(1).uid_name(), "app2");
+    EXPECT_EQ(user_0_item_0.records().entries(1).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_0.records().entries(1).uid_io().rd_fg_chg_off(), 1000UL);
+    const UidIOItem& user_0_item_1 = protos[0].uid_io_usage().uid_io_items(1);
+    EXPECT_EQ(user_0_item_1.end_ts(), 300UL);
+    EXPECT_EQ(user_0_item_1.records().start_ts(), 200UL);
+    EXPECT_EQ(user_0_item_1.records().entries_size(), 1);
+    EXPECT_EQ(user_0_item_1.records().entries(0).uid_name(), "app3");
+    EXPECT_EQ(user_0_item_1.records().entries(0).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_1.records().entries(0).uid_io().rd_bg_chg_off(), 1000UL);
+
+    EXPECT_EQ(protos[1].uid_io_usage().uid_io_items_size(), 2);
+    const UidIOItem& user_1_item_0 = protos[1].uid_io_usage().uid_io_items(0);
+    EXPECT_EQ(user_1_item_0.end_ts(), 200UL);
+    EXPECT_EQ(user_1_item_0.records().start_ts(), 100UL);
+    EXPECT_EQ(user_1_item_0.records().entries_size(), 1);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_1_item_0.records().entries(0).user_id(), 1UL);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().rd_fg_chg_on(), 1000UL);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+    const UidIOItem& user_1_item_1 = protos[1].uid_io_usage().uid_io_items(1);
+    EXPECT_EQ(user_1_item_1.end_ts(), 300UL);
+    EXPECT_EQ(user_1_item_1.records().start_ts(), 200UL);
+    EXPECT_EQ(user_1_item_1.records().entries_size(), 1);
+    EXPECT_EQ(user_1_item_1.records().entries(0).uid_name(), "app1");
+    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();
+
+    uidm.io_history[300] = {
+        .start_ts = 200,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.io_history[400] = {
+        .start_ts = 300,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.load_uid_io_proto(protos[0].uid_io_usage());
+    uidm.load_uid_io_proto(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(uidm.io_history[200].start_ts, 100UL);
+    const vector<struct uid_record>& entries_0 = uidm.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);
+    EXPECT_EQ(entries_0[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_0[1].name, "app2");
+    EXPECT_EQ(entries_0[1].ios.user_id, 0UL);
+    EXPECT_EQ(entries_0[1].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(entries_0[2].name, "app1");
+    EXPECT_EQ(entries_0[2].ios.user_id, 1UL);
+    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(entries_1.size(), 3UL);
+    EXPECT_EQ(entries_1[0].name, "app1");
+    EXPECT_EQ(entries_1[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_1[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_1[1].name, "app3");
+    EXPECT_EQ(entries_1[1].ios.user_id, 0UL);
+    EXPECT_EQ(entries_1[1].ios.uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(entries_1[2].name, "app1");
+    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(entries_2.size(), 1UL);
+    EXPECT_EQ(entries_2[0].name, "app1");
+    EXPECT_EQ(entries_2[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_2[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+    map<string, io_usage> merged_entries_0 = merge_io_usage(entries_0);
+    EXPECT_EQ(merged_entries_0.size(), 2UL);
+    EXPECT_EQ(merged_entries_0.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_0.count("app2"), 1UL);
+    EXPECT_EQ(merged_entries_0["app1"].bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(merged_entries_0["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 2000UL);
+    EXPECT_EQ(merged_entries_0["app2"].bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+
+    map<string, io_usage> merged_entries_1 = merge_io_usage(entries_1);
+    EXPECT_EQ(merged_entries_1.size(), 2UL);
+    EXPECT_EQ(merged_entries_1.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_1.count("app3"), 1UL);
+    EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(merged_entries_1["app3"].bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+
+    map<string, io_usage> merged_entries_2 = merge_io_usage(entries_2);
+    EXPECT_EQ(merged_entries_2.size(), 1UL);
+    EXPECT_EQ(merged_entries_2.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_2["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+    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(uidm.io_history[200].entries.size(), 1UL);
+    EXPECT_EQ(uidm.io_history[300].entries.size(), 1UL);
+
+    uidm.clear_user_history(1);
+
+    EXPECT_EQ(uidm.io_history.size(), 0UL);
+}
diff --git a/storaged/tools/ranker.py b/storaged/tools/ranker.py
new file mode 100644
index 0000000..d8096b7
--- /dev/null
+++ b/storaged/tools/ranker.py
@@ -0,0 +1,181 @@
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Parser and ranker for dumpsys storaged output.
+
+This module parses output from dumpsys storaged by ranking uids based on
+their io usage measured in 8 different stats. It must be provided the input
+file through command line argument -i/--input.
+
+For more details, see:
+    $ python ranker.py -h
+
+Example:
+    $ python ranker.py -i io.txt -o output.txt -u 20 -cnt
+"""
+
+import argparse
+import sys
+
+IO_NAMES = ["[READ][FOREGROUND][CHARGER_OFF]",
+            "[WRITE][FOREGROUND][CHARGER_OFF]",
+            "[READ][BACKGROUND][CHARGER_OFF]",
+            "[WRITE][BACKGROUND][CHARGER_OFF]",
+            "[READ][FOREGROUND][CHARGER_ON]",
+            "[WRITE][FOREGROUND][CHARGER_ON]",
+            "[READ][BACKGROUND][CHARGER_ON]",
+            "[WRITE][BACKGROUND][CHARGER_ON]"]
+
+
+def get_args():
+  """Get arguments from command line.
+
+  The only required argument is input file.
+
+  Returns:
+    Args containing cmdline arguments
+  """
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument("-i", "--input", dest="input", required="true",
+                      help="input io FILE, must provide", metavar="FILE")
+  parser.add_argument("-o", "--output", dest="output", default="stdout",
+                      help="output FILE, default to stdout", metavar="FILE")
+  parser.add_argument("-u", "--uidcnt", dest="uidcnt", type=int, default=10,
+                      help="set number of uids to display for each rank, "
+                      "default 10")
+  parser.add_argument("-c", "--combine", dest="combine", default=False,
+                      action="store_true", help="add io stats for same uids, "
+                      "default to take io stats of last appearing uids")
+  parser.add_argument("-n", "--native", dest="native", default=False,
+                      action="store_true", help="only include native apps in "
+                      "ranking, default to include all apps")
+  parser.add_argument("-t", "--task", dest="task", default=False,
+                      action="store_true", help="display task io under uids, "
+                      "default to not display tasks")
+  return parser.parse_args()
+
+
+def is_number(word):
+  try:
+    int(word)
+    return True
+  except ValueError:
+    return False
+
+
+def combine_or_filter(args):
+  """Parser for io input.
+
+  Either args.combine io stats for the same uids
+  or take the io stats for the last uid and ignore
+  the same uids before it.
+
+  If task is required, store task ios along with uid
+  for later display.
+
+  Returns:
+    The structure for the return value uids is as follows:
+    uids: {uid -> [UID_STATS, TASK_STATS(optional)]}
+    UID_STATS: [io1, io2, ..., io8]
+    TASK_STATS: {task_name -> [io1, io2, ..., io8]}
+  """
+  fin = open(args.input, "r")
+  uids = {}
+  cur_uid = 0
+  task_enabled = args.task
+  for line in fin:
+    words = line.split()
+    if words[0] == "->":
+      # task io
+      if not task_enabled:
+        continue
+      # get task command line
+      i = len(words) - 8
+      task = " ".join(words[1:i])
+      if task in uids[cur_uid][1]:
+        task_io = uids[cur_uid][1][task]
+        for j in range(8):
+          task_io[j] += long(words[i+j])
+      else:
+        task_io = []
+        for j in range(8):
+          task_io.append(long(words[i+j]))
+      uids[cur_uid][1][task] = task_io
+
+    elif len(words) > 8:
+      if not is_number(words[0]) and args.native:
+        # uid not requested, ignore its tasks as well
+        task_enabled = False
+        continue
+      task_enabled = args.task
+      i = len(words) - 8
+      uid = " ".join(words[:i])
+      if uid in uids and args.combine:
+        uid_io = uids[uid][0]
+        for j in range(8):
+          uid_io[j] += long(words[i+j])
+        uids[uid][0] = uid_io
+      else:
+        uid_io = [long(words[i+j]) for j in range(8)]
+        uids[uid] = [uid_io]
+        if task_enabled:
+          uids[uid].append({})
+      cur_uid = uid
+
+  return uids
+
+
+def rank_uids(uids):
+  """Sort uids based on eight different io stats.
+
+  Returns:
+    uid_rank is a 2d list of tuples:
+    The first dimension represent the 8 different io stats.
+    The second dimension is a sorted list of tuples by tup[0],
+    each tuple is a uid's perticular stat at the first dimension and the uid.
+  """
+  uid_rank = [[(uids[uid][0][i], uid) for uid in uids] for i in range(8)]
+  for i in range(8):
+    uid_rank[i].sort(key=lambda tup: tup[0], reverse=True)
+  return uid_rank
+
+
+def display_uids(uid_rank, uids, args):
+  """Display ranked uid io, along with task io if specified."""
+  fout = sys.stdout
+  if args.output != "stdout":
+    fout = open(args.output, "w")
+
+  for i in range(8):
+    fout.write("RANKING BY " + IO_NAMES[i] + "\n")
+    for j in range(min(args.uidcnt, len(uid_rank[0]))):
+      uid = uid_rank[i][j][1]
+      uid_stat = " ".join([str(uid_io) for uid_io in uids[uid][0]])
+      fout.write(uid + " " + uid_stat + "\n")
+      if args.task:
+        for task in uids[uid][1]:
+          task_stat = " ".join([str(task_io) for task_io in uids[uid][1][task]])
+          fout.write("-> " + task + " " + task_stat + "\n")
+      fout.write("\n")
+
+
+def main():
+  args = get_args()
+  uids = combine_or_filter(args)
+  uid_rank = rank_uids(uids)
+  display_uids(uid_rank, uids, args)
+
+if __name__ == "__main__":
+  main()
diff --git a/storaged/uid_info.cpp b/storaged/uid_info.cpp
new file mode 100644
index 0000000..58e3fd2
--- /dev/null
+++ b/storaged/uid_info.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Parcel.h>
+
+#include "uid_info.h"
+
+using namespace android;
+using namespace android::os::storaged;
+
+status_t UidInfo::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(uid);
+    parcel->writeCString(name.c_str());
+    parcel->write(&io, sizeof(io));
+
+    parcel->writeInt32(tasks.size());
+    for (const auto& task_it : tasks) {
+        parcel->writeInt32(task_it.first);
+        parcel->writeCString(task_it.second.comm.c_str());
+        parcel->write(&task_it.second.io, sizeof(task_it.second.io));
+    }
+    return NO_ERROR;
+}
+
+status_t UidInfo::readFromParcel(const Parcel* parcel) {
+    uid = parcel->readInt32();
+    name = parcel->readCString();
+    parcel->read(&io, sizeof(io));
+
+    uint32_t tasks_size = parcel->readInt32();
+    for (uint32_t i = 0; i < tasks_size; i++) {
+        task_info task;
+        task.pid = parcel->readInt32();
+        task.comm = parcel->readCString();
+        parcel->read(&task.io, sizeof(task.io));
+        tasks[task.pid] = task;
+    }
+    return NO_ERROR;
+}
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index e75e4af..f08cf93 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -62,8 +62,8 @@
 }
 
 // We build BSD grep separately (but see http://b/111849261).
-cc_binary {
-    name: "grep",
+cc_defaults {
+    name: "grep_common",
     defaults: ["toolbox_defaults"],
     srcs: [
         "upstream-netbsd/usr.bin/grep/fastgrep.c",
@@ -72,8 +72,27 @@
         "upstream-netbsd/usr.bin/grep/queue.c",
         "upstream-netbsd/usr.bin/grep/util.c",
     ],
-
+    symlinks: [
+        "egrep",
+        "fgrep",
+    ],
     sanitize: {
         integer_overflow: false,
     },
 }
+
+cc_binary {
+    name: "grep",
+    defaults: ["grep_common"],
+    recovery_available: true,
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+//       when vendor_available is fully supported.
+cc_binary {
+    name: "grep_vendor",
+    stem: "grep",
+    vendor: true,
+    defaults: ["grep_common"],
+}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 5428e73..52a879e 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -27,19 +27,21 @@
     name: "trusty_keymaster_tipc",
     vendor: true,
     srcs: [
-        "trusty_keymaster_device.cpp",
-        "trusty_keymaster_ipc.cpp",
-        "trusty_keymaster_main.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "legacy/trusty_keymaster_device.cpp",
+        "legacy/trusty_keymaster_main.cpp",
     ],
     cflags: [
         "-Wall",
         "-Werror",
     ],
+
+    local_include_dirs: ["include"],
+
     shared_libs: [
         "libcrypto",
         "libcutils",
         "libkeymaster_portable",
-        "libkeymaster_staging",
         "libtrusty",
         "libkeymaster_messages",
         "libsoftkeymasterdevice",
@@ -53,9 +55,9 @@
     vendor: true,
     relative_install_path: "hw",
     srcs: [
-        "module.cpp",
-        "trusty_keymaster_ipc.cpp",
-        "trusty_keymaster_device.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "legacy/module.cpp",
+        "legacy/trusty_keymaster_device.cpp",
     ],
 
     cflags: [
@@ -64,6 +66,8 @@
         "-Werror",
     ],
 
+    local_include_dirs: ["include"],
+
     shared_libs: [
         "libcrypto",
         "libkeymaster_messages",
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
similarity index 100%
rename from trusty/keymaster/keymaster_ipc.h
rename to trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
similarity index 66%
rename from trusty/keymaster/trusty_keymaster_ipc.h
rename to trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
index c15f7c1..16207e6 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
@@ -17,13 +17,24 @@
 #ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
 #define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
 
+#include <keymaster/android_keymaster_messages.h>
+#include <trusty_keymaster/ipc/keymaster_ipc.h>
+
 __BEGIN_DECLS
 
+const uint32_t TRUSTY_KEYMASTER_RECV_BUF_SIZE = 2 * PAGE_SIZE;
+const uint32_t TRUSTY_KEYMASTER_SEND_BUF_SIZE =
+        (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+
 int trusty_keymaster_connect(void);
 int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
                           uint32_t* out_size);
 void trusty_keymaster_disconnect(void);
 
+keymaster_error_t translate_error(int err);
+keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req,
+                                        keymaster::KeymasterResponse* rsp);
+
 __END_DECLS
 
 #endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
similarity index 96%
rename from trusty/keymaster/trusty_keymaster_device.h
rename to trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
index cfada1b..5a80795 100644
--- a/trusty/keymaster/trusty_keymaster_device.h
+++ b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
@@ -36,7 +36,8 @@
      * These are the only symbols that will be exported by libtrustykeymaster.  All functionality
      * can be reached via the function pointers in device_.
      */
-    __attribute__((visibility("default"))) explicit TrustyKeymasterDevice(const hw_module_t* module);
+    __attribute__((visibility("default"))) explicit TrustyKeymasterDevice(
+            const hw_module_t* module);
     __attribute__((visibility("default"))) hw_device_t* hw_device();
 
     ~TrustyKeymasterDevice();
@@ -134,12 +135,15 @@
                                     keymaster_operation_handle_t operation_handle,
                                     const keymaster_key_param_set_t* in_params,
                                     const keymaster_blob_t* input, size_t* input_consumed,
-                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+                                    keymaster_key_param_set_t* out_params,
+                                    keymaster_blob_t* output);
     static keymaster_error_t finish(const keymaster2_device_t* dev,
                                     keymaster_operation_handle_t operation_handle,
                                     const keymaster_key_param_set_t* in_params,
-                                    const keymaster_blob_t* input, const keymaster_blob_t* signature,
-                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+                                    const keymaster_blob_t* input,
+                                    const keymaster_blob_t* signature,
+                                    keymaster_key_param_set_t* out_params,
+                                    keymaster_blob_t* output);
     static keymaster_error_t abort(const keymaster2_device_t* dev,
                                    keymaster_operation_handle_t operation_handle);
 
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
new file mode 100644
index 0000000..8c5cff6
--- /dev/null
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrustyKeymaster"
+
+// TODO: make this generic in libtrusty
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <log/log.h>
+#include <trusty/tipc.h>
+
+#include <trusty_keymaster/ipc/keymaster_ipc.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = -1;
+
+int trusty_keymaster_connect() {
+    int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
+    if (rc < 0) {
+        return rc;
+    }
+
+    handle_ = rc;
+    return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size) {
+    if (handle_ < 0) {
+        ALOGE("not connected\n");
+        return -EINVAL;
+    }
+
+    size_t msg_size = in_size + sizeof(struct keymaster_message);
+    struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
+    if (!msg) {
+        ALOGE("failed to allocate msg buffer\n");
+        return -EINVAL;
+    }
+
+    msg->cmd = cmd;
+    memcpy(msg->payload, in, in_size);
+
+    ssize_t rc = write(handle_, msg, msg_size);
+    free(msg);
+
+    if (rc < 0) {
+        ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
+        return -errno;
+    }
+    size_t out_max_size = *out_size;
+    *out_size = 0;
+    struct iovec iov[2];
+    struct keymaster_message header;
+    iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
+    while (true) {
+        iov[1] = {.iov_base = out + *out_size,
+                  .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH,
+                                                out_max_size - *out_size)};
+        rc = readv(handle_, iov, 2);
+        if (rc < 0) {
+            ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+                  strerror(errno));
+            return -errno;
+        }
+
+        if ((size_t)rc < sizeof(struct keymaster_message)) {
+            ALOGE("invalid response size (%d)\n", (int)rc);
+            return -EINVAL;
+        }
+
+        if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
+            ALOGE("invalid command (%d)", header.cmd);
+            return -EINVAL;
+        }
+        *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+        if (header.cmd & KEYMASTER_STOP_BIT) {
+            break;
+        }
+    }
+
+    return rc;
+}
+
+void trusty_keymaster_disconnect() {
+    if (handle_ >= 0) {
+        tipc_close(handle_);
+    }
+    handle_ = -1;
+}
+
+keymaster_error_t translate_error(int err) {
+    switch (err) {
+        case 0:
+            return KM_ERROR_OK;
+        case -EPERM:
+        case -EACCES:
+            return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+
+        case -ECANCELED:
+            return KM_ERROR_OPERATION_CANCELLED;
+
+        case -ENODEV:
+            return KM_ERROR_UNIMPLEMENTED;
+
+        case -ENOMEM:
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+        case -EBUSY:
+            return KM_ERROR_SECURE_HW_BUSY;
+
+        case -EIO:
+            return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+
+        case -EOVERFLOW:
+            return KM_ERROR_INVALID_INPUT_LENGTH;
+
+        default:
+            return KM_ERROR_UNKNOWN_ERROR;
+    }
+}
+
+keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req,
+                                        keymaster::KeymasterResponse* rsp) {
+    uint32_t req_size = req.SerializedSize();
+    if (req_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        ALOGE("Request too big: %u Max size: %u", req_size, TRUSTY_KEYMASTER_SEND_BUF_SIZE);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    }
+
+    uint8_t send_buf[TRUSTY_KEYMASTER_SEND_BUF_SIZE];
+    keymaster::Eraser send_buf_eraser(send_buf, TRUSTY_KEYMASTER_SEND_BUF_SIZE);
+    req.Serialize(send_buf, send_buf + req_size);
+
+    // Send it
+    uint8_t recv_buf[TRUSTY_KEYMASTER_RECV_BUF_SIZE];
+    keymaster::Eraser recv_buf_eraser(recv_buf, TRUSTY_KEYMASTER_RECV_BUF_SIZE);
+    uint32_t rsp_size = TRUSTY_KEYMASTER_RECV_BUF_SIZE;
+    int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
+    if (rc < 0) {
+        // Reset the connection on tipc error
+        trusty_keymaster_disconnect();
+        trusty_keymaster_connect();
+        ALOGE("tipc error: %d\n", rc);
+        // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
+        return translate_error(rc);
+    } else {
+        ALOGE("Received %d byte response\n", rsp_size);
+    }
+
+    const uint8_t* p = recv_buf;
+    if (!rsp->Deserialize(&p, p + rsp_size)) {
+        ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
+        return KM_ERROR_UNKNOWN_ERROR;
+    } else if (rsp->error != KM_ERROR_OK) {
+        ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
+        return rsp->error;
+    }
+    return rsp->error;
+}
diff --git a/trusty/keymaster/Makefile b/trusty/keymaster/legacy/Makefile
similarity index 100%
rename from trusty/keymaster/Makefile
rename to trusty/keymaster/legacy/Makefile
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/legacy/module.cpp
similarity index 65%
rename from trusty/keymaster/module.cpp
rename to trusty/keymaster/legacy/module.cpp
index b472680..7aa1a4e 100644
--- a/trusty/keymaster/module.cpp
+++ b/trusty/keymaster/legacy/module.cpp
@@ -19,14 +19,15 @@
 #include <hardware/hardware.h>
 #include <hardware/keymaster0.h>
 
-#include "trusty_keymaster_device.h"
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
 
 using keymaster::TrustyKeymasterDevice;
 
 /*
  * Generic device handling
  */
-static int trusty_keymaster_open(const hw_module_t* module, const char* name, hw_device_t** device) {
+static int trusty_keymaster_open(const hw_module_t* module, const char* name,
+                                 hw_device_t** device) {
     if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
         return -EINVAL;
     }
@@ -42,20 +43,20 @@
 }
 
 static struct hw_module_methods_t keystore_module_methods = {
-    .open = trusty_keymaster_open,
+        .open = trusty_keymaster_open,
 };
 
 struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
-    .common =
-        {
-            .tag = HARDWARE_MODULE_TAG,
-            .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
-            .hal_api_version = HARDWARE_HAL_API_VERSION,
-            .id = KEYSTORE_HARDWARE_MODULE_ID,
-            .name = "Trusty Keymaster HAL",
-            .author = "The Android Open Source Project",
-            .methods = &keystore_module_methods,
-            .dso = 0,
-            .reserved = {},
-        },
+        .common =
+                {
+                        .tag = HARDWARE_MODULE_TAG,
+                        .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+                        .hal_api_version = HARDWARE_HAL_API_VERSION,
+                        .id = KEYSTORE_HARDWARE_MODULE_ID,
+                        .name = "Trusty Keymaster HAL",
+                        .author = "The Android Open Source Project",
+                        .methods = &keystore_module_methods,
+                        .dso = 0,
+                        .reserved = {},
+                },
 };
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
similarity index 79%
rename from trusty/keymaster/trusty_keymaster_device.cpp
rename to trusty/keymaster/legacy/trusty_keymaster_device.cpp
index b8c2032..ea00a92 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
@@ -33,50 +33,15 @@
 #include <keymaster/authorization_set.h>
 #include <log/log.h>
 
-#include "keymaster_ipc.h"
-#include "trusty_keymaster_device.h"
-#include "trusty_keymaster_ipc.h"
-
-// Maximum size of message from Trusty is 8K (for RSA attestation key and chain)
-const uint32_t RECV_BUF_SIZE = 2*PAGE_SIZE;
-const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+#include <trusty_keymaster/ipc/keymaster_ipc.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
 
 const size_t kMaximumAttestationChallengeLength = 128;
 const size_t kMaximumFinishInputLength = 2048;
 
 namespace keymaster {
 
-static keymaster_error_t translate_error(int err) {
-    switch (err) {
-        case 0:
-            return KM_ERROR_OK;
-        case -EPERM:
-        case -EACCES:
-            return KM_ERROR_SECURE_HW_ACCESS_DENIED;
-
-        case -ECANCELED:
-            return KM_ERROR_OPERATION_CANCELLED;
-
-        case -ENODEV:
-            return KM_ERROR_UNIMPLEMENTED;
-
-        case -ENOMEM:
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-
-        case -EBUSY:
-            return KM_ERROR_SECURE_HW_BUSY;
-
-        case -EIO:
-            return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
-
-        case -EOVERFLOW:
-            return KM_ERROR_INVALID_INPUT_LENGTH;
-
-        default:
-            return KM_ERROR_UNKNOWN_ERROR;
-    }
-}
-
 TrustyKeymasterDevice::TrustyKeymasterDevice(const hw_module_t* module) {
     static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
                   "TrustyKeymasterDevice must be standard layout");
@@ -121,7 +86,7 @@
 
     GetVersionRequest version_request;
     GetVersionResponse version_response;
-    error_ = Send(KM_GET_VERSION, version_request, &version_response);
+    error_ = trusty_keymaster_send(KM_GET_VERSION, version_request, &version_response);
     if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
         ALOGE("\"Bad parameters\" error on GetVersion call.  Version 0 is not supported.");
         error_ = KM_ERROR_VERSION_MISMATCH;
@@ -186,7 +151,7 @@
     }
 
     ConfigureResponse response(message_version_);
-    keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_CONFIGURE, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -204,12 +169,12 @@
     AddEntropyRequest request(message_version_);
     request.random_data.Reinitialize(data, data_length);
     AddEntropyResponse response(message_version_);
-    return Send(KM_ADD_RNG_ENTROPY, request, &response);
+    return trusty_keymaster_send(KM_ADD_RNG_ENTROPY, request, &response);
 }
 
 keymaster_error_t TrustyKeymasterDevice::generate_key(
-    const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
-    keymaster_key_characteristics_t* characteristics) {
+        const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+        keymaster_key_characteristics_t* characteristics) {
     ALOGD("Device received generate_key");
 
     if (error_ != KM_ERROR_OK) {
@@ -227,14 +192,14 @@
     request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
 
     GenerateKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_GENERATE_KEY, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_GENERATE_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
 
     key_blob->key_material_size = response.key_blob.key_material_size;
     key_blob->key_material =
-        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+            DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
     if (!key_blob->key_material) {
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
     }
@@ -248,8 +213,8 @@
 }
 
 keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
-    const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
-    const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+        const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+        const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
     ALOGD("Device received get_key_characteristics");
 
     if (error_ != KM_ERROR_OK) {
@@ -267,7 +232,7 @@
     AddClientAndAppData(client_id, app_data, &request);
 
     GetKeyCharacteristicsResponse response(message_version_);
-    keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_GET_KEY_CHARACTERISTICS, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -279,9 +244,9 @@
 }
 
 keymaster_error_t TrustyKeymasterDevice::import_key(
-    const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
-    const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
-    keymaster_key_characteristics_t* characteristics) {
+        const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+        const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+        keymaster_key_characteristics_t* characteristics) {
     ALOGD("Device received import_key");
 
     if (error_ != KM_ERROR_OK) {
@@ -302,14 +267,14 @@
     request.SetKeyMaterial(key_data->data, key_data->data_length);
 
     ImportKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_IMPORT_KEY, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_IMPORT_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
 
     key_blob->key_material_size = response.key_blob.key_material_size;
     key_blob->key_material =
-        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+            DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
     if (!key_blob->key_material) {
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
     }
@@ -348,7 +313,7 @@
     AddClientAndAppData(client_id, app_data, &request);
 
     ExportKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_EXPORT_KEY, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_EXPORT_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -393,7 +358,7 @@
     }
 
     AttestKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_ATTEST_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -401,7 +366,7 @@
     // Allocate and clear storage for cert_chain.
     keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
     cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
-        malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+            malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
     if (!cert_chain->entries) {
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
     }
@@ -425,9 +390,9 @@
     return KM_ERROR_OK;
 }
 
-keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
-                                                     const keymaster_key_param_set_t* upgrade_params,
-                                                     keymaster_key_blob_t* upgraded_key) {
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(
+        const keymaster_key_blob_t* key_to_upgrade, const keymaster_key_param_set_t* upgrade_params,
+        keymaster_key_blob_t* upgraded_key) {
     ALOGD("Device received upgrade_key");
 
     if (error_ != KM_ERROR_OK) {
@@ -445,7 +410,7 @@
     request.upgrade_params.Reinitialize(*upgrade_params);
 
     UpgradeKeyResponse response(message_version_);
-    keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_UPGRADE_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -487,7 +452,7 @@
     request.additional_params.Reinitialize(*in_params);
 
     BeginOperationResponse response(message_version_);
-    keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_BEGIN_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -535,12 +500,12 @@
         request.additional_params.Reinitialize(*in_params);
     }
     if (input && input->data_length > 0) {
-        size_t max_input_size = SEND_BUF_SIZE - request.SerializedSize();
+        size_t max_input_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - request.SerializedSize();
         request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
     }
 
     UpdateOperationResponse response(message_version_);
-    keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_UPDATE_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -578,8 +543,8 @@
         return error_;
     }
     if (input && input->data_length > kMaximumFinishInputLength) {
-        ALOGE("%zu-byte input to finish; only %zu bytes allowed",
-              input->data_length, kMaximumFinishInputLength);
+        ALOGE("%zu-byte input to finish; only %zu bytes allowed", input->data_length,
+              kMaximumFinishInputLength);
         return KM_ERROR_INVALID_INPUT_LENGTH;
     }
 
@@ -603,7 +568,7 @@
     }
 
     FinishOperationResponse response(message_version_);
-    keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
+    keymaster_error_t err = trusty_keymaster_send(KM_FINISH_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
     }
@@ -638,7 +603,7 @@
     AbortOperationRequest request(message_version_);
     request.op_handle = operation_handle;
     AbortOperationResponse response(message_version_);
-    return Send(KM_ABORT_OPERATION, request, &response);
+    return trusty_keymaster_send(KM_ABORT_OPERATION, request, &response);
 }
 
 hw_device_t* TrustyKeymasterDevice::hw_device() {
@@ -669,25 +634,25 @@
 
 /* static */
 keymaster_error_t TrustyKeymasterDevice::generate_key(
-    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
-    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+        const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+        keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
     return convert_device(dev)->generate_key(params, key_blob, characteristics);
 }
 
 /* static */
 keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
-    const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
-    const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
-    keymaster_key_characteristics_t* characteristics) {
+        const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+        const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+        keymaster_key_characteristics_t* characteristics) {
     return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
                                                         characteristics);
 }
 
 /* static */
 keymaster_error_t TrustyKeymasterDevice::import_key(
-    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
-    keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
-    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+        const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+        keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+        keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
     return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
 }
 
@@ -711,10 +676,9 @@
 }
 
 /* static */
-keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster2_device_t* dev,
-                                                     const keymaster_key_blob_t* key_to_upgrade,
-                                                     const keymaster_key_param_set_t* upgrade_params,
-                                                     keymaster_key_blob_t* upgraded_key) {
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(
+        const keymaster2_device_t* dev, const keymaster_key_blob_t* key_to_upgrade,
+        const keymaster_key_param_set_t* upgrade_params, keymaster_key_blob_t* upgraded_key) {
     return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
 }
 
@@ -730,9 +694,9 @@
 
 /* static */
 keymaster_error_t TrustyKeymasterDevice::update(
-    const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
-    const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
-    size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+        const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+        const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+        size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
     return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
                                        out_params, output);
 }
@@ -755,42 +719,4 @@
     return convert_device(dev)->abort(operation_handle);
 }
 
-keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
-                                              KeymasterResponse* rsp) {
-    uint32_t req_size = req.SerializedSize();
-    if (req_size > SEND_BUF_SIZE) {
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    }
-    uint8_t send_buf[SEND_BUF_SIZE];
-    Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
-    req.Serialize(send_buf, send_buf + req_size);
-
-    // Send it
-    uint8_t recv_buf[RECV_BUF_SIZE];
-    Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
-    uint32_t rsp_size = RECV_BUF_SIZE;
-    ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
-    int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
-    if (rc < 0) {
-        // Reset the connection on tipc error
-        trusty_keymaster_disconnect();
-        trusty_keymaster_connect();
-        ALOGE("tipc error: %d\n", rc);
-        // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
-        return translate_error(rc);
-    } else {
-        ALOGV("Received %d byte response\n", rsp_size);
-    }
-
-    const uint8_t* p = recv_buf;
-    if (!rsp->Deserialize(&p, p + rsp_size)) {
-        ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
-        return KM_ERROR_UNKNOWN_ERROR;
-    } else if (rsp->error != KM_ERROR_OK) {
-        ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
-        return rsp->error;
-    }
-    return rsp->error;
-}
-
 }  // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp
similarity index 90%
rename from trusty/keymaster/trusty_keymaster_device_test.cpp
rename to trusty/keymaster/legacy/trusty_keymaster_device_test.cpp
index 9227964..68def58 100644
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ b/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp
@@ -28,15 +28,15 @@
 #include <keymaster/keymaster_tags.h>
 #include <keymaster/soft_keymaster_context.h>
 
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
 #include "android_keymaster_test_utils.h"
-#include "trusty_keymaster_device.h"
 #include "openssl_utils.h"
 
-using std::string;
 using std::ifstream;
 using std::istreambuf_iterator;
+using std::string;
 
-static keymaster::AndroidKeymaster *impl_ =  nullptr;
+static keymaster::AndroidKeymaster* impl_ = nullptr;
 
 extern "C" {
 int __android_log_print();
@@ -65,8 +65,8 @@
 
 template <typename Req, typename Rsp>
 static int fake_call(keymaster::AndroidKeymaster* device,
-                       void (keymaster::AndroidKeymaster::*method)(const Req&, Rsp*), void* in_buf,
-                       uint32_t in_size, void* out_buf, uint32_t* out_size) {
+                     void (keymaster::AndroidKeymaster::*method)(const Req&, Rsp*), void* in_buf,
+                     uint32_t in_size, void* out_buf, uint32_t* out_size) {
     Req req;
     const uint8_t* in = static_cast<uint8_t*>(in_buf);
     req.Deserialize(&in, in + in_size);
@@ -80,29 +80,28 @@
 }
 
 int trusty_keymaster_call(uint32_t cmd, void* in_buf, uint32_t in_size, void* out_buf,
-                       uint32_t* out_size) {
+                          uint32_t* out_size) {
     switch (cmd) {
-    case KM_GENERATE_KEY:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::GenerateKey, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_BEGIN_OPERATION:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::BeginOperation, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_UPDATE_OPERATION:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::UpdateOperation, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_FINISH_OPERATION:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::FinishOperation, in_buf, in_size,
-                           out_buf, out_size);
-    case KM_IMPORT_KEY:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::ImportKey, in_buf, in_size, out_buf,
-                           out_size);
-    case KM_EXPORT_KEY:
-        return fake_call(impl_, &keymaster::AndroidKeymaster::ExportKey, in_buf, in_size, out_buf,
-                           out_size);
+        case KM_GENERATE_KEY:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::GenerateKey, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_BEGIN_OPERATION:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::BeginOperation, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_UPDATE_OPERATION:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::UpdateOperation, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_FINISH_OPERATION:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::FinishOperation, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_IMPORT_KEY:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::ImportKey, in_buf, in_size,
+                             out_buf, out_size);
+        case KM_EXPORT_KEY:
+            return fake_call(impl_, &keymaster::AndroidKeymaster::ExportKey, in_buf, in_size,
+                             out_buf, out_size);
     }
     return -EINVAL;
-
 }
 
 namespace keymaster {
@@ -127,15 +126,15 @@
 
     size_t dsa_message_len(const keymaster_dsa_keygen_params_t& params) {
         switch (params.key_size) {
-        case 256:
-        case 1024:
-            return 48;
-        case 2048:
-        case 4096:
-            return 72;
-        default:
-            // Oops.
-            return 0;
+            case 256:
+            case 1024:
+                return 48;
+            case 2048:
+            case 4096:
+                return 72;
+            default:
+                // Oops.
+                return 0;
         }
     }
 
@@ -323,9 +322,9 @@
 
     Malloc_Delete sig_deleter(signature);
     signature[siglen / 2]++;
-    EXPECT_EQ(
-        KM_ERROR_VERIFICATION_FAILED,
-        device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED,
+              device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature,
+                                 siglen));
 }
 
 TEST_F(VerificationTest, RsaBadMessage) {
@@ -345,9 +344,9 @@
                                             &signature, &siglen));
     Malloc_Delete sig_deleter(signature);
     message[0]++;
-    EXPECT_EQ(
-        KM_ERROR_VERIFICATION_FAILED,
-        device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature, siglen));
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED,
+              device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature,
+                                 siglen));
 }
 
 TEST_F(VerificationTest, RsaShortMessage) {
diff --git a/trusty/keymaster/legacy/trusty_keymaster_main.cpp b/trusty/keymaster/legacy/trusty_keymaster_main.cpp
new file mode 100644
index 0000000..e3e70e6
--- /dev/null
+++ b/trusty/keymaster/legacy/trusty_keymaster_main.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keymaster/keymaster_configuration.h>
+
+#include <stdio.h>
+#include <memory>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+
+using keymaster::TrustyKeymasterDevice;
+
+unsigned char rsa_privkey_pk8_der[] = {
+        0x30, 0x82, 0x02, 0x75, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+        0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5f, 0x30, 0x82, 0x02, 0x5b,
+        0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xc6, 0x09, 0x54, 0x09, 0x04, 0x7d, 0x86, 0x34,
+        0x81, 0x2d, 0x5a, 0x21, 0x81, 0x76, 0xe4, 0x5c, 0x41, 0xd6, 0x0a, 0x75, 0xb1, 0x39, 0x01,
+        0xf2, 0x34, 0x22, 0x6c, 0xff, 0xe7, 0x76, 0x52, 0x1c, 0x5a, 0x77, 0xb9, 0xe3, 0x89, 0x41,
+        0x7b, 0x71, 0xc0, 0xb6, 0xa4, 0x4d, 0x13, 0xaf, 0xe4, 0xe4, 0xa2, 0x80, 0x5d, 0x46, 0xc9,
+        0xda, 0x29, 0x35, 0xad, 0xb1, 0xff, 0x0c, 0x1f, 0x24, 0xea, 0x06, 0xe6, 0x2b, 0x20, 0xd7,
+        0x76, 0x43, 0x0a, 0x4d, 0x43, 0x51, 0x57, 0x23, 0x3c, 0x6f, 0x91, 0x67, 0x83, 0xc3, 0x0e,
+        0x31, 0x0f, 0xcb, 0xd8, 0x9b, 0x85, 0xc2, 0xd5, 0x67, 0x71, 0x16, 0x97, 0x85, 0xac, 0x12,
+        0xbc, 0xa2, 0x44, 0xab, 0xda, 0x72, 0xbf, 0xb1, 0x9f, 0xc4, 0x4d, 0x27, 0xc8, 0x1e, 0x1d,
+        0x92, 0xde, 0x28, 0x4f, 0x40, 0x61, 0xed, 0xfd, 0x99, 0x28, 0x07, 0x45, 0xea, 0x6d, 0x25,
+        0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x1b, 0xe0, 0xf0, 0x4d, 0x9c, 0xae, 0x37,
+        0x18, 0x69, 0x1f, 0x03, 0x53, 0x38, 0x30, 0x8e, 0x91, 0x56, 0x4b, 0x55, 0x89, 0x9f, 0xfb,
+        0x50, 0x84, 0xd2, 0x46, 0x0e, 0x66, 0x30, 0x25, 0x7e, 0x05, 0xb3, 0xce, 0xab, 0x02, 0x97,
+        0x2d, 0xfa, 0xbc, 0xd6, 0xce, 0x5f, 0x6e, 0xe2, 0x58, 0x9e, 0xb6, 0x79, 0x11, 0xed, 0x0f,
+        0xac, 0x16, 0xe4, 0x3a, 0x44, 0x4b, 0x8c, 0x86, 0x1e, 0x54, 0x4a, 0x05, 0x93, 0x36, 0x57,
+        0x72, 0xf8, 0xba, 0xf6, 0xb2, 0x2f, 0xc9, 0xe3, 0xc5, 0xf1, 0x02, 0x4b, 0x06, 0x3a, 0xc0,
+        0x80, 0xa7, 0xb2, 0x23, 0x4c, 0xf8, 0xae, 0xe8, 0xf6, 0xc4, 0x7b, 0xbf, 0x4f, 0xd3, 0xac,
+        0xe7, 0x24, 0x02, 0x90, 0xbe, 0xf1, 0x6c, 0x0b, 0x3f, 0x7f, 0x3c, 0xdd, 0x64, 0xce, 0x3a,
+        0xb5, 0x91, 0x2c, 0xf6, 0xe3, 0x2f, 0x39, 0xab, 0x18, 0x83, 0x58, 0xaf, 0xcc, 0xcd, 0x80,
+        0x81, 0x02, 0x41, 0x00, 0xe4, 0xb4, 0x9e, 0xf5, 0x0f, 0x76, 0x5d, 0x3b, 0x24, 0xdd, 0xe0,
+        0x1a, 0xce, 0xaa, 0xf1, 0x30, 0xf2, 0xc7, 0x66, 0x70, 0xa9, 0x1a, 0x61, 0xae, 0x08, 0xaf,
+        0x49, 0x7b, 0x4a, 0x82, 0xbe, 0x6d, 0xee, 0x8f, 0xcd, 0xd5, 0xe3, 0xf7, 0xba, 0x1c, 0xfb,
+        0x1f, 0x0c, 0x92, 0x6b, 0x88, 0xf8, 0x8c, 0x92, 0xbf, 0xab, 0x13, 0x7f, 0xba, 0x22, 0x85,
+        0x22, 0x7b, 0x83, 0xc3, 0x42, 0xff, 0x7c, 0x55, 0x02, 0x41, 0x00, 0xdd, 0xab, 0xb5, 0x83,
+        0x9c, 0x4c, 0x7f, 0x6b, 0xf3, 0xd4, 0x18, 0x32, 0x31, 0xf0, 0x05, 0xb3, 0x1a, 0xa5, 0x8a,
+        0xff, 0xdd, 0xa5, 0xc7, 0x9e, 0x4c, 0xce, 0x21, 0x7f, 0x6b, 0xc9, 0x30, 0xdb, 0xe5, 0x63,
+        0xd4, 0x80, 0x70, 0x6c, 0x24, 0xe9, 0xeb, 0xfc, 0xab, 0x28, 0xa6, 0xcd, 0xef, 0xd3, 0x24,
+        0xb7, 0x7e, 0x1b, 0xf7, 0x25, 0x1b, 0x70, 0x90, 0x92, 0xc2, 0x4f, 0xf5, 0x01, 0xfd, 0x91,
+        0x02, 0x40, 0x23, 0xd4, 0x34, 0x0e, 0xda, 0x34, 0x45, 0xd8, 0xcd, 0x26, 0xc1, 0x44, 0x11,
+        0xda, 0x6f, 0xdc, 0xa6, 0x3c, 0x1c, 0xcd, 0x4b, 0x80, 0xa9, 0x8a, 0xd5, 0x2b, 0x78, 0xcc,
+        0x8a, 0xd8, 0xbe, 0xb2, 0x84, 0x2c, 0x1d, 0x28, 0x04, 0x05, 0xbc, 0x2f, 0x6c, 0x1b, 0xea,
+        0x21, 0x4a, 0x1d, 0x74, 0x2a, 0xb9, 0x96, 0xb3, 0x5b, 0x63, 0xa8, 0x2a, 0x5e, 0x47, 0x0f,
+        0xa8, 0x8d, 0xbf, 0x82, 0x3c, 0xdd, 0x02, 0x40, 0x1b, 0x7b, 0x57, 0x44, 0x9a, 0xd3, 0x0d,
+        0x15, 0x18, 0x24, 0x9a, 0x5f, 0x56, 0xbb, 0x98, 0x29, 0x4d, 0x4b, 0x6a, 0xc1, 0x2f, 0xfc,
+        0x86, 0x94, 0x04, 0x97, 0xa5, 0xa5, 0x83, 0x7a, 0x6c, 0xf9, 0x46, 0x26, 0x2b, 0x49, 0x45,
+        0x26, 0xd3, 0x28, 0xc1, 0x1e, 0x11, 0x26, 0x38, 0x0f, 0xde, 0x04, 0xc2, 0x4f, 0x91, 0x6d,
+        0xec, 0x25, 0x08, 0x92, 0xdb, 0x09, 0xa6, 0xd7, 0x7c, 0xdb, 0xa3, 0x51, 0x02, 0x40, 0x77,
+        0x62, 0xcd, 0x8f, 0x4d, 0x05, 0x0d, 0xa5, 0x6b, 0xd5, 0x91, 0xad, 0xb5, 0x15, 0xd2, 0x4d,
+        0x7c, 0xcd, 0x32, 0xcc, 0xa0, 0xd0, 0x5f, 0x86, 0x6d, 0x58, 0x35, 0x14, 0xbd, 0x73, 0x24,
+        0xd5, 0xf3, 0x36, 0x45, 0xe8, 0xed, 0x8b, 0x4a, 0x1c, 0xb3, 0xcc, 0x4a, 0x1d, 0x67, 0x98,
+        0x73, 0x99, 0xf2, 0xa0, 0x9f, 0x5b, 0x3f, 0xb6, 0x8c, 0x88, 0xd5, 0xe5, 0xd9, 0x0a, 0xc3,
+        0x34, 0x92, 0xd6};
+unsigned int rsa_privkey_pk8_der_len = 633;
+
+unsigned char dsa_privkey_pk8_der[] = {
+        0x30, 0x82, 0x01, 0x4b, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x2b, 0x06, 0x07, 0x2a, 0x86,
+        0x48, 0xce, 0x38, 0x04, 0x01, 0x30, 0x82, 0x01, 0x1e, 0x02, 0x81, 0x81, 0x00, 0xa3, 0xf3,
+        0xe9, 0xb6, 0x7e, 0x7d, 0x88, 0xf6, 0xb7, 0xe5, 0xf5, 0x1f, 0x3b, 0xee, 0xac, 0xd7, 0xad,
+        0xbc, 0xc9, 0xd1, 0x5a, 0xf8, 0x88, 0xc4, 0xef, 0x6e, 0x3d, 0x74, 0x19, 0x74, 0xe7, 0xd8,
+        0xe0, 0x26, 0x44, 0x19, 0x86, 0xaf, 0x19, 0xdb, 0x05, 0xe9, 0x3b, 0x8b, 0x58, 0x58, 0xde,
+        0xe5, 0x4f, 0x48, 0x15, 0x01, 0xea, 0xe6, 0x83, 0x52, 0xd7, 0xc1, 0x21, 0xdf, 0xb9, 0xb8,
+        0x07, 0x66, 0x50, 0xfb, 0x3a, 0x0c, 0xb3, 0x85, 0xee, 0xbb, 0x04, 0x5f, 0xc2, 0x6d, 0x6d,
+        0x95, 0xfa, 0x11, 0x93, 0x1e, 0x59, 0x5b, 0xb1, 0x45, 0x8d, 0xe0, 0x3d, 0x73, 0xaa, 0xf2,
+        0x41, 0x14, 0x51, 0x07, 0x72, 0x3d, 0xa2, 0xf7, 0x58, 0xcd, 0x11, 0xa1, 0x32, 0xcf, 0xda,
+        0x42, 0xb7, 0xcc, 0x32, 0x80, 0xdb, 0x87, 0x82, 0xec, 0x42, 0xdb, 0x5a, 0x55, 0x24, 0x24,
+        0xa2, 0xd1, 0x55, 0x29, 0xad, 0xeb, 0x02, 0x15, 0x00, 0xeb, 0xea, 0x17, 0xd2, 0x09, 0xb3,
+        0xd7, 0x21, 0x9a, 0x21, 0x07, 0x82, 0x8f, 0xab, 0xfe, 0x88, 0x71, 0x68, 0xf7, 0xe3, 0x02,
+        0x81, 0x80, 0x19, 0x1c, 0x71, 0xfd, 0xe0, 0x03, 0x0c, 0x43, 0xd9, 0x0b, 0xf6, 0xcd, 0xd6,
+        0xa9, 0x70, 0xe7, 0x37, 0x86, 0x3a, 0x78, 0xe9, 0xa7, 0x47, 0xa7, 0x47, 0x06, 0x88, 0xb1,
+        0xaf, 0xd7, 0xf3, 0xf1, 0xa1, 0xd7, 0x00, 0x61, 0x28, 0x88, 0x31, 0x48, 0x60, 0xd8, 0x11,
+        0xef, 0xa5, 0x24, 0x1a, 0x81, 0xc4, 0x2a, 0xe2, 0xea, 0x0e, 0x36, 0xd2, 0xd2, 0x05, 0x84,
+        0x37, 0xcf, 0x32, 0x7d, 0x09, 0xe6, 0x0f, 0x8b, 0x0c, 0xc8, 0xc2, 0xa4, 0xb1, 0xdc, 0x80,
+        0xca, 0x68, 0xdf, 0xaf, 0xd2, 0x90, 0xc0, 0x37, 0x58, 0x54, 0x36, 0x8f, 0x49, 0xb8, 0x62,
+        0x75, 0x8b, 0x48, 0x47, 0xc0, 0xbe, 0xf7, 0x9a, 0x92, 0xa6, 0x68, 0x05, 0xda, 0x9d, 0xaf,
+        0x72, 0x9a, 0x67, 0xb3, 0xb4, 0x14, 0x03, 0xae, 0x4f, 0x4c, 0x76, 0xb9, 0xd8, 0x64, 0x0a,
+        0xba, 0x3b, 0xa8, 0x00, 0x60, 0x4d, 0xae, 0x81, 0xc3, 0xc5, 0x04, 0x17, 0x02, 0x15, 0x00,
+        0x81, 0x9d, 0xfd, 0x53, 0x0c, 0xc1, 0x8f, 0xbe, 0x8b, 0xea, 0x00, 0x26, 0x19, 0x29, 0x33,
+        0x91, 0x84, 0xbe, 0xad, 0x81};
+unsigned int dsa_privkey_pk8_der_len = 335;
+
+unsigned char ec_privkey_pk8_der[] = {
+        0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
+        0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04,
+        0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0x73, 0x7c, 0x2e, 0xcd, 0x7b, 0x8d,
+        0x19, 0x40, 0xbf, 0x29, 0x30, 0xaa, 0x9b, 0x4e, 0xd3, 0xff, 0x94, 0x1e, 0xed, 0x09,
+        0x36, 0x6b, 0xc0, 0x32, 0x99, 0x98, 0x64, 0x81, 0xf3, 0xa4, 0xd8, 0x59, 0xa1, 0x44,
+        0x03, 0x42, 0x00, 0x04, 0xbf, 0x85, 0xd7, 0x72, 0x0d, 0x07, 0xc2, 0x54, 0x61, 0x68,
+        0x3b, 0xc6, 0x48, 0xb4, 0x77, 0x8a, 0x9a, 0x14, 0xdd, 0x8a, 0x02, 0x4e, 0x3b, 0xdd,
+        0x8c, 0x7d, 0xdd, 0x9a, 0xb2, 0xb5, 0x28, 0xbb, 0xc7, 0xaa, 0x1b, 0x51, 0xf1, 0x4e,
+        0xbb, 0xbb, 0x0b, 0xd0, 0xce, 0x21, 0xbc, 0xc4, 0x1c, 0x6e, 0xb0, 0x00, 0x83, 0xcf,
+        0x33, 0x76, 0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
+unsigned int ec_privkey_pk8_der_len = 138;
+
+keymaster_key_param_t ec_params[] = {
+        keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
+        keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
+
+keymaster_key_param_t rsa_params[] = {
+        keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+        keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
+        keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+        keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
+
+struct EVP_PKEY_Delete {
+    void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct EVP_PKEY_CTX_Delete {
+    void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
+                         keymaster_key_blob_t* key, keymaster_blob_t* input,
+                         keymaster_blob_t* signature, keymaster_blob_t* output) {
+    keymaster_key_param_t params[] = {
+            keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+            keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    };
+    keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
+    keymaster_operation_handle_t op_handle;
+    keymaster_error_t error = device->begin(purpose, key, &param_set, nullptr, &op_handle);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster begin() failed: %d\n", error);
+        return false;
+    }
+    size_t input_consumed;
+    error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster update() failed: %d\n", error);
+        return false;
+    }
+    if (input_consumed != input->data_length) {
+        // This should never happen. If it does, it's a bug in the keymaster implementation.
+        printf("Keymaster update() did not consume all data.\n");
+        device->abort(op_handle);
+        return false;
+    }
+    error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster finish() failed: %d\n", error);
+        return false;
+    }
+    return true;
+}
+
+static bool test_import_rsa(TrustyKeymasterDevice* device) {
+    printf("===================\n");
+    printf("= RSA Import Test =\n");
+    printf("===================\n\n");
+
+    printf("=== Importing RSA keypair === \n");
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
+    int error =
+            device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error importing RSA key: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+    printf("=== Signing with imported RSA key ===\n");
+    size_t message_len = 1024 / 8;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported RSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with imported RSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported RSA key\n\n");
+        return false;
+    }
+
+    printf("\n");
+    return true;
+}
+
+static bool test_rsa(TrustyKeymasterDevice* device) {
+    printf("============\n");
+    printf("= RSA Test =\n");
+    printf("============\n\n");
+
+    printf("=== Generating RSA key pair ===\n");
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&rsa_param_set, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error generating RSA key pair: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+    printf("=== Signing with RSA key === \n");
+    size_t message_len = 1024 / 8;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with RSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with RSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with RSA key\n\n");
+        return false;
+    }
+
+    printf("=== Exporting RSA public key ===\n");
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
+    if (error != KM_ERROR_OK) {
+        printf("Error exporting RSA public key: %d\n\n", error);
+        return false;
+    }
+
+    printf("=== Verifying with exported key ===\n");
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+            d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+        printf("Error initializing openss EVP context\n\n");
+        return false;
+    }
+    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+        printf("Exported key was the wrong type?!?\n\n");
+        return false;
+    }
+
+    EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
+        return false;
+    } else {
+        printf("Verification succeeded\n");
+    }
+
+    printf("\n");
+    return true;
+}
+
+static bool test_import_ecdsa(TrustyKeymasterDevice* device) {
+    printf("=====================\n");
+    printf("= ECDSA Import Test =\n");
+    printf("=====================\n\n");
+
+    printf("=== Importing ECDSA keypair === \n");
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
+    int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error importing ECDSA key: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> deleter(key.key_material);
+
+    printf("=== Signing with imported ECDSA key ===\n");
+    size_t message_len = 30 /* arbitrary */;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported ECDSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with imported ECDSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported ECDSA key\n\n");
+        return false;
+    }
+
+    printf("\n");
+    return true;
+}
+
+static bool test_ecdsa(TrustyKeymasterDevice* device) {
+    printf("==============\n");
+    printf("= ECDSA Test =\n");
+    printf("==============\n\n");
+
+    printf("=== Generating ECDSA key pair ===\n");
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&ec_param_set, &key, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Error generating ECDSA key pair: %d\n\n", error);
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+    printf("=== Signing with ECDSA key === \n");
+    size_t message_len = 30 /* arbitrary */;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+    memset(message.get(), 'a', message_len);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with ECDSA key\n\n");
+        return false;
+    }
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+    printf("=== Verifying with ECDSA key === \n");
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with ECDSA key\n\n");
+        return false;
+    }
+
+    printf("=== Exporting ECDSA public key ===\n");
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
+    if (error != KM_ERROR_OK) {
+        printf("Error exporting ECDSA public key: %d\n\n", error);
+        return false;
+    }
+
+    printf("=== Verifying with exported key ===\n");
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+            d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+        printf("Error initializing openssl EVP context\n\n");
+        return false;
+    }
+    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
+        printf("Exported key was the wrong type?!?\n\n");
+        return false;
+    }
+
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
+        return false;
+    } else {
+        printf("Verification succeeded\n");
+    }
+
+    printf("\n");
+    return true;
+}
+
+int main(void) {
+    TrustyKeymasterDevice device(NULL);
+    keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
+    if (device.session_error() != KM_ERROR_OK) {
+        printf("Failed to initialize Trusty session: %d\n", device.session_error());
+        return 1;
+    }
+    printf("Trusty session initialized\n");
+
+    bool success = true;
+    success &= test_rsa(&device);
+    success &= test_import_rsa(&device);
+    success &= test_ecdsa(&device);
+    success &= test_import_ecdsa(&device);
+
+    if (success) {
+        printf("\nTESTS PASSED!\n");
+    } else {
+        printf("\n!!!!TESTS FAILED!!!\n");
+    }
+
+    return success ? 0 : 1;
+}
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
deleted file mode 100644
index 686e7ae..0000000
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "TrustyKeymaster"
-
-// TODO: make this generic in libtrusty
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
-#include <unistd.h>
-
-#include <algorithm>
-
-#include <log/log.h>
-#include <trusty/tipc.h>
-
-#include "keymaster_ipc.h"
-#include "trusty_keymaster_ipc.h"
-
-#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
-
-static int handle_ = -1;
-
-int trusty_keymaster_connect() {
-    int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
-    if (rc < 0) {
-        return rc;
-    }
-
-    handle_ = rc;
-    return 0;
-}
-
-int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
-                          uint32_t* out_size) {
-    if (handle_ < 0) {
-        ALOGE("not connected\n");
-        return -EINVAL;
-    }
-
-    size_t msg_size = in_size + sizeof(struct keymaster_message);
-    struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
-    if (!msg) {
-        ALOGE("failed to allocate msg buffer\n");
-        return -EINVAL;
-    }
-
-    msg->cmd = cmd;
-    memcpy(msg->payload, in, in_size);
-
-    ssize_t rc = write(handle_, msg, msg_size);
-    free(msg);
-
-    if (rc < 0) {
-        ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
-        return -errno;
-    }
-    size_t out_max_size = *out_size;
-    *out_size = 0;
-    struct iovec iov[2];
-    struct keymaster_message header;
-    iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
-    while (true) {
-        iov[1] = {
-            .iov_base = out + *out_size,
-            .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, out_max_size - *out_size)};
-        rc = readv(handle_, iov, 2);
-        if (rc < 0) {
-            ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
-                  strerror(errno));
-            return -errno;
-        }
-
-        if ((size_t)rc < sizeof(struct keymaster_message)) {
-            ALOGE("invalid response size (%d)\n", (int)rc);
-            return -EINVAL;
-        }
-
-        if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
-            ALOGE("invalid command (%d)", header.cmd);
-            return -EINVAL;
-        }
-        *out_size += ((size_t)rc - sizeof(struct keymaster_message));
-        if (header.cmd & KEYMASTER_STOP_BIT) {
-            break;
-        }
-    }
-
-    return rc;
-}
-
-void trusty_keymaster_disconnect() {
-    if (handle_ >= 0) {
-        tipc_close(handle_);
-    }
-    handle_ = -1;
-}
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
deleted file mode 100644
index ed78b7f..0000000
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <keymaster/keymaster_configuration.h>
-
-#include <stdio.h>
-#include <memory>
-
-#include <openssl/evp.h>
-#include <openssl/x509.h>
-
-#include "trusty_keymaster_device.h"
-
-using keymaster::TrustyKeymasterDevice;
-
-unsigned char rsa_privkey_pk8_der[] = {
-    0x30, 0x82, 0x02, 0x75, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5f, 0x30, 0x82, 0x02, 0x5b, 0x02, 0x01,
-    0x00, 0x02, 0x81, 0x81, 0x00, 0xc6, 0x09, 0x54, 0x09, 0x04, 0x7d, 0x86, 0x34, 0x81, 0x2d, 0x5a,
-    0x21, 0x81, 0x76, 0xe4, 0x5c, 0x41, 0xd6, 0x0a, 0x75, 0xb1, 0x39, 0x01, 0xf2, 0x34, 0x22, 0x6c,
-    0xff, 0xe7, 0x76, 0x52, 0x1c, 0x5a, 0x77, 0xb9, 0xe3, 0x89, 0x41, 0x7b, 0x71, 0xc0, 0xb6, 0xa4,
-    0x4d, 0x13, 0xaf, 0xe4, 0xe4, 0xa2, 0x80, 0x5d, 0x46, 0xc9, 0xda, 0x29, 0x35, 0xad, 0xb1, 0xff,
-    0x0c, 0x1f, 0x24, 0xea, 0x06, 0xe6, 0x2b, 0x20, 0xd7, 0x76, 0x43, 0x0a, 0x4d, 0x43, 0x51, 0x57,
-    0x23, 0x3c, 0x6f, 0x91, 0x67, 0x83, 0xc3, 0x0e, 0x31, 0x0f, 0xcb, 0xd8, 0x9b, 0x85, 0xc2, 0xd5,
-    0x67, 0x71, 0x16, 0x97, 0x85, 0xac, 0x12, 0xbc, 0xa2, 0x44, 0xab, 0xda, 0x72, 0xbf, 0xb1, 0x9f,
-    0xc4, 0x4d, 0x27, 0xc8, 0x1e, 0x1d, 0x92, 0xde, 0x28, 0x4f, 0x40, 0x61, 0xed, 0xfd, 0x99, 0x28,
-    0x07, 0x45, 0xea, 0x6d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x1b, 0xe0, 0xf0,
-    0x4d, 0x9c, 0xae, 0x37, 0x18, 0x69, 0x1f, 0x03, 0x53, 0x38, 0x30, 0x8e, 0x91, 0x56, 0x4b, 0x55,
-    0x89, 0x9f, 0xfb, 0x50, 0x84, 0xd2, 0x46, 0x0e, 0x66, 0x30, 0x25, 0x7e, 0x05, 0xb3, 0xce, 0xab,
-    0x02, 0x97, 0x2d, 0xfa, 0xbc, 0xd6, 0xce, 0x5f, 0x6e, 0xe2, 0x58, 0x9e, 0xb6, 0x79, 0x11, 0xed,
-    0x0f, 0xac, 0x16, 0xe4, 0x3a, 0x44, 0x4b, 0x8c, 0x86, 0x1e, 0x54, 0x4a, 0x05, 0x93, 0x36, 0x57,
-    0x72, 0xf8, 0xba, 0xf6, 0xb2, 0x2f, 0xc9, 0xe3, 0xc5, 0xf1, 0x02, 0x4b, 0x06, 0x3a, 0xc0, 0x80,
-    0xa7, 0xb2, 0x23, 0x4c, 0xf8, 0xae, 0xe8, 0xf6, 0xc4, 0x7b, 0xbf, 0x4f, 0xd3, 0xac, 0xe7, 0x24,
-    0x02, 0x90, 0xbe, 0xf1, 0x6c, 0x0b, 0x3f, 0x7f, 0x3c, 0xdd, 0x64, 0xce, 0x3a, 0xb5, 0x91, 0x2c,
-    0xf6, 0xe3, 0x2f, 0x39, 0xab, 0x18, 0x83, 0x58, 0xaf, 0xcc, 0xcd, 0x80, 0x81, 0x02, 0x41, 0x00,
-    0xe4, 0xb4, 0x9e, 0xf5, 0x0f, 0x76, 0x5d, 0x3b, 0x24, 0xdd, 0xe0, 0x1a, 0xce, 0xaa, 0xf1, 0x30,
-    0xf2, 0xc7, 0x66, 0x70, 0xa9, 0x1a, 0x61, 0xae, 0x08, 0xaf, 0x49, 0x7b, 0x4a, 0x82, 0xbe, 0x6d,
-    0xee, 0x8f, 0xcd, 0xd5, 0xe3, 0xf7, 0xba, 0x1c, 0xfb, 0x1f, 0x0c, 0x92, 0x6b, 0x88, 0xf8, 0x8c,
-    0x92, 0xbf, 0xab, 0x13, 0x7f, 0xba, 0x22, 0x85, 0x22, 0x7b, 0x83, 0xc3, 0x42, 0xff, 0x7c, 0x55,
-    0x02, 0x41, 0x00, 0xdd, 0xab, 0xb5, 0x83, 0x9c, 0x4c, 0x7f, 0x6b, 0xf3, 0xd4, 0x18, 0x32, 0x31,
-    0xf0, 0x05, 0xb3, 0x1a, 0xa5, 0x8a, 0xff, 0xdd, 0xa5, 0xc7, 0x9e, 0x4c, 0xce, 0x21, 0x7f, 0x6b,
-    0xc9, 0x30, 0xdb, 0xe5, 0x63, 0xd4, 0x80, 0x70, 0x6c, 0x24, 0xe9, 0xeb, 0xfc, 0xab, 0x28, 0xa6,
-    0xcd, 0xef, 0xd3, 0x24, 0xb7, 0x7e, 0x1b, 0xf7, 0x25, 0x1b, 0x70, 0x90, 0x92, 0xc2, 0x4f, 0xf5,
-    0x01, 0xfd, 0x91, 0x02, 0x40, 0x23, 0xd4, 0x34, 0x0e, 0xda, 0x34, 0x45, 0xd8, 0xcd, 0x26, 0xc1,
-    0x44, 0x11, 0xda, 0x6f, 0xdc, 0xa6, 0x3c, 0x1c, 0xcd, 0x4b, 0x80, 0xa9, 0x8a, 0xd5, 0x2b, 0x78,
-    0xcc, 0x8a, 0xd8, 0xbe, 0xb2, 0x84, 0x2c, 0x1d, 0x28, 0x04, 0x05, 0xbc, 0x2f, 0x6c, 0x1b, 0xea,
-    0x21, 0x4a, 0x1d, 0x74, 0x2a, 0xb9, 0x96, 0xb3, 0x5b, 0x63, 0xa8, 0x2a, 0x5e, 0x47, 0x0f, 0xa8,
-    0x8d, 0xbf, 0x82, 0x3c, 0xdd, 0x02, 0x40, 0x1b, 0x7b, 0x57, 0x44, 0x9a, 0xd3, 0x0d, 0x15, 0x18,
-    0x24, 0x9a, 0x5f, 0x56, 0xbb, 0x98, 0x29, 0x4d, 0x4b, 0x6a, 0xc1, 0x2f, 0xfc, 0x86, 0x94, 0x04,
-    0x97, 0xa5, 0xa5, 0x83, 0x7a, 0x6c, 0xf9, 0x46, 0x26, 0x2b, 0x49, 0x45, 0x26, 0xd3, 0x28, 0xc1,
-    0x1e, 0x11, 0x26, 0x38, 0x0f, 0xde, 0x04, 0xc2, 0x4f, 0x91, 0x6d, 0xec, 0x25, 0x08, 0x92, 0xdb,
-    0x09, 0xa6, 0xd7, 0x7c, 0xdb, 0xa3, 0x51, 0x02, 0x40, 0x77, 0x62, 0xcd, 0x8f, 0x4d, 0x05, 0x0d,
-    0xa5, 0x6b, 0xd5, 0x91, 0xad, 0xb5, 0x15, 0xd2, 0x4d, 0x7c, 0xcd, 0x32, 0xcc, 0xa0, 0xd0, 0x5f,
-    0x86, 0x6d, 0x58, 0x35, 0x14, 0xbd, 0x73, 0x24, 0xd5, 0xf3, 0x36, 0x45, 0xe8, 0xed, 0x8b, 0x4a,
-    0x1c, 0xb3, 0xcc, 0x4a, 0x1d, 0x67, 0x98, 0x73, 0x99, 0xf2, 0xa0, 0x9f, 0x5b, 0x3f, 0xb6, 0x8c,
-    0x88, 0xd5, 0xe5, 0xd9, 0x0a, 0xc3, 0x34, 0x92, 0xd6};
-unsigned int rsa_privkey_pk8_der_len = 633;
-
-unsigned char dsa_privkey_pk8_der[] = {
-    0x30, 0x82, 0x01, 0x4b, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x2b, 0x06, 0x07, 0x2a, 0x86, 0x48,
-    0xce, 0x38, 0x04, 0x01, 0x30, 0x82, 0x01, 0x1e, 0x02, 0x81, 0x81, 0x00, 0xa3, 0xf3, 0xe9, 0xb6,
-    0x7e, 0x7d, 0x88, 0xf6, 0xb7, 0xe5, 0xf5, 0x1f, 0x3b, 0xee, 0xac, 0xd7, 0xad, 0xbc, 0xc9, 0xd1,
-    0x5a, 0xf8, 0x88, 0xc4, 0xef, 0x6e, 0x3d, 0x74, 0x19, 0x74, 0xe7, 0xd8, 0xe0, 0x26, 0x44, 0x19,
-    0x86, 0xaf, 0x19, 0xdb, 0x05, 0xe9, 0x3b, 0x8b, 0x58, 0x58, 0xde, 0xe5, 0x4f, 0x48, 0x15, 0x01,
-    0xea, 0xe6, 0x83, 0x52, 0xd7, 0xc1, 0x21, 0xdf, 0xb9, 0xb8, 0x07, 0x66, 0x50, 0xfb, 0x3a, 0x0c,
-    0xb3, 0x85, 0xee, 0xbb, 0x04, 0x5f, 0xc2, 0x6d, 0x6d, 0x95, 0xfa, 0x11, 0x93, 0x1e, 0x59, 0x5b,
-    0xb1, 0x45, 0x8d, 0xe0, 0x3d, 0x73, 0xaa, 0xf2, 0x41, 0x14, 0x51, 0x07, 0x72, 0x3d, 0xa2, 0xf7,
-    0x58, 0xcd, 0x11, 0xa1, 0x32, 0xcf, 0xda, 0x42, 0xb7, 0xcc, 0x32, 0x80, 0xdb, 0x87, 0x82, 0xec,
-    0x42, 0xdb, 0x5a, 0x55, 0x24, 0x24, 0xa2, 0xd1, 0x55, 0x29, 0xad, 0xeb, 0x02, 0x15, 0x00, 0xeb,
-    0xea, 0x17, 0xd2, 0x09, 0xb3, 0xd7, 0x21, 0x9a, 0x21, 0x07, 0x82, 0x8f, 0xab, 0xfe, 0x88, 0x71,
-    0x68, 0xf7, 0xe3, 0x02, 0x81, 0x80, 0x19, 0x1c, 0x71, 0xfd, 0xe0, 0x03, 0x0c, 0x43, 0xd9, 0x0b,
-    0xf6, 0xcd, 0xd6, 0xa9, 0x70, 0xe7, 0x37, 0x86, 0x3a, 0x78, 0xe9, 0xa7, 0x47, 0xa7, 0x47, 0x06,
-    0x88, 0xb1, 0xaf, 0xd7, 0xf3, 0xf1, 0xa1, 0xd7, 0x00, 0x61, 0x28, 0x88, 0x31, 0x48, 0x60, 0xd8,
-    0x11, 0xef, 0xa5, 0x24, 0x1a, 0x81, 0xc4, 0x2a, 0xe2, 0xea, 0x0e, 0x36, 0xd2, 0xd2, 0x05, 0x84,
-    0x37, 0xcf, 0x32, 0x7d, 0x09, 0xe6, 0x0f, 0x8b, 0x0c, 0xc8, 0xc2, 0xa4, 0xb1, 0xdc, 0x80, 0xca,
-    0x68, 0xdf, 0xaf, 0xd2, 0x90, 0xc0, 0x37, 0x58, 0x54, 0x36, 0x8f, 0x49, 0xb8, 0x62, 0x75, 0x8b,
-    0x48, 0x47, 0xc0, 0xbe, 0xf7, 0x9a, 0x92, 0xa6, 0x68, 0x05, 0xda, 0x9d, 0xaf, 0x72, 0x9a, 0x67,
-    0xb3, 0xb4, 0x14, 0x03, 0xae, 0x4f, 0x4c, 0x76, 0xb9, 0xd8, 0x64, 0x0a, 0xba, 0x3b, 0xa8, 0x00,
-    0x60, 0x4d, 0xae, 0x81, 0xc3, 0xc5, 0x04, 0x17, 0x02, 0x15, 0x00, 0x81, 0x9d, 0xfd, 0x53, 0x0c,
-    0xc1, 0x8f, 0xbe, 0x8b, 0xea, 0x00, 0x26, 0x19, 0x29, 0x33, 0x91, 0x84, 0xbe, 0xad, 0x81};
-unsigned int dsa_privkey_pk8_der_len = 335;
-
-unsigned char ec_privkey_pk8_der[] = {
-    0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
-    0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02,
-    0x01, 0x01, 0x04, 0x20, 0x73, 0x7c, 0x2e, 0xcd, 0x7b, 0x8d, 0x19, 0x40, 0xbf, 0x29, 0x30, 0xaa,
-    0x9b, 0x4e, 0xd3, 0xff, 0x94, 0x1e, 0xed, 0x09, 0x36, 0x6b, 0xc0, 0x32, 0x99, 0x98, 0x64, 0x81,
-    0xf3, 0xa4, 0xd8, 0x59, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xbf, 0x85, 0xd7, 0x72, 0x0d, 0x07,
-    0xc2, 0x54, 0x61, 0x68, 0x3b, 0xc6, 0x48, 0xb4, 0x77, 0x8a, 0x9a, 0x14, 0xdd, 0x8a, 0x02, 0x4e,
-    0x3b, 0xdd, 0x8c, 0x7d, 0xdd, 0x9a, 0xb2, 0xb5, 0x28, 0xbb, 0xc7, 0xaa, 0x1b, 0x51, 0xf1, 0x4e,
-    0xbb, 0xbb, 0x0b, 0xd0, 0xce, 0x21, 0xbc, 0xc4, 0x1c, 0x6e, 0xb0, 0x00, 0x83, 0xcf, 0x33, 0x76,
-    0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
-unsigned int ec_privkey_pk8_der_len = 138;
-
-keymaster_key_param_t ec_params[] = {
-    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
-    keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
-    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
-    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
-};
-keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
-
-keymaster_key_param_t rsa_params[] = {
-    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
-    keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
-    keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
-    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
-    keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
-    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
-    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
-};
-keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
-
-struct EVP_PKEY_Delete {
-    void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
-};
-
-struct EVP_PKEY_CTX_Delete {
-    void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
-};
-
-static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
-                         keymaster_key_blob_t* key, keymaster_blob_t* input,
-                         keymaster_blob_t* signature, keymaster_blob_t* output) {
-    keymaster_key_param_t params[] = {
-        keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
-        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
-    };
-    keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
-    keymaster_operation_handle_t op_handle;
-    keymaster_error_t error = device->begin(purpose, key, &param_set, nullptr, &op_handle);
-    if (error != KM_ERROR_OK) {
-        printf("Keymaster begin() failed: %d\n", error);
-        return false;
-    }
-    size_t input_consumed;
-    error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Keymaster update() failed: %d\n", error);
-        return false;
-    }
-    if (input_consumed != input->data_length) {
-        // This should never happen. If it does, it's a bug in the keymaster implementation.
-        printf("Keymaster update() did not consume all data.\n");
-        device->abort(op_handle);
-        return false;
-    }
-    error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
-    if (error != KM_ERROR_OK) {
-        printf("Keymaster finish() failed: %d\n", error);
-        return false;
-    }
-    return true;
-}
-
-static bool test_import_rsa(TrustyKeymasterDevice* device) {
-    printf("===================\n");
-    printf("= RSA Import Test =\n");
-    printf("===================\n\n");
-
-    printf("=== Importing RSA keypair === \n");
-    keymaster_key_blob_t key;
-    keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
-    int error = device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error importing RSA key: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
-
-    printf("=== Signing with imported RSA key ===\n");
-    size_t message_len = 1024 / 8;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with imported RSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with imported RSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with imported RSA key\n\n");
-        return false;
-    }
-
-    printf("\n");
-    return true;
-}
-
-static bool test_rsa(TrustyKeymasterDevice* device) {
-    printf("============\n");
-    printf("= RSA Test =\n");
-    printf("============\n\n");
-
-    printf("=== Generating RSA key pair ===\n");
-    keymaster_key_blob_t key;
-    int error = device->generate_key(&rsa_param_set, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error generating RSA key pair: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
-
-    printf("=== Signing with RSA key === \n");
-    size_t message_len = 1024 / 8;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with RSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with RSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with RSA key\n\n");
-        return false;
-    }
-
-    printf("=== Exporting RSA public key ===\n");
-    keymaster_blob_t exported_key;
-    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
-    if (error != KM_ERROR_OK) {
-        printf("Error exporting RSA public key: %d\n\n", error);
-        return false;
-    }
-
-    printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key.data;
-    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
-        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
-    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
-    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openss EVP context\n\n");
-        return false;
-    }
-    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
-        printf("Exported key was the wrong type?!?\n\n");
-        return false;
-    }
-
-    EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
-    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
-                        message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n\n");
-        return false;
-    } else {
-        printf("Verification succeeded\n");
-    }
-
-    printf("\n");
-    return true;
-}
-
-static bool test_import_ecdsa(TrustyKeymasterDevice* device) {
-    printf("=====================\n");
-    printf("= ECDSA Import Test =\n");
-    printf("=====================\n\n");
-
-    printf("=== Importing ECDSA keypair === \n");
-    keymaster_key_blob_t key;
-    keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
-    int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error importing ECDSA key: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> deleter(key.key_material);
-
-    printf("=== Signing with imported ECDSA key ===\n");
-    size_t message_len = 30 /* arbitrary */;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with imported ECDSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with imported ECDSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with imported ECDSA key\n\n");
-        return false;
-    }
-
-    printf("\n");
-    return true;
-}
-
-static bool test_ecdsa(TrustyKeymasterDevice* device) {
-    printf("==============\n");
-    printf("= ECDSA Test =\n");
-    printf("==============\n\n");
-
-    printf("=== Generating ECDSA key pair ===\n");
-    keymaster_key_blob_t key;
-    int error = device->generate_key(&ec_param_set, &key, nullptr);
-    if (error != KM_ERROR_OK) {
-        printf("Error generating ECDSA key pair: %d\n\n", error);
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
-
-    printf("=== Signing with ECDSA key === \n");
-    size_t message_len = 30 /* arbitrary */;
-    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
-    memset(message.get(), 'a', message_len);
-    keymaster_blob_t input = {message.get(), message_len}, signature;
-
-    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
-        printf("Error signing data with ECDSA key\n\n");
-        return false;
-    }
-    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
-
-    printf("=== Verifying with ECDSA key === \n");
-    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
-        printf("Error verifying data with ECDSA key\n\n");
-        return false;
-    }
-
-    printf("=== Exporting ECDSA public key ===\n");
-    keymaster_blob_t exported_key;
-    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
-    if (error != KM_ERROR_OK) {
-        printf("Error exporting ECDSA public key: %d\n\n", error);
-        return false;
-    }
-
-    printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key.data;
-    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
-        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
-    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
-    if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openssl EVP context\n\n");
-        return false;
-    }
-    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
-        printf("Exported key was the wrong type?!?\n\n");
-        return false;
-    }
-
-    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
-                        message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n\n");
-        return false;
-    } else {
-        printf("Verification succeeded\n");
-    }
-
-    printf("\n");
-    return true;
-}
-
-int main(void) {
-    TrustyKeymasterDevice device(NULL);
-    keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
-    if (device.session_error() != KM_ERROR_OK) {
-        printf("Failed to initialize Trusty session: %d\n", device.session_error());
-        return 1;
-    }
-    printf("Trusty session initialized\n");
-
-    bool success = true;
-    success &= test_rsa(&device);
-    success &= test_import_rsa(&device);
-    success &= test_ecdsa(&device);
-    success &= test_import_ecdsa(&device);
-
-    if (success) {
-        printf("\nTESTS PASSED!\n");
-    } else {
-        printf("\n!!!!TESTS FAILED!!!\n");
-    }
-
-    return success ? 0 : 1;
-}
diff --git a/watchdogd/Android.bp b/watchdogd/Android.bp
new file mode 100644
index 0000000..0fbc33c
--- /dev/null
+++ b/watchdogd/Android.bp
@@ -0,0 +1,14 @@
+cc_binary {
+    name: "watchdogd",
+    recovery_available: true,
+    srcs: ["watchdogd.cpp"],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    shared_libs: ["libbase"],
+    sanitize: {
+        misc_undefined: ["signed-integer-overflow"],
+    },
+}
diff --git a/init/watchdogd.cpp b/watchdogd/watchdogd.cpp
similarity index 79%
rename from init/watchdogd.cpp
rename to watchdogd/watchdogd.cpp
index e03a2c3..5dc41e6 100644
--- a/init/watchdogd.cpp
+++ b/watchdogd/watchdogd.cpp
@@ -23,16 +23,9 @@
 
 #include <android-base/logging.h>
 
-#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
-#endif
-
 #define DEV_NAME "/dev/watchdog"
 
-namespace android {
-namespace init {
-
-int watchdogd_main(int argc, char **argv) {
+int main(int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::KernelLogger);
 
     int interval = 10;
@@ -43,7 +36,7 @@
 
     LOG(INFO) << "watchdogd started (interval " << interval << ", margin " << margin << ")!";
 
-    int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
+    int fd = open(DEV_NAME, O_RDWR | O_CLOEXEC);
     if (fd == -1) {
         PLOG(ERROR) << "Failed to open " << DEV_NAME;
         return 1;
@@ -63,9 +56,8 @@
                 interval = 1;
             }
             LOG(WARNING) << "Adjusted interval to timeout returned by driver: "
-                         << "timeout " << timeout
-                         << ", interval " << interval
-                         << ", margin " << margin;
+                         << "timeout " << timeout << ", interval " << interval << ", margin "
+                         << margin;
         }
     }
 
@@ -74,6 +66,3 @@
         sleep(interval);
     }
 }
-
-}  // namespace init
-}  // namespace android