Merge "COW partition creator uses DmSnapshotCowSizeCalculator"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc1978d..375207b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,7 +10,7 @@
       "name": "debuggerd_test"
     },
     {
-      "name": "fs_mgr_unit_test"
+      "name": "CtsFsMgrTestCases"
     },
     {
       "name": "fs_mgr_vendor_overlay_test"
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/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 120340c..431fea1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -33,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
@@ -120,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();
@@ -209,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);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 758f69b..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_;
@@ -1786,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.";
@@ -2065,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 d33a04e..5728582 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;
@@ -656,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);
@@ -765,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,
@@ -1071,6 +1077,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
 
@@ -1112,6 +1261,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 91a6989..0f70afe 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/Android.bp b/fs_mgr/tests/Android.bp
index 83668e9..4f6ec5a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -13,8 +13,21 @@
 // limitations under the License.
 
 cc_test {
-    name: "fs_mgr_unit_test",
-    test_suites: ["device-tests"],
+    name: "CtsFsMgrTestCases",
+    test_suites: [
+        "cts",
+        "device-tests",
+        "vts",
+    ],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
 
     shared_libs: [
         "libbase",
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
new file mode 100644
index 0000000..91c3fb9
--- /dev/null
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS fs_mgr test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="systems" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsFsMgrTestCases" />
+        <option name="runtime-hint" value="65s" />
+    </test>
+</configuration>
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index a7ea817..1cbaf45 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) {
@@ -1081,3 +962,10 @@
     EXPECT_EQ("none5", entry->mount_point);
     EXPECT_EQ("/dev/path2", entry->zram_backing_dev_path);
 }
+
+TEST(fs_mgr, DefaultFstabContainsUserdata) {
+    Fstab fstab;
+    ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
+    ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
+            << "Default fstab doesn't contain /data entry";
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 4f89bfb..e04f70f 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -17,6 +17,10 @@
     shared_libs: [
         "libutils",
         "libbase",
+
+        // Need latest HealthInfo definition from headers of this shared
+        // library. Clients don't need to link to this.
+        "android.hardware.health@2.1",
     ],
     header_libs: ["libhealthd_headers"],
     export_header_lib_headers: ["libhealthd_headers"],
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 57ed362..9e168e9 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -29,10 +29,12 @@
 
 #include <algorithm>
 #include <memory>
+#include <optional>
 
 #include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <android/hardware/health/2.1/types.h>
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 #include <cutils/properties.h>
@@ -47,97 +49,93 @@
 #define MILLION 1.0e6
 #define DEFAULT_VBUS_VOLTAGE 5000000
 
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+using android::hardware::health::V1_0::BatteryHealth;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V2_1::BatteryCapacityLevel;
+
 namespace android {
 
-struct sysfsStringEnumMap {
+template <typename T>
+struct SysfsStringEnumMap {
     const char* s;
-    int val;
+    T val;
 };
 
-static int mapSysfsString(const char* str,
-                          struct sysfsStringEnumMap map[]) {
+template <typename T>
+static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
     for (int i = 0; map[i].s; i++)
         if (!strcmp(str, map[i].s))
             return map[i].val;
 
-    return -1;
-}
-
-static void initBatteryProperties(BatteryProperties* props) {
-    props->chargerAcOnline = false;
-    props->chargerUsbOnline = false;
-    props->chargerWirelessOnline = false;
-    props->maxChargingCurrent = 0;
-    props->maxChargingVoltage = 0;
-    props->batteryStatus = BATTERY_STATUS_UNKNOWN;
-    props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
-    props->batteryPresent = false;
-    props->batteryLevel = 0;
-    props->batteryVoltage = 0;
-    props->batteryTemperature = 0;
-    props->batteryCurrent = 0;
-    props->batteryCycleCount = 0;
-    props->batteryFullCharge = 0;
-    props->batteryChargeCounter = 0;
-    props->batteryTechnology.clear();
+    return std::nullopt;
 }
 
 BatteryMonitor::BatteryMonitor()
     : mHealthdConfig(nullptr),
       mBatteryDevicePresent(false),
       mBatteryFixedCapacity(0),
-      mBatteryFixedTemperature(0) {
-    initBatteryProperties(&props);
+      mBatteryFixedTemperature(0),
+      mHealthInfo(std::make_unique<HealthInfo_2_1>()) {}
+
+BatteryMonitor::~BatteryMonitor() {}
+
+const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
+    return getHealthInfo_2_0().legacy;
 }
 
-struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
-    return batteryMonitor->props;
+const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
+    return getHealthInfo_2_1().legacy;
 }
 
-int BatteryMonitor::getBatteryStatus(const char* status) {
-    int ret;
-    struct sysfsStringEnumMap batteryStatusMap[] = {
-        { "Unknown", BATTERY_STATUS_UNKNOWN },
-        { "Charging", BATTERY_STATUS_CHARGING },
-        { "Discharging", BATTERY_STATUS_DISCHARGING },
-        { "Not charging", BATTERY_STATUS_NOT_CHARGING },
-        { "Full", BATTERY_STATUS_FULL },
-        { NULL, 0 },
+const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
+    return *mHealthInfo;
+}
+
+BatteryStatus getBatteryStatus(const char* status) {
+    static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
+            {"Unknown", BatteryStatus::UNKNOWN},
+            {"Charging", BatteryStatus::CHARGING},
+            {"Discharging", BatteryStatus::DISCHARGING},
+            {"Not charging", BatteryStatus::NOT_CHARGING},
+            {"Full", BatteryStatus::FULL},
+            {NULL, BatteryStatus::UNKNOWN},
     };
 
-    ret = mapSysfsString(status, batteryStatusMap);
-    if (ret < 0) {
+    auto ret = mapSysfsString(status, batteryStatusMap);
+    if (!ret) {
         KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
-        ret = BATTERY_STATUS_UNKNOWN;
+        *ret = BatteryStatus::UNKNOWN;
     }
 
-    return ret;
+    return *ret;
 }
 
-int BatteryMonitor::getBatteryHealth(const char* status) {
-    int ret;
-    struct sysfsStringEnumMap batteryHealthMap[] = {
-        { "Unknown", BATTERY_HEALTH_UNKNOWN },
-        { "Good", BATTERY_HEALTH_GOOD },
-        { "Overheat", BATTERY_HEALTH_OVERHEAT },
-        { "Dead", BATTERY_HEALTH_DEAD },
-        { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
-        { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
-        { "Cold", BATTERY_HEALTH_COLD },
-        // battery health values from JEITA spec
-        { "Warm", BATTERY_HEALTH_GOOD },
-        { "Cool", BATTERY_HEALTH_GOOD },
-        { "Hot", BATTERY_HEALTH_OVERHEAT },
-        { NULL, 0 },
+BatteryHealth getBatteryHealth(const char* status) {
+    static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
+            {"Unknown", BatteryHealth::UNKNOWN},
+            {"Good", BatteryHealth::GOOD},
+            {"Overheat", BatteryHealth::OVERHEAT},
+            {"Dead", BatteryHealth::DEAD},
+            {"Over voltage", BatteryHealth::OVER_VOLTAGE},
+            {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
+            {"Cold", BatteryHealth::COLD},
+            // battery health values from JEITA spec
+            {"Warm", BatteryHealth::GOOD},
+            {"Cool", BatteryHealth::GOOD},
+            {"Hot", BatteryHealth::OVERHEAT},
+            {NULL, BatteryHealth::UNKNOWN},
     };
 
-    ret = mapSysfsString(status, batteryHealthMap);
-    if (ret < 0) {
+    auto ret = mapSysfsString(status, batteryHealthMap);
+    if (!ret) {
         KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
-        ret = BATTERY_HEALTH_UNKNOWN;
+        *ret = BatteryHealth::UNKNOWN;
     }
 
-    return ret;
+    return *ret;
 }
 
 int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
@@ -148,35 +146,34 @@
 }
 
 BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
-    std::string buf;
-    int ret;
-    struct sysfsStringEnumMap supplyTypeMap[] = {
-            { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
-            { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
-            { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
-            { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
-            { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
-            { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
-            { NULL, 0 },
+    static SysfsStringEnumMap<int> supplyTypeMap[] = {
+            {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+            {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+            {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
+            {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
+            {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+            {NULL, 0},
     };
+    std::string buf;
 
     if (readFromFile(path, &buf) <= 0)
         return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
 
-    ret = mapSysfsString(buf.c_str(), supplyTypeMap);
+    auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
     if (ret < 0) {
         KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
-        ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+        *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
     }
 
-    return static_cast<BatteryMonitor::PowerSupplyType>(ret);
+    return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
 }
 
 bool BatteryMonitor::getBooleanField(const String8& path) {
@@ -201,7 +198,9 @@
 }
 
 void BatteryMonitor::updateValues(void) {
-    initBatteryProperties(&props);
+    *mHealthInfo = HealthInfo_2_1{};
+
+    HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -225,6 +224,15 @@
     if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
         props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
 
+    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+        mHealthInfo->legacy.batteryCurrentAverage =
+                getIntField(mHealthdConfig->batteryCurrentAvgPath);
+
+    // TODO(b/142260281): Retrieve these values correctly.
+    mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+    mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
+    mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
+
     props.batteryTemperature = mBatteryFixedTemperature ?
         mBatteryFixedTemperature :
         getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -292,6 +300,7 @@
 void BatteryMonitor::logValues(void) {
     char dmesgline[256];
     size_t len;
+    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
     if (props.batteryPresent) {
         snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
                  props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
@@ -325,18 +334,19 @@
 }
 
 bool BatteryMonitor::isChargerOnline() {
+    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
     return props.chargerAcOnline | props.chargerUsbOnline |
             props.chargerWirelessOnline;
 }
 
 int BatteryMonitor::getChargeStatus() {
-    int result = BATTERY_STATUS_UNKNOWN;
+    BatteryStatus result = BatteryStatus::UNKNOWN;
     if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
         std::string buf;
         if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
             result = getBatteryStatus(buf.c_str());
     }
-    return result;
+    return static_cast<int>(result);
 }
 
 status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
@@ -409,6 +419,7 @@
 void BatteryMonitor::dumpState(int fd) {
     int v;
     char vs[128];
+    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
 
     snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
              props.chargerAcOnline, props.chargerUsbOnline,
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 0fd3824..d41a374 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -17,6 +17,8 @@
 #ifndef HEALTHD_BATTERYMONITOR_H
 #define HEALTHD_BATTERYMONITOR_H
 
+#include <memory>
+
 #include <batteryservice/BatteryService.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
@@ -24,6 +26,19 @@
 #include <healthd/healthd.h>
 
 namespace android {
+namespace hardware {
+namespace health {
+namespace V1_0 {
+struct HealthInfo;
+}  // namespace V1_0
+namespace V2_0 {
+struct HealthInfo;
+}  // namespace V2_0
+namespace V2_1 {
+struct HealthInfo;
+}  // namespace V2_1
+}  // namespace health
+}  // namespace hardware
 
 class BatteryMonitor {
   public:
@@ -37,11 +52,15 @@
     };
 
     BatteryMonitor();
+    ~BatteryMonitor();
     void init(struct healthd_config *hc);
     int getChargeStatus();
     status_t getProperty(int id, struct BatteryProperty *val);
     void dumpState(int fd);
-    friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
+
+    const android::hardware::health::V1_0::HealthInfo& getHealthInfo_1_0() const;
+    const android::hardware::health::V2_0::HealthInfo& getHealthInfo_2_0() const;
+    const android::hardware::health::V2_1::HealthInfo& getHealthInfo_2_1() const;
 
     void updateValues(void);
     void logValues(void);
@@ -53,10 +72,8 @@
     bool mBatteryDevicePresent;
     int mBatteryFixedCapacity;
     int mBatteryFixedTemperature;
-    struct BatteryProperties props;
+    std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
 
-    int getBatteryStatus(const char* status);
-    int getBatteryHealth(const char* status);
     int readFromFile(const String8& path, std::string* buf);
     PowerSupplyType readPowerSupplyType(const String8& path);
     bool getBooleanField(const String8& path);
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/reboot.cpp b/init/reboot.cpp
index d453655..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) {
@@ -427,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);
@@ -440,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.
@@ -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/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 f164a12..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,169 +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, &entry, context->eventTagMap,
+        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, &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"
@@ -514,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"
@@ -532,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"
@@ -568,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) {
@@ -607,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) {
@@ -712,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;
@@ -732,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;
@@ -760,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
@@ -912,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;
                 }
@@ -930,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) {
@@ -958,7 +677,7 @@
 
             case 's':
                 // default to all silent
-                android_log_addFilterRule(context->logformat, "*:s");
+                android_log_addFilterRule(logformat_.get(), "*:s");
                 break;
 
             case 'c':
@@ -983,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;
                     }
                 }
@@ -1012,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;
 
@@ -1059,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;
 
@@ -1093,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);
@@ -1111,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;
@@ -1129,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':
@@ -1144,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;
@@ -1198,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, "");
@@ -1210,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);
@@ -1230,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");
@@ -1248,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;
 
@@ -1287,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,
@@ -1358,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),
@@ -1366,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 {
@@ -1411,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 {
@@ -1439,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()) {
@@ -1497,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) {
@@ -1537,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) {
@@ -1574,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
@@ -1592,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) {
@@ -1742,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/rootdir/Android.mk b/rootdir/Android.mk
index 5241730..e1bb02f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -326,7 +326,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.com.android.vndk.current)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
@@ -342,7 +342,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.com.android.vndk.current)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
@@ -358,7 +358,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.com.android.vndk.current)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 674ed01..9b77ce2 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -926,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/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",
     ],
 }