Merge "Use soong-generated vndk*libraries.txt files"
diff --git a/base/Android.bp b/base/Android.bp
index f5000c1..25ae535 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -61,7 +61,6 @@
         "parsenetaddress.cpp",
         "process.cpp",
         "properties.cpp",
-        "quick_exit.cpp",
         "stringprintf.cpp",
         "strings.cpp",
         "threads.cpp",
@@ -154,7 +153,6 @@
         "parsenetaddress_test.cpp",
         "process_test.cpp",
         "properties_test.cpp",
-        "quick_exit_test.cpp",
         "result_test.cpp",
         "scopeguard_test.cpp",
         "stringprintf_test.cpp",
diff --git a/base/include/android-base/quick_exit.h b/base/include/android-base/quick_exit.h
deleted file mode 100644
index a03b14f..0000000
--- a/base/include/android-base/quick_exit.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdlib.h>
-
-// Provide emulation for at_quick_exit/quick_exit on platforms that don't have it.
-namespace android {
-namespace base {
-
-// Bionic and glibc have quick_exit, Darwin and Windows don't.
-#if !defined(__linux__)
-  void quick_exit(int exit_code) __attribute__((noreturn));
-  int at_quick_exit(void (*func)());
-#else
-  using ::at_quick_exit;
-  using ::quick_exit;
-#endif
-}
-}
diff --git a/base/quick_exit.cpp b/base/quick_exit.cpp
deleted file mode 100644
index e4dd62b..0000000
--- a/base/quick_exit.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/quick_exit.h"
-
-#if !defined(__linux__)
-
-#include <mutex>
-#include <vector>
-
-namespace android {
-namespace base {
-
-static auto& quick_exit_mutex = *new std::mutex();
-static auto& quick_exit_handlers = *new std::vector<void (*)()>();
-
-void quick_exit(int exit_code) {
-  std::lock_guard<std::mutex> lock(quick_exit_mutex);
-  for (auto it = quick_exit_handlers.rbegin(); it != quick_exit_handlers.rend(); ++it) {
-    (*it)();
-  }
-  _Exit(exit_code);
-}
-
-int at_quick_exit(void (*func)()) {
-  std::lock_guard<std::mutex> lock(quick_exit_mutex);
-  quick_exit_handlers.push_back(func);
-  return 0;
-}
-
-}  // namespace base
-}  // namespace android
-#endif
diff --git a/base/quick_exit_test.cpp b/base/quick_exit_test.cpp
deleted file mode 100644
index 7ca8156..0000000
--- a/base/quick_exit_test.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/quick_exit.h"
-
-#include <gtest/gtest.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <string>
-
-#include "android-base/test_utils.h"
-
-// These tests are a bit sketchy, since each test run adds global state that affects subsequent
-// tests (including ones not in this file!). Exit with 0 in Exiter and stick the at_quick_exit test
-// at the end to hack around this.
-struct Exiter {
-  ~Exiter() {
-    _Exit(0);
-  }
-};
-
-TEST(quick_exit, smoke) {
-  ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
-}
-
-TEST(quick_exit, skip_static_destructors) {
-  static Exiter exiter;
-  ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
-}
-
-TEST(quick_exit, at_quick_exit) {
-  static int counter = 4;
-  // "Functions passed to at_quick_exit are called in reverse order of their registration."
-  ASSERT_EQ(0, android::base::at_quick_exit([]() { _exit(counter); }));
-  ASSERT_EQ(0, android::base::at_quick_exit([]() { counter += 2; }));
-  ASSERT_EQ(0, android::base::at_quick_exit([]() { counter *= 10; }));
-  ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(42), "");
-}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index f452a64..02a887e 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -122,6 +122,7 @@
 
     shared_libs: [
         "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
         "android.hardware.fastboot@1.0",
         "android.hardware.health@2.0",
         "libadbd",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 8a72627..7fba67c 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -34,6 +34,7 @@
 #define FB_CMD_UPDATE_SUPER "update-super"
 #define FB_CMD_OEM "oem"
 #define FB_CMD_GSI "gsi"
+#define FB_CMD_SNAPSHOT_UPDATE "snapshot-update"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
@@ -66,3 +67,4 @@
 #define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
 #define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
 #define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
+#define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 4c77c75..dfd5690 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -25,6 +25,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
@@ -44,8 +45,10 @@
 using ::android::hardware::boot::V1_0::BoolResult;
 using ::android::hardware::boot::V1_0::CommandResult;
 using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::boot::V1_1::MergeStatus;
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
+using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
 
 struct VariableHandlers {
     // Callback to retrieve the value of a single variable.
@@ -101,7 +104,8 @@
             {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
             {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
             {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
-            {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
+            {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
+            {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}}};
 
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
@@ -547,3 +551,40 @@
     }
     return device->WriteStatus(FastbootResult::OKAY, "Success");
 }
+
+bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    // Note that we use the HAL rather than mounting /metadata, since we want
+    // our results to match the bootloader.
+    auto hal = device->boot_control_hal();
+    if (!hal) return device->WriteFail("Not supported");
+
+    android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
+    if (!hal11) return device->WriteFail("Not supported");
+
+    // If no arguments, return the same thing as a getvar. Note that we get the
+    // HAL first so we can return "not supported" before we return the less
+    // specific error message below.
+    if (args.size() < 2 || args[1].empty()) {
+        std::string message;
+        if (!GetSnapshotUpdateStatus(device, {}, &message)) {
+            return device->WriteFail("Could not determine update status");
+        }
+        device->WriteInfo(message);
+        return device->WriteOkay("");
+    }
+
+    if (args.size() != 2 || args[1] != "cancel") {
+        return device->WriteFail("Invalid arguments");
+    }
+
+    MergeStatus status = hal11->getSnapshotMergeStatus();
+    switch (status) {
+        case MergeStatus::SNAPSHOTTED:
+        case MergeStatus::MERGING:
+            hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+            break;
+        default:
+            break;
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Success");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 9b6e7b6..c1324bc 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -49,3 +49,4 @@
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 56fafab..d3c2bda 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -54,6 +54,7 @@
               {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
               {FB_CMD_OEM, OemCmdHandler},
               {FB_CMD_GSI, GsiHandler},
+              {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 130a3cf..6e613d6 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -23,6 +23,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
 #include <healthhalutils/HealthHalUtils.h>
@@ -34,9 +35,11 @@
 
 using ::android::hardware::boot::V1_0::BoolResult;
 using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::boot::V1_1::MergeStatus;
 using ::android::hardware::fastboot::V1_0::FileSystemType;
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
+using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
 using namespace android::fs_mgr;
 
 constexpr char kFastbootProtocolVersion[] = "0.4";
@@ -424,3 +427,34 @@
     *message = fs_mgr_get_super_partition_name(slot_number);
     return true;
 }
+
+bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                             std::string* message) {
+    // Note that we use the HAL rather than mounting /metadata, since we want
+    // our results to match the bootloader.
+    auto hal = device->boot_control_hal();
+    if (!hal) {
+        *message = "not supported";
+        return false;
+    }
+
+    android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
+    if (!hal11) {
+        *message = "not supported";
+        return false;
+    }
+
+    MergeStatus status = hal11->getSnapshotMergeStatus();
+    switch (status) {
+        case MergeStatus::SNAPSHOTTED:
+            *message = "snapshotted";
+            break;
+        case MergeStatus::MERGING:
+            *message = "merging";
+            break;
+        default:
+            *message = "none";
+            break;
+    }
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 015a4c5..4dec10f 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -61,6 +61,8 @@
                      std::string* message);
 bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
                            std::string* message);
+bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
+                             std::string* message);
 
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2fe3b1a..7ce7c7c 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -396,6 +396,9 @@
             " gsi wipe|disable           Wipe or disable a GSI installation (fastbootd only).\n"
             " wipe-super [SUPER_EMPTY]   Wipe the super partition. This will reset it to\n"
             "                            contain an empty set of default dynamic partitions.\n"
+            " snapshot-update cancel     On devices that support snapshot-based updates, cancel\n"
+            "                            an in-progress update. This may make the device\n"
+            "                            unbootable until it is reflashed.\n"
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -1216,6 +1219,14 @@
     target_sparse_limit = -1;
 }
 
+static void CancelSnapshotIfNeeded() {
+    std::string merge_status = "none";
+    if (fb->GetVar(FB_VAR_SNAPSHOT_UPDATE_STATUS, &merge_status) == fastboot::SUCCESS &&
+        merge_status != "none") {
+        fb->SnapshotUpdateCommand("Cancel");
+    }
+}
+
 class ImageSource {
   public:
     virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
@@ -1268,6 +1279,8 @@
     DetermineSecondarySlot();
     CollectImages();
 
+    CancelSnapshotIfNeeded();
+
     // First flash boot partitions. We allow this to happen either in userspace
     // or in bootloader fastboot.
     FlashImages(boot_images_);
@@ -2071,12 +2084,24 @@
                 image = next_arg(&args);
             }
             do_wipe_super(image, slot_override);
+        } else if (command == "snapshot-update") {
+            std::string arg;
+            if (!args.empty()) {
+                arg = next_arg(&args);
+            }
+            if (!arg.empty() && arg != "cancel") {
+                syntax_error("expected: snapshot-update [cancel]");
+            }
+            fb->SnapshotUpdateCommand(arg);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
     }
 
     if (wants_wipe) {
+        if (force_flash) {
+            CancelSnapshotIfNeeded();
+        }
         std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
         for (const auto& partition : partitions) {
             std::string partition_type;
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index b897182..6a5ad20 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -122,6 +122,12 @@
                       response, info);
 }
 
+RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
+                                              std::vector<std::string>* info) {
+    std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
+    return RawCommand(raw, response, info);
+}
+
 RetCode FastBootDriver::FlashPartition(const std::string& partition,
                                        const std::vector<char>& data) {
     RetCode ret;
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index af02637..7265632 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -104,6 +104,8 @@
                       std::vector<std::string>* info = nullptr);
     RetCode Upload(const std::string& outfile, std::string* response = nullptr,
                    std::vector<std::string>* info = nullptr);
+    RetCode SnapshotUpdateCommand(const std::string& command, std::string* response = nullptr,
+                                  std::vector<std::string>* info = nullptr);
 
     /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
     RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 65f0eff..eb737bb 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -74,6 +74,7 @@
         "liblogwrap",
         "libdm",
         "libext2_uuid",
+        "libfscrypt",
         "libfstab",
     ],
     cppflags: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6faead0..a8059b7 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -58,6 +58,7 @@
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr/file_wait.h>
 #include <fs_mgr_overlayfs.h>
+#include <fscrypt/fscrypt.h>
 #include <libdm/dm.h>
 #include <liblp/metadata_format.h>
 #include <linux/fs.h>
@@ -84,6 +85,9 @@
 
 #define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
 
+// FIXME: this should be in system/extras
+#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
 using android::base::Basename;
@@ -412,25 +416,43 @@
 // Enable file-based encryption if needed.
 static void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,
                          const struct ext4_super_block* sb, int* fs_stat) {
-    bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
-    bool want_encrypt = entry.fs_mgr_flags.file_encryption;
-
-    if (has_encrypt || !want_encrypt) {
+    if (!entry.fs_mgr_flags.file_encryption) {
+        return;  // Nothing needs done.
+    }
+    std::vector<std::string> features_needed;
+    if ((sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) == 0) {
+        features_needed.emplace_back("encrypt");
+    }
+    android::fscrypt::EncryptionOptions options;
+    if (!android::fscrypt::ParseOptions(entry.encryption_options, &options)) {
+        LERROR << "Unable to parse encryption options on " << blk_device << ": "
+               << entry.encryption_options;
         return;
     }
-
+    if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) != 0) {
+        // We can only use this policy on ext4 if the "stable_inodes" feature
+        // is set on the filesystem, otherwise shrinking will break encrypted files.
+        if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {
+            features_needed.emplace_back("stable_inodes");
+        }
+    }
+    if (features_needed.size() == 0) {
+        return;
+    }
     if (!tune2fs_available()) {
         LERROR << "Unable to enable ext4 encryption on " << blk_device
                << " because " TUNE2FS_BIN " is missing";
         return;
     }
 
-    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device.c_str()};
+    auto flags = android::base::Join(features_needed, ',');
+    auto flag_arg = "-O"s + flags;
+    const char* argv[] = {TUNE2FS_BIN, flag_arg.c_str(), blk_device.c_str()};
 
-    LINFO << "Enabling ext4 encryption on " << blk_device;
+    LINFO << "Enabling ext4 flags " << flags << " on " << blk_device;
     if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to enable "
-               << "ext4 encryption on " << blk_device;
+               << "ext4 flags " << flags << " on " << blk_device;
         *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
     }
 }
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 615cbca..1b2f528 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -19,6 +19,7 @@
 #include <android-base/strings.h>
 #include <fs_mgr/roots.h>
 
+using android::dm::kSectorSize;
 using android::fs_mgr::EnsurePathMounted;
 using android::fs_mgr::EnsurePathUnmounted;
 using android::fs_mgr::Fstab;
@@ -94,13 +95,11 @@
     // so it can be used to resume the last state of a snapshot device;
     // - an _INVALID_ snapshot otherwise.
     // To avoid zero-filling the whole CoW file when a new dm-snapshot is
-    // created, here we zero-fill only the first 32 bits. This is a temporary
-    // workaround that will be discussed again when the kernel API gets
-    // consolidated.
-    // TODO(b/139202197): Remove this hack once the kernel API is consolidated.
-    constexpr ssize_t kDmSnapZeroFillSize = 4;  // 32-bit
+    // created, here we zero-fill only the first chunk to be compliant with
+    // lvm.
+    constexpr ssize_t kDmSnapZeroFillSize = kSectorSize * kSnapshotChunkSize;
 
-    char zeros[kDmSnapZeroFillSize] = {0};
+    std::vector<uint8_t> zeros(kDmSnapZeroFillSize, 0);
     android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
     if (fd < 0) {
         PLOG(ERROR) << "Can't open COW device: " << device;
@@ -108,7 +107,7 @@
     }
 
     LOG(INFO) << "Zero-filling COW device: " << device;
-    if (!android::base::WriteFully(fd, zeros, kDmSnapZeroFillSize)) {
+    if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
         PLOG(ERROR) << "Can't zero-fill COW device for " << device;
         return false;
     }
diff --git a/init/Android.bp b/init/Android.bp
index bd2d38c..776a3a6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -79,7 +79,6 @@
         "libdl",
         "libext4_utils",
         "libfs_mgr",
-        "libfscrypt",
         "libgsi",
         "libhidl-gen-utils",
         "libkeyutils",
diff --git a/init/Android.mk b/init/Android.mk
index 4e4c002..997b2bc 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -91,7 +91,6 @@
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
-    libfscrypt \
     libcrypto_utils \
     libsparse \
     libavb \
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index dcb4ffb..421d826 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -19,6 +19,7 @@
 #include <set>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 class Modprobe {
@@ -59,5 +60,6 @@
     std::vector<std::string> module_load_;
     std::unordered_map<std::string, std::string> module_options_;
     std::set<std::string> module_blacklist_;
+    std::unordered_set<std::string> module_loaded_;
     bool blacklist_enabled = false;
 };
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 73ae15b..3c78ec9 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -330,7 +330,12 @@
 
 bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict,
                                const std::string& parameters) {
-    std::set<std::string> modules_to_load = {MakeCanonical(module_name)};
+    auto canonical_name = MakeCanonical(module_name);
+    if (module_loaded_.count(canonical_name)) {
+        return true;
+    }
+
+    std::set<std::string> modules_to_load = {canonical_name};
     bool module_loaded = false;
 
     // use aliases to expand list of modules to load (multiple modules
@@ -338,6 +343,7 @@
     for (const auto& [alias, aliased_module] : module_aliases_) {
         if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;
         LOG(VERBOSE) << "Found alias for '" << module_name << "': '" << aliased_module;
+        if (module_loaded_.count(MakeCanonical(aliased_module))) continue;
         modules_to_load.emplace(aliased_module);
     }
 
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 2efcac2..8bebe4c 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -30,8 +30,9 @@
         return false;
     }
 
+    auto canonical_name = MakeCanonical(path_name);
     std::string options = "";
-    auto options_iter = module_options_.find(MakeCanonical(path_name));
+    auto options_iter = module_options_.find(canonical_name);
     if (options_iter != module_options_.end()) {
         options = options_iter->second;
     }
@@ -44,6 +45,7 @@
     if (ret != 0) {
         if (errno == EEXIST) {
             // Module already loaded
+            module_loaded_.emplace(canonical_name);
             return true;
         }
         LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
@@ -51,15 +53,18 @@
     }
 
     LOG(INFO) << "Loaded kernel module " << path_name;
+    module_loaded_.emplace(canonical_name);
     return true;
 }
 
 bool Modprobe::Rmmod(const std::string& module_name) {
-    int ret = syscall(__NR_delete_module, MakeCanonical(module_name).c_str(), O_NONBLOCK);
+    auto canonical_name = MakeCanonical(module_name);
+    int ret = syscall(__NR_delete_module, canonical_name.c_str(), O_NONBLOCK);
     if (ret != 0) {
         PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
         return false;
     }
+    module_loaded_.erase(canonical_name);
     return true;
 }
 
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 388ab0a..635cefd 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -26,9 +26,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfDebugFrame : public DwarfSectionImplNoHdr<AddressType> {
+class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
  public:
-  DwarfDebugFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
     this->cie32_value_ = static_cast<uint32_t>(-1);
     this->cie64_value_ = static_cast<uint64_t>(-1);
   }
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index df441fb..7a41e45 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -25,9 +25,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfEhFrame : public DwarfSectionImplNoHdr<AddressType> {
+class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
  public:
-  DwarfEhFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {}
+  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
   virtual ~DwarfEhFrame() = default;
 
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 24b94f0..1358e51 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -32,14 +32,19 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
-  section_bias_ = section_bias;
+bool DwarfEhFrameWithHdr<AddressType>::EhFrameInit(uint64_t offset, uint64_t size,
+                                                   int64_t section_bias) {
+  return DwarfSectionImpl<AddressType>::Init(offset, size, section_bias);
+}
 
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t, int64_t section_bias) {
   memory_.clear_func_offset();
   memory_.clear_text_offset();
   memory_.set_data_offset(offset);
   memory_.set_cur_offset(offset);
-  pc_offset_ = offset;
+
+  hdr_section_bias_ = section_bias;
 
   // Read the first four bytes all at once.
   uint8_t data[4];
@@ -56,7 +61,7 @@
     return false;
   }
 
-  ptr_encoding_ = data[1];
+  uint8_t ptr_encoding = data[1];
   uint8_t fde_count_encoding = data[2];
   table_encoding_ = data[3];
   table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
@@ -70,7 +75,8 @@
   }
 
   memory_.set_pc_offset(memory_.cur_offset());
-  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+  uint64_t ptr_offset;
+  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding, &ptr_offset)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     last_error_.address = memory_.cur_offset();
     return false;
@@ -88,10 +94,8 @@
     return false;
   }
 
-  entries_offset_ = memory_.cur_offset();
-  entries_end_ = offset + size;
-  entries_data_offset_ = offset;
-  cur_entries_offset_ = entries_offset_;
+  hdr_entries_offset_ = memory_.cur_offset();
+  hdr_entries_data_offset_ = offset;
 
   return true;
 }
@@ -107,6 +111,16 @@
     return nullptr;
   }
 
+  // There is a possibility that this entry points to a zero length FDE
+  // due to a bug. If this happens, try and find the non-zero length FDE
+  // from eh_frame directly. See b/142483624.
+  if (fde->pc_start == fde->pc_end) {
+    fde = DwarfSectionImpl<AddressType>::GetFdeFromPc(pc);
+    if (fde == nullptr) {
+      return nullptr;
+    }
+  }
+
   // Guaranteed pc >= pc_start, need to check pc in the fde range.
   if (pc < fde->pc_end) {
     return fde;
@@ -124,8 +138,8 @@
   }
   FdeInfo* info = &fde_info_[index];
 
-  memory_.set_data_offset(entries_data_offset_);
-  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+  memory_.set_data_offset(hdr_entries_data_offset_);
+  memory_.set_cur_offset(hdr_entries_offset_ + 2 * index * table_entry_size_);
   memory_.set_pc_offset(0);
   uint64_t value;
   if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
@@ -138,7 +152,7 @@
 
   // Relative encodings require adding in the load bias.
   if (IsEncodingRelative(table_encoding_)) {
-    value += section_bias_;
+    value += hdr_section_bias_;
   }
   info->pc = value;
   return info;
@@ -190,6 +204,16 @@
     if (fde == nullptr) {
       break;
     }
+
+    // There is a possibility that this entry points to a zero length FDE
+    // due to a bug. If this happens, try and find the non-zero length FDE
+    // from eh_frame directly. See b/142483624.
+    if (fde->pc_start == fde->pc_end) {
+      const DwarfFde* fde_real = DwarfSectionImpl<AddressType>::GetFdeFromPc(fde->pc_start);
+      if (fde_real != nullptr) {
+        fde = fde_real;
+      }
+    }
     fdes->push_back(fde);
   }
 }
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index b8dd3dd..f7c010c 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -34,11 +34,7 @@
   // Add these so that the protected members of DwarfSectionImpl
   // can be accessed without needing a this->.
   using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::pc_offset_;
-  using DwarfSectionImpl<AddressType>::entries_offset_;
-  using DwarfSectionImpl<AddressType>::entries_end_;
   using DwarfSectionImpl<AddressType>::last_error_;
-  using DwarfSectionImpl<AddressType>::section_bias_;
 
   struct FdeInfo {
     AddressType pc;
@@ -49,18 +45,19 @@
   virtual ~DwarfEhFrameWithHdr() = default;
 
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
-    return this->memory_.cur_offset() - pointer - 4;
+    return memory_.cur_offset() - pointer - 4;
   }
 
   uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
-    return this->memory_.cur_offset() - pointer - 8;
+    return memory_.cur_offset() - pointer - 8;
   }
 
   uint64_t AdjustPcFromFde(uint64_t pc) override {
     // The eh_frame uses relative pcs.
-    return pc + this->memory_.cur_offset() - 4;
+    return pc + memory_.cur_offset() - 4;
   }
 
+  bool EhFrameInit(uint64_t offset, uint64_t size, int64_t section_bias);
   bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
 
   const DwarfFde* GetFdeFromPc(uint64_t pc) override;
@@ -72,17 +69,15 @@
   void GetFdes(std::vector<const DwarfFde*>* fdes) override;
 
  protected:
-  uint8_t version_;
-  uint8_t ptr_encoding_;
-  uint8_t table_encoding_;
-  size_t table_entry_size_;
+  uint8_t version_ = 0;
+  uint8_t table_encoding_ = 0;
+  size_t table_entry_size_ = 0;
 
-  uint64_t ptr_offset_;
+  uint64_t hdr_entries_offset_ = 0;
+  uint64_t hdr_entries_data_offset_ = 0;
+  uint64_t hdr_section_bias_ = 0;
 
-  uint64_t entries_data_offset_;
-  uint64_t cur_entries_offset_ = 0;
-
-  uint64_t fde_count_;
+  uint64_t fde_count_ = 0;
   std::unordered_map<uint64_t, FdeInfo> fde_info_;
 };
 
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index cdb6141..e6263f8 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -69,6 +69,7 @@
     return &cie_entry->second;
   }
   DwarfCie* cie = &cie_entries_[offset];
+  memory_.set_data_offset(entries_offset_);
   memory_.set_cur_offset(offset);
   if (!FillInCieHeader(cie) || !FillInCie(cie)) {
     // Erase the cached entry.
@@ -251,6 +252,7 @@
     return &fde_entry->second;
   }
   DwarfFde* fde = &fde_entries_[offset];
+  memory_.set_data_offset(entries_offset_);
   memory_.set_cur_offset(offset);
   if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
     fde_entries_.erase(offset);
@@ -591,8 +593,7 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImplNoHdr<AddressType>::Init(uint64_t offset, uint64_t size,
-                                              int64_t section_bias) {
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
   section_bias_ = section_bias;
   entries_offset_ = offset;
   next_entries_offset_ = offset;
@@ -601,7 +602,6 @@
   memory_.clear_func_offset();
   memory_.clear_text_offset();
   memory_.set_cur_offset(offset);
-  memory_.set_data_offset(offset);
   pc_offset_ = offset;
 
   return true;
@@ -617,7 +617,7 @@
 // and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
 // will be added: 0x200, 0x100 and 0x500, 0x400.
 template <typename AddressType>
-void DwarfSectionImplNoHdr<AddressType>::InsertFde(const DwarfFde* fde) {
+void DwarfSectionImpl<AddressType>::InsertFde(const DwarfFde* fde) {
   uint64_t start = fde->pc_start;
   uint64_t end = fde->pc_end;
   auto it = fdes_.upper_bound(start);
@@ -654,9 +654,10 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImplNoHdr<AddressType>::GetNextCieOrFde(DwarfFde** fde_entry) {
+bool DwarfSectionImpl<AddressType>::GetNextCieOrFde(const DwarfFde** fde_entry) {
   uint64_t start_offset = next_entries_offset_;
 
+  memory_.set_data_offset(entries_offset_);
   memory_.set_cur_offset(next_entries_offset_);
   uint32_t value32;
   if (!memory_.ReadBytes(&value32, sizeof(value32))) {
@@ -689,7 +690,7 @@
       entry_is_cie = true;
       cie_fde_encoding = DW_EH_PE_sdata8;
     } else {
-      cie_offset = this->GetCieOffsetFromFde64(value64);
+      cie_offset = GetCieOffsetFromFde64(value64);
     }
   } else {
     next_entries_offset_ = memory_.cur_offset() + value32;
@@ -705,37 +706,45 @@
       entry_is_cie = true;
       cie_fde_encoding = DW_EH_PE_sdata4;
     } else {
-      cie_offset = this->GetCieOffsetFromFde32(value32);
+      cie_offset = GetCieOffsetFromFde32(value32);
     }
   }
 
   if (entry_is_cie) {
-    DwarfCie* cie = &cie_entries_[start_offset];
-    cie->lsda_encoding = DW_EH_PE_omit;
-    cie->cfa_instructions_end = next_entries_offset_;
-    cie->fde_address_encoding = cie_fde_encoding;
+    auto entry = cie_entries_.find(start_offset);
+    if (entry == cie_entries_.end()) {
+      DwarfCie* cie = &cie_entries_[start_offset];
+      cie->lsda_encoding = DW_EH_PE_omit;
+      cie->cfa_instructions_end = next_entries_offset_;
+      cie->fde_address_encoding = cie_fde_encoding;
 
-    if (!this->FillInCie(cie)) {
-      cie_entries_.erase(start_offset);
-      return false;
+      if (!FillInCie(cie)) {
+        cie_entries_.erase(start_offset);
+        return false;
+      }
     }
     *fde_entry = nullptr;
   } else {
-    DwarfFde* fde = &fde_entries_[start_offset];
-    fde->cfa_instructions_end = next_entries_offset_;
-    fde->cie_offset = cie_offset;
+    auto entry = fde_entries_.find(start_offset);
+    if (entry != fde_entries_.end()) {
+      *fde_entry = &entry->second;
+    } else {
+      DwarfFde* fde = &fde_entries_[start_offset];
+      fde->cfa_instructions_end = next_entries_offset_;
+      fde->cie_offset = cie_offset;
 
-    if (!this->FillInFde(fde)) {
-      fde_entries_.erase(start_offset);
-      return false;
+      if (!FillInFde(fde)) {
+        fde_entries_.erase(start_offset);
+        return false;
+      }
+      *fde_entry = fde;
     }
-    *fde_entry = fde;
   }
   return true;
 }
 
 template <typename AddressType>
-void DwarfSectionImplNoHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+void DwarfSectionImpl<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
   // Loop through the already cached entries.
   uint64_t entry_offset = entries_offset_;
   while (entry_offset < next_entries_offset_) {
@@ -754,7 +763,7 @@
   }
 
   while (next_entries_offset_ < entries_end_) {
-    DwarfFde* fde;
+    const DwarfFde* fde;
     if (!GetNextCieOrFde(&fde)) {
       break;
     }
@@ -771,7 +780,7 @@
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfSectionImplNoHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromPc(uint64_t pc) {
   // Search in the list of fdes we already have.
   auto it = fdes_.upper_bound(pc);
   if (it != fdes_.end()) {
@@ -784,7 +793,7 @@
   // to do a linear search of the fdes by pc. As fdes are read, a cached
   // search map is created.
   while (next_entries_offset_ < entries_end_) {
-    DwarfFde* fde;
+    const DwarfFde* fde;
     if (!GetNextCieOrFde(&fde)) {
       return nullptr;
     }
@@ -807,10 +816,6 @@
 template class DwarfSectionImpl<uint32_t>;
 template class DwarfSectionImpl<uint64_t>;
 
-// Explicitly instantiate DwarfSectionImplNoHdr
-template class DwarfSectionImplNoHdr<uint32_t>;
-template class DwarfSectionImplNoHdr<uint64_t>;
-
 // Explicitly instantiate DwarfDebugFrame
 template class DwarfDebugFrame<uint32_t>;
 template class DwarfDebugFrame<uint64_t>;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index e863f22..5f95fa8 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -126,8 +126,10 @@
 template <typename AddressType>
 void ElfInterface::InitHeadersWithTemplate() {
   if (eh_frame_hdr_offset_ != 0) {
-    eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
+    DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_);
+    eh_frame_.reset(eh_frame_hdr);
+    if (!eh_frame_hdr->EhFrameInit(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_) ||
+        !eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
       eh_frame_.reset(nullptr);
     }
   }
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 0b3f6d4..c244749 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -125,10 +125,16 @@
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
+  bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
+
   const DwarfCie* GetCieFromOffset(uint64_t offset);
 
   const DwarfFde* GetFdeFromOffset(uint64_t offset);
 
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
   bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
 
   bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
@@ -139,6 +145,8 @@
   bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
 
  protected:
+  bool GetNextCieOrFde(const DwarfFde** fde_entry);
+
   bool FillInCieHeader(DwarfCie* cie);
 
   bool FillInCie(DwarfCie* cie);
@@ -150,43 +158,13 @@
   bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
                       RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
 
+  void InsertFde(const DwarfFde* fde);
+
   int64_t section_bias_ = 0;
   uint64_t entries_offset_ = 0;
   uint64_t entries_end_ = 0;
-  uint64_t pc_offset_ = 0;
-};
-
-template <typename AddressType>
-class DwarfSectionImplNoHdr : public DwarfSectionImpl<AddressType> {
- public:
-  // Add these so that the protected members of DwarfSectionImpl
-  // can be accessed without needing a this->.
-  using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::pc_offset_;
-  using DwarfSectionImpl<AddressType>::entries_offset_;
-  using DwarfSectionImpl<AddressType>::entries_end_;
-  using DwarfSectionImpl<AddressType>::last_error_;
-  using DwarfSectionImpl<AddressType>::section_bias_;
-  using DwarfSectionImpl<AddressType>::cie_entries_;
-  using DwarfSectionImpl<AddressType>::fde_entries_;
-  using DwarfSectionImpl<AddressType>::cie32_value_;
-  using DwarfSectionImpl<AddressType>::cie64_value_;
-
-  DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
-  virtual ~DwarfSectionImplNoHdr() = default;
-
-  bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
-
-  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
-
-  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
-
- protected:
-  bool GetNextCieOrFde(DwarfFde** fde_entry);
-
-  void InsertFde(const DwarfFde* fde);
-
   uint64_t next_entries_offset_ = 0;
+  uint64_t pc_offset_ = 0;
 
   std::map<uint64_t, std::pair<uint64_t, const DwarfFde*>> fdes_;
 };
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 78608e3..768a808 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -36,10 +36,8 @@
   ~TestDwarfEhFrameWithHdr() = default;
 
   void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
-  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
-  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
-  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+  void TestSetHdrEntriesOffset(uint64_t offset) { this->hdr_entries_offset_ = offset; }
+  void TestSetHdrEntriesDataOffset(uint64_t offset) { this->hdr_entries_data_offset_ = offset; }
   void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
 
   void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
@@ -48,15 +46,11 @@
   }
 
   uint8_t TestGetVersion() { return this->version_; }
-  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
-  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
   uint8_t TestGetTableEncoding() { return this->table_encoding_; }
   uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
   uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
-  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
-  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
-  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+  uint64_t TestGetHdrEntriesOffset() { return this->hdr_entries_offset_; }
+  uint64_t TestGetHdrEntriesDataOffset() { return this->hdr_entries_data_offset_; }
 };
 
 template <typename TypeParam>
@@ -85,15 +79,11 @@
 
   ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
   EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
-  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
   EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
   EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
   EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
-  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
-  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
-  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
 
   // Verify a zero table entry size fails to init.
   this->memory_.SetData8(0x1003, 0x1);
@@ -137,17 +127,14 @@
   this->memory_.SetData32(0x140c, 0x200);
   this->memory_.SetData16(0x1410, 0);
 
+  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x2000));
   ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
   EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
-  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
   EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
   EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
   EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
-  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
-  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
 
   const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
   ASSERT_TRUE(fde != nullptr);
@@ -155,6 +142,115 @@
   EXPECT_EQ(0x4700U, fde->pc_end);
 }
 
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias_different_from_eh_frame_bias) {
+  this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+                                                       DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 1);
+  this->memory_.SetData32(0x100a, 0x2500);
+  this->memory_.SetData32(0x100e, 0x1400);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x20f8);
+  this->memory_.SetData32(0x140c, 0x200);
+  this->memory_.SetData16(0x1410, 0);
+
+  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x1000));
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+  EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeFromPc_wtih_empty_fde) {
+  this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+                                                       DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 1);
+  this->memory_.SetData32(0x100a, 0x2500);
+  this->memory_.SetData32(0x100e, 0x1400);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x30f8);
+  this->memory_.SetData32(0x140c, 0);
+  this->memory_.SetData16(0x1410, 0);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1500, 0xfc);
+  this->memory_.SetData32(0x1504, 0x204);
+  this->memory_.SetData32(0x1508, 0x2ff8);
+  this->memory_.SetData32(0x150c, 0x200);
+  this->memory_.SetData16(0x1510, 0);
+
+  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+  EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes_with_empty_fde) {
+  this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+                                                       DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 1);
+  this->memory_.SetData32(0x100a, 0x2500);
+  this->memory_.SetData32(0x100e, 0x1400);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x30f8);
+  this->memory_.SetData32(0x140c, 0);
+  this->memory_.SetData16(0x1410, 0);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1500, 0xfc);
+  this->memory_.SetData32(0x1504, 0x204);
+  this->memory_.SetData32(0x1508, 0x2ff8);
+  this->memory_.SetData32(0x150c, 0x200);
+  this->memory_.SetData16(0x1510, 0);
+
+  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->eh_frame_->GetFdes(&fdes);
+  ASSERT_FALSE(fdes.empty());
+  ASSERT_EQ(1U, fdes.size());
+  EXPECT_EQ(0x4500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x4700U, fdes[0]->pc_end);
+}
+
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes) {
   this->memory_.SetMemory(
       0x1000, std::vector<uint8_t>{1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
@@ -220,7 +316,7 @@
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
 
   ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
@@ -233,8 +329,8 @@
 // We are assuming that pc rel, is really relative to the load_bias.
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+  this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
   this->eh_frame_->TestSetTableEntrySize(0x10);
 
   this->memory_.SetData32(0x1040, 0x340);
@@ -248,8 +344,8 @@
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+  this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
   this->eh_frame_->TestSetTableEntrySize(0x10);
 
   this->memory_.SetData32(0x1040, 0x340);
@@ -263,7 +359,7 @@
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_cached) {
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
   this->eh_frame_->TestSetTableEntrySize(0x10);
 
   this->memory_.SetData32(0x1040, 0x340);
@@ -446,7 +542,9 @@
   ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
 }
 
-REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
+                            Init_non_zero_load_bias_different_from_eh_frame_bias,
+                            GetFdeFromPc_wtih_empty_fde, GetFdes_with_empty_fde, GetFdes,
                             GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
                             GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
                             GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 6c2221e..af70f1d 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -435,6 +435,12 @@
   };
 
   if (role == kUnzip) {
+    // `unzip -Z` is "zipinfo mode", so in that case just restart...
+    if (argc > 1 && !strcmp(argv[1], "-Z")) {
+      argv[1] = const_cast<char*>("zipinfo");
+      return main(argc - 1, argv + 1);
+    }
+
     int opt;
     while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
       switch (opt) {
diff --git a/logcat/Android.bp b/logcat/Android.bp
index e472d08..61fba59 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -36,19 +36,13 @@
 
     defaults: ["logcat_defaults"],
     srcs: [
-        "logcat_main.cpp",
         "logcat.cpp",
     ],
 }
 
-cc_binary {
+sh_binary {
     name: "logcatd",
-
-    defaults: ["logcat_defaults"],
-    srcs: [
-        "logcatd_main.cpp",
-        "logcat.cpp",
-    ],
+    src: "logcatd",
 }
 
 sh_binary {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 1517c33..70ccb80 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include "logcat.h"
-
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
@@ -43,11 +41,14 @@
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <android/log.h>
 #include <log/event_tag_map.h>
+#include <log/log_id.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <processgroup/sched_policy.h>
@@ -55,62 +56,51 @@
 
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
+using android::base::Join;
+using android::base::ParseByteCount;
+using android::base::ParseUint;
+using android::base::Split;
 using android::base::StringPrintf;
 
-struct log_device_t {
-    const char* device;
-    bool binary;
-    struct logger* logger;
-    struct logger_list* logger_list;
-    bool printed;
-
-    log_device_t* next;
-
-    log_device_t(const char* d, bool b) {
-        device = d;
-        binary = b;
-        next = nullptr;
-        printed = false;
-        logger = nullptr;
-        logger_list = nullptr;
-    }
-};
-
 class Logcat {
   public:
-    ~Logcat();
-
     int Run(int argc, char** argv);
 
   private:
     void RotateLogs();
-    void ProcessBuffer(log_device_t* dev, struct log_msg* buf);
-    void MaybePrintStart(log_device_t* dev, bool print_dividers);
+    void ProcessBuffer(struct log_msg* buf);
+    void PrintDividers(log_id_t log_id, bool print_dividers);
     void SetupOutputAndSchedulingPolicy(bool blocking);
     int SetLogFormat(const char* format_string);
 
+    // Used for all options
     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};
+
+    // For logging to a file and log rotation
     const char* output_file_name_ = nullptr;
-    // 0 means "no log rotation"
-    size_t log_rotate_size_kb_ = 0;
-    // 0 means "unbounded"
-    size_t max_rotated_logs_ = DEFAULT_MAX_ROTATED_LOGS;
+    size_t log_rotate_size_kb_ = 0;                       // 0 means "no log rotation"
+    size_t max_rotated_logs_ = DEFAULT_MAX_ROTATED_LOGS;  // 0 means "unbounded"
     size_t out_byte_count_ = 0;
+
+    // For binary log buffers
     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 max_count_ = 0;
-    size_t print_count_ = 0;
-
-    bool print_it_anyways_ = false;
-    bool debug_ = false;
     bool has_opened_event_tag_map_ = false;
+
+    // For the related --regex, --max-count, --print
+    std::unique_ptr<std::regex> regex_;
+    size_t max_count_ = 0;  // 0 means "infinite"
+    size_t print_count_ = 0;
+    bool print_it_anyways_ = false;
+
+    // For PrintDividers()
+    log_id_t last_printed_id_ = LOG_ID_MAX;
+    bool printed_start_[LOG_ID_MAX] = {};
+
+    bool debug_ = false;
 };
 
 // logd prefixes records with a length field
@@ -186,13 +176,16 @@
     out_byte_count_ = 0;
 }
 
-void Logcat::ProcessBuffer(log_device_t* dev, struct log_msg* buf) {
+void Logcat::ProcessBuffer(struct log_msg* buf) {
     int bytesWritten = 0;
     int err;
     AndroidLogEntry entry;
     char binaryMsgBuf[1024];
 
-    if (dev->binary) {
+    bool is_binary =
+            buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;
+
+    if (is_binary) {
         if (!event_tag_map_ && !has_opened_event_tag_map_) {
             event_tag_map_.reset(android_openEventTagMap(nullptr));
             has_opened_event_tag_map_ = true;
@@ -228,16 +221,19 @@
     }
 }
 
-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;
+void Logcat::PrintDividers(log_id_t log_id, bool print_dividers) {
+    if (log_id == last_printed_id_ || print_binary_) {
+        return;
     }
+    if (!printed_start_[log_id] || print_dividers) {
+        if (dprintf(output_fd_.get(), "--------- %s %s\n",
+                    printed_start_[log_id] ? "switch to" : "beginning of",
+                    android_log_id_to_name(log_id)) < 0) {
+            LogcatPanic(HELP_FALSE, "output error");
+        }
+    }
+    last_printed_id_ = log_id;
+    printed_start_[log_id] = true;
 }
 
 void Logcat::SetupOutputAndSchedulingPolicy(bool blocking) {
@@ -425,23 +421,6 @@
     return std::make_pair(value, multipliers[i]);
 }
 
-// String to unsigned int, returns -1 if it fails
-static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
-                        size_t max = SIZE_MAX) {
-    if (!ptr) return false;
-
-    char* endp;
-    errno = 0;
-    size_t ret = (size_t)strtoll(ptr, &endp, 0);
-
-    if (endp[0] || errno) return false;
-
-    if ((ret > max) || (ret < min)) return false;
-
-    *val = ret;
-    return true;
-}
-
 static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) {
     va_list args;
     va_start(args, fmt);
@@ -540,19 +519,18 @@
     return retval;
 }
 
-void reportErrorName(const char** current, const char* name,
-                     bool blockSecurity) {
-    if (*current) return;
-    if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
-        *current = name;
+void ReportErrorName(const std::string& name, bool allow_security,
+                     std::vector<std::string>* errors) {
+    if (allow_security || name != "security") {
+        errors->emplace_back(name);
     }
 }
 
 int Logcat::Run(int argc, char** argv) {
-    int err;
     bool hasSetLogFormat = false;
     bool clearLog = false;
-    bool allSelected = false;
+    bool security_buffer_selected =
+            false;  // Do not report errors on the security buffer unless it is explicitly named.
     bool getLogSize = false;
     bool getPruneList = false;
     bool printStatistics = false;
@@ -562,19 +540,11 @@
     const char* setId = nullptr;
     int mode = ANDROID_LOG_RDONLY;
     std::string forceFilters;
-    log_device_t* dev;
-    struct logger_list* logger_list;
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
     size_t pid = 0;
     bool got_t = false;
-
-    // object instantiations before goto's can happen
-    log_device_t unexpected("unexpected", false);
-    const char* openDeviceFail = nullptr;
-    const char* clearFail = nullptr;
-    const char* setSizeFail = nullptr;
-    const char* getSizeFail = nullptr;
+    unsigned id_mask = 0;
 
     if (argc == 2 && !strcmp(argv[1], "--help")) {
         show_help();
@@ -640,7 +610,7 @@
                     }
 
                     // ToDo: determine runtime PID_MAX?
-                    if (!getSizeTArg(optarg, &pid, 1)) {
+                    if (!ParseUint(optarg, &pid) || pid < 1) {
                         LogcatPanic(HELP_TRUE, "%s %s out of range\n",
                                     long_options[option_index].name, optarg);
                     }
@@ -651,7 +621,7 @@
                             ANDROID_LOG_NONBLOCK;
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
-                    if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
+                    if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
                         LogcatPanic(HELP_TRUE, "%s %s out of range\n",
                                     long_options[option_index].name, optarg);
                     }
@@ -712,7 +682,7 @@
                         *cp = ch;
                     }
                 } else {
-                    if (!getSizeTArg(optarg, &tail_lines, 1)) {
+                    if (!ParseUint(optarg, &tail_lines) || tail_lines < 1) {
                         fprintf(stderr, "WARNING: -%c %s invalid, setting to 1\n", c, optarg);
                         tail_lines = 1;
                     }
@@ -728,7 +698,7 @@
                 break;
 
             case 'm': {
-                if (!getSizeTArg(optarg, &max_count_)) {
+                if (!ParseUint(optarg, &max_count_) || max_count_ < 1) {
                     LogcatPanic(HELP_FALSE, "-%c \"%s\" isn't an integer greater than zero\n", c,
                                 optarg);
                 }
@@ -742,34 +712,7 @@
                 FALLTHROUGH_INTENDED;
 
             case 'G': {
-                char* cp;
-                if (strtoll(optarg, &cp, 0) > 0) {
-                    setLogSize = strtoll(optarg, &cp, 0);
-                } else {
-                    setLogSize = 0;
-                }
-
-                switch (*cp) {
-                    case 'g':
-                    case 'G':
-                        setLogSize *= 1024;
-                        FALLTHROUGH_INTENDED;
-                    case 'm':
-                    case 'M':
-                        setLogSize *= 1024;
-                        FALLTHROUGH_INTENDED;
-                    case 'k':
-                    case 'K':
-                        setLogSize *= 1024;
-                        FALLTHROUGH_INTENDED;
-                    case '\0':
-                        break;
-
-                    default:
-                        setLogSize = 0;
-                }
-
-                if (!setLogSize) {
+                if (!ParseByteCount(optarg, &setLogSize) || setLogSize < 1) {
                     LogcatPanic(HELP_FALSE, "ERROR: -G <num><multiplier>\n");
                 }
             } break;
@@ -785,62 +728,24 @@
                 setPruneList = optarg;
                 break;
 
-            case 'b': {
-                std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
-                char* arg = buffers.get();
-                unsigned idMask = 0;
-                char* sv = nullptr;  // protect against -ENOMEM above
-                while (!!(arg = strtok_r(arg, delimiters, &sv))) {
-                    if (!strcmp(arg, "default")) {
-                        idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
-                                  (1 << LOG_ID_CRASH);
-                    } else if (!strcmp(arg, "all")) {
-                        allSelected = true;
-                        idMask = (unsigned)-1;
+            case 'b':
+                for (const auto& buffer : Split(optarg, delimiters)) {
+                    if (buffer == "default") {
+                        id_mask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH);
+                    } else if (buffer == "all") {
+                        id_mask = -1;
                     } else {
-                        log_id_t log_id = android_name_to_log_id(arg);
-                        const char* name = android_log_id_to_name(log_id);
-
-                        if (!!strcmp(name, arg)) {
-                            LogcatPanic(HELP_TRUE, "unknown buffer %s\n", arg);
+                        log_id_t log_id = android_name_to_log_id(buffer.c_str());
+                        if (log_id >= LOG_ID_MAX) {
+                            LogcatPanic(HELP_TRUE, "unknown buffer %s\n", buffer.c_str());
                         }
-                        if (log_id == LOG_ID_SECURITY) allSelected = false;
-                        idMask |= (1 << log_id);
-                    }
-                    arg = nullptr;
-                }
-
-                for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-                    const char* name = android_log_id_to_name((log_id_t)i);
-                    log_id_t log_id = android_name_to_log_id(name);
-
-                    if (log_id != (log_id_t)i) continue;
-                    if (!(idMask & (1 << i))) continue;
-
-                    bool found = false;
-                    for (dev = devices_; dev; dev = dev->next) {
-                        if (!strcmp(name, dev->device)) {
-                            found = true;
-                            break;
+                        if (log_id == LOG_ID_SECURITY) {
+                            security_buffer_selected = true;
                         }
-                        if (!dev->next) break;
+                        id_mask |= (1 << log_id);
                     }
-                    if (found) continue;
-
-                    bool binary = !strcmp(name, "events") ||
-                                  !strcmp(name, "security") ||
-                                  !strcmp(name, "stats");
-                    log_device_t* d = new log_device_t(name, binary);
-
-                    if (dev) {
-                        dev->next = d;
-                        dev = d;
-                    } else {
-                        devices_ = dev = d;
-                    }
-                    dev_count_++;
                 }
-            } break;
+                break;
 
             case 'B':
                 print_binary_ = 1;
@@ -855,34 +760,30 @@
                 break;
 
             case 'r':
-                if (!getSizeTArg(optarg, &log_rotate_size_kb_, 1)) {
+                if (!ParseUint(optarg, &log_rotate_size_kb_) || log_rotate_size_kb_ < 1) {
                     LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
                 }
                 break;
 
             case 'n':
-                if (!getSizeTArg(optarg, &max_rotated_logs_, 1)) {
+                if (!ParseUint(optarg, &max_rotated_logs_) || max_rotated_logs_ < 1) {
                     LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
                 }
                 break;
 
-            case 'v': {
+            case 'v':
                 if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
                     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(arg);
+                for (const auto& arg : Split(optarg, delimiters)) {
+                    int err = SetLogFormat(arg.c_str());
                     if (err < 0) {
-                        LogcatPanic(HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", arg);
+                        LogcatPanic(HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", arg.c_str());
                     }
-                    arg = nullptr;
                     if (err) hasSetLogFormat = true;
                 }
-            } break;
+                break;
 
             case 'Q':
 #define LOGCAT_FILTER "androidboot.logcat="
@@ -1001,21 +902,10 @@
         print_it_anyways_ = false;
     }
 
-    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);
-            dev_count_++;
-        }
-        if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
-            dev = dev->next = new log_device_t("crash", false);
-            dev_count_++;
-        }
-        if (android_name_to_log_id("kernel") == LOG_ID_KERNEL) {
-            dev = dev->next = new log_device_t("kernel", false);
-            dev_count_++;
-        }
+    // If no buffers are specified, default to using these buffers.
+    if (id_mask == 0) {
+        id_mask = (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH) |
+                  (1 << LOG_ID_KERNEL);
     }
 
     if (log_rotate_size_kb_ != 0 && !output_file_name_) {
@@ -1039,17 +929,12 @@
         const char* logFormat = getenv("ANDROID_PRINTF_LOG");
 
         if (!!logFormat) {
-            std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
-                                                           free);
-            char* sv = nullptr;  // protect against -ENOMEM above
-            char* arg = formats.get();
-            while (!!(arg = strtok_r(arg, delimiters, &sv))) {
-                err = SetLogFormat(arg);
+            for (const auto& arg : Split(logFormat, delimiters)) {
+                int err = SetLogFormat(arg.c_str());
                 // environment should not cause crash of logcat
                 if (err < 0) {
-                    fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
+                    fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg.c_str());
                 }
-                arg = nullptr;
                 if (err > 0) hasSetLogFormat = true;
             }
         }
@@ -1059,7 +944,7 @@
     }
 
     if (forceFilters.size()) {
-        err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
+        int err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
         if (err < 0) {
             LogcatPanic(HELP_FALSE, "Invalid filter expression in logcat args\n");
         }
@@ -1068,7 +953,7 @@
         const char* env_tags_orig = getenv("ANDROID_LOG_TAGS");
 
         if (!!env_tags_orig) {
-            err = android_log_addFilterString(logformat_.get(), env_tags_orig);
+            int err = android_log_addFilterString(logformat_.get(), env_tags_orig);
 
             if (err < 0) {
                 LogcatPanic(HELP_TRUE, "Invalid filter expression in ANDROID_LOG_TAGS\n");
@@ -1077,33 +962,34 @@
     } else {
         // Add from commandline
         for (int i = optind ; i < argc ; i++) {
-            // skip stderr redirections of _all_ kinds
-            if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
-            // skip stdout redirections of _all_ kinds
-            if (argv[i][0] == '>') continue;
-
-            err = android_log_addFilterString(logformat_.get(), argv[i]);
+            int err = android_log_addFilterString(logformat_.get(), argv[i]);
             if (err < 0) {
                 LogcatPanic(HELP_TRUE, "Invalid filter expression '%s'\n", argv[i]);
             }
         }
     }
 
-    dev = devices_;
+    std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
+            nullptr, &android_logger_list_free};
     if (tail_time != log_time::EPOCH) {
-        logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
+        logger_list.reset(android_logger_list_alloc_time(mode, tail_time, pid));
     } else {
-        logger_list = android_logger_list_alloc(mode, tail_lines, pid);
+        logger_list.reset(android_logger_list_alloc(mode, tail_lines, pid));
     }
     // We have three orthogonal actions below to clear, set log size and
     // get log size. All sharing the same iteration loop.
-    while (dev) {
-        dev->logger_list = logger_list;
-        dev->logger = android_logger_open(logger_list,
-                                          android_name_to_log_id(dev->device));
-        if (!dev->logger) {
-            reportErrorName(&openDeviceFail, dev->device, allSelected);
-            dev = dev->next;
+    std::vector<std::string> open_device_failures;
+    std::vector<std::string> clear_failures;
+    std::vector<std::string> set_size_failures;
+    std::vector<std::string> get_size_failures;
+
+    for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+        if (!(id_mask & (1 << i))) continue;
+        const char* buffer_name = android_log_id_to_name(static_cast<log_id_t>(i));
+
+        auto logger = android_logger_open(logger_list.get(), static_cast<log_id_t>(i));
+        if (logger == nullptr) {
+            ReportErrorName(buffer_name, security_buffer_selected, &open_device_failures);
             continue;
         }
 
@@ -1124,64 +1010,65 @@
 
                     if (!file.length()) {
                         perror("while clearing log files");
-                        reportErrorName(&clearFail, dev->device, allSelected);
+                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
                         break;
                     }
 
-                    err = unlink(file.c_str());
+                    int err = unlink(file.c_str());
 
-                    if (err < 0 && errno != ENOENT && !clearFail) {
+                    if (err < 0 && errno != ENOENT) {
                         perror("while clearing log files");
-                        reportErrorName(&clearFail, dev->device, allSelected);
+                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
                     }
                 }
-            } else if (android_logger_clear(dev->logger)) {
-                reportErrorName(&clearFail, dev->device, allSelected);
+            } else if (android_logger_clear(logger)) {
+                ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
             }
         }
 
         if (setLogSize) {
-            if (android_logger_set_log_size(dev->logger, setLogSize)) {
-                reportErrorName(&setSizeFail, dev->device, allSelected);
+            if (android_logger_set_log_size(logger, setLogSize)) {
+                ReportErrorName(buffer_name, security_buffer_selected, &set_size_failures);
             }
         }
 
         if (getLogSize) {
-            long size = android_logger_get_log_size(dev->logger);
-            long readable = android_logger_get_log_readable_size(dev->logger);
+            long size = android_logger_get_log_size(logger);
+            long readable = android_logger_get_log_readable_size(logger);
 
-            if ((size < 0) || (readable < 0)) {
-                reportErrorName(&getSizeFail, dev->device, allSelected);
+            if (size < 0 || readable < 0) {
+                ReportErrorName(buffer_name, security_buffer_selected, &get_size_failures);
             } else {
                 auto size_format = format_of_size(size);
                 auto readable_format = format_of_size(readable);
                 std::string str = android::base::StringPrintf(
-                       "%s: ring buffer is %lu %sB (%lu %sB consumed),"
-                         " max entry is %d B, max payload is %d B\n",
-                       dev->device,
-                       size_format.first, size_format.second,
-                       readable_format.first, readable_format.second,
-                       (int)LOGGER_ENTRY_MAX_LEN,
-                       (int)LOGGER_ENTRY_MAX_PAYLOAD);
+                        "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+                        " max entry is %d B, max payload is %d B\n",
+                        buffer_name, size_format.first, size_format.second, readable_format.first,
+                        readable_format.second, (int)LOGGER_ENTRY_MAX_LEN,
+                        (int)LOGGER_ENTRY_MAX_PAYLOAD);
                 TEMP_FAILURE_RETRY(write(output_fd_.get(), str.data(), str.length()));
             }
         }
-
-        dev = dev->next;
     }
 
     // report any errors in the above loop and exit
-    if (openDeviceFail) {
-        LogcatPanic(HELP_FALSE, "Unable to open log device '%s'\n", openDeviceFail);
+    if (!open_device_failures.empty()) {
+        LogcatPanic(HELP_FALSE, "Unable to open log device%s '%s'\n",
+                    open_device_failures.size() > 1 ? "s" : "",
+                    Join(open_device_failures, ",").c_str());
     }
-    if (clearFail) {
-        LogcatPanic(HELP_FALSE, "failed to clear the '%s' log\n", clearFail);
+    if (!clear_failures.empty()) {
+        LogcatPanic(HELP_FALSE, "failed to clear the '%s' log%s\n",
+                    Join(clear_failures, ",").c_str(), clear_failures.size() > 1 ? "s" : "");
     }
-    if (setSizeFail) {
-        LogcatPanic(HELP_FALSE, "failed to set the '%s' log size\n", setSizeFail);
+    if (!set_size_failures.empty()) {
+        LogcatPanic(HELP_FALSE, "failed to set the '%s' log size%s\n",
+                    Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
     }
-    if (getSizeFail) {
-        LogcatPanic(HELP_FALSE, "failed to get the readable '%s' log size", getSizeFail);
+    if (!get_size_failures.empty()) {
+        LogcatPanic(HELP_FALSE, "failed to get the readable '%s' log size%s\n",
+                    Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
     }
 
     if (setPruneList) {
@@ -1191,7 +1078,7 @@
         char* buf = nullptr;
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
-            if (android_logger_set_prune_list(logger_list, buf, bLen)) {
+            if (android_logger_set_prune_list(logger_list.get(), buf, bLen)) {
                 LogcatPanic(HELP_FALSE, "failed to set the prune list");
             }
             free(buf);
@@ -1208,9 +1095,9 @@
         for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
              delete[] buf, buf = nullptr, --retry) {
             if (getPruneList) {
-                android_logger_get_prune_list(logger_list, buf, len);
+                android_logger_get_prune_list(logger_list.get(), buf, len);
             } else {
-                android_logger_get_statistics(logger_list, buf, len);
+                android_logger_get_statistics(logger_list.get(), buf, len);
             }
             buf[len - 1] = '\0';
             if (atol(buf) < 3) {
@@ -1253,11 +1140,9 @@
 
     SetupOutputAndSchedulingPolicy(!(mode & ANDROID_LOG_NONBLOCK));
 
-    dev = nullptr;
-
     while (!max_count_ || print_count_ < max_count_) {
         struct log_msg log_msg;
-        int ret = android_logger_list_read(logger_list, &log_msg);
+        int ret = android_logger_list_read(logger_list.get(), &log_msg);
         if (!ret) {
             LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
         }
@@ -1274,46 +1159,23 @@
             LogcatPanic(HELP_FALSE, "logcat read failure\n");
         }
 
-        log_device_t* d;
-        for (d = devices_; d; d = d->next) {
-            if (android_name_to_log_id(d->device) == log_msg.id()) break;
-        }
-        if (!d) {
-            dev_count_ = 2;  // set to Multiple
-            d = &unexpected;
-            d->binary = log_msg.id() == LOG_ID_EVENTS;
+        if (log_msg.id() > LOG_ID_MAX) {
+            LogcatPanic(HELP_FALSE, "read: unexpected log id (%d) over LOG_ID_MAX (%d)",
+                        log_msg.id(), LOG_ID_MAX);
         }
 
-        if (dev != d) {
-            dev = d;
-            MaybePrintStart(dev, printDividers);
-        }
+        PrintDividers(log_msg.id(), printDividers);
+
         if (print_binary_) {
             TEMP_FAILURE_RETRY(write(output_fd_.get(), &log_msg, log_msg.len()));
         } else {
-            ProcessBuffer(dev, &log_msg);
+            ProcessBuffer(&log_msg);
         }
     }
     return EXIT_SUCCESS;
 }
 
-int RunLogcat(int argc, char** argv) {
+int main(int argc, char** argv) {
     Logcat logcat;
     return logcat.Run(argc, argv);
 }
-
-Logcat::~Logcat() {
-    // generic cleanup of devices list to handle all possible dirty cases
-    log_device_t* dev;
-    while (!!(dev = devices_)) {
-        struct logger_list* logger_list = dev->logger_list;
-        if (logger_list) {
-            for (log_device_t* d = dev; d; d = d->next) {
-                if (d->logger_list == logger_list) d->logger_list = nullptr;
-            }
-            android_logger_list_free(logger_list);
-        }
-        devices_ = dev->next;
-        delete dev;
-    }
-}
diff --git a/logcat/logcat.h b/logcat/logcat.h
deleted file mode 100644
index 4cc112b..0000000
--- a/logcat/logcat.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2005-2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-int RunLogcat(int argc, char** argv);
\ No newline at end of file
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
deleted file mode 100644
index 59ab716..0000000
--- a/logcat/logcat_main.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <signal.h>
-#include <stdlib.h>
-
-#include "logcat.h"
-
-int main(int argc, char** argv) {
-    signal(SIGPIPE, exit);
-    return RunLogcat(argc, argv);
-}
diff --git a/logcat/logcatd b/logcat/logcatd
new file mode 100755
index 0000000..622e567
--- /dev/null
+++ b/logcat/logcatd
@@ -0,0 +1,25 @@
+#! /system/bin/sh
+
+# This is primarily meant to be used by logpersist.  This script is run as an init service, which
+# first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
+# `-L` to read the current logcat buffers to persistent storage.
+
+has_last="false"
+for arg in "$@"; do
+  if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
+    has_last="true"
+  fi
+done
+
+if [ "$has_last" == "true" ]; then
+  logcat "$@"
+fi
+
+args_without_last=()
+for arg in "$@"; do
+  if [ "$arg" != "-L" -a "$arg" != "--last" ]; then
+    ARGS+=("$arg")
+  fi
+done
+
+exec logcat "${ARGS[@]}"
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
deleted file mode 100644
index 4da933f..0000000
--- a/logcat/logcatd_main.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <vector>
-
-#include "logcat.h"
-
-int main(int argc, char** argv) {
-    signal(SIGPIPE, exit);
-
-    // Save and detect presence of -L or --last flag
-    std::vector<std::string> args;
-    bool last = false;
-    for (int i = 0; i < argc; ++i) {
-        if (!argv[i]) continue;
-        args.push_back(std::string(argv[i]));
-        if (!strcmp(argv[i], "-L") || !strcmp(argv[i], "--last")) last = true;
-    }
-
-    // Generate argv from saved content
-    std::vector<const char*> argv_hold;
-    for (auto& str : args) argv_hold.push_back(str.c_str());
-    argv_hold.push_back(nullptr);
-
-    int ret = 0;
-    if (last) {
-        // Run logcat command with -L flag
-        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();) {
-            if (!*it || (strcmp(*it, "-L") && strcmp(*it, "--last"))) {
-                ++it;
-            } else {
-                it = argv_hold.erase(it);
-            }
-        }
-        // fall through to re-run the command regardless of the arguments
-        // passed in.  For instance, we expect -h to report help stutter.
-    }
-
-    // Run logcat command without -L flag
-    int retval = RunLogcat(argv_hold.size() - 1, (char**)&argv_hold[0]);
-    if (!ret) ret = retval;
-    return ret;
-}
diff --git a/logcat/logpersist b/logcat/logpersist
index c09b6b2..05b46f0 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -148,9 +148,9 @@
     echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
   fi
   if [ "true" = "${clear}" ]; then
-    setprop ${property} "clear"
+    setprop ${property#persist.} "clear"
   else
-    setprop ${property} "stop"
+    setprop ${property#persist.} "stop"
   fi
   if [ -n "`getprop ${property#persist.}.buffer`" ]; then
     setprop ${property}.buffer ""
diff --git a/trusty/OWNERS b/trusty/OWNERS
index e807d71..1fb473e 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -2,6 +2,8 @@
 dkrahn@google.com
 drewry@google.com
 gmar@google.com
+mmaurer@google.com
 ncbray@google.com
-rpere@google.com
 swillden@google.com
+trong@google.com
+wenhaowang@google.com
diff --git a/trusty/utils/trusty-ut-ctrl/Android.bp b/trusty/utils/trusty-ut-ctrl/Android.bp
index 77d1f70..9c8af7b 100644
--- a/trusty/utils/trusty-ut-ctrl/Android.bp
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -20,6 +20,8 @@
     shared_libs: [
         "libc",
         "liblog",
+    ],
+    static_libs: [
         "libtrusty",
     ],
     gtest: false,