[REFACTOR] healthd: BatteryMonitor use health 2.1 types
am: 1d4368b494

Change-Id: Ibd3288cd26fbbd747877e559f2a9a46cb6ef0202
diff --git a/adb/Android.bp b/adb/Android.bp
index 170053b..d14fe56 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -428,18 +428,10 @@
                 "daemon/abb_service.cpp",
                 "daemon/framebuffer_service.cpp",
                 "daemon/mdns.cpp",
-                "daemon/reboot_service.cpp",
                 "daemon/restart_service.cpp",
-                "daemon/set_verity_enable_state_service.cpp",
-            ],
-            static_libs: [
-                "libavb_user",
             ],
             shared_libs: [
-                "libbootloader_message",
                 "libmdnssd",
-                "libfec",
-                "libfs_mgr",
                 "libselinux",
             ],
         },
@@ -513,6 +505,22 @@
     ],
 }
 
+phony {
+    name: "adbd_system_binaries",
+    required: [
+        "abb",
+        "reboot",
+        "set-verity-state",
+    ]
+}
+
+phony {
+    name: "adbd_system_binaries_recovery",
+    required: [
+        "reboot.recovery",
+    ],
+}
+
 cc_binary {
     name: "static_adbd",
     defaults: ["adbd_defaults", "host_adbd_supported"],
@@ -608,7 +616,6 @@
     static_libs: [
         "libadbd",
         "libbase",
-        "libbootloader_message",
         "libcutils",
         "libcrypto_utils",
         "libcrypto_static",
diff --git a/adb/daemon/reboot_service.cpp b/adb/daemon/reboot_service.cpp
deleted file mode 100644
index 13398af..0000000
--- a/adb/daemon/reboot_service.cpp
+++ /dev/null
@@ -1,84 +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.
- */
-
-#define TRACE_TAG SERVICES
-
-#include "sysdeps.h"
-
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
-
-#include "adb_io.h"
-#include "adb_unique_fd.h"
-
-void reboot_service(unique_fd fd, const std::string& arg) {
-    std::string reboot_arg = arg;
-    sync();
-
-    if (reboot_arg.empty()) reboot_arg = "adb";
-    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
-
-    if (reboot_arg == "fastboot" &&
-        android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
-        access("/dev/socket/recovery", F_OK) == 0) {
-        LOG(INFO) << "Recovery specific reboot fastboot";
-        /*
-         * The socket is created to allow switching between recovery and
-         * fastboot.
-         */
-        android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
-        if (sock < 0) {
-            WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
-            PLOG(ERROR) << "Creating recovery socket failed";
-            return;
-        }
-
-        sockaddr_un addr = {.sun_family = AF_UNIX};
-        strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
-        if (connect(sock.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
-            WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
-            PLOG(ERROR) << "Couldn't connect to recovery socket";
-            return;
-        }
-        const char msg_switch_to_fastboot = 'f';
-        auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
-        if (ret != sizeof(msg_switch_to_fastboot)) {
-            WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
-            PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
-            return;
-        }
-    } else {
-        if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
-            WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
-            return;
-        }
-    }
-    // Don't return early. Give the reboot command time to take effect
-    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
-    while (true) {
-        pause();
-    }
-}
diff --git a/adb/daemon/reboot_service.h b/adb/daemon/reboot_service.h
deleted file mode 100644
index f68913e..0000000
--- a/adb/daemon/reboot_service.h
+++ /dev/null
@@ -1,25 +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.
- */
-
-#pragma once
-
-#include <string>
-
-#include "adb_unique_fd.h"
-
-#if defined(__ANDROID__)
-void reboot_service(unique_fd fd, const std::string& arg);
-#endif
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 181a8c5..a44c10b 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -53,9 +53,7 @@
 
 #include "daemon/file_sync_service.h"
 #include "daemon/framebuffer_service.h"
-#include "daemon/reboot_service.h"
 #include "daemon/restart_service.h"
-#include "daemon/set_verity_enable_state_service.h"
 #include "daemon/shell_service.h"
 
 
@@ -254,9 +252,9 @@
         cmd += name;
         return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if (android::base::ConsumePrefix(&name, "reboot:")) {
-        std::string arg(name);
-        return create_service_thread("reboot",
-                                     std::bind(reboot_service, std::placeholders::_1, arg));
+        std::string cmd = "/system/bin/reboot ";
+        cmd += name;
+        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if (name.starts_with("root:")) {
         return create_service_thread("root", restart_root_service);
     } else if (name.starts_with("unroot:")) {
@@ -269,11 +267,11 @@
         return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
     } else if (name.starts_with("disable-verity:")) {
-        return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
-                                                            std::placeholders::_1, false));
+        return StartSubprocess("/system/bin/disable-verity", nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
     } else if (name.starts_with("enable-verity:")) {
-        return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
-                                                             std::placeholders::_1, true));
+        return StartSubprocess("/system/bin/enable-verity", nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
     } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
         std::string str(name);
 
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
deleted file mode 100644
index 4fbccdb..0000000
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ /dev/null
@@ -1,250 +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.
- */
-
-#define TRACE_TAG ADB
-
-#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>
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-
-#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 "fec/io.h"
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
-static const bool kAllowDisableVerity = true;
-#else
-static const bool kAllowDisableVerity = false;
-#endif
-
-void suggest_run_adb_root(int fd) {
-    if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
-}
-
-static bool make_block_device_writable(const std::string& dev) {
-    unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
-    if (fd == -1) {
-        return false;
-    }
-
-    int OFF = 0;
-    bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
-    return result;
-}
-
-/* Turn verity on/off */
-static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
-                                     bool enable) {
-    if (!make_block_device_writable(block_device)) {
-        WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
-                   block_device, strerror(errno));
-        return false;
-    }
-
-    fec::io fh(block_device, O_RDWR);
-
-    if (!fh) {
-        WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
-        suggest_run_adb_root(fd);
-        return false;
-    }
-
-    fec_verity_metadata metadata;
-
-    if (!fh.get_verity_metadata(metadata)) {
-        WriteFdExactly(fd, "Couldn't find verity metadata!\n");
-        return false;
-    }
-
-    if (!enable && metadata.disabled) {
-        WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
-        return false;
-    }
-
-    if (enable && !metadata.disabled) {
-        WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
-        return false;
-    }
-
-    if (!fh.set_verity_status(enable)) {
-        WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
-                   enable ? "enabled" : "disabled",
-                   block_device, strerror(errno));
-        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) {
-        int expected_errno = enable ? EBUSY : ENOENT;
-        if (errno != expected_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;
-}
-
-/* Helper function to get A/B suffix, if any. If the device isn't
- * using A/B the empty string is returned. Otherwise either "_a",
- * "_b", ... is returned.
- */
-static std::string get_ab_suffix() {
-    return android::base::GetProperty("ro.boot.slot_suffix", "");
-}
-
-static bool is_avb_device_locked() {
-    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();
-    bool verity_enabled;
-
-    if (is_avb_device_locked()) {
-        WriteFdExactly(fd, "Device is locked. Please unlock the device first\n");
-        return false;
-    }
-
-    if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
-        WriteFdExactly(fd, "Error getting verity state. Try adb root first?\n");
-        return false;
-    }
-
-    if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
-        WriteFdFmt(fd, "verity is already %s\n", verity_enabled ? "enabled" : "disabled");
-        return false;
-    }
-
-    if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
-        WriteFdExactly(fd, "Error setting verity\n");
-        return false;
-    }
-
-    overlayfs_setup(fd, enable_verity);
-    WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
-    return true;
-}
-
-void set_verity_enabled_state_service(unique_fd fd, bool enable) {
-    bool any_changed = false;
-
-    // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
-    // contract, androidboot.vbmeta.digest is set by the bootloader
-    // when using AVB).
-    bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
-
-    // If using AVB, dm-verity is used on any build so we want it to
-    // be possible to disable/enable on any build (except USER). For
-    // VB1.0 dm-verity is only enabled on certain builds.
-    if (!using_avb) {
-        if (!kAllowDisableVerity) {
-            WriteFdFmt(fd.get(), "%s-verity only works for userdebug builds\n",
-                       enable ? "enable" : "disable");
-        }
-
-        if (!android::base::GetBoolProperty("ro.secure", false)) {
-            overlayfs_setup(fd.get(), enable);
-            WriteFdExactly(fd.get(), "verity not enabled - ENG build\n");
-            return;
-        }
-    }
-
-    // Should never be possible to disable dm-verity on a USER build
-    // regardless of using AVB or VB1.0.
-    if (!__android_log_is_debuggable()) {
-        WriteFdExactly(fd.get(), "verity cannot be disabled/enabled - USER build\n");
-        return;
-    }
-
-    if (using_avb) {
-        // Yep, the system is using AVB.
-        AvbOps* ops = avb_ops_user_new();
-        if (ops == nullptr) {
-            WriteFdExactly(fd.get(), "Error getting AVB ops\n");
-            return;
-        }
-        if (set_avb_verity_enabled_state(fd.get(), ops, enable)) {
-            any_changed = true;
-        }
-        avb_ops_user_free(ops);
-    } else {
-        // Not using AVB - assume VB1.0.
-
-        // read all fstab entries at once from all sources
-        android::fs_mgr::Fstab fstab;
-        if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
-            WriteFdExactly(fd.get(), "Failed to read fstab\n");
-            suggest_run_adb_root(fd.get());
-            return;
-        }
-
-        // Loop through entries looking for ones that verity manages.
-        for (const auto& entry : fstab) {
-            if (entry.fs_mgr_flags.verify) {
-                if (set_verity_enabled_state(fd.get(), entry.blk_device.c_str(),
-                                             entry.mount_point.c_str(), enable)) {
-                    any_changed = true;
-                }
-            }
-        }
-    }
-    if (!any_changed) any_changed = overlayfs_setup(fd.get(), enable);
-
-    if (any_changed) {
-        WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
-    }
-}
diff --git a/adb/daemon/set_verity_enable_state_service.h b/adb/daemon/set_verity_enable_state_service.h
deleted file mode 100644
index c0ed98e..0000000
--- a/adb/daemon/set_verity_enable_state_service.h
+++ /dev/null
@@ -1,23 +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.
- */
-
-#pragma once
-
-#include "adb_unique_fd.h"
-
-#if defined(__ANDROID__)
-void set_verity_enabled_state_service(unique_fd fd, bool enable);
-#endif
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 245d52a..f5893aa 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -27,6 +27,7 @@
 
 java_binary {
     name: "deployagent",
+    sdk_version: "24",
     static_libs: [
         "deployagent_lib",
     ],
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index da2ba58..1993840 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -595,7 +595,7 @@
       }
       AndroidLogEntry e;
       char buf[512];
-      if (android_log_processBinaryLogBuffer(&log_entry.entry_v1, &e, g_eventTagMap, buf,
+      if (android_log_processBinaryLogBuffer(&log_entry.entry, &e, g_eventTagMap, buf,
                                              sizeof(buf)) == 0) {
         _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n", timeBuf,
              log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, 'I',
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index afd6d08..9b6e7b6 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -19,7 +19,7 @@
 #include <string>
 #include <vector>
 
-constexpr unsigned int kMaxDownloadSizeDefault = 0x20000000;
+constexpr unsigned int kMaxDownloadSizeDefault = 0x10000000;
 
 class FastbootDevice;
 
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 4ba1c49..6faead0 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -888,6 +888,17 @@
   public:
     CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
 
+    bool NeedsCheckpoint() {
+        if (needs_checkpoint_ != UNKNOWN) {
+            return needs_checkpoint_ == YES;
+        }
+        if (!call_vdc({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
+            LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+            needs_checkpoint_ = NO;
+        }
+        return needs_checkpoint_ == YES;
+    }
+
     bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
         if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
             return true;
@@ -897,13 +908,7 @@
             call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device}, nullptr);
         }
 
-        if (needs_checkpoint_ == UNKNOWN &&
-            !call_vdc({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
-            LERROR << "Failed to find if checkpointing is needed. Assuming no.";
-            needs_checkpoint_ = NO;
-        }
-
-        if (needs_checkpoint_ != YES) {
+        if (!NeedsCheckpoint()) {
             return true;
         }
 
@@ -1324,6 +1329,69 @@
     return ret;
 }
 
+static std::string GetUserdataBlockDevice() {
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        LERROR << "Failed to read /proc/mounts";
+        return "";
+    }
+    auto entry = GetEntryForMountPoint(&fstab, "/data");
+    if (entry == nullptr) {
+        LERROR << "Didn't find /data mount point in /proc/mounts";
+        return "";
+    }
+    return entry->blk_device;
+}
+
+int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
+    const std::string& block_device = GetUserdataBlockDevice();
+    LINFO << "Userdata is mounted on " << block_device;
+    auto entry = std::find_if(fstab->begin(), fstab->end(), [&block_device](const FstabEntry& e) {
+        if (e.mount_point != "/data") {
+            return false;
+        }
+        if (e.blk_device == block_device) {
+            return true;
+        }
+        DeviceMapper& dm = DeviceMapper::Instance();
+        std::string path;
+        if (!dm.GetDmDevicePathByName("userdata", &path)) {
+            return false;
+        }
+        return path == block_device;
+    });
+    if (entry == fstab->end()) {
+        LERROR << "Can't find /data in fstab";
+        return -1;
+    }
+    if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+        LINFO << "Userdata doesn't support checkpointing. Nothing to do";
+        return 0;
+    }
+    CheckpointManager checkpoint_manager;
+    if (!checkpoint_manager.NeedsCheckpoint()) {
+        LINFO << "Checkpointing not needed. Don't remount";
+        return 0;
+    }
+    if (entry->fs_mgr_flags.checkpoint_fs) {
+        // Userdata is f2fs, simply remount it.
+        if (!checkpoint_manager.Update(&(*entry))) {
+            LERROR << "Failed to remount userdata in checkpointing mode";
+            return -1;
+        }
+        if (mount(entry->blk_device.c_str(), entry->mount_point.c_str(), "none",
+                  MS_REMOUNT | entry->flags, entry->fs_options.c_str()) != 0) {
+            LERROR << "Failed to remount userdata in checkpointing mode";
+            return -1;
+        }
+    } else {
+        // TODO(b/135984674): support remounting for ext4.
+        LERROR << "Remounting in checkpointing mode is not yet supported for ext4";
+        return -1;
+    }
+    return 0;
+}
+
 // wrapper to __mount() and expects a fully prepared fstab_rec,
 // unlike fs_mgr_do_mount which does more things with avb / verity etc.
 int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2ff5243..d216458 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -99,71 +99,9 @@
     return false;
 }
 
-const std::array<const char*, 3> kFileContentsEncryptionMode = {
-        "aes-256-xts",
-        "adiantum",
-        "ice",
-};
-
-const std::array<const char*, 3> kFileNamesEncryptionMode = {
-        "aes-256-cts",
-        "aes-256-heh",
-        "adiantum",
-};
-
 void ParseFileEncryption(const std::string& arg, FstabEntry* entry) {
-    // The fileencryption flag is followed by an = and 1 to 3 colon-separated fields:
-    //
-    // 1. Contents encryption mode
-    // 2. Filenames encryption mode (defaults to "aes-256-cts" or "adiantum"
-    //    depending on the contents encryption mode)
-    // 3. Encryption policy version (defaults to "v1". Use "v2" on new devices.)
     entry->fs_mgr_flags.file_encryption = true;
-
-    auto parts = Split(arg, ":");
-    if (parts.empty() || parts.size() > 3) {
-        LWARNING << "Warning: fileencryption= flag malformed: " << arg;
-        return;
-    }
-
-    // Alias for backwards compatibility.
-    if (parts[0] == "software") {
-        parts[0] = "aes-256-xts";
-    }
-
-    if (std::find(kFileContentsEncryptionMode.begin(), kFileContentsEncryptionMode.end(),
-                  parts[0]) == kFileContentsEncryptionMode.end()) {
-        LWARNING << "fileencryption= flag malformed, file contents encryption mode not found: "
-                 << arg;
-        return;
-    }
-
-    entry->file_contents_mode = parts[0];
-
-    if (parts.size() >= 2) {
-        if (std::find(kFileNamesEncryptionMode.begin(), kFileNamesEncryptionMode.end(), parts[1]) ==
-            kFileNamesEncryptionMode.end()) {
-            LWARNING << "fileencryption= flag malformed, file names encryption mode not found: "
-                     << arg;
-            return;
-        }
-
-        entry->file_names_mode = parts[1];
-    } else if (entry->file_contents_mode == "adiantum") {
-        entry->file_names_mode = "adiantum";
-    } else {
-        entry->file_names_mode = "aes-256-cts";
-    }
-
-    if (parts.size() >= 3) {
-        if (!android::base::StartsWith(parts[2], 'v') ||
-            !android::base::ParseInt(&parts[2][1], &entry->file_policy_version)) {
-            LWARNING << "fileencryption= flag malformed, unknown options: " << arg;
-            return;
-        }
-    } else {
-        entry->file_policy_version = 1;
-    }
+    entry->encryption_options = arg;
 }
 
 bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
@@ -299,9 +237,7 @@
             // return it.
             entry->fs_mgr_flags.force_fde_or_fbe = true;
             entry->key_loc = arg;
-            entry->file_contents_mode = "aes-256-xts";
-            entry->file_names_mode = "aes-256-cts";
-            entry->file_policy_version = 1;
+            entry->encryption_options = "aes-256-xts:aes-256-cts";
         } else if (StartsWith(flag, "max_comp_streams=")) {
             if (!ParseInt(arg, &entry->max_comp_streams)) {
                 LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index bdec7be..ca67f37 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -105,6 +105,8 @@
 // it destroys verity devices from device mapper after the device is unmounted.
 int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
 
+int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
+
 // Finds the dm_bow device on which this block device is stacked, or returns
 // empty string
 std::string fs_mgr_find_bow_device(const std::string& block_device);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 3c517dc..c6a16e3 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -45,9 +45,7 @@
     int max_comp_streams = 0;
     off64_t zram_size = 0;
     off64_t reserved_size = 0;
-    std::string file_contents_mode;
-    std::string file_names_mode;
-    int file_policy_version = 0;
+    std::string encryption_options;
     off64_t erase_blk_size = 0;
     off64_t logical_blk_size = 0;
     std::string sysfs_path;
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
index 4903de1..fef256d 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#pragma once
+
 #include <stdint.h>
 
 #include <vector>
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
new file mode 100644
index 0000000..d5ceb0e
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <string>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace snapshot {
+
+// An abstract "device" that will be cleaned up (unmapped, unmounted, etc.) upon
+// destruction.
+struct AutoDevice {
+    virtual ~AutoDevice(){};
+    void Release();
+
+  protected:
+    AutoDevice(const std::string& name) : name_(name) {}
+    std::string name_;
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(AutoDevice);
+    AutoDevice(AutoDevice&& other) = delete;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index fcaa73a..431fea1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -19,6 +19,7 @@
 #include <chrono>
 #include <map>
 #include <memory>
+#include <optional>
 #include <ostream>
 #include <string>
 #include <string_view>
@@ -32,6 +33,8 @@
 #include <liblp/liblp.h>
 #include <update_engine/update_metadata.pb.h>
 
+#include <libsnapshot/auto_device.h>
+
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
     friend class test_set_name##_##individual_test##_Test
@@ -119,6 +122,7 @@
         virtual const IPartitionOpener& GetPartitionOpener() const = 0;
         virtual bool IsOverlayfsSetup() const = 0;
         virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+        virtual bool IsRecovery() const = 0;
     };
 
     ~SnapshotManager();
@@ -208,6 +212,22 @@
     // Dump debug information.
     bool Dump(std::ostream& os);
 
+    // Ensure metadata directory is mounted in recovery. When the returned
+    // AutoDevice is destroyed, the metadata directory is automatically
+    // unmounted.
+    // Return nullptr if any failure.
+    // In Android mode, Return an AutoDevice that does nothing
+    // In recovery, return an AutoDevice that does nothing if metadata entry
+    // is not found in fstab.
+    // Note: if this function is called the second time before the AutoDevice returned from the
+    // first call is destroyed, the device will be unmounted when any of these AutoDevices is
+    // destroyed. FOr example:
+    //   auto a = mgr->EnsureMetadataMounted(); // mounts
+    //   auto b = mgr->EnsureMetadataMounted(); // does nothing
+    //   b.reset() // unmounts
+    //   a.reset() // does nothing
+    std::unique_ptr<AutoDevice> EnsureMetadataMounted();
+
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -298,7 +318,8 @@
                      std::string* dev_path);
 
     // Map a COW image that was previous created with CreateCowImage.
-    bool MapCowImage(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+    std::optional<std::string> MapCowImage(const std::string& name,
+                                           const std::chrono::milliseconds& timeout_ms);
 
     // Remove the backing copy-on-write image and snapshot states for the named snapshot. The
     // caller is responsible for ensuring that the snapshot is unmapped.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 48a94e4..63d97d0 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -77,6 +77,12 @@
 
 static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
 
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
 class DeviceInfo final : public SnapshotManager::IDeviceInfo {
   public:
     std::string GetGsidDir() const override { return "ota"s; }
@@ -89,6 +95,7 @@
     }
     bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
     bool SetBootControlMergeStatus(MergeStatus status) override;
+    bool IsRecovery() const override { return kIsRecovery; }
 
   private:
     android::fs_mgr::PartitionOpener opener_;
@@ -342,7 +349,6 @@
                                   const std::chrono::milliseconds& timeout_ms,
                                   std::string* dev_path) {
     CHECK(lock);
-    if (!EnsureImageManager()) return false;
 
     SnapshotStatus status;
     if (!ReadSnapshotStatus(lock, name, &status)) {
@@ -450,9 +456,9 @@
     return true;
 }
 
-bool SnapshotManager::MapCowImage(const std::string& name,
-                                  const std::chrono::milliseconds& timeout_ms) {
-    if (!EnsureImageManager()) return false;
+std::optional<std::string> SnapshotManager::MapCowImage(
+        const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+    if (!EnsureImageManager()) return std::nullopt;
     auto cow_image_name = GetCowImageDeviceName(name);
 
     bool ok;
@@ -468,10 +474,10 @@
 
     if (ok) {
         LOG(INFO) << "Mapped " << cow_image_name << " to " << cow_dev;
-    } else {
-        LOG(ERROR) << "Could not map image device: " << cow_image_name;
+        return cow_dev;
     }
-    return ok;
+    LOG(ERROR) << "Could not map image device: " << cow_image_name;
+    return std::nullopt;
 }
 
 bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
@@ -1428,7 +1434,6 @@
                                     const SnapshotStatus& snapshot_status,
                                     AutoDeviceList* created_devices, std::string* cow_name) {
     CHECK(lock);
-    if (!EnsureImageManager()) return false;
     CHECK(snapshot_status.cow_partition_size() + snapshot_status.cow_file_size() > 0);
     auto begin = std::chrono::steady_clock::now();
 
@@ -1440,10 +1445,11 @@
 
     // Map COW image if necessary.
     if (snapshot_status.cow_file_size() > 0) {
+        if (!EnsureImageManager()) return false;
         auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
         if (remaining_time.count() < 0) return false;
 
-        if (!MapCowImage(partition_name, remaining_time)) {
+        if (!MapCowImage(partition_name, remaining_time).has_value()) {
             LOG(ERROR) << "Could not map cow image for partition: " << partition_name;
             return false;
         }
@@ -1787,6 +1793,14 @@
     auto target_metadata =
             MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);
 
+    // Delete partitions with target suffix in |current_metadata|. Otherwise,
+    // partition_cow_creator recognizes these left-over partitions as used space.
+    for (const auto& group_name : current_metadata->ListGroups()) {
+        if (android::base::EndsWith(group_name, target_suffix)) {
+            current_metadata->RemoveGroupAndPartitions(group_name);
+        }
+    }
+
     SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
     if (!metadata_updater.Update()) {
         LOG(ERROR) << "Cannot calculate new metadata.";
@@ -2066,5 +2080,14 @@
     return ok;
 }
 
+std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {
+    if (!device_->IsRecovery()) {
+        // No need to mount anything in recovery.
+        LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
+        return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
+    }
+    return AutoUnmountDevice::New(device_->GetMetadataDir());
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index a008294..2161e5b 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,6 +26,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <fs_mgr/roots.h>
 #include <fs_mgr_dm_linear.h>
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
@@ -47,12 +48,16 @@
 using android::fs_mgr::BlockDeviceInfo;
 using android::fs_mgr::CreateLogicalPartitionParams;
 using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::EnsurePathMounted;
+using android::fs_mgr::EnsurePathUnmounted;
 using android::fs_mgr::Extent;
+using android::fs_mgr::Fstab;
 using android::fs_mgr::GetPartitionGroupName;
 using android::fs_mgr::GetPartitionName;
 using android::fs_mgr::Interval;
 using android::fs_mgr::MetadataBuilder;
 using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::DynamicPartitionGroup;
 using chromeos_update_engine::PartitionUpdate;
 using namespace ::testing;
 using namespace android::storage_literals;
@@ -254,12 +259,11 @@
 
     AssertionResult MapCowImage(const std::string& name,
                                 const std::chrono::milliseconds& timeout_ms, std::string* path) {
-        if (!sm->MapCowImage(name, timeout_ms)) {
+        auto cow_image_path = sm->MapCowImage(name, timeout_ms);
+        if (!cow_image_path.has_value()) {
             return AssertionFailure() << "Cannot map cow image " << name;
         }
-        if (!dm_.GetDmDevicePathByName(name + "-cow-img"s, path)) {
-            return AssertionFailure() << "No path for " << name << "-cow-img";
-        }
+        *path = *cow_image_path;
         return AssertionSuccess();
     }
 
@@ -657,12 +661,12 @@
         // Not using full name "system", "vendor", "product" because these names collide with the
         // mapped partitions on the running device.
         // Each test modifies manifest_ slightly to indicate changes to the partition layout.
-        auto group = manifest_.mutable_dynamic_partition_metadata()->add_groups();
-        group->set_name("group");
-        group->set_size(kGroupSize);
-        group->add_partition_names("sys");
-        group->add_partition_names("vnd");
-        group->add_partition_names("prd");
+        group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
+        group_->set_name("group");
+        group_->set_size(kGroupSize);
+        group_->add_partition_names("sys");
+        group_->add_partition_names("vnd");
+        group_->add_partition_names("prd");
         sys_ = manifest_.add_partitions();
         sys_->set_partition_name("sys");
         SetSize(sys_, 3_MiB);
@@ -766,6 +770,7 @@
     PartitionUpdate* sys_ = nullptr;
     PartitionUpdate* vnd_ = nullptr;
     PartitionUpdate* prd_ = nullptr;
+    DynamicPartitionGroup* group_ = nullptr;
 };
 
 // Test full update flow executed by update_engine. Some partitions uses super empty space,
@@ -1057,6 +1062,149 @@
     }
 }
 
+TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
+    constexpr auto kRetrofitGroupSize = kGroupSize / 2;
+
+    // Initialize device-mapper / disk
+    ASSERT_TRUE(UnmapAll());
+    FormatFakeSuper();
+
+    // Setup source partition metadata to have both _a and _b partitions.
+    src_ = MetadataBuilder::New(*opener_, "super", 0);
+    ASSERT_NE(nullptr, src_);
+    for (const auto& suffix : {"_a"s, "_b"s}) {
+        ASSERT_TRUE(src_->AddGroup(group_->name() + suffix, kRetrofitGroupSize));
+        for (const auto& name : {"sys"s, "vnd"s, "prd"s}) {
+            auto partition = src_->AddPartition(name + suffix, group_->name() + suffix, 0);
+            ASSERT_NE(nullptr, partition);
+            ASSERT_TRUE(src_->ResizePartition(partition, 2_MiB));
+        }
+    }
+    auto metadata = src_->Export();
+    ASSERT_NE(nullptr, metadata);
+    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
+
+    // Flash source partitions
+    std::string path;
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(CreateLogicalPartition(
+                CreateLogicalPartitionParams{
+                        .block_device = fake_super,
+                        .metadata_slot = 0,
+                        .partition_name = name,
+                        .timeout_ms = 1s,
+                        .partition_opener = opener_.get(),
+                },
+                &path));
+        ASSERT_TRUE(WriteRandomData(path));
+        auto hash = GetHash(path);
+        ASSERT_TRUE(hash.has_value());
+        hashes_[name] = *hash;
+    }
+
+    // Setup manifest.
+    group_->set_size(kRetrofitGroupSize);
+    for (auto* partition : {sys_, vnd_, prd_}) {
+        SetSize(partition, 2_MiB);
+        auto* e = partition->add_operations()->add_dst_extents();
+        e->set_start_block(0);
+        e->set_num_blocks(2_MiB / manifest_.block_size());
+    }
+
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    // Test that COW image should not be created for retrofit devices; super
+    // should be big enough.
+    ASSERT_FALSE(image_manager_->BackingImageExists("sys_b-cow-img"));
+    ASSERT_FALSE(image_manager_->BackingImageExists("vnd_b-cow-img"));
+    ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
+
+    // Write some data to target partitions.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        std::string path;
+        ASSERT_TRUE(sm->MapUpdateSnapshot(
+                CreateLogicalPartitionParams{
+                        .block_device = fake_super,
+                        .metadata_slot = 1,
+                        .partition_name = name,
+                        .timeout_ms = 10s,
+                        .partition_opener = opener_.get(),
+                },
+                &path))
+                << name;
+        ASSERT_TRUE(WriteRandomData(path));
+        auto hash = GetHash(path);
+        ASSERT_TRUE(hash.has_value());
+        hashes_[name] = *hash;
+    }
+
+    // Assert that source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+}
+
+class MetadataMountedTest : public SnapshotUpdateTest {
+  public:
+    void SetUp() override {
+        metadata_dir_ = test_device->GetMetadataDir();
+        ASSERT_TRUE(ReadDefaultFstab(&fstab_));
+    }
+    void TearDown() override {
+        SetUp();
+        // Remount /metadata
+        test_device->set_recovery(false);
+        EXPECT_TRUE(android::fs_mgr::EnsurePathMounted(&fstab_, metadata_dir_));
+    }
+    AssertionResult IsMetadataMounted() {
+        Fstab mounted_fstab;
+        if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+            ADD_FAILURE() << "Failed to scan mounted volumes";
+            return AssertionFailure() << "Failed to scan mounted volumes";
+        }
+
+        auto entry = GetEntryForPath(&fstab_, metadata_dir_);
+        if (entry == nullptr) {
+            return AssertionFailure() << "No mount point found in fstab for path " << metadata_dir_;
+        }
+
+        auto mv = GetEntryForMountPoint(&mounted_fstab, entry->mount_point);
+        if (mv == nullptr) {
+            return AssertionFailure() << metadata_dir_ << " is not mounted";
+        }
+        return AssertionSuccess() << metadata_dir_ << " is mounted";
+    }
+    std::string metadata_dir_;
+    Fstab fstab_;
+};
+
+TEST_F(MetadataMountedTest, Android) {
+    auto device = sm->EnsureMetadataMounted();
+    EXPECT_NE(nullptr, device);
+    device.reset();
+
+    EXPECT_TRUE(IsMetadataMounted());
+    EXPECT_TRUE(sm->CancelUpdate()) << "Metadata dir should never be unmounted in Android mode";
+}
+
+TEST_F(MetadataMountedTest, Recovery) {
+    test_device->set_recovery(true);
+    metadata_dir_ = test_device->GetMetadataDir();
+
+    EXPECT_TRUE(android::fs_mgr::EnsurePathUnmounted(&fstab_, metadata_dir_));
+    EXPECT_FALSE(IsMetadataMounted());
+
+    auto device = sm->EnsureMetadataMounted();
+    EXPECT_NE(nullptr, device);
+    EXPECT_TRUE(IsMetadataMounted());
+
+    device.reset();
+    EXPECT_FALSE(IsMetadataMounted());
+}
+
 }  // namespace snapshot
 }  // namespace android
 
@@ -1098,6 +1246,7 @@
     }
 
     // Clean up previous run.
+    MetadataMountedTest().TearDown();
     SnapshotUpdateTest().Cleanup();
     SnapshotTest().Cleanup();
 
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index ea2c5b6..5fe701f 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -88,17 +88,20 @@
         return true;
     }
     bool IsOverlayfsSetup() const override { return false; }
+    bool IsRecovery() const override { return recovery_; }
 
     void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
     void set_fake_super(const std::string& path) {
         opener_ = std::make_unique<TestPartitionOpener>(path);
     }
+    void set_recovery(bool value) { recovery_ = value; }
     MergeStatus merge_status() const { return merge_status_; }
 
   private:
     std::string slot_suffix_ = "_a";
     std::unique_ptr<TestPartitionOpener> opener_;
     MergeStatus merge_status_;
+    bool recovery_ = false;
 };
 
 class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 66629e8..615cbca 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -17,9 +17,15 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <fs_mgr/roots.h>
 
+using android::fs_mgr::EnsurePathMounted;
+using android::fs_mgr::EnsurePathUnmounted;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::GetEntryForPath;
 using android::fs_mgr::MetadataBuilder;
 using android::fs_mgr::Partition;
+using android::fs_mgr::ReadDefaultFstab;
 
 namespace android {
 namespace snapshot {
@@ -109,5 +115,31 @@
     return true;
 }
 
+std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        LOG(ERROR) << "Cannot read default fstab";
+        return nullptr;
+    }
+
+    if (GetEntryForPath(&fstab, path) == nullptr) {
+        LOG(INFO) << "EnsureMetadataMounted can't find entry for " << path << ", skipping";
+        return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice("", {}));
+    }
+
+    if (!EnsurePathMounted(&fstab, path)) {
+        LOG(ERROR) << "Cannot mount " << path;
+        return nullptr;
+    }
+    return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(path, std::move(fstab)));
+}
+
+AutoUnmountDevice::~AutoUnmountDevice() {
+    if (name_.empty()) return;
+    if (!EnsurePathUnmounted(&fstab_, name_)) {
+        LOG(ERROR) << "Cannot unmount " << name_;
+    }
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 3051184..0c08ed2 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -18,31 +18,21 @@
 #include <string>
 
 #include <android-base/macros.h>
+#include <fstab/fstab.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
 #include <libsnapshot/snapshot.h>
 #include <update_engine/update_metadata.pb.h>
 
+#include <libsnapshot/auto_device.h>
+
 namespace android {
 namespace snapshot {
 
 // Unit is sectors, this is a 4K chunk.
 static constexpr uint32_t kSnapshotChunkSize = 8;
 
-struct AutoDevice {
-    virtual ~AutoDevice(){};
-    void Release();
-
-  protected:
-    AutoDevice(const std::string& name) : name_(name) {}
-    std::string name_;
-
-  private:
-    DISALLOW_COPY_AND_ASSIGN(AutoDevice);
-    AutoDevice(AutoDevice&& other) = delete;
-};
-
 // A list of devices we created along the way.
 // - Whenever a device is created that is subject to GC'ed at the end of
 //   this function, add it to this list.
@@ -103,6 +93,18 @@
     SnapshotManager::LockedFile* lock_ = nullptr;
 };
 
+struct AutoUnmountDevice : AutoDevice {
+    // Empty object that does nothing.
+    AutoUnmountDevice() : AutoDevice("") {}
+    static std::unique_ptr<AutoUnmountDevice> New(const std::string& path);
+    ~AutoUnmountDevice();
+
+  private:
+    AutoUnmountDevice(const std::string& path, android::fs_mgr::Fstab&& fstab)
+        : AutoDevice(path), fstab_(std::move(fstab)) {}
+    android::fs_mgr::Fstab fstab_;
+};
+
 // Return a list of partitions in |builder| with the name ending in |suffix|.
 std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
         android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index a7ea817..05607a0 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -179,6 +179,7 @@
                 {"nodiratime", MS_NODIRATIME},
                 {"ro", MS_RDONLY},
                 {"rw", 0},
+                {"sync", MS_SYNCHRONOUS},
                 {"remount", MS_REMOUNT},
                 {"bind", MS_BIND},
                 {"rec", MS_REC},
@@ -197,7 +198,7 @@
         if (!(entry.flags & MS_RDONLY)) {
             fs_options.emplace("rw");
         }
-        EXPECT_EQ(mnt_opts, fs_options);
+        EXPECT_EQ(mnt_opts, fs_options) << "At line " << i;
         ++i;
     }
     EXPECT_EQ(i, fstab.size());
@@ -420,8 +421,7 @@
     EXPECT_EQ(0, entry->max_comp_streams);
     EXPECT_EQ(0, entry->zram_size);
     EXPECT_EQ(0, entry->reserved_size);
-    EXPECT_EQ("", entry->file_contents_mode);
-    EXPECT_EQ("", entry->file_names_mode);
+    EXPECT_EQ("", entry->encryption_options);
     EXPECT_EQ(0, entry->erase_blk_size);
     EXPECT_EQ(0, entry->logical_blk_size);
     EXPECT_EQ("", entry->sysfs_path);
@@ -448,8 +448,7 @@
     EXPECT_EQ(0, entry->max_comp_streams);
     EXPECT_EQ(0, entry->zram_size);
     EXPECT_EQ(0, entry->reserved_size);
-    EXPECT_EQ("", entry->file_contents_mode);
-    EXPECT_EQ("", entry->file_names_mode);
+    EXPECT_EQ("", entry->encryption_options);
     EXPECT_EQ(0, entry->erase_blk_size);
     EXPECT_EQ(0, entry->logical_blk_size);
     EXPECT_EQ("", entry->sysfs_path);
@@ -458,16 +457,14 @@
     EXPECT_EQ("", entry->zram_backing_dev_path);
     entry++;
 
-    // forcefdeorfbe sets file_contents_mode and file_names_mode by default, so test it separately.
+    // forcefdeorfbe has its own encryption_options defaults, so test it separately.
     EXPECT_EQ("none2", entry->mount_point);
     {
         FstabEntry::FsMgrFlags flags = {};
         flags.force_fde_or_fbe = true;
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
+    EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
     EXPECT_EQ("", entry->key_loc);
 }
 
@@ -681,37 +678,21 @@
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
 
     EXPECT_EQ("/dir/key", entry->key_loc);
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
+    EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
-source none0       swap   defaults      fileencryption=blah
-source none1       swap   defaults      fileencryption=software
-source none2       swap   defaults      fileencryption=aes-256-xts
-source none3       swap   defaults      fileencryption=adiantum
-source none4       swap   defaults      fileencryption=adiantum:aes-256-heh
-source none5       swap   defaults      fileencryption=ice
-source none6       swap   defaults      fileencryption=ice:blah
-source none7       swap   defaults      fileencryption=ice:aes-256-cts
-source none8       swap   defaults      fileencryption=ice:aes-256-heh
-source none9       swap   defaults      fileencryption=ice:adiantum
-source none10      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v1
-source none11      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v2
-source none12      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v2:
-source none13      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:blah
-source none14      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:vblah
+source none0       swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v1
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    ASSERT_EQ(15U, fstab.size());
+    ASSERT_EQ(1U, fstab.size());
 
     FstabEntry::FsMgrFlags flags = {};
     flags.file_encryption = true;
@@ -719,107 +700,7 @@
     auto entry = fstab.begin();
     EXPECT_EQ("none0", entry->mount_point);
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("", entry->file_contents_mode);
-    EXPECT_EQ("", entry->file_names_mode);
-    EXPECT_EQ(0, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none1", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none2", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none3", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("adiantum", entry->file_contents_mode);
-    EXPECT_EQ("adiantum", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none4", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("adiantum", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-heh", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none5", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("ice", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none6", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("ice", entry->file_contents_mode);
-    EXPECT_EQ("", entry->file_names_mode);
-    EXPECT_EQ(0, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none7", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("ice", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none8", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("ice", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-heh", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none9", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("ice", entry->file_contents_mode);
-    EXPECT_EQ("adiantum", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none10", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(1, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none11", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(2, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none12", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("", entry->file_contents_mode);
-    EXPECT_EQ("", entry->file_names_mode);
-    EXPECT_EQ(0, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none13", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(0, entry->file_policy_version);
-
-    entry++;
-    EXPECT_EQ("none14", entry->mount_point);
-    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
-    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
-    EXPECT_EQ(0, entry->file_policy_version);
+    EXPECT_EQ("aes-256-xts:aes-256-cts:v1", entry->encryption_options);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MaxCompStreams) {
diff --git a/init/Android.bp b/init/Android.bp
index ce5b12a..bd2d38c 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -82,7 +82,6 @@
         "libfscrypt",
         "libgsi",
         "libhidl-gen-utils",
-        "libjsoncpp",
         "libkeyutils",
         "liblog",
         "liblogwrap",
@@ -286,13 +285,11 @@
     shared_libs: [
         "libcutils",
         "libhidl-gen-utils",
+        "libhidlmetadata",
         "liblog",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
     ],
-    header_libs: [
-        "libjsoncpp_headers",
-    ],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2f2ead0..b2c6461 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -42,6 +42,8 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <memory>
+
 #include <ApexProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
@@ -627,6 +629,8 @@
     return Error() << "Invalid code: " << code;
 }
 
+static int initial_mount_fstab_return_code = -1;
+
 /* mount_all <fstab> [ <path> ]* [--<options>]*
  *
  * This function might request a reboot, in which case it will
@@ -662,6 +666,7 @@
     if (!ReadFstabFromFile(fstab_file, &fstab)) {
         return Error() << "Could not read fstab";
     }
+
     auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
     property_set(prop_name, std::to_string(t.duration().count()));
 
@@ -673,6 +678,7 @@
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
+        initial_mount_fstab_return_code = mount_fstab_return_code;
         auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
         if (!queue_fs_result) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
@@ -1132,6 +1138,25 @@
     return ExecWithFunctionOnFailure(args, reboot);
 }
 
+static Result<void> do_remount_userdata(const BuiltinArguments& args) {
+    if (initial_mount_fstab_return_code == -1) {
+        return Error() << "Calling remount_userdata too early";
+    }
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        // TODO(b/135984674): should we reboot here?
+        return Error() << "Failed to read fstab";
+    }
+    // TODO(b/135984674): check that fstab contains /data.
+    if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
+        TriggerShutdown("reboot,mount-userdata-failed");
+    }
+    if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result) {
+        return Error() << "queue_fs_event() failed: " << result.error();
+    }
+    return {};
+}
+
 static Result<void> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return {};
 
@@ -1243,6 +1268,7 @@
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"umount_all",              {1,     1,    {false,  do_umount_all}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
+        {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
         {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
         {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index bbebbe8..5fa07dd 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -39,6 +39,8 @@
 
 #define TAG "fscrypt"
 
+using namespace android::fscrypt;
+
 static int set_policy_on(const std::string& ref_basename, const std::string& dir);
 
 int fscrypt_install_keyring() {
@@ -164,32 +166,12 @@
     return err;
 }
 
-static int parse_encryption_options_string(const std::string& options_string,
-                                           std::string* contents_mode_ret,
-                                           std::string* filenames_mode_ret,
-                                           int* policy_version_ret) {
-    auto parts = android::base::Split(options_string, ":");
-
-    if (parts.size() != 3) {
-        return -1;
-    }
-
-    *contents_mode_ret = parts[0];
-    *filenames_mode_ret = parts[1];
-    if (!android::base::StartsWith(parts[2], 'v') ||
-        !android::base::ParseInt(&parts[2][1], policy_version_ret)) {
-        return -1;
-    }
-
-    return 0;
-}
-
 // Set an encryption policy on the given directory.  The policy (key reference
 // and encryption options) to use is read from files that were written by vold.
 static int set_policy_on(const std::string& ref_basename, const std::string& dir) {
+    EncryptionPolicy policy;
     std::string ref_filename = std::string("/data") + ref_basename;
-    std::string key_ref;
-    if (!android::base::ReadFileToString(ref_filename, &key_ref)) {
+    if (!android::base::ReadFileToString(ref_filename, &policy.key_raw_ref)) {
         LOG(ERROR) << "Unable to read system policy to set on " << dir;
         return -1;
     }
@@ -200,24 +182,15 @@
         LOG(ERROR) << "Cannot read encryption options string";
         return -1;
     }
-
-    std::string contents_mode;
-    std::string filenames_mode;
-    int policy_version = 0;
-
-    if (parse_encryption_options_string(options_string, &contents_mode, &filenames_mode,
-                                        &policy_version)) {
+    if (!ParseOptions(options_string, &policy.options)) {
         LOG(ERROR) << "Invalid encryption options string: " << options_string;
         return -1;
     }
 
-    int result =
-            fscrypt_policy_ensure(dir.c_str(), key_ref.c_str(), key_ref.length(),
-                                  contents_mode.c_str(), filenames_mode.c_str(), policy_version);
-    if (result) {
-        LOG(ERROR) << android::base::StringPrintf("Setting %02x%02x%02x%02x policy on %s failed!",
-                                                  key_ref[0], key_ref[1], key_ref[2], key_ref[3],
-                                                  dir.c_str());
+    if (!EnsurePolicy(policy, dir)) {
+        std::string ref_hex;
+        BytesToHex(policy.key_raw_ref, &ref_hex);
+        LOG(ERROR) << "Setting " << ref_hex << " policy on " << dir << " failed!";
         return -1;
     }
 
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index b2402b3..522709e 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -30,6 +30,7 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <hidl/metadata.h>
 
 #include "action.h"
 #include "action_manager.h"
@@ -142,28 +143,46 @@
 #include "generated_stub_builtin_function_map.h"
 
 void PrintUsage() {
-    std::cout << "usage: host_init_verifier [-p FILE] -i FILE <init rc file>\n"
+    std::cout << "usage: host_init_verifier [-p FILE] <init rc file>\n"
                  "\n"
                  "Tests an init script for correctness\n"
                  "\n"
                  "-p FILE\tSearch this passwd file for users and groups\n"
-                 "-i FILE\tParse this JSON file for the HIDL interface inheritance hierarchy\n"
               << std::endl;
 }
 
+Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {
+    InterfaceInheritanceHierarchyMap result;
+    for (const HidlInterfaceMetadata& iface : HidlInterfaceMetadata::all()) {
+        std::set<FQName> inherited_interfaces;
+        for (const std::string& intf : iface.inherited) {
+            FQName fqname;
+            if (!fqname.setTo(intf)) {
+                return Error() << "Unable to parse interface '" << intf << "'";
+            }
+            inherited_interfaces.insert(fqname);
+        }
+        FQName fqname;
+        if (!fqname.setTo(iface.name)) {
+            return Error() << "Unable to parse interface '" << iface.name << "'";
+        }
+        result[fqname] = inherited_interfaces;
+    }
+
+    return result;
+}
+
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::StdioLogger);
     android::base::SetMinimumLogSeverity(android::base::ERROR);
 
-    std::string interface_inheritance_hierarchy_file;
-
     while (true) {
         static const struct option long_options[] = {
                 {"help", no_argument, nullptr, 'h'},
                 {nullptr, 0, nullptr, 0},
         };
 
-        int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
+        int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
 
         if (arg == -1) {
             break;
@@ -176,9 +195,6 @@
             case 'p':
                 passwd_files.emplace_back(optarg);
                 break;
-            case 'i':
-                interface_inheritance_hierarchy_file = optarg;
-                break;
             default:
                 std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
                 return EXIT_FAILURE;
@@ -188,13 +204,12 @@
     argc -= optind;
     argv += optind;
 
-    if (argc != 1 || interface_inheritance_hierarchy_file.empty()) {
+    if (argc != 1) {
         PrintUsage();
         return EXIT_FAILURE;
     }
 
-    auto interface_inheritance_hierarchy_map =
-            ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file);
+    auto interface_inheritance_hierarchy_map = ReadInterfaceInheritanceHierarchy();
     if (!interface_inheritance_hierarchy_map) {
         LOG(ERROR) << interface_inheritance_hierarchy_map.error();
         return EXIT_FAILURE;
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
index ddbacd7..1b76bba 100644
--- a/init/interface_utils.cpp
+++ b/init/interface_utils.cpp
@@ -21,7 +21,6 @@
 
 #include <android-base/strings.h>
 #include <hidl-util/FqInstance.h>
-#include <json/json.h>
 
 using android::FqInstance;
 using android::FQName;
@@ -42,37 +41,6 @@
 
 }  // namespace
 
-Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(
-        const std::string& path) {
-    Json::Value root;
-    Json::Reader reader;
-    std::ifstream stream(path);
-    if (!reader.parse(stream, root)) {
-        return Error() << "Failed to read interface inheritance hierarchy file: " << path << "\n"
-                       << reader.getFormattedErrorMessages();
-    }
-
-    InterfaceInheritanceHierarchyMap result;
-    for (const Json::Value& entry : root) {
-        std::set<FQName> inherited_interfaces;
-        for (const Json::Value& intf : entry["inheritedInterfaces"]) {
-            FQName fqname;
-            if (!fqname.setTo(intf.asString())) {
-                return Error() << "Unable to parse interface '" << intf.asString() << "'";
-            }
-            inherited_interfaces.insert(fqname);
-        }
-        std::string intf_string = entry["interface"].asString();
-        FQName fqname;
-        if (!fqname.setTo(intf_string)) {
-            return Error() << "Unable to parse interface '" << intf_string << "'";
-        }
-        result[fqname] = inherited_interfaces;
-    }
-
-    return result;
-}
-
 Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
                                                 const InterfaceInheritanceHierarchyMap& hierarchy) {
     std::set<FQName> interface_fqnames;
diff --git a/init/interface_utils.h b/init/interface_utils.h
index bd0c104..4ca377f 100644
--- a/init/interface_utils.h
+++ b/init/interface_utils.h
@@ -29,9 +29,6 @@
 
 using InterfaceInheritanceHierarchyMap = std::map<android::FQName, std::set<android::FQName>>;
 
-// Reads the HIDL interface inheritance hierarchy JSON file at the given path.
-Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(const std::string& path);
-
 // For the given set of interfaces / interface instances, checks that each
 // interface's hierarchy of inherited interfaces is also included in the given
 // interface set. Uses the provided hierarchy data.
diff --git a/init/reboot.cpp b/init/reboot.cpp
index d77b975..fc18ecb 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -181,10 +181,17 @@
     }
 }
 
-static void ShutdownVold() {
+static Result<void> ShutdownVold() {
     const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
     int status;
-    logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true, nullptr);
+    if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,
+                            nullptr) != 0) {
+        return ErrnoError() << "Failed to call 'vdc volume shutdown'";
+    }
+    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+        return {};
+    }
+    return Error() << "'vdc volume shutdown' failed : " << status;
 }
 
 static void LogShutdownTime(UmountStat stat, Timer* t) {
@@ -209,8 +216,8 @@
 
 // Find all read+write block devices and emulated devices in /proc/mounts and add them to
 // the correpsponding list.
-static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
-                                   std::vector<MountEntry>* emulatedPartitions, bool dump) {
+static bool FindPartitionsToUmount(std::vector<MountEntry>* block_dev_partitions,
+                                   std::vector<MountEntry>* emulated_partitions, bool dump) {
     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
     if (fp == nullptr) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
@@ -227,10 +234,10 @@
             // Do not umount them as shutdown critical services may rely on them.
             if (mount_dir != "/" && mount_dir != "/system" && mount_dir != "/vendor" &&
                 mount_dir != "/oem") {
-                blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+                block_dev_partitions->emplace(block_dev_partitions->begin(), *mentry);
             }
         } else if (MountEntry::IsEmulatedDevice(*mentry)) {
-            emulatedPartitions->emplace(emulatedPartitions->begin(), *mentry);
+            emulated_partitions->emplace(emulated_partitions->begin(), *mentry);
         }
     }
     return true;
@@ -292,8 +299,9 @@
 }
 
 // Create reboot/shutdwon monitor thread
-void RebootMonitorThread(unsigned int cmd, const std::string& rebootTarget, sem_t* reboot_semaphore,
-                         std::chrono::milliseconds shutdown_timeout, bool* reboot_monitor_run) {
+void RebootMonitorThread(unsigned int cmd, const std::string& reboot_target,
+                         sem_t* reboot_semaphore, std::chrono::milliseconds shutdown_timeout,
+                         bool* reboot_monitor_run) {
     unsigned int remaining_shutdown_time = 0;
 
     // 30 seconds more than the timeout passed to the thread as there is a final Umount pass
@@ -355,7 +363,7 @@
 
                 WriteStringToFile("u", PROC_SYSRQ);
 
-                RebootSystem(cmd, rebootTarget);
+                RebootSystem(cmd, reboot_target);
             }
 
             LOG(ERROR) << "Trigger crash at last!";
@@ -385,13 +393,13 @@
  *
  * return true when umount was successful. false when timed out.
  */
-static UmountStat TryUmountAndFsck(unsigned int cmd, const std::string& rebootTarget, bool runFsck,
+static UmountStat TryUmountAndFsck(unsigned int cmd, bool run_fsck,
                                    std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
     Timer t;
     std::vector<MountEntry> block_devices;
     std::vector<MountEntry> emulated_devices;
 
-    if (runFsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
+    if (run_fsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
         return UMOUNT_STAT_ERROR;
     }
 
@@ -405,7 +413,7 @@
         if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
     }
 
-    if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+    if (stat == UMOUNT_STAT_SUCCESS && run_fsck) {
         LOG(INFO) << "Pause reboot monitor thread before fsck";
         sem_post(reboot_semaphore);
 
@@ -426,11 +434,11 @@
 #define ZRAM_DEVICE   "/dev/block/zram0"
 #define ZRAM_RESET    "/sys/block/zram0/reset"
 #define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
-static void KillZramBackingDevice() {
+static Result<void> KillZramBackingDevice() {
     std::string backing_dev;
-    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return;
+    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return {};
 
-    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return;
+    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return {};
 
     // cut the last "\n"
     backing_dev.erase(backing_dev.length() - 1);
@@ -439,28 +447,29 @@
     Timer swap_timer;
     LOG(INFO) << "swapoff() start...";
     if (swapoff(ZRAM_DEVICE) == -1) {
-        LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed";
-        return;
+        return ErrnoError() << "zram_backing_dev: swapoff (" << backing_dev << ")"
+                            << " failed";
     }
     LOG(INFO) << "swapoff() took " << swap_timer;;
 
     if (!WriteStringToFile("1", ZRAM_RESET)) {
-        LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
-        return;
+        return Error() << "zram_backing_dev: reset (" << backing_dev << ")"
+                       << " failed";
     }
 
     // clear loopback device
     unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
     if (loop.get() < 0) {
-        LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed";
-        return;
+        return ErrnoError() << "zram_backing_dev: open(" << backing_dev << ")"
+                            << " failed";
     }
 
     if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {
-        LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed";
-        return;
+        return ErrnoError() << "zram_backing_dev: loop_clear (" << backing_dev << ")"
+                            << " failed";
     }
     LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
+    return {};
 }
 
 // Stops given services, waits for them to be stopped for |timeout| ms.
@@ -509,20 +518,19 @@
 //* 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.
+// reboot_target Reboot target string like "bootloader". Otherwise, it should be an empty string.
+// run_fsck Whether to run fsck after umount is done.
 //
-static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
-                     bool runFsck) {
+static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
+                     bool run_fsck) {
     Timer t;
-    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
+    LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
 
     // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
     // worry about unmounting it.
     if (!IsDataMounted()) {
         sync();
-        RebootSystem(cmd, rebootTarget);
+        RebootSystem(cmd, reboot_target);
         abort();
     }
 
@@ -557,13 +565,13 @@
     if (sem_init(&reboot_semaphore, false, 0) == -1) {
         // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
         LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
-        RebootSystem(cmd, rebootTarget);
+        RebootSystem(cmd, reboot_target);
     }
 
     // Start a thread to monitor init shutdown process
     LOG(INFO) << "Create reboot monitor thread.";
     bool reboot_monitor_run = true;
-    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, rebootTarget, &reboot_semaphore,
+    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,
                                       shutdown_timeout, &reboot_monitor_run);
     reboot_monitor_thread.detach();
 
@@ -600,16 +608,16 @@
         TurnOffBacklight();
     }
 
-    Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
-    Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
-    if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
+    Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
+    Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
+    if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
         bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
 
         if (do_shutdown_animation) {
             property_set("service.bootanim.exit", "0");
             // Could be in the middle of animation. Stop and start so that it can pick
             // up the right mode.
-            bootAnim->Stop();
+            boot_anim->Stop();
         }
 
         for (const auto& service : ServiceList::GetInstance()) {
@@ -625,9 +633,9 @@
         }
 
         if (do_shutdown_animation) {
-            bootAnim->Start();
-            surfaceFlinger->SetShutdownCritical();
-            bootAnim->SetShutdownCritical();
+            boot_anim->Start();
+            surface_flinger->SetShutdownCritical();
+            boot_anim->SetShutdownCritical();
         }
     }
 
@@ -643,10 +651,10 @@
     ReapAnyOutstandingChildren();
 
     // 3. send volume shutdown to vold
-    Service* voldService = ServiceList::GetInstance().FindService("vold");
-    if (voldService != nullptr && voldService->IsRunning()) {
+    Service* vold_service = ServiceList::GetInstance().FindService("vold");
+    if (vold_service != nullptr && vold_service->IsRunning()) {
         ShutdownVold();
-        voldService->Stop();
+        vold_service->Stop();
     } else {
         LOG(INFO) << "vold not running, skipping vold shutdown";
     }
@@ -662,8 +670,8 @@
     // 5. drop caches and disable zram backing device, if exist
     KillZramBackingDevice();
 
-    UmountStat stat = TryUmountAndFsck(cmd, rebootTarget, runFsck, shutdown_timeout - t.duration(),
-                                       &reboot_semaphore);
+    UmountStat stat =
+            TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
     // Follow what linux shutdown is doing: one more sync with little bit delay
     {
         Timer sync_timer;
@@ -679,7 +687,7 @@
     sem_post(&reboot_semaphore);
 
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
-    RebootSystem(cmd, rebootTarget);
+    RebootSystem(cmd, reboot_target);
     abort();
 }
 
@@ -738,7 +746,23 @@
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " post-data services are still running";
     }
-    // TODO(b/135984674): remount userdata
+    // We only really need to restart vold if userdata is ext4 filesystem.
+    // TODO(b/135984674): get userdata fs type here, and do nothing in case of f2fs.
+    // First shutdown volumes managed by vold. They will be recreated by
+    // system_server.
+    Service* vold_service = ServiceList::GetInstance().FindService("vold");
+    if (vold_service != nullptr && vold_service->IsRunning()) {
+        if (auto result = ShutdownVold(); !result) {
+            return result;
+        }
+        LOG(INFO) << "Restarting vold";
+        vold_service->Restart();
+    }
+    // Again, we only need to kill zram backing device in case of ext4 userdata.
+    // TODO(b/135984674): get userdata fs type here, and do nothing in case of f2fs.
+    if (auto result = KillZramBackingDevice(); !result) {
+        return result;
+    }
     if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */), 5s,
                                              false /* SIGKILL */);
         r > 0) {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index c40c5ef..91bd52c 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -105,6 +105,11 @@
         versions: ["10000"],
     },
 
+    // TODO(tomcherry): Renable this before release branch is cut
+    header_abi_checker: {
+        enabled: false,
+    },
+
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/liblog/README.protocol.md b/liblog/README.protocol.md
new file mode 100644
index 0000000..fef29c9
--- /dev/null
+++ b/liblog/README.protocol.md
@@ -0,0 +1,49 @@
+# liblog -> logd
+
+The data that liblog sends to logd is represented below.
+
+    struct {
+        android_log_header_t header;
+        union {
+           struct {
+                char     prio;
+                char     tag[...];
+                char     message[...];
+            } string;
+            struct {
+                android_event_header_t event_header;
+                android_event_*_t      payload[...];
+            } binary;
+        };
+    };
+
+The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
+
+## header
+
+The header is added immediately before sending the log message to logd.
+
+## `string` payload
+
+The `string` part of the union is for normal buffers (main, system, radio, etc) and consists of a
+single character priority, followed by a variable length null terminated string for the tag, and
+finally a variable length null terminated string for the message.
+
+This payload is used for the `__android_log_buf_write()` family of functions.
+
+## `binary` payload
+
+The `binary` part of the union is for binary buffers (events, security, etc) and consists of an
+android_event_header_t struct followed by a variable number of android_event_*_t
+(android_event_list_t, android_event_int_t, etc) structs.
+
+If multiple android_event_*_t elements are present, then they must be in a list and the first
+element in payload must be an android_event_list_t.
+
+This payload is used for the `__android_log_bwrite()` family of functions. It is additionally used
+for `android_log_write_list()` and the related functions that manipulate event lists.
+
+# logd -> liblog
+
+logd sends a `logger_entry` struct to liblog followed by the payload. The payload is identical to
+the payloads defined above. The max size of the entire message from logd is LOGGER_ENTRY_MAX_LEN.
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 2079e7a..ee3b250 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -50,53 +50,9 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wzero-length-array"
-/*
- * The userspace structure for version 1 of the logger_entry ABI.
- */
 struct logger_entry {
-  uint16_t len;   /* length of the payload */
-  uint16_t __pad; /* no matter what, we get 2 bytes of padding */
-  int32_t pid;    /* generating process's pid */
-  int32_t tid;    /* generating process's tid */
-  int32_t sec;    /* seconds since Epoch */
-  int32_t nsec;   /* nanoseconds */
-  char msg[0]; /* the entry's payload */
-};
-
-/*
- * The userspace structure for version 2 of the logger_entry ABI.
- */
-struct logger_entry_v2 {
   uint16_t len;      /* length of the payload */
-  uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
-  int32_t pid;       /* generating process's pid */
-  int32_t tid;       /* generating process's tid */
-  int32_t sec;       /* seconds since Epoch */
-  int32_t nsec;      /* nanoseconds */
-  uint32_t euid;     /* effective UID of logger */
-  char msg[0]; /* the entry's payload */
-} __attribute__((__packed__));
-
-/*
- * The userspace structure for version 3 of the logger_entry ABI.
- */
-struct logger_entry_v3 {
-  uint16_t len;      /* length of the payload */
-  uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */
-  int32_t pid;       /* generating process's pid */
-  int32_t tid;       /* generating process's tid */
-  int32_t sec;       /* seconds since Epoch */
-  int32_t nsec;      /* nanoseconds */
-  uint32_t lid;      /* log id of the payload */
-  char msg[0]; /* the entry's payload */
-} __attribute__((__packed__));
-
-/*
- * The userspace structure for version 4 of the logger_entry ABI.
- */
-struct logger_entry_v4 {
-  uint16_t len;      /* length of the payload */
-  uint16_t hdr_size; /* sizeof(struct logger_entry_v4) */
+  uint16_t hdr_size; /* sizeof(struct logger_entry) */
   int32_t pid;       /* generating process's pid */
   uint32_t tid;      /* generating process's tid */
   uint32_t sec;      /* seconds since Epoch */
@@ -124,11 +80,7 @@
 struct log_msg {
   union {
     unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
-    struct logger_entry_v4 entry;
-    struct logger_entry_v4 entry_v4;
-    struct logger_entry_v3 entry_v3;
-    struct logger_entry_v2 entry_v2;
-    struct logger_entry entry_v1;
+    struct logger_entry entry;
   } __attribute__((aligned(4)));
 #ifdef __cplusplus
   /* Matching log_time operators */
@@ -162,19 +114,12 @@
   }
   char* msg() {
     unsigned short hdr_size = entry.hdr_size;
-    if (!hdr_size) {
-      hdr_size = sizeof(entry_v1);
-    }
-    if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
+    if (hdr_size != sizeof(entry)) {
       return nullptr;
     }
     return reinterpret_cast<char*>(buf) + hdr_size;
   }
-  unsigned int len() {
-    return (entry.hdr_size ? entry.hdr_size
-                           : static_cast<uint16_t>(sizeof(entry_v1))) +
-           entry.len;
-  }
+  unsigned int len() { return entry.hdr_size + entry.len; }
 #endif
 };
 
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 06a2baf..a22c3be 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -148,24 +148,6 @@
     return 0;
   }
 
-  /*
-   *  struct {
-   *      // what we provide to socket
-   *      android_log_header_t header;
-   *      // caller provides
-   *      union {
-   *          struct {
-   *              char     prio;
-   *              char     payload[];
-   *          } string;
-   *          struct {
-   *              uint32_t tag
-   *              char     payload[];
-   *          } binary;
-   *      };
-   *  };
-   */
-
   header.tid = gettid();
   header.realtime.tv_sec = ts->tv_sec;
   header.realtime.tv_nsec = ts->tv_nsec;
diff --git a/liblog/logger.h b/liblog/logger.h
index 8cae66c..02cad22 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -96,8 +96,6 @@
 
   struct android_log_transport_read* transport;
   unsigned logMask;      /* mask of requested log buffers */
-  int ret;               /* return value associated with following data */
-  struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
 };
 
 struct android_log_logger_list {
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index ff816b7..4b4012a 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -92,7 +92,6 @@
 
   logger_list->transport_context.transport = transport;
   logger_list->transport_context.logMask = logMask;
-  logger_list->transport_context.ret = 1;
 #endif
   return 0;
 }
@@ -273,34 +272,24 @@
                                   struct log_msg* log_msg) {
   int ret = (*transp->transport->read)(logger_list, transp, log_msg);
 
+  if (ret < 0) {
+    return ret;
+  }
+
   if (ret > (int)sizeof(*log_msg)) {
     ret = sizeof(*log_msg);
   }
 
-  transp->ret = ret;
-
-  /* propagate errors, or make sure len & hdr_size members visible */
-  if (ret < (int)(sizeof(log_msg->entry.len) + sizeof(log_msg->entry.hdr_size))) {
-    if (ret >= (int)sizeof(log_msg->entry.len)) {
-      log_msg->entry.len = 0;
-    }
-    return ret;
-  }
-
-  /* hdr_size correction (logger_entry -> logger_entry_v2+ conversion) */
-  if (log_msg->entry_v2.hdr_size == 0) {
-    log_msg->entry_v2.hdr_size = sizeof(struct logger_entry);
-  }
-  if ((log_msg->entry_v2.hdr_size < sizeof(log_msg->entry_v1)) ||
-      (log_msg->entry_v2.hdr_size > sizeof(log_msg->entry))) {
+  if (ret < static_cast<int>(sizeof(log_msg->entry))) {
     return -EINVAL;
   }
 
-  /* len validation */
-  if (ret <= log_msg->entry_v2.hdr_size) {
-    log_msg->entry.len = 0;
-  } else {
-    log_msg->entry.len = ret - log_msg->entry_v2.hdr_size;
+  if (log_msg->entry.hdr_size != sizeof(log_msg->entry)) {
+    return -EINVAL;
+  }
+
+  if (log_msg->entry.len > ret - log_msg->entry.hdr_size) {
+    return -EINVAL;
   }
 
   return ret;
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 82fbafd..4b61828 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -532,18 +532,12 @@
 
   int i;
   char* msg = buf->msg;
-  struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
-  if (buf2->hdr_size) {
-    if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
-        (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
-      fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
-      return -1;
-    }
-    msg = ((char*)buf2) + buf2->hdr_size;
-    if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
-      entry->uid = ((struct logger_entry_v4*)buf)->uid;
-    }
+  if (buf->hdr_size != sizeof(struct logger_entry)) {
+    fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+    return -1;
   }
+  entry->uid = buf->uid;
+
   for (i = 1; i < buf->len; i++) {
     if (msg[i] == '\0') {
       if (msgStart == -1) {
@@ -993,27 +987,15 @@
   entry->pid = buf->pid;
   entry->tid = buf->tid;
 
-  /*
-   * Pull the tag out, fill in some additional details based on incoming
-   * buffer version (v3 adds lid, v4 adds uid).
-   */
   eventData = (const unsigned char*)buf->msg;
-  struct logger_entry_v2* buf2 = (struct logger_entry_v2*)buf;
-  if (buf2->hdr_size) {
-    if ((buf2->hdr_size < sizeof(((struct log_msg*)NULL)->entry_v1)) ||
-        (buf2->hdr_size > sizeof(((struct log_msg*)NULL)->entry))) {
-      fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
-      return -1;
-    }
-    eventData = ((unsigned char*)buf2) + buf2->hdr_size;
-    if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) &&
-        (((struct logger_entry_v3*)buf)->lid == LOG_ID_SECURITY)) {
-      entry->priority = ANDROID_LOG_WARN;
-    }
-    if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
-      entry->uid = ((struct logger_entry_v4*)buf)->uid;
-    }
+  if (buf->hdr_size != sizeof(struct logger_entry)) {
+    fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+    return -1;
   }
+  if (buf->lid == LOG_ID_SECURITY) {
+    entry->priority = ANDROID_LOG_WARN;
+  }
+  entry->uid = buf->uid;
   inCount = buf->len;
   if (inCount < sizeof(android_event_header_t)) return -1;
   auto* event_header = reinterpret_cast<const android_event_header_t*>(eventData);
@@ -1069,9 +1051,6 @@
   if ((result == 1) && fmtStr) {
     /* We overflowed :-(, let's repaint the line w/o format dressings */
     eventData = (const unsigned char*)buf->msg;
-    if (buf2->hdr_size) {
-      eventData = ((unsigned char*)buf2) + buf2->hdr_size;
-    }
     eventData += 4;
     outBuf = messageBuf;
     outRemaining = messageBufLen - 1;
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index ce923f3..f43ce3a 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -144,7 +144,7 @@
           ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
            (logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
         (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
-      char* msg = log_msg->entry_v4.msg;
+      char* msg = log_msg->entry.msg;
       *msg = buf.prio;
       fd = atomic_load(&transp->context.fd);
       if (fd <= 0) {
@@ -158,16 +158,16 @@
         return -EIO;
       }
 
-      log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
-      log_msg->entry_v4.hdr_size = sizeof(log_msg->entry_v4);
-      log_msg->entry_v4.pid = buf.p.pid;
-      log_msg->entry_v4.tid = buf.l.tid;
-      log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
-      log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
-      log_msg->entry_v4.lid = buf.l.id;
-      log_msg->entry_v4.uid = buf.p.uid;
+      log_msg->entry.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
+      log_msg->entry.hdr_size = sizeof(log_msg->entry);
+      log_msg->entry.pid = buf.p.pid;
+      log_msg->entry.tid = buf.l.tid;
+      log_msg->entry.sec = buf.l.realtime.tv_sec;
+      log_msg->entry.nsec = buf.l.realtime.tv_nsec;
+      log_msg->entry.lid = buf.l.id;
+      log_msg->entry.uid = buf.p.uid;
 
-      return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
+      return ret + sizeof(buf.prio) + log_msg->entry.hdr_size;
     }
 
     fd = atomic_load(&transp->context.fd);
@@ -215,7 +215,7 @@
   struct android_log_transport_context transp;
   struct content {
     struct listnode node;
-    struct logger_entry_v4 entry;
+    struct logger_entry entry;
   } * content;
   struct names {
     struct listnode node;
@@ -267,25 +267,26 @@
   }
 
   /* Read the file content */
-  while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
+  log_msg log_msg;
+  while (pmsgRead(&logger_list, &transp, &log_msg) > 0) {
     const char* cp;
-    size_t hdr_size = transp.logMsg.entry.hdr_size ? transp.logMsg.entry.hdr_size
-                                                   : sizeof(transp.logMsg.entry_v1);
-    char* msg = (char*)&transp.logMsg + hdr_size;
+    size_t hdr_size = log_msg.entry.hdr_size;
+
+    char* msg = (char*)&log_msg + hdr_size;
     const char* split = NULL;
 
-    if ((hdr_size < sizeof(transp.logMsg.entry_v1)) || (hdr_size > sizeof(transp.logMsg.entry))) {
+    if (hdr_size != sizeof(log_msg.entry)) {
       continue;
     }
     /* Check for invalid sequence number */
-    if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) ||
-        ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
-         ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) {
+    if (log_msg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE ||
+        (log_msg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+            ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
       continue;
     }
 
     /* Determine if it has <dirbase>:<filebase> format for tag */
-    len = transp.logMsg.entry.len - sizeof(prio);
+    len = log_msg.entry.len - sizeof(prio);
     for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
       if (*cp == ':') {
         if (split) {
@@ -331,8 +332,8 @@
     /* check if there is an existing entry */
     list_for_each(node, &name_list) {
       names = node_to_item(node, struct names, node);
-      if (!strcmp(names->name, msg + sizeof(prio)) && (names->id == transp.logMsg.entry.lid) &&
-          (names->prio == *msg)) {
+      if (!strcmp(names->name, msg + sizeof(prio)) && names->id == log_msg.entry.lid &&
+          names->prio == *msg) {
         break;
       }
     }
@@ -349,7 +350,7 @@
         break;
       }
       strcpy(names->name, msg + sizeof(prio));
-      names->id = static_cast<log_id_t>(transp.logMsg.entry.lid);
+      names->id = static_cast<log_id_t>(log_msg.entry.lid);
       names->prio = *msg;
       list_init(&names->content);
       /*
@@ -402,7 +403,7 @@
     /* Remove any file fragments that match our sequence number */
     list_for_each_safe(node, n, &names->content) {
       content = node_to_item(node, struct content, node);
-      if (transp.logMsg.entry.nsec == content->entry.nsec) {
+      if (log_msg.entry.nsec == content->entry.nsec) {
         list_remove(&content->node);
         free(content);
       }
@@ -410,16 +411,16 @@
 
     /* Add content */
     content = static_cast<struct content*>(
-        calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len));
+        calloc(1, sizeof(content->node) + hdr_size + log_msg.entry.len));
     if (!content) {
       ret = -ENOMEM;
       break;
     }
-    memcpy(&content->entry, &transp.logMsg.entry, hdr_size + transp.logMsg.entry.len);
+    memcpy(&content->entry, &log_msg.entry, hdr_size + log_msg.entry.len);
 
     /* Insert in sequence number sorted order, to ease reconstruction */
     list_for_each_reverse(node, &names->content) {
-      if ((node_to_item(node, struct content, node))->entry.nsec < transp.logMsg.entry.nsec) {
+      if ((node_to_item(node, struct content, node))->entry.nsec < log_msg.entry.nsec) {
         break;
       }
     }
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 94c4fbb..c402e20 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -385,7 +385,7 @@
         fprintf(stderr, "Expect \"Binary log entry conversion failed\"\n");
       }
       int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
-          &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+          &log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
       EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
       if ((processBinaryLogBuffer == 0) || entry.message) {
         size_t line_overhead = 20;
@@ -469,8 +469,7 @@
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
     AndroidLogEntry entry;
-    int processLogBuffer =
-        android_log_processLogBuffer(&log_msg.entry_v1, &entry);
+    int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
     EXPECT_EQ(0, processLogBuffer);
     if (processLogBuffer == 0) {
       size_t line_overhead = 11;
@@ -1013,8 +1012,7 @@
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
     AndroidLogEntry entry;
-    int processLogBuffer =
-        android_log_processLogBuffer(&log_msg.entry_v1, &entry);
+    int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
     EXPECT_EQ(0, processLogBuffer);
     if (processLogBuffer == 0) {
       fflush(stderr);
@@ -2507,8 +2505,8 @@
     EXPECT_TRUE(NULL != logformat);
     AndroidLogEntry entry;
     char msgBuf[1024];
-    int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
-        &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+    int processBinaryLogBuffer =
+        android_log_processBinaryLogBuffer(&log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
     EXPECT_EQ(0, processBinaryLogBuffer);
     if (processBinaryLogBuffer == 0) {
       int line_overhead = 20;
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 2251479..667bddc 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -175,7 +175,7 @@
 }
 
 cc_binary {
-    name: "unzip",
+    name: "ziptool",
     defaults: ["libziparchive_flags"],
     srcs: ["unzip.cpp"],
     shared_libs: [
@@ -183,6 +183,12 @@
         "libziparchive",
     ],
     recovery_available: true,
+    host_supported: true,
+    target: {
+        android: {
+            symlinks: ["unzip", "zipinfo"],
+        },
+    },
 }
 
 cc_fuzz {
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 391cff9..f0f5a1d 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -79,6 +79,12 @@
 
   // The offset to the start of data for this ZipEntry.
   off64_t offset;
+
+  // The version of zip and the host file system this came from.
+  uint16_t version_made_by;
+
+  // Whether this entry is believed to be text or binary.
+  bool is_text;
 };
 
 struct ZipArchive;
@@ -125,6 +131,19 @@
  */
 void CloseArchive(ZipArchiveHandle archive);
 
+/** See GetArchiveInfo(). */
+struct ZipArchiveInfo {
+  /** The size in bytes of the archive itself. Used by zipinfo. */
+  off64_t archive_size;
+  /** The number of entries in the archive. */
+  size_t entry_count;
+};
+
+/**
+ * Returns information about the given archive.
+ */
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
+
 /*
  * Find an entry in the Zip archive, by name. |data| must be non-null.
  *
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 426325e..e936614 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -40,12 +40,15 @@
   kPrompt,
 };
 
+static bool is_unzip;
 static OverwriteMode overwrite_mode = kPrompt;
+static bool flag_1 = false;
 static const char* flag_d = nullptr;
 static bool flag_l = false;
 static bool flag_p = false;
 static bool flag_q = false;
 static bool flag_v = false;
+static bool flag_x = false;
 static const char* archive_name = nullptr;
 static std::set<std::string> includes;
 static std::set<std::string> excludes;
@@ -88,32 +91,51 @@
   return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
 }
 
-static void MaybeShowHeader() {
-  if (!flag_q) printf("Archive:  %s\n", archive_name);
-  if (flag_v) {
-    printf(
-        " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
-        "--------  ------  ------- ---- ---------- ----- --------  ----\n");
-  } else if (flag_l) {
-    printf(
-        "  Length      Date    Time    Name\n"
-        "---------  ---------- -----   ----\n");
+static void MaybeShowHeader(ZipArchiveHandle zah) {
+  if (is_unzip) {
+    // unzip has three formats.
+    if (!flag_q) printf("Archive:  %s\n", archive_name);
+    if (flag_v) {
+      printf(
+          " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
+          "--------  ------  ------- ---- ---------- ----- --------  ----\n");
+    } else if (flag_l) {
+      printf(
+          "  Length      Date    Time    Name\n"
+          "---------  ---------- -----   ----\n");
+    }
+  } else {
+    // zipinfo.
+    if (!flag_1 && includes.empty() && excludes.empty()) {
+      ZipArchiveInfo info{GetArchiveInfo(zah)};
+      printf("Archive:  %s\n", archive_name);
+      printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
+             info.entry_count);
+    }
   }
 }
 
 static void MaybeShowFooter() {
-  if (flag_v) {
-    printf(
-        "--------          -------  ---                            -------\n"
-        "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
-        total_uncompressed_length, total_compressed_length,
-        CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
-        (file_count == 1) ? "" : "s");
-  } else if (flag_l) {
-    printf(
-        "---------                     -------\n"
-        "%9" PRId64 "                     %zu file%s\n",
-        total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+  if (is_unzip) {
+    if (flag_v) {
+      printf(
+          "--------          -------  ---                            -------\n"
+          "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
+          total_uncompressed_length, total_compressed_length,
+          CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+          (file_count == 1) ? "" : "s");
+    } else if (flag_l) {
+      printf(
+          "---------                     -------\n"
+          "%9" PRId64 "                     %zu file%s\n",
+          total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+    }
+  } else {
+    if (!flag_1 && includes.empty() && excludes.empty()) {
+      printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %3d%%\n",
+             file_count, total_uncompressed_length, total_compressed_length,
+             CompressionRatio(total_uncompressed_length, total_compressed_length));
+    }
   }
 }
 
@@ -226,17 +248,61 @@
   }
 }
 
+static void InfoOne(const ZipEntry& entry, const std::string& name) {
+  if (flag_1) {
+    // "android-ndk-r19b/sources/android/NOTICE"
+    printf("%s\n", name.c_str());
+    return;
+  }
+
+  int version = entry.version_made_by & 0xff;
+  int os = (entry.version_made_by >> 8) & 0xff;
+
+  // TODO: Support suid/sgid? Non-Unix host file system attributes?
+  char mode[] = "??????????";
+  if (os == 3) {
+    mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
+    mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
+    mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
+    mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
+    mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
+    mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
+    mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
+    mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
+    mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
+    mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
+  }
+
+  // TODO: zipinfo (unlike unzip) sometimes uses time zone?
+  // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
+  tm t = entry.GetModificationTime();
+  char time[32];
+  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+           t.tm_mday, t.tm_hour, t.tm_min);
+
+  // "-rw-r--r--  3.0 unx      577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
+  printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10,
+         os == 3 ? "unx" : "???", entry.uncompressed_length, entry.is_text ? 't' : 'b',
+         entry.has_data_descriptor ? 'X' : 'x', entry.method == kCompressStored ? "stor" : "defX",
+         time, name.c_str());
+}
+
 static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
-  if (flag_l || flag_v) {
-    // -l or -lv or -lq or -v.
-    ListOne(entry, name);
-  } else {
-    // Actually extract.
-    if (flag_p) {
-      ExtractToPipe(zah, entry, name);
+  if (is_unzip) {
+    if (flag_l || flag_v) {
+      // -l or -lv or -lq or -v.
+      ListOne(entry, name);
     } else {
-      ExtractOne(zah, entry, name);
+      // Actually extract.
+      if (flag_p) {
+        ExtractToPipe(zah, entry, name);
+      } else {
+        ExtractOne(zah, entry, name);
+      }
     }
+  } else {
+    // zipinfo or zipinfo -1.
+    InfoOne(entry, name);
   }
   total_uncompressed_length += entry.uncompressed_length;
   total_compressed_length += entry.compressed_length;
@@ -244,7 +310,7 @@
 }
 
 static void ProcessAll(ZipArchiveHandle zah) {
-  MaybeShowHeader();
+  MaybeShowHeader(zah);
 
   // libziparchive iteration order doesn't match the central directory.
   // We could sort, but that would cost extra and wouldn't match either.
@@ -267,73 +333,110 @@
 }
 
 static void ShowHelp(bool full) {
-  fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
-  if (!full) exit(EXIT_FAILURE);
+  if (is_unzip) {
+    fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+    if (!full) exit(EXIT_FAILURE);
 
-  printf(
-      "\n"
-      "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
-      "exclude (-x) lists use shell glob patterns.\n"
-      "\n"
-      "-d DIR	Extract into DIR\n"
-      "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
-      "-n	Never overwrite files (default: prompt)\n"
-      "-o	Always overwrite files\n"
-      "-p	Pipe to stdout\n"
-      "-q	Quiet\n"
-      "-v	List contents verbosely\n"
-      "-x FILE	Exclude files\n");
+    printf(
+        "\n"
+        "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+        "exclude (-x) lists use shell glob patterns.\n"
+        "\n"
+        "-d DIR	Extract into DIR\n"
+        "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
+        "-n	Never overwrite files (default: prompt)\n"
+        "-o	Always overwrite files\n"
+        "-p	Pipe to stdout\n"
+        "-q	Quiet\n"
+        "-v	List contents verbosely\n"
+        "-x FILE	Exclude files\n");
+  } else {
+    fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
+    if (!full) exit(EXIT_FAILURE);
+
+    printf(
+        "\n"
+        "Show information about FILEs from ZIP archive. Default is all files.\n"
+        "Both the include and exclude (-x) lists use shell glob patterns.\n"
+        "\n"
+        "-1	Show filenames only, one per line\n"
+        "-x FILE	Exclude files\n");
+  }
   exit(EXIT_SUCCESS);
 }
 
+static void HandleCommonOption(int opt) {
+  switch (opt) {
+    case 'h':
+      ShowHelp(true);
+      break;
+    case 'x':
+      flag_x = true;
+      break;
+    case 1:
+      // -x swallows all following arguments, so we use '-' in the getopt
+      // string and collect files here.
+      if (!archive_name) {
+        archive_name = optarg;
+      } else if (flag_x) {
+        excludes.insert(optarg);
+      } else {
+        includes.insert(optarg);
+      }
+      break;
+    default:
+      ShowHelp(false);
+      break;
+  }
+}
+
 int main(int argc, char* argv[]) {
   static struct option opts[] = {
       {"help", no_argument, 0, 'h'},
   };
-  bool saw_x = false;
-  int opt;
-  while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
-    switch (opt) {
-      case 'd':
-        flag_d = optarg;
-        break;
-      case 'h':
-        ShowHelp(true);
-        break;
-      case 'l':
-        flag_l = true;
-        break;
-      case 'n':
-        overwrite_mode = kNever;
-        break;
-      case 'o':
-        overwrite_mode = kAlways;
-        break;
-      case 'p':
-        flag_p = flag_q = true;
-        break;
-      case 'q':
-        flag_q = true;
-        break;
-      case 'v':
-        flag_v = true;
-        break;
-      case 'x':
-        saw_x = true;
-        break;
-      case 1:
-        // -x swallows all following arguments, so we use '-' in the getopt
-        // string and collect files here.
-        if (!archive_name) {
-          archive_name = optarg;
-        } else if (saw_x) {
-          excludes.insert(optarg);
-        } else {
-          includes.insert(optarg);
-        }
-        break;
-      default:
-        ShowHelp(false);
+
+  is_unzip = !strcmp(basename(argv[0]), "unzip");
+  if (is_unzip) {
+    int opt;
+    while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+      switch (opt) {
+        case 'd':
+          flag_d = optarg;
+          break;
+        case 'l':
+          flag_l = true;
+          break;
+        case 'n':
+          overwrite_mode = kNever;
+          break;
+        case 'o':
+          overwrite_mode = kAlways;
+          break;
+        case 'p':
+          flag_p = flag_q = true;
+          break;
+        case 'q':
+          flag_q = true;
+          break;
+        case 'v':
+          flag_v = true;
+          break;
+        default:
+          HandleCommonOption(opt);
+          break;
+      }
+    }
+  } else {
+    int opt;
+    while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
+      switch (opt) {
+        case '1':
+          flag_1 = true;
+          break;
+        default:
+          HandleCommonOption(opt);
+          break;
+      }
     }
   }
 
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3a552d8..caf8fae 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -478,6 +478,13 @@
   return OpenArchiveInternal(archive, debug_file_name);
 }
 
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
+  ZipArchiveInfo result;
+  result.archive_size = archive->mapped_zip.GetFileLength();
+  result.entry_count = archive->num_entries;
+  return result;
+}
+
 /*
  * Close a ZipArchive, closing the file and freeing the contents.
  */
@@ -614,12 +621,17 @@
   }
 
   // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
-  if ((cdr->version_made_by >> 8) == 3) {
+  data->version_made_by = cdr->version_made_by;
+  if ((data->version_made_by >> 8) == 3) {
     data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
   } else {
     data->unix_mode = 0777;
   }
 
+  // 4.4.14: the lowest bit of the internal file attributes field indicates text.
+  // Currently only needed to implement zipinfo.
+  data->is_text = (cdr->internal_file_attributes & 1);
+
   // Check that the local file header name matches the declared
   // name in the central directory.
   if (lfh->file_name_length != nameLen) {
diff --git a/logcat/Android.bp b/logcat/Android.bp
index e6b0c7d..e472d08 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -21,6 +21,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
     ],
     shared_libs: [
         "libbase",
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 4dcb338..1517c33 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -16,16 +16,12 @@
 
 #include "logcat.h"
 
-#include <android-base/macros.h>
-#include <arpa/inet.h>
-#include <assert.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <math.h>
-#include <pthread.h>
 #include <sched.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -34,13 +30,11 @@
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/resource.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
-#include <atomic>
 #include <memory>
 #include <regex>
 #include <string>
@@ -48,10 +42,11 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/sockets.h>
+#include <android-base/unique_fd.h>
 #include <log/event_tag_map.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
@@ -60,6 +55,8 @@
 
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
+using android::base::StringPrintf;
+
 struct log_device_t {
     const char* device;
     bool binary;
@@ -79,85 +76,51 @@
     }
 };
 
-struct android_logcat_context_internal {
-    // status
-    volatile std::atomic_int retval;  // valid if thread_stopped set
-    // Arguments passed in, or copies and storage thereof if a thread.
-    int argc;
-    char* const* argv;
-    char* const* envp;
-    std::vector<std::string> args;
-    std::vector<const char*> argv_hold;
-    std::vector<std::string> envs;
-    std::vector<const char*> envp_hold;
-    int output_fd;  // duplication of fileno(output) (below)
-    int error_fd;   // duplication of fileno(error) (below)
+class Logcat {
+  public:
+    ~Logcat();
 
-    // library
-    int fds[2];    // From popen call
-    FILE* output;  // everything writes to fileno(output), buffer unused
-    FILE* error;   // unless error == output.
-    pthread_t thr;
-    volatile std::atomic_bool stop;  // quick exit flag
-    volatile std::atomic_bool thread_stopped;
-    bool stderr_null;    // shell "2>/dev/null"
-    bool stderr_stdout;  // shell "2>&1"
+    int Run(int argc, char** argv);
 
-    // global variables
-    AndroidLogFormat* logformat;
-    const char* outputFileName;
+  private:
+    void RotateLogs();
+    void ProcessBuffer(log_device_t* dev, struct log_msg* buf);
+    void MaybePrintStart(log_device_t* dev, bool print_dividers);
+    void SetupOutputAndSchedulingPolicy(bool blocking);
+    int SetLogFormat(const char* format_string);
+
+    android::base::unique_fd output_fd_{dup(STDOUT_FILENO)};
+    std::unique_ptr<AndroidLogFormat, decltype(&android_log_format_free)> logformat_{
+            android_log_format_new(), &android_log_format_free};
+    const char* output_file_name_ = nullptr;
     // 0 means "no log rotation"
-    size_t logRotateSizeKBytes;
+    size_t log_rotate_size_kb_ = 0;
     // 0 means "unbounded"
-    size_t maxRotatedLogs;
-    size_t outByteCount;
-    int printBinary;
-    int devCount;  // >1 means multiple
-    std::unique_ptr<std::regex> regex;
-    log_device_t* devices;
-    EventTagMap* eventTagMap;
+    size_t max_rotated_logs_ = DEFAULT_MAX_ROTATED_LOGS;
+    size_t out_byte_count_ = 0;
+    int print_binary_ = 0;
+    int dev_count_ = 0;  // >1 means multiple
+    std::unique_ptr<std::regex> regex_;
+    log_device_t* devices_ = nullptr;
+    std::unique_ptr<EventTagMap, decltype(&android_closeEventTagMap)> event_tag_map_{
+            nullptr, &android_closeEventTagMap};
     // 0 means "infinite"
-    size_t maxCount;
-    size_t printCount;
+    size_t max_count_ = 0;
+    size_t print_count_ = 0;
 
-    bool printItAnyways;
-    bool debug;
-    bool hasOpenedEventTagMap;
+    bool print_it_anyways_ = false;
+    bool debug_ = false;
+    bool has_opened_event_tag_map_ = false;
 };
 
-// Creates a context associated with this logcat instance
-android_logcat_context create_android_logcat() {
-    android_logcat_context_internal* context;
-
-    context = (android_logcat_context_internal*)calloc(
-        1, sizeof(android_logcat_context_internal));
-    if (!context) return nullptr;
-
-    context->fds[0] = -1;
-    context->fds[1] = -1;
-    context->output_fd = -1;
-    context->error_fd = -1;
-    context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
-
-    context->argv_hold.clear();
-    context->args.clear();
-    context->envp_hold.clear();
-    context->envs.clear();
-
-    return (android_logcat_context)context;
-}
-
 // logd prefixes records with a length field
 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
 
-namespace android {
-
 enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
 
-// if showHelp is set, newline required in fmt statement to transition to usage
-static void logcat_panic(android_logcat_context_internal* context,
-                         enum helpType showHelp, const char* fmt, ...)
-    __printflike(3, 4);
+// if show_help is set, newline required in fmt statement to transition to usage
+static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) __printflike(2, 3)
+        __attribute__((__noreturn__));
 
 #ifndef F2FS_IOC_SET_PIN_FILE
 #define F2FS_IOCTL_MAGIC       0xf5
@@ -165,7 +128,7 @@
 #endif
 
 static int openLogFile(const char* pathname, size_t sizeKB) {
-    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
     if (fd < 0) {
         return fd;
     }
@@ -177,107 +140,29 @@
     return fd;
 }
 
-static void close_output(android_logcat_context_internal* context) {
-    // split output_from_error
-    if (context->error == context->output) {
-        context->output = nullptr;
-        context->output_fd = -1;
-    }
-    if (context->error && (context->output_fd == fileno(context->error))) {
-        context->output_fd = -1;
-    }
-    if (context->output_fd == context->error_fd) {
-        context->output_fd = -1;
-    }
-    // close output channel
-    if (context->output) {
-        if (context->output != stdout) {
-            if (context->output_fd == fileno(context->output)) {
-                context->output_fd = -1;
-            }
-            if (context->fds[1] == fileno(context->output)) {
-                context->fds[1] = -1;
-            }
-            fclose(context->output);
-        }
-        context->output = nullptr;
-    }
-    if (context->output_fd >= 0) {
-        if (context->output_fd != fileno(stdout)) {
-            if (context->fds[1] == context->output_fd) {
-                context->fds[1] = -1;
-            }
-            posix_fadvise(context->output_fd, 0, 0, POSIX_FADV_DONTNEED);
-            close(context->output_fd);
-        }
-        context->output_fd = -1;
-    }
-}
-
-static void close_error(android_logcat_context_internal* context) {
-    // split error_from_output
-    if (context->output == context->error) {
-        context->error = nullptr;
-        context->error_fd = -1;
-    }
-    if (context->output && (context->error_fd == fileno(context->output))) {
-        context->error_fd = -1;
-    }
-    if (context->error_fd == context->output_fd) {
-        context->error_fd = -1;
-    }
-    // close error channel
-    if (context->error) {
-        if ((context->error != stderr) && (context->error != stdout)) {
-            if (context->error_fd == fileno(context->error)) {
-                context->error_fd = -1;
-            }
-            if (context->fds[1] == fileno(context->error)) {
-                context->fds[1] = -1;
-            }
-            fclose(context->error);
-        }
-        context->error = nullptr;
-    }
-    if (context->error_fd >= 0) {
-        if ((context->error_fd != fileno(stdout)) &&
-            (context->error_fd != fileno(stderr))) {
-            if (context->fds[1] == context->error_fd) context->fds[1] = -1;
-            close(context->error_fd);
-        }
-        context->error_fd = -1;
-    }
-}
-
-static void rotateLogs(android_logcat_context_internal* context) {
-    int err;
-
+void Logcat::RotateLogs() {
     // Can't rotate logs if we're not outputting to a file
-    if (!context->outputFileName) return;
+    if (!output_file_name_) return;
 
-    close_output(context);
+    output_fd_.reset();
 
     // Compute the maximum number of digits needed to count up to
     // maxRotatedLogs in decimal.  eg:
     // maxRotatedLogs == 30
     //   -> log10(30) == 1.477
     //   -> maxRotationCountDigits == 2
-    int maxRotationCountDigits =
-        (context->maxRotatedLogs > 0)
-            ? (int)(floor(log10(context->maxRotatedLogs) + 1))
-            : 0;
+    int max_rotation_count_digits =
+            max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
 
-    for (int i = context->maxRotatedLogs; i > 0; i--) {
-        std::string file1 = android::base::StringPrintf(
-            "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
+    for (int i = max_rotated_logs_; i > 0; i--) {
+        std::string file1 =
+                StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);
 
         std::string file0;
         if (!(i - 1)) {
-            file0 = android::base::StringPrintf("%s", context->outputFileName);
+            file0 = output_file_name_;
         } else {
-            file0 =
-                android::base::StringPrintf("%s.%.*d", context->outputFileName,
-                                            maxRotationCountDigits, i - 1);
+            file0 = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i - 1);
         }
 
         if (!file0.length() || !file1.length()) {
@@ -285,170 +170,123 @@
             break;
         }
 
-        err = rename(file0.c_str(), file1.c_str());
+        int err = rename(file0.c_str(), file1.c_str());
 
         if (err < 0 && errno != ENOENT) {
             perror("while rotating log files");
         }
     }
 
-    context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
+    output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
 
-    if (context->output_fd < 0) {
-        logcat_panic(context, HELP_FALSE, "couldn't open output file");
-        return;
-    }
-    context->output = fdopen(context->output_fd, "web");
-    if (!context->output) {
-        logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
-        return;
-    }
-    if (context->stderr_stdout) {
-        close_error(context);
-        context->error = context->output;
-        context->error_fd = context->output_fd;
+    if (!output_fd_.ok()) {
+        LogcatPanic(HELP_FALSE, "couldn't open output file");
     }
 
-    context->outByteCount = 0;
+    out_byte_count_ = 0;
 }
 
-void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
-    size_t size = buf->len();
-
-    TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
-}
-
-static bool regexOk(android_logcat_context_internal* context,
-                    const AndroidLogEntry& entry) {
-    if (!context->regex) return true;
-
-    return std::regex_search(entry.message, entry.message + entry.messageLen, *context->regex);
-}
-
-static void processBuffer(android_logcat_context_internal* context,
-                          log_device_t* dev, struct log_msg* buf) {
+void Logcat::ProcessBuffer(log_device_t* dev, struct log_msg* buf) {
     int bytesWritten = 0;
     int err;
     AndroidLogEntry entry;
     char binaryMsgBuf[1024];
 
     if (dev->binary) {
-        if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
-            context->eventTagMap = android_openEventTagMap(nullptr);
-            context->hasOpenedEventTagMap = true;
+        if (!event_tag_map_ && !has_opened_event_tag_map_) {
+            event_tag_map_.reset(android_openEventTagMap(nullptr));
+            has_opened_event_tag_map_ = true;
         }
-        err = android_log_processBinaryLogBuffer(
-            &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
-            sizeof(binaryMsgBuf));
+        err = android_log_processBinaryLogBuffer(&buf->entry, &entry, event_tag_map_.get(),
+                                                 binaryMsgBuf, sizeof(binaryMsgBuf));
         // printf(">>> pri=%d len=%d msg='%s'\n",
         //    entry.priority, entry.messageLen, entry.message);
     } else {
-        err = android_log_processLogBuffer(&buf->entry_v1, &entry);
+        err = android_log_processLogBuffer(&buf->entry, &entry);
     }
-    if ((err < 0) && !context->debug) return;
+    if (err < 0 && !debug_) return;
 
-    if (android_log_shouldPrintLine(
-            context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
-            entry.priority)) {
-        bool match = regexOk(context, entry);
+    if (android_log_shouldPrintLine(logformat_.get(), std::string(entry.tag, entry.tagLen).c_str(),
+                                    entry.priority)) {
+        bool match = !regex_ ||
+                     std::regex_search(entry.message, entry.message + entry.messageLen, *regex_);
 
-        context->printCount += match;
-        if (match || context->printItAnyways) {
-            bytesWritten = android_log_printLogLine(context->logformat,
-                                                    context->output_fd, &entry);
+        print_count_ += match;
+        if (match || print_it_anyways_) {
+            bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry);
 
             if (bytesWritten < 0) {
-                logcat_panic(context, HELP_FALSE, "output error");
-                return;
+                LogcatPanic(HELP_FALSE, "output error");
             }
         }
     }
 
-    context->outByteCount += bytesWritten;
+    out_byte_count_ += bytesWritten;
 
-    if (context->logRotateSizeKBytes > 0 &&
-        (context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
-        rotateLogs(context);
+    if (log_rotate_size_kb_ > 0 && (out_byte_count_ / 1024) >= log_rotate_size_kb_) {
+        RotateLogs();
     }
 }
 
-static void maybePrintStart(android_logcat_context_internal* context,
-                            log_device_t* dev, bool printDividers) {
-    if (!dev->printed || printDividers) {
-        if (context->devCount > 1 && !context->printBinary) {
-            char buf[1024];
-            snprintf(buf, sizeof(buf), "--------- %s %s\n",
-                     dev->printed ? "switch to" : "beginning of", dev->device);
-            if (write(context->output_fd, buf, strlen(buf)) < 0) {
-                logcat_panic(context, HELP_FALSE, "output error");
-                return;
+void Logcat::MaybePrintStart(log_device_t* dev, bool print_dividers) {
+    if (!dev->printed || print_dividers) {
+        if (dev_count_ > 1 && !print_binary_) {
+            if (dprintf(output_fd_.get(), "--------- %s %s\n",
+                        dev->printed ? "switch to" : "beginning of", dev->device) < 0) {
+                LogcatPanic(HELP_FALSE, "output error");
             }
         }
         dev->printed = true;
     }
 }
 
-static void setupOutputAndSchedulingPolicy(
-    android_logcat_context_internal* context, bool blocking) {
-    if (!context->outputFileName) return;
+void Logcat::SetupOutputAndSchedulingPolicy(bool blocking) {
+    if (!output_file_name_) return;
 
     if (blocking) {
         // Lower priority and set to batch scheduling if we are saving
         // the logs into files and taking continuous content.
-        if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) {
-            fprintf(context->error,
-                    "failed to set background scheduling policy\n");
+        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+            fprintf(stderr, "failed to set background scheduling policy\n");
         }
 
-        struct sched_param param;
-        memset(&param, 0, sizeof(param));
+        struct sched_param param = {};
         if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
             fprintf(stderr, "failed to set to batch scheduler\n");
         }
 
-        if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) &&
-            context->error) {
-            fprintf(context->error, "failed set to priority\n");
+        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+            fprintf(stderr, "failed set to priority\n");
         }
     }
 
-    close_output(context);
+    output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
 
-    context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
-
-    if (context->output_fd < 0) {
-        logcat_panic(context, HELP_FALSE, "couldn't open output file");
-        return;
+    if (!output_fd_.ok()) {
+        LogcatPanic(HELP_FALSE, "couldn't open output file");
     }
 
     struct stat statbuf;
-    if (fstat(context->output_fd, &statbuf) == -1) {
-        close_output(context);
-        logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n");
-        return;
+    if (fstat(output_fd_.get(), &statbuf) == -1) {
+        output_fd_.reset();
+        LogcatPanic(HELP_FALSE, "couldn't get output file stat\n");
     }
 
     if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
-        close_output(context);
-        logcat_panic(context, HELP_FALSE, "invalid output file stat\n");
-        return;
+        output_fd_.reset();
+        LogcatPanic(HELP_FALSE, "invalid output file stat\n");
     }
 
-    context->output = fdopen(context->output_fd, "web");
-
-    context->outByteCount = statbuf.st_size;
+    out_byte_count_ = statbuf.st_size;
 }
 
 // clang-format off
-static void show_help(android_logcat_context_internal* context) {
-    if (!context->error) return;
+static void show_help() {
+    const char* cmd = getprogname();
 
-    const char* cmd = strrchr(context->argv[0], '/');
-    cmd = cmd ? cmd + 1 : context->argv[0];
+    fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd);
 
-    fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd);
-
-    fprintf(context->error, "options include:\n"
+    fprintf(stderr, "options include:\n"
                     "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
                     "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
@@ -515,7 +353,7 @@
                     "                  comes first. Improves efficiency of polling by providing\n"
                     "                  an about-to-wrap wakeup.\n");
 
-    fprintf(context->error, "\nfilterspecs are a series of \n"
+    fprintf(stderr, "\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
                    "where <tag> is a log component tag (or * for all) and priority is:\n"
                    "  V    Verbose (default for <tag>)\n"
@@ -533,9 +371,8 @@
                    "or defaults to \"threadtime\"\n\n");
 }
 
-static void show_format_help(android_logcat_context_internal* context) {
-    if (!context->error) return;
-    fprintf(context->error,
+static void show_format_help() {
+    fprintf(stderr,
         "-v <format>, --format=<format> options:\n"
         "  Sets log print format verb and adverbs, where <format> is:\n"
         "    brief long process raw tag thread threadtime time\n"
@@ -569,16 +406,13 @@
 }
 // clang-format on
 
-static int setLogFormat(android_logcat_context_internal* context,
-                        const char* formatString) {
-    AndroidLogPrintFormat format;
-
-    format = android_log_formatFromString(formatString);
+int Logcat::SetLogFormat(const char* format_string) {
+    AndroidLogPrintFormat format = android_log_formatFromString(format_string);
 
     // invalid string?
     if (format == FORMAT_OFF) return -1;
 
-    return android_log_setPrintFormat(context->logformat, format);
+    return android_log_setPrintFormat(logformat_.get(), format);
 }
 
 static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
@@ -608,32 +442,25 @@
     return true;
 }
 
-static void logcat_panic(android_logcat_context_internal* context,
-                         enum helpType showHelp, const char* fmt, ...) {
-    context->retval = EXIT_FAILURE;
-    if (!context->error) {
-        context->stop = true;
-        return;
-    }
-
+static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) {
     va_list args;
     va_start(args, fmt);
-    vfprintf(context->error, fmt, args);
+    vfprintf(stderr, fmt, args);
     va_end(args);
 
     switch (showHelp) {
         case HELP_TRUE:
-            show_help(context);
+            show_help();
             break;
         case HELP_FORMAT:
-            show_format_help(context);
+            show_format_help();
             break;
         case HELP_FALSE:
         default:
             break;
     }
 
-    context->stop = true;
+    exit(EXIT_FAILURE);
 }
 
 static char* parseTime(log_time& t, const char* cp) {
@@ -713,18 +540,6 @@
     return retval;
 }
 
-const char* getenv(android_logcat_context_internal* context, const char* name) {
-    if (!context->envp || !name || !*name) return nullptr;
-
-    for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
-        if (strncmp(context->envp[i], name, len)) continue;
-        if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
-    }
-    return nullptr;
-}
-
-}  // namespace android
-
 void reportErrorName(const char** current, const char* name,
                      bool blockSecurity) {
     if (*current) return;
@@ -733,8 +548,7 @@
     }
 }
 
-static int __logcat(android_logcat_context_internal* context) {
-    using namespace android;
+int Logcat::Run(int argc, char** argv) {
     int err;
     bool hasSetLogFormat = false;
     bool clearLog = false;
@@ -761,101 +575,10 @@
     const char* clearFail = nullptr;
     const char* setSizeFail = nullptr;
     const char* getSizeFail = nullptr;
-    int argc = context->argc;
-    char* const* argv = context->argv;
-
-    context->output = stdout;
-    context->error = stderr;
-
-    for (int i = 0; i < argc; ++i) {
-        // Simulate shell stderr redirect parsing
-        if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
-
-        // Append to file not implemented, just open file
-        size_t skip = (argv[i][2] == '>') + 2;
-        if (!strcmp(&argv[i][skip], "/dev/null")) {
-            context->stderr_null = true;
-        } else if (!strcmp(&argv[i][skip], "&1")) {
-            context->stderr_stdout = true;
-        } else {
-            // stderr file redirections are not supported
-            fprintf(context->stderr_stdout ? stdout : stderr,
-                    "stderr redirection to file %s unsupported, skipping\n",
-                    &argv[i][skip]);
-        }
-        // Only the first one
-        break;
-    }
-
-    const char* filename = nullptr;
-    for (int i = 0; i < argc; ++i) {
-        // Simulate shell stdout redirect parsing
-        if (argv[i][0] != '>') continue;
-
-        // Append to file not implemented, just open file
-        filename = &argv[i][(argv[i][1] == '>') + 1];
-        // Only the first one
-        break;
-    }
-
-    // Deal with setting up file descriptors and FILE pointers
-    if (context->error_fd >= 0) {  // Is an error file descriptor supplied?
-        if (context->error_fd == context->output_fd) {
-            context->stderr_stdout = true;
-        } else if (context->stderr_null) {  // redirection told us to close it
-            close(context->error_fd);
-            context->error_fd = -1;
-        } else {  // All Ok, convert error to a FILE pointer
-            context->error = fdopen(context->error_fd, "web");
-            if (!context->error) {
-                context->retval = -errno;
-                fprintf(context->stderr_stdout ? stdout : stderr,
-                        "Failed to fdopen(error_fd=%d) %s\n", context->error_fd,
-                        strerror(errno));
-                goto exit;
-            }
-        }
-    }
-    if (context->output_fd >= 0) {  // Is an output file descriptor supplied?
-        if (filename) {  // redirect to file, close supplied file descriptor.
-            close(context->output_fd);
-            context->output_fd = -1;
-        } else {  // All Ok, convert output to a FILE pointer
-            context->output = fdopen(context->output_fd, "web");
-            if (!context->output) {
-                context->retval = -errno;
-                fprintf(context->stderr_stdout ? stdout : context->error,
-                        "Failed to fdopen(output_fd=%d) %s\n",
-                        context->output_fd, strerror(errno));
-                goto exit;
-            }
-        }
-    }
-    if (filename) {  // We supplied an output file redirected in command line
-        context->output = fopen(filename, "web");
-    }
-    // Deal with 2>&1
-    if (context->stderr_stdout) context->error = context->output;
-    // Deal with 2>/dev/null
-    if (context->stderr_null) {
-        context->error_fd = -1;
-        context->error = nullptr;
-    }
-    // Only happens if output=stdout or output=filename
-    if ((context->output_fd < 0) && context->output) {
-        context->output_fd = fileno(context->output);
-    }
-    // Only happens if error=stdout || error=stderr
-    if ((context->error_fd < 0) && context->error) {
-        context->error_fd = fileno(context->error);
-    }
-
-    context->logformat = android_log_format_new();
 
     if (argc == 2 && !strcmp(argv[1], "--help")) {
-        show_help(context);
-        context->retval = EXIT_SUCCESS;
-        goto exit;
+        show_help();
+        return EXIT_SUCCESS;
     }
 
     // meant to catch comma-delimited values, but cast a wider
@@ -913,15 +636,13 @@
                 // only long options
                 if (long_options[option_index].name == pid_str) {
                     if (pid != 0) {
-                        logcat_panic(context, HELP_TRUE, "Only supports one PID argument.\n");
-                        goto exit;
+                        LogcatPanic(HELP_TRUE, "Only supports one PID argument.\n");
                     }
 
                     // ToDo: determine runtime PID_MAX?
                     if (!getSizeTArg(optarg, &pid, 1)) {
-                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name, optarg);
-                        goto exit;
+                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
+                                    long_options[option_index].name, optarg);
                     }
                     break;
                 }
@@ -931,25 +652,22 @@
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
                     if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
-                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name, optarg);
-                        goto exit;
+                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
+                                    long_options[option_index].name, optarg);
                     }
-                    if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
-                        context->error) {
-                        fprintf(context->error,
-                                "WARNING: %s %u seconds, ignoring %zu\n",
-                                long_options[option_index].name,
-                                ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
+                    if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+                        fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
+                                long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT,
+                                dummy);
                     }
                     break;
                 }
                 if (long_options[option_index].name == print_str) {
-                    context->printItAnyways = true;
+                    print_it_anyways_ = true;
                     break;
                 }
                 if (long_options[option_index].name == debug_str) {
-                    context->debug = true;
+                    debug_ = true;
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
@@ -959,7 +677,7 @@
 
             case 's':
                 // default to all silent
-                android_log_addFilterRule(context->logformat, "*:s");
+                android_log_addFilterRule(logformat_.get(), "*:s");
                 break;
 
             case 'c':
@@ -984,25 +702,18 @@
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
                     char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
-                                     optarg);
-                        goto exit;
+                        LogcatPanic(HELP_FALSE, "-%c \"%s\" not in time format\n", c, optarg);
                     }
                     if (*cp) {
                         char ch = *cp;
                         *cp = '\0';
-                        if (context->error) {
-                            fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
-                                    c, optarg, ch, cp + 1);
-                        }
+                        fprintf(stderr, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", c, optarg,
+                                ch, cp + 1);
                         *cp = ch;
                     }
                 } else {
                     if (!getSizeTArg(optarg, &tail_lines, 1)) {
-                        if (context->error) {
-                            fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
-                                    optarg);
-                        }
+                        fprintf(stderr, "WARNING: -%c %s invalid, setting to 1\n", c, optarg);
                         tail_lines = 1;
                     }
                 }
@@ -1013,14 +724,13 @@
                 break;
 
             case 'e':
-                context->regex.reset(new std::regex(optarg));
+                regex_.reset(new std::regex(optarg));
                 break;
 
             case 'm': {
-                if (!getSizeTArg(optarg, &context->maxCount)) {
-                    logcat_panic(context, HELP_FALSE,
-                                 "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
-                    goto exit;
+                if (!getSizeTArg(optarg, &max_count_)) {
+                    LogcatPanic(HELP_FALSE, "-%c \"%s\" isn't an integer greater than zero\n", c,
+                                optarg);
                 }
             } break;
 
@@ -1060,9 +770,7 @@
                 }
 
                 if (!setLogSize) {
-                    logcat_panic(context, HELP_FALSE,
-                                 "ERROR: -G <num><multiplier>\n");
-                    goto exit;
+                    LogcatPanic(HELP_FALSE, "ERROR: -G <num><multiplier>\n");
                 }
             } break;
 
@@ -1094,9 +802,7 @@
                         const char* name = android_log_id_to_name(log_id);
 
                         if (!!strcmp(name, arg)) {
-                            logcat_panic(context, HELP_TRUE,
-                                         "unknown buffer %s\n", arg);
-                            goto exit;
+                            LogcatPanic(HELP_TRUE, "unknown buffer %s\n", arg);
                         }
                         if (log_id == LOG_ID_SECURITY) allSelected = false;
                         idMask |= (1 << log_id);
@@ -1112,7 +818,7 @@
                     if (!(idMask & (1 << i))) continue;
 
                     bool found = false;
-                    for (dev = context->devices; dev; dev = dev->next) {
+                    for (dev = devices_; dev; dev = dev->next) {
                         if (!strcmp(name, dev->device)) {
                             found = true;
                             break;
@@ -1130,14 +836,14 @@
                         dev->next = d;
                         dev = d;
                     } else {
-                        context->devices = dev = d;
+                        devices_ = dev = d;
                     }
-                    context->devCount++;
+                    dev_count_++;
                 }
             } break;
 
             case 'B':
-                context->printBinary = 1;
+                print_binary_ = 1;
                 break;
 
             case 'f':
@@ -1145,38 +851,33 @@
                     tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
-                context->outputFileName = optarg;
+                output_file_name_ = optarg;
                 break;
 
             case 'r':
-                if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
-                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
-                    goto exit;
+                if (!getSizeTArg(optarg, &log_rotate_size_kb_, 1)) {
+                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
                 }
                 break;
 
             case 'n':
-                if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
-                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
-                    goto exit;
+                if (!getSizeTArg(optarg, &max_rotated_logs_, 1)) {
+                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
                 }
                 break;
 
             case 'v': {
                 if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
-                    show_format_help(context);
-                    context->retval = EXIT_SUCCESS;
-                    goto exit;
+                    show_format_help();
+                    return EXIT_SUCCESS;
                 }
                 std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
                 char* arg = formats.get();
                 char* sv = nullptr;  // protect against -ENOMEM above
                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
-                    err = setLogFormat(context, arg);
+                    err = SetLogFormat(arg);
                     if (err < 0) {
-                        logcat_panic(context, HELP_FORMAT,
-                                     "Invalid parameter \"%s\" to -v\n", arg);
-                        goto exit;
+                        LogcatPanic(HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", arg);
                     }
                     arg = nullptr;
                     if (err) hasSetLogFormat = true;
@@ -1199,8 +900,7 @@
                 {
                     // if not in emulator, exit quietly
                     if (false == android::base::GetBoolProperty(QEMU_PROPERTY, false)) {
-                        context->retval = EXIT_SUCCESS;
-                        goto exit;
+                        return EXIT_SUCCESS;
                     }
 
                     std::string cmdline = android::base::GetProperty(QEMU_CMDLINE, "");
@@ -1211,8 +911,7 @@
                     const char* logcatFilter = strstr(cmdline.c_str(), LOGCAT_FILTER);
                     // if nothing found or invalid filters, exit quietly
                     if (!logcatFilter) {
-                        context->retval = EXIT_SUCCESS;
-                        goto exit;
+                        return EXIT_SUCCESS;
                     }
 
                     const char* p = logcatFilter + strlen(LOGCAT_FILTER);
@@ -1231,8 +930,7 @@
                     } else if (console) {
                         p = console + strlen(CONSOLE_OPTION);
                     } else {
-                        context->retval = EXIT_FAILURE;
-                        goto exit;
+                        return EXIT_FAILURE;
                     }
 
                     q = strpbrk(p, " \t\n\r");
@@ -1249,37 +947,26 @@
                             devname = devname.substr(0, pos);
                         }
                     }
-                    cmdline.erase();
 
-                    if (context->error) {
-                        fprintf(context->error, "logcat using %s\n",
-                                devname.c_str());
+                    fprintf(stderr, "logcat using %s\n", devname.c_str());
+
+                    int fd = open(devname.c_str(), O_WRONLY | O_CLOEXEC);
+                    if (fd < 0) {
+                        break;
                     }
 
-                    FILE* fp = fopen(devname.c_str(), "web");
-                    devname.erase();
-                    if (!fp) break;
-
                     if (consolePipe) {
                         // need the trailing '\0'
-                        if(!android::base::WriteFully(fileno(fp), pipePurpose.c_str(),
-                                    pipePurpose.size() + 1)) {
-                            fclose(fp);
-                            context->retval = EXIT_FAILURE;
-                            goto exit;
+                        if (!android::base::WriteFully(fd, pipePurpose.c_str(),
+                                                       pipePurpose.size() + 1)) {
+                            close(fd);
+                            return EXIT_FAILURE;
                         }
                     }
-
                     // close output and error channels, replace with console
-                    android::close_output(context);
-                    android::close_error(context);
-                    context->stderr_stdout = true;
-                    context->output = fp;
-                    context->output_fd = fileno(fp);
-                    if (context->stderr_null) break;
-                    context->stderr_stdout = true;
-                    context->error = fp;
-                    context->error_fd = fileno(fp);
+                    dup2(fd, STDOUT_FILENO);
+                    dup2(fd, STDERR_FILENO);
+                    close(fd);
                 }
                 break;
 
@@ -1288,69 +975,59 @@
                 break;
 
             case ':':
-                logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
-                goto exit;
+                LogcatPanic(HELP_TRUE, "Option -%c needs an argument\n", optopt);
 
             case 'h':
-                show_help(context);
-                show_format_help(context);
-                goto exit;
+                show_help();
+                show_format_help();
+                return EXIT_SUCCESS;
 
             default:
-                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
-                goto exit;
+                LogcatPanic(HELP_TRUE, "Unrecognized Option %c\n", optopt);
         }
     }
 
-    if (context->maxCount && got_t) {
-        logcat_panic(context, HELP_TRUE,
-                     "Cannot use -m (--max-count) and -t together\n");
-        goto exit;
+    if (max_count_ && got_t) {
+        LogcatPanic(HELP_TRUE, "Cannot use -m (--max-count) and -t together\n");
     }
-    if (context->printItAnyways && (!context->regex || !context->maxCount)) {
+    if (print_it_anyways_ && (!regex_ || !max_count_)) {
         // One day it would be nice if --print -v color and --regex <expr>
         // could play with each other and show regex highlighted content.
-        // clang-format off
-        if (context->error) {
-            fprintf(context->error, "WARNING: "
-                            "--print ignored, to be used in combination with\n"
-                                "         "
-                            "--regex <expr> and --max-count <N>\n");
-        }
-        context->printItAnyways = false;
+        fprintf(stderr,
+                "WARNING: "
+                "--print ignored, to be used in combination with\n"
+                "         "
+                "--regex <expr> and --max-count <N>\n");
+        print_it_anyways_ = false;
     }
 
-    if (!context->devices) {
-        dev = context->devices = new log_device_t("main", false);
-        context->devCount = 1;
+    if (!devices_) {
+        dev = devices_ = new log_device_t("main", false);
+        dev_count_ = 1;
         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
             dev = dev->next = new log_device_t("system", false);
-            context->devCount++;
+            dev_count_++;
         }
         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
             dev = dev->next = new log_device_t("crash", false);
-            context->devCount++;
+            dev_count_++;
         }
         if (android_name_to_log_id("kernel") == LOG_ID_KERNEL) {
             dev = dev->next = new log_device_t("kernel", false);
-            context->devCount++;
+            dev_count_++;
         }
     }
 
-    if (!!context->logRotateSizeKBytes && !context->outputFileName) {
-        logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
-        goto exit;
+    if (log_rotate_size_kb_ != 0 && !output_file_name_) {
+        LogcatPanic(HELP_TRUE, "-r requires -f as well\n");
     }
 
-    if (!!setId) {
-        if (!context->outputFileName) {
-            logcat_panic(context, HELP_TRUE,
-                         "--id='%s' requires -f as well\n", setId);
-            goto exit;
+    if (setId != 0) {
+        if (!output_file_name_) {
+            LogcatPanic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
         }
 
-        std::string file_name = android::base::StringPrintf(
-                                        "%s.id", context->outputFileName);
+        std::string file_name = StringPrintf("%s.id", output_file_name_);
         std::string file;
         bool file_ok = android::base::ReadFileToString(file_name, &file);
         android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
@@ -1359,7 +1036,7 @@
     }
 
     if (!hasSetLogFormat) {
-        const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
+        const char* logFormat = getenv("ANDROID_PRINTF_LOG");
 
         if (!!logFormat) {
             std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
@@ -1367,41 +1044,34 @@
             char* sv = nullptr;  // protect against -ENOMEM above
             char* arg = formats.get();
             while (!!(arg = strtok_r(arg, delimiters, &sv))) {
-                err = setLogFormat(context, arg);
+                err = SetLogFormat(arg);
                 // environment should not cause crash of logcat
-                if ((err < 0) && context->error) {
-                    fprintf(context->error,
-                            "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
+                if (err < 0) {
+                    fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
                 }
                 arg = nullptr;
                 if (err > 0) hasSetLogFormat = true;
             }
         }
         if (!hasSetLogFormat) {
-            setLogFormat(context, "threadtime");
+            SetLogFormat("threadtime");
         }
     }
 
     if (forceFilters.size()) {
-        err = android_log_addFilterString(context->logformat,
-                                          forceFilters.c_str());
+        err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
         if (err < 0) {
-            logcat_panic(context, HELP_FALSE,
-                         "Invalid filter expression in logcat args\n");
-            goto exit;
+            LogcatPanic(HELP_FALSE, "Invalid filter expression in logcat args\n");
         }
     } else if (argc == optind) {
         // Add from environment variable
-        const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
+        const char* env_tags_orig = getenv("ANDROID_LOG_TAGS");
 
         if (!!env_tags_orig) {
-            err = android_log_addFilterString(context->logformat,
-                                              env_tags_orig);
+            err = android_log_addFilterString(logformat_.get(), env_tags_orig);
 
             if (err < 0) {
-                logcat_panic(context, HELP_TRUE,
-                            "Invalid filter expression in ANDROID_LOG_TAGS\n");
-                goto exit;
+                LogcatPanic(HELP_TRUE, "Invalid filter expression in ANDROID_LOG_TAGS\n");
             }
         }
     } else {
@@ -1412,16 +1082,14 @@
             // skip stdout redirections of _all_ kinds
             if (argv[i][0] == '>') continue;
 
-            err = android_log_addFilterString(context->logformat, argv[i]);
+            err = android_log_addFilterString(logformat_.get(), argv[i]);
             if (err < 0) {
-                logcat_panic(context, HELP_TRUE,
-                             "Invalid filter expression '%s'\n", argv[i]);
-                goto exit;
+                LogcatPanic(HELP_TRUE, "Invalid filter expression '%s'\n", argv[i]);
             }
         }
     }
 
-    dev = context->devices;
+    dev = devices_;
     if (tail_time != log_time::EPOCH) {
         logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
     } else {
@@ -1440,21 +1108,18 @@
         }
 
         if (clearLog || setId) {
-            if (context->outputFileName) {
-                int maxRotationCountDigits =
-                    (context->maxRotatedLogs > 0) ?
-                        (int)(floor(log10(context->maxRotatedLogs) + 1)) :
-                        0;
+            if (output_file_name_) {
+                int max_rotation_count_digits =
+                        max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
 
-                for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
+                for (int i = max_rotated_logs_; i >= 0; --i) {
                     std::string file;
 
                     if (!i) {
-                        file = android::base::StringPrintf(
-                            "%s", context->outputFileName);
+                        file = output_file_name_;
                     } else {
-                        file = android::base::StringPrintf("%s.%.*d",
-                            context->outputFileName, maxRotationCountDigits, i);
+                        file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits,
+                                            i);
                     }
 
                     if (!file.length()) {
@@ -1498,36 +1163,25 @@
                        readable_format.first, readable_format.second,
                        (int)LOGGER_ENTRY_MAX_LEN,
                        (int)LOGGER_ENTRY_MAX_PAYLOAD);
-                TEMP_FAILURE_RETRY(write(context->output_fd,
-                                         str.data(), str.length()));
+                TEMP_FAILURE_RETRY(write(output_fd_.get(), str.data(), str.length()));
             }
         }
 
         dev = dev->next;
     }
 
-    context->retval = EXIT_SUCCESS;
-
     // report any errors in the above loop and exit
     if (openDeviceFail) {
-        logcat_panic(context, HELP_FALSE,
-                     "Unable to open log device '%s'\n", openDeviceFail);
-        goto close;
+        LogcatPanic(HELP_FALSE, "Unable to open log device '%s'\n", openDeviceFail);
     }
     if (clearFail) {
-        logcat_panic(context, HELP_FALSE,
-                     "failed to clear the '%s' log\n", clearFail);
-        goto close;
+        LogcatPanic(HELP_FALSE, "failed to clear the '%s' log\n", clearFail);
     }
     if (setSizeFail) {
-        logcat_panic(context, HELP_FALSE,
-                     "failed to set the '%s' log size\n", setSizeFail);
-        goto close;
+        LogcatPanic(HELP_FALSE, "failed to set the '%s' log size\n", setSizeFail);
     }
     if (getSizeFail) {
-        logcat_panic(context, HELP_FALSE,
-                     "failed to get the readable '%s' log size", getSizeFail);
-        goto close;
+        LogcatPanic(HELP_FALSE, "failed to get the readable '%s' log size", getSizeFail);
     }
 
     if (setPruneList) {
@@ -1538,15 +1192,13 @@
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
-                logcat_panic(context, HELP_FALSE,
-                             "failed to set the prune list");
+                LogcatPanic(HELP_FALSE, "failed to set the prune list");
             }
             free(buf);
         } else {
-            logcat_panic(context, HELP_FALSE,
-                         "failed to set the prune list (alloc)");
+            LogcatPanic(HELP_FALSE, "failed to set the prune list (alloc)");
         }
-        goto close;
+        return EXIT_SUCCESS;
     }
 
     if (printStatistics || getPruneList) {
@@ -1575,8 +1227,7 @@
         }
 
         if (!buf) {
-            logcat_panic(context, HELP_FALSE, "failed to read data");
-            goto close;
+            LogcatPanic(HELP_FALSE, "failed to read data");
         }
 
         // remove trailing FF
@@ -1593,149 +1244,68 @@
         }
 
         len = strlen(cp);
-        TEMP_FAILURE_RETRY(write(context->output_fd, cp, len));
+        TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
         delete[] buf;
-        goto close;
+        return EXIT_SUCCESS;
     }
 
-    if (getLogSize || setLogSize || clearLog) goto close;
+    if (getLogSize || setLogSize || clearLog) return EXIT_SUCCESS;
 
-    setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
-    if (context->stop) goto close;
-
-    // LOG_EVENT_INT(10, 12345);
-    // LOG_EVENT_LONG(11, 0x1122334455667788LL);
-    // LOG_EVENT_STRING(0, "whassup, doc?");
+    SetupOutputAndSchedulingPolicy(!(mode & ANDROID_LOG_NONBLOCK));
 
     dev = nullptr;
 
-    while (!context->stop &&
-           (!context->maxCount || (context->printCount < context->maxCount))) {
+    while (!max_count_ || print_count_ < max_count_) {
         struct log_msg log_msg;
         int ret = android_logger_list_read(logger_list, &log_msg);
         if (!ret) {
-            logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
-            break;
+            LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
         }
 
         if (ret < 0) {
             if (ret == -EAGAIN) break;
 
             if (ret == -EIO) {
-                logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
-                break;
+                LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
             }
             if (ret == -EINVAL) {
-                logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
-                break;
+                LogcatPanic(HELP_FALSE, "read: unexpected length.\n");
             }
-            logcat_panic(context, HELP_FALSE, "logcat read failure\n");
-            break;
+            LogcatPanic(HELP_FALSE, "logcat read failure\n");
         }
 
         log_device_t* d;
-        for (d = context->devices; d; d = d->next) {
+        for (d = devices_; d; d = d->next) {
             if (android_name_to_log_id(d->device) == log_msg.id()) break;
         }
         if (!d) {
-            context->devCount = 2; // set to Multiple
+            dev_count_ = 2;  // set to Multiple
             d = &unexpected;
             d->binary = log_msg.id() == LOG_ID_EVENTS;
         }
 
         if (dev != d) {
             dev = d;
-            maybePrintStart(context, dev, printDividers);
-            if (context->stop) break;
+            MaybePrintStart(dev, printDividers);
         }
-        if (context->printBinary) {
-            printBinary(context, &log_msg);
+        if (print_binary_) {
+            TEMP_FAILURE_RETRY(write(output_fd_.get(), &log_msg, log_msg.len()));
         } else {
-            processBuffer(context, dev, &log_msg);
+            ProcessBuffer(dev, &log_msg);
         }
     }
-
-close:
-    // Short and sweet. Implemented generic version in android_logcat_destroy.
-    while (!!(dev = context->devices)) {
-        context->devices = dev->next;
-        delete dev;
-    }
-    android_logger_list_free(logger_list);
-
-exit:
-    // close write end of pipe to help things along
-    if (context->output_fd == context->fds[1]) {
-        android::close_output(context);
-    }
-    if (context->error_fd == context->fds[1]) {
-        android::close_error(context);
-    }
-    if (context->fds[1] >= 0) {
-        // NB: should be closed by the above
-        int save_errno = errno;
-        close(context->fds[1]);
-        errno = save_errno;
-        context->fds[1] = -1;
-    }
-    context->thread_stopped = true;
-    return context->retval;
+    return EXIT_SUCCESS;
 }
 
-// Can block
-int android_logcat_run_command(android_logcat_context ctx,
-                               int output, int error,
-                               int argc, char* const* argv,
-                               char* const* envp) {
-    android_logcat_context_internal* context = ctx;
-
-    context->output_fd = output;
-    context->error_fd = error;
-    context->argc = argc;
-    context->argv = argv;
-    context->envp = envp;
-    context->stop = false;
-    context->thread_stopped = false;
-    return __logcat(context);
+int RunLogcat(int argc, char** argv) {
+    Logcat logcat;
+    return logcat.Run(argc, argv);
 }
 
-// Finished with context
-int android_logcat_destroy(android_logcat_context* ctx) {
-    android_logcat_context_internal* context = *ctx;
-
-    if (!context) return -EBADF;
-
-    *ctx = nullptr;
-
-    context->stop = true;
-
-    while (context->thread_stopped == false) {
-        // Makes me sad, replace thread_stopped with semaphore.  Short lived.
-        sched_yield();
-    }
-
-    context->argv_hold.clear();
-    context->args.clear();
-    context->envp_hold.clear();
-    context->envs.clear();
-    if (context->fds[0] >= 0) {
-        close(context->fds[0]);
-        context->fds[0] = -1;
-    }
-    android::close_output(context);
-    android::close_error(context);
-
-    if (context->fds[1] >= 0) {
-        // NB: this should be closed by close_output, but just in case...
-        close(context->fds[1]);
-        context->fds[1] = -1;
-    }
-
-    android_closeEventTagMap(context->eventTagMap);
-
+Logcat::~Logcat() {
     // generic cleanup of devices list to handle all possible dirty cases
     log_device_t* dev;
-    while (!!(dev = context->devices)) {
+    while (!!(dev = devices_)) {
         struct logger_list* logger_list = dev->logger_list;
         if (logger_list) {
             for (log_device_t* d = dev; d; d = d->next) {
@@ -1743,13 +1313,7 @@
             }
             android_logger_list_free(logger_list);
         }
-        context->devices = dev->next;
+        devices_ = dev->next;
         delete dev;
     }
-
-    int retval = context->retval;
-
-    free(context);
-
-    return retval;
 }
diff --git a/logcat/logcat.h b/logcat/logcat.h
index 85ed7da..4cc112b 100644
--- a/logcat/logcat.h
+++ b/logcat/logcat.h
@@ -16,45 +16,4 @@
 
 #pragma once
 
-#include <stdio.h>
-
-/*
- * The opaque context
- */
-typedef struct android_logcat_context_internal* android_logcat_context;
-
-/* Creates a context associated with this logcat instance
- *
- * Returns a pointer to the context, or a NULL on error.
- */
-android_logcat_context create_android_logcat();
-
-/* Collects and outputs the logcat data to output and error file descriptors
- *
- * Will block, performed in-thread and in-process
- *
- * The output file descriptor variable, if greater than or equal to 0, is
- * where the output (ie: stdout) will be sent. The file descriptor is closed
- * on android_logcat_destroy which terminates the instance, or when an -f flag
- * (output redirect to a file) is present in the command.  The error file
- * descriptor variable, if greater than or equal to 0, is where the error
- * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
- * The error file descriptor can be set to equal to the output file descriptor,
- * which will mix output and error stream content, and will defer closure of
- * the file descriptor on -f flag redirection.  Negative values for the file
- * descriptors will use stdout and stderr FILE references respectively
- * internally, and will not close the references as noted above.
- *
- * Return value is 0 for success, non-zero for errors.
- */
-int android_logcat_run_command(android_logcat_context ctx, int output, int error, int argc,
-                               char* const* argv, char* const* envp);
-
-/* Finished with context
- *
- * Kill the command thread ASAP (if any), and free up all associated resources.
- *
- * Return value is the result of the android_logcat_run_command, or
- * non-zero for any errors.
- */
-int android_logcat_destroy(android_logcat_context* ctx);
+int RunLogcat(int argc, char** argv);
\ No newline at end of file
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
index ecfa2ba..59ab716 100644
--- a/logcat/logcat_main.cpp
+++ b/logcat/logcat_main.cpp
@@ -19,12 +19,7 @@
 
 #include "logcat.h"
 
-int main(int argc, char** argv, char** envp) {
-    android_logcat_context ctx = create_android_logcat();
-    if (!ctx) return -1;
+int main(int argc, char** argv) {
     signal(SIGPIPE, exit);
-    int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
-    int ret = android_logcat_destroy(&ctx);
-    if (!ret) ret = retval;
-    return ret;
+    return RunLogcat(argc, argv);
 }
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
index c131846..4da933f 100644
--- a/logcat/logcatd_main.cpp
+++ b/logcat/logcatd_main.cpp
@@ -23,10 +23,7 @@
 
 #include "logcat.h"
 
-int main(int argc, char** argv, char** envp) {
-    android_logcat_context ctx = create_android_logcat();
-    if (!ctx) return -1;
-
+int main(int argc, char** argv) {
     signal(SIGPIPE, exit);
 
     // Save and detect presence of -L or --last flag
@@ -46,8 +43,7 @@
     int ret = 0;
     if (last) {
         // Run logcat command with -L flag
-        ret = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
-                                         (char* const*)&argv_hold[0], envp);
+        ret = RunLogcat(argv_hold.size() - 1, (char**)&argv_hold[0]);
         // Remove -L and --last flags from argument list
         for (std::vector<const char*>::iterator it = argv_hold.begin();
              it != argv_hold.end();) {
@@ -62,10 +58,7 @@
     }
 
     // Run logcat command without -L flag
-    int retval = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
-                                            (char* const*)&argv_hold[0], envp);
-    if (!ret) ret = retval;
-    retval = android_logcat_destroy(&ctx);
+    int retval = RunLogcat(argv_hold.size() - 1, (char**)&argv_hold[0]);
     if (!ret) ret = retval;
     return ret;
 }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 5c43e18..ec81933 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -245,9 +245,9 @@
 }
 
 log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
-    struct logger_entry_v4 entry = {};
+    struct logger_entry entry = {};
 
-    entry.hdr_size = sizeof(struct logger_entry_v4);
+    entry.hdr_size = sizeof(struct logger_entry);
     entry.lid = mLogId;
     entry.pid = mPid;
     entry.tid = mTid;
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index f19e7b0..0cc7886 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -311,9 +311,7 @@
         if (log_msg.entry.len <= sizeof(uint32_t)) continue;
         uint32_t Tag = get4LE(msg);
         if (Tag != TAG_DEF_LOG_TAG) continue;
-        uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4))
-                        ? log_msg.entry.uid
-                        : AID_ROOT;
+        uid_t uid = log_msg.entry.uid;
 
         std::string Name;
         std::string Format;
diff --git a/logd/fuzz/Android.bp b/logd/fuzz/Android.bp
index 3215b24..299242d 100644
--- a/logd/fuzz/Android.bp
+++ b/logd/fuzz/Android.bp
@@ -1,16 +1,18 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 cc_fuzz {
     name: "log_buffer_log_fuzzer",
     srcs: [
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index be4c7c3..4d1589b 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,64 +13,79 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <string>
+
 #include "../LogBuffer.h"
 #include "../LogTimes.h"
 
+// We don't want to waste a lot of entropy on messages
+#define MAX_MSG_LENGTH 5
+
+// Tag IDs usually start at 1000, we only want to try 1000 through 1009
+#define MIN_TAG_ID 1000
+#define TAG_MOD 10
+
 namespace android {
 struct LogInput {
   public:
-    log_id_t log_id;  // char
+    log_id_t log_id;
     log_time realtime;
     uid_t uid;
     pid_t pid;
     pid_t tid;
+    unsigned int log_mask;
 };
 
-int write_log_messages(const uint8_t* data, size_t* data_left, LogBuffer* log_buffer) {
+int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
+    const uint8_t* data = *pdata;
     const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
     data += sizeof(LogInput);
     *data_left -= sizeof(LogInput);
 
-    uint8_t tag_length = data[0] % 32;
-    uint8_t msg_length = data[1] % 32;
-    if (tag_length < 2 || msg_length < 2) {
-        // Not enough data for tag and message
+    uint32_t tag = MIN_TAG_ID + data[0] % TAG_MOD;
+    uint8_t msg_length = data[1] % MAX_MSG_LENGTH;
+    if (msg_length < 2) {
+        // Not enough data for message
         return 0;
     }
 
     data += 2 * sizeof(uint8_t);
     *data_left -= 2 * sizeof(uint8_t);
 
-    if (*data_left < tag_length + msg_length) {
+    if (*data_left < msg_length) {
         // Not enough data for tag and message
+        *pdata = data;
         return 0;
     }
 
     // We need nullterm'd strings
-    char* msg = new char[tag_length + msg_length + 2];
-    char* msg_only = msg + tag_length + 1;
-    memcpy(msg, data, tag_length);
-    msg[tag_length] = '\0';
+    char msg[sizeof(uint32_t) + MAX_MSG_LENGTH + sizeof(char)];
+    char* msg_only = msg + sizeof(uint32_t);
+    memcpy(msg, &tag, sizeof(uint32_t));
     memcpy(msg_only, data, msg_length);
     msg_only[msg_length] = '\0';
-    data += tag_length + msg_length;
-    *data_left -= tag_length + msg_length;
+    data += msg_length;
+    *data_left -= msg_length;
 
     // Other elements not in enum.
     log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
     log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
-                    tag_length + msg_length + 2);
-    delete[] msg;
+                    sizeof(uint32_t) + msg_length + 1);
+    log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
+    *pdata = data;
     return 1;
 }
 
-// Because system/core/logd/main.cpp redefines this.
+// Because system/core/logd/main.cpp redefines these.
 void prdebug(char const* fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     vfprintf(stderr, fmt, ap);
     va_end(ap);
 }
+char* uidToName(uid_t) {
+    return strdup("fake");
+}
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     // We want a random tag length and a random remaining message length
@@ -81,13 +96,15 @@
     LastLogTimes times;
     LogBuffer log_buffer(&times);
     size_t data_left = size;
+    const uint8_t** pdata = &data;
 
     log_buffer.enableStatistics();
+    log_buffer.initPrune(nullptr);
     // We want to get pruning code to get called.
     log_id_for_each(i) { log_buffer.setSize(i, 10000); }
 
     while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
-        if (!write_log_messages(data, &data_left, &log_buffer)) {
+        if (!write_log_messages(pdata, &data_left, &log_buffer)) {
             return 0;
         }
     }
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 80625a7..f47bee1 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -241,47 +241,18 @@
 static void caught_signal(int /* signum */) {
 }
 
-static void dump_log_msg(const char* prefix, log_msg* msg, unsigned int version,
-                         int lid) {
+static void dump_log_msg(const char* prefix, log_msg* msg, int lid) {
     std::cout << std::flush;
     std::cerr << std::flush;
     fflush(stdout);
     fflush(stderr);
-    switch (msg->entry.hdr_size) {
-        case 0:
-            version = 1;
-            break;
+    EXPECT_EQ(sizeof(logger_entry), msg->entry.hdr_size);
 
-        case sizeof(msg->entry_v2): /* PLUS case sizeof(msg->entry_v3): */
-            if (version == 0) {
-                version = (msg->entry_v3.lid < LOG_ID_MAX) ? 3 : 2;
-            }
-            break;
-
-        case sizeof(msg->entry_v4):
-            if (version == 0) {
-                version = 4;
-            }
-            break;
-    }
-
-    fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len());
-    if (version != 1) {
-        fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
-    }
-    fprintf(stderr, "pid=%u tid=%u %u.%09u ", msg->entry.pid, msg->entry.tid,
-            msg->entry.sec, msg->entry.nsec);
-    switch (version) {
-        case 1:
-            break;
-        case 2:
-            fprintf(stderr, "euid=%u ", msg->entry_v2.euid);
-            break;
-        case 3:
-        default:
-            lid = msg->entry.lid;
-            break;
-    }
+    fprintf(stderr, "%s: [%u] ", prefix, msg->len());
+    fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
+    fprintf(stderr, "pid=%u tid=%u %u.%09u ", msg->entry.pid, msg->entry.tid, msg->entry.sec,
+            msg->entry.nsec);
+    lid = msg->entry.lid;
 
     switch (lid) {
         case 0:
@@ -584,11 +555,11 @@
     }
 
     if (content_wrap) {
-        dump_log_msg("wrap", &msg_wrap, 3, -1);
+        dump_log_msg("wrap", &msg_wrap, -1);
     }
 
     if (content_timeout) {
-        dump_log_msg("timeout", &msg_timeout, 3, -1);
+        dump_log_msg("timeout", &msg_timeout, -1);
     }
 
     EXPECT_TRUE(written);
@@ -721,11 +692,11 @@
     }
 
     if (content_wrap) {
-        dump_log_msg("wrap", &msg_wrap, 3, -1);
+        dump_log_msg("wrap", &msg_wrap, -1);
     }
 
     if (content_timeout) {
-        dump_log_msg("timeout", &msg_timeout, 3, -1);
+        dump_log_msg("timeout", &msg_timeout, -1);
     }
 
     if (content_wrap || !content_timeout) {
@@ -776,7 +747,7 @@
 
     EXPECT_TRUE(read_one);
     if (read_one) {
-        dump_log_msg("user", &msg, 3, -1);
+        dump_log_msg("user", &msg, -1);
     }
 
     fprintf(stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
@@ -794,7 +765,7 @@
 
     EXPECT_EQ(0, recv_ret);
     if (recv_ret > 0) {
-        dump_log_msg("user", &msg, 3, -1);
+        dump_log_msg("user", &msg, -1);
     }
     EXPECT_EQ(0, save_errno);
 
diff --git a/reboot/Android.bp b/reboot/Android.bp
index 805fd9a..cc71723 100644
--- a/reboot/Android.bp
+++ b/reboot/Android.bp
@@ -5,4 +5,5 @@
     srcs: ["reboot.c"],
     shared_libs: ["libcutils"],
     cflags: ["-Werror"],
+    recovery_available: true,
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c5bf1603..9b77ce2 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -754,6 +754,7 @@
     # to make it too large, since it may bring userdata loss, if they
     # are not aware of using fsync()/sync() to prepare sudden power-cut.
     write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
+    write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
 
     # Permissions for System Server and daemons.
     chown system system /sys/power/autosleep
@@ -925,9 +926,17 @@
   setprop sys.init.updatable_crashing 0
   setprop apexd.status 0
 
+on userspace-reboot-fs-remount
+  # Make sure that vold is running.
+  # This is mostly a precaution measure in case vold for some reason wasn't running when
+  # userspace reboot was initiated.
+  start vold
+  exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
+  exec - system system -- /system/bin/vdc checkpoint markBootAttempt
+  remount_userdata
+
 on userspace-reboot-resume
-  # TODO(b/135984674): remount userdata and reset checkpointing
-  trigger nonencrypted
+  trigger userspace-reboot-fs-remount
   trigger post-fs-data
   trigger zygote-start
   trigger early-boot
diff --git a/set-verity-state/.clang-format b/set-verity-state/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/set-verity-state/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
new file mode 100644
index 0000000..cd8c8c5
--- /dev/null
+++ b/set-verity-state/Android.bp
@@ -0,0 +1,24 @@
+// Copyright 2019 The Android Open Source Project
+
+cc_binary {
+    name: "set-verity-state",
+    srcs: ["set-verity-state.cpp"],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "libfec",
+        "libfs_mgr",
+        "liblog",
+    ],
+    static_libs: [
+        "libavb_user",
+    ],
+
+    cflags: ["-Werror"],
+    symlinks: [
+        "enable-verity",
+        "disable-verity",
+    ],
+}
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
new file mode 100644
index 0000000..0a26aba
--- /dev/null
+++ b/set-verity-state/set-verity-state.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libavb_user/libavb_user.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <log/log_properties.h>
+
+#include "fec/io.h"
+
+#ifdef ALLOW_DISABLE_VERITY
+static const bool kAllowDisableVerity = true;
+#else
+static const bool kAllowDisableVerity = false;
+#endif
+
+using android::base::unique_fd;
+
+static void suggest_run_adb_root() {
+  if (getuid() != 0) printf("Maybe run adb root?\n");
+}
+
+static bool make_block_device_writable(const std::string& dev) {
+  unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+  if (fd == -1) {
+    return false;
+  }
+
+  int OFF = 0;
+  bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
+  return result;
+}
+
+/* Turn verity on/off */
+static bool set_verity_enabled_state(const char* block_device, const char* mount_point,
+                                     bool enable) {
+  if (!make_block_device_writable(block_device)) {
+    printf("Could not make block device %s writable (%s).\n", block_device, strerror(errno));
+    return false;
+  }
+
+  fec::io fh(block_device, O_RDWR);
+
+  if (!fh) {
+    printf("Could not open block device %s (%s).\n", block_device, strerror(errno));
+    suggest_run_adb_root();
+    return false;
+  }
+
+  fec_verity_metadata metadata;
+
+  if (!fh.get_verity_metadata(metadata)) {
+    printf("Couldn't find verity metadata!\n");
+    return false;
+  }
+
+  if (!enable && metadata.disabled) {
+    printf("Verity already disabled on %s\n", mount_point);
+    return false;
+  }
+
+  if (enable && !metadata.disabled) {
+    printf("Verity already enabled on %s\n", mount_point);
+    return false;
+  }
+
+  if (!fh.set_verity_status(enable)) {
+    printf("Could not set verity %s flag on device %s with error %s\n",
+           enable ? "enabled" : "disabled", block_device, strerror(errno));
+    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) {
+      printf("%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
+    }
+  } else if (errno) {
+    int expected_errno = enable ? EBUSY : ENOENT;
+    if (errno != expected_errno) {
+      printf("Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
+             mount_point, strerror(errno));
+    }
+  }
+  printf("Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
+  return true;
+}
+
+/* Helper function to get A/B suffix, if any. If the device isn't
+ * using A/B the empty string is returned. Otherwise either "_a",
+ * "_b", ... is returned.
+ */
+static std::string get_ab_suffix() {
+  return android::base::GetProperty("ro.boot.slot_suffix", "");
+}
+
+static bool is_avb_device_locked() {
+  return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
+}
+
+static bool overlayfs_setup(bool enable) {
+  auto change = false;
+  errno = 0;
+  if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
+             : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
+    if (change) {
+      printf("%s overlayfs\n", enable ? "disabling" : "using");
+    }
+  } else if (errno) {
+    printf("Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup", strerror(errno));
+    suggest_run_adb_root();
+  }
+  return change;
+}
+
+/* Use AVB to turn verity on/off */
+static bool set_avb_verity_enabled_state(AvbOps* ops, bool enable_verity) {
+  std::string ab_suffix = get_ab_suffix();
+  bool verity_enabled;
+
+  if (is_avb_device_locked()) {
+    printf("Device is locked. Please unlock the device first\n");
+    return false;
+  }
+
+  if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
+    printf("Error getting verity state. Try adb root first?\n");
+    return false;
+  }
+
+  if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
+    printf("verity is already %s\n", verity_enabled ? "enabled" : "disabled");
+    return false;
+  }
+
+  if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
+    printf("Error setting verity\n");
+    return false;
+  }
+
+  overlayfs_setup(enable_verity);
+  printf("Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
+  return true;
+}
+
+int main(int argc, char* argv[]) {
+  if (argc == 0) {
+    LOG(FATAL) << "set-verity-state called with empty argv";
+  }
+
+  std::optional<bool> enable_opt;
+  std::string procname = android::base::Basename(argv[0]);
+  if (procname == "enable-verity") {
+    enable_opt = true;
+  } else if (procname == "disable-verity") {
+    enable_opt = false;
+  }
+
+  if (!enable_opt.has_value()) {
+    if (argc != 2) {
+      printf("usage: %s [1|0]\n", argv[0]);
+      return 1;
+    }
+
+    if (strcmp(argv[1], "1") == 0) {
+      enable_opt = true;
+    } else if (strcmp(argv[1], "0") == 0) {
+      enable_opt = false;
+    } else {
+      printf("usage: %s [1|0]\n", argv[0]);
+      return 1;
+    }
+  }
+
+  bool enable = enable_opt.value();
+
+  bool any_changed = false;
+
+  // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
+  // contract, androidboot.vbmeta.digest is set by the bootloader
+  // when using AVB).
+  bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+
+  // If using AVB, dm-verity is used on any build so we want it to
+  // be possible to disable/enable on any build (except USER). For
+  // VB1.0 dm-verity is only enabled on certain builds.
+  if (!using_avb) {
+    if (!kAllowDisableVerity) {
+      printf("%s only works for userdebug builds\n", argv[0]);
+    }
+
+    if (!android::base::GetBoolProperty("ro.secure", false)) {
+      overlayfs_setup(enable);
+      printf("verity not enabled - ENG build\n");
+      return 0;
+    }
+  }
+
+  // Should never be possible to disable dm-verity on a USER build
+  // regardless of using AVB or VB1.0.
+  if (!__android_log_is_debuggable()) {
+    printf("verity cannot be disabled/enabled - USER build\n");
+    return 0;
+  }
+
+  if (using_avb) {
+    // Yep, the system is using AVB.
+    AvbOps* ops = avb_ops_user_new();
+    if (ops == nullptr) {
+      printf("Error getting AVB ops\n");
+      return 1;
+    }
+    if (set_avb_verity_enabled_state(ops, enable)) {
+      any_changed = true;
+    }
+    avb_ops_user_free(ops);
+  } else {
+    // Not using AVB - assume VB1.0.
+
+    // read all fstab entries at once from all sources
+    android::fs_mgr::Fstab fstab;
+    if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
+      printf("Failed to read fstab\n");
+      suggest_run_adb_root();
+      return 0;
+    }
+
+    // Loop through entries looking for ones that verity manages.
+    for (const auto& entry : fstab) {
+      if (entry.fs_mgr_flags.verify) {
+        if (set_verity_enabled_state(entry.blk_device.c_str(), entry.mount_point.c_str(), enable)) {
+          any_changed = true;
+        }
+      }
+    }
+  }
+  if (!any_changed) any_changed = overlayfs_setup(enable);
+
+  if (any_changed) {
+    printf("Now reboot your device for settings to take effect\n");
+  }
+
+  return 0;
+}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 694b50e..ec4f6ab 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -25,7 +25,7 @@
         "tcpdump",
         "toolbox",
         "toybox",
-        "unzip",
+        "ziptool",
     ],
 }