Merge "Add absl-like StringReplace."
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 1993840..236fcf7 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -428,7 +428,7 @@
   std::vector<std::pair<std::string, uint64_t>> special_row;
 
 #if defined(__arm__) || defined(__aarch64__)
-  static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"};
+  static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc", "pst"};
 #elif defined(__i386__)
   static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
 #elif defined(__x86_64__)
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 02a887e..a757d56 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -137,12 +137,14 @@
         "libhidlbase",
         "liblog",
         "liblp",
+        "libprotobuf-cpp-lite",
         "libsparse",
         "libutils",
     ],
 
     static_libs: [
         "libhealthhalutils",
+        "libsnapshot_nobinder",
     ],
 
     header_libs: [
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index a2c95d6..1a745ab 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -19,6 +19,8 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include <unordered_set>
+
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
@@ -33,6 +35,7 @@
 #include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
 #include <uuid/uuid.h>
 
 #include "constants.h"
@@ -48,6 +51,7 @@
 using ::android::hardware::boot::V1_1::MergeStatus;
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
+using android::snapshot::SnapshotManager;
 using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
 
 struct VariableHandlers {
@@ -57,6 +61,24 @@
     std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
 };
 
+static bool IsSnapshotUpdateInProgress(FastbootDevice* device) {
+    auto hal = device->boot1_1();
+    if (!hal) {
+        return false;
+    }
+    auto merge_status = hal->getSnapshotMergeStatus();
+    return merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;
+}
+
+static bool IsProtectedPartitionDuringMerge(FastbootDevice* device, const std::string& name) {
+    static const std::unordered_set<std::string> ProtectedPartitionsDuringMerge = {
+            "userdata", "metadata", "misc"};
+    if (ProtectedPartitionsDuringMerge.count(name) == 0) {
+        return false;
+    }
+    return IsSnapshotUpdateInProgress(device);
+}
+
 static void GetAllVars(FastbootDevice* device, const std::string& name,
                        const VariableHandlers& handlers) {
     if (!handlers.get_all_args) {
@@ -143,8 +165,14 @@
         return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
     }
 
+    const auto& partition_name = args[1];
+    if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+        auto message = "Cannot erase " + partition_name + " while a snapshot update is in progress";
+        return device->WriteFail(message);
+    }
+
     PartitionHandle handle;
-    if (!OpenPartition(device, args[1], &handle)) {
+    if (!OpenPartition(device, partition_name, &handle)) {
         return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
     }
     if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
@@ -209,9 +237,9 @@
                                    "set_active command is not allowed on locked devices");
     }
 
-    // Slot suffix needs to be between 'a' and 'z'.
     Slot slot;
     if (!GetSlotNumber(args[1], &slot)) {
+        // Slot suffix needs to be between 'a' and 'z'.
         return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
     }
 
@@ -224,6 +252,32 @@
     if (slot >= boot_control_hal->getNumberSlots()) {
         return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
     }
+
+    // If the slot is not changing, do nothing.
+    if (slot == boot_control_hal->getCurrentSlot()) {
+        return device->WriteOkay("");
+    }
+
+    // Check how to handle the current snapshot state.
+    if (auto hal11 = device->boot1_1()) {
+        auto merge_status = hal11->getSnapshotMergeStatus();
+        if (merge_status == MergeStatus::MERGING) {
+            return device->WriteFail("Cannot change slots while a snapshot update is in progress");
+        }
+        // Note: we allow the slot change if the state is SNAPSHOTTED. First-
+        // stage init does not have access to the HAL, and uses the slot number
+        // and /metadata OTA state to determine whether a slot change occurred.
+        // Booting into the old slot would erase the OTA, and switching A->B->A
+        // would simply resume it if no boots occur in between. Re-flashing
+        // partitions implicitly cancels the OTA, so leaving the state as-is is
+        // safe.
+        if (merge_status == MergeStatus::SNAPSHOTTED) {
+            device->WriteInfo(
+                    "Changing the active slot with a snapshot applied may cancel the"
+                    " update.");
+        }
+    }
+
     CommandResult ret;
     auto cb = [&ret](CommandResult result) { ret = result; };
     auto result = boot_control_hal->setActiveBootSlot(slot, cb);
@@ -467,6 +521,11 @@
     }
 
     const auto& partition_name = args[1];
+    if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+        auto message = "Cannot flash " + partition_name + " while a snapshot update is in progress";
+        return device->WriteFail(message);
+    }
+
     if (LogicalPartitionExists(device, partition_name)) {
         CancelPartitionSnapshot(device, partition_name);
     }
@@ -556,12 +615,9 @@
 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();
+    auto hal = device->boot1_1();
     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.
@@ -574,18 +630,34 @@
         return device->WriteOkay("");
     }
 
-    if (args.size() != 2 || args[1] != "cancel") {
+    MergeStatus status = hal->getSnapshotMergeStatus();
+
+    if (args.size() != 2) {
         return device->WriteFail("Invalid arguments");
     }
+    if (args[1] == "cancel") {
+        switch (status) {
+            case MergeStatus::SNAPSHOTTED:
+            case MergeStatus::MERGING:
+                hal->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+                break;
+            default:
+                break;
+        }
+    } else if (args[1] == "merge") {
+        if (status != MergeStatus::MERGING) {
+            return device->WriteFail("No snapshot merge is in progress");
+        }
 
-    MergeStatus status = hal11->getSnapshotMergeStatus();
-    switch (status) {
-        case MergeStatus::SNAPSHOTTED:
-        case MergeStatus::MERGING:
-            hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
-            break;
-        default:
-            break;
+        auto sm = SnapshotManager::NewForFirstStageMount();
+        if (!sm) {
+            return device->WriteFail("Unable to create SnapshotManager");
+        }
+        if (!sm->HandleImminentDataWipe()) {
+            return device->WriteFail("Unable to finish snapshot merge");
+        }
+    } else {
+        return device->WriteFail("Invalid parameter to snapshot-update");
     }
     return device->WriteStatus(FastbootResult::OKAY, "Success");
 }
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index d3c2bda..31fc359 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -60,7 +60,11 @@
       boot_control_hal_(IBootControl::getService()),
       health_hal_(get_health_service()),
       fastboot_hal_(IFastboot::getService()),
-      active_slot_("") {}
+      active_slot_("") {
+    if (boot_control_hal_) {
+        boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
+    }
+}
 
 FastbootDevice::~FastbootDevice() {
     CloseDevice();
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 091aadf..bbe8172 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
 #include <android/hardware/health/2.0/IHealth.h>
 
@@ -51,6 +52,7 @@
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
         return boot_control_hal_;
     }
+    android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
         return fastboot_hal_;
     }
@@ -63,6 +65,7 @@
 
     std::unique_ptr<Transport> transport_;
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+    android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
     android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 717db06..10eac01 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -432,19 +432,13 @@
                              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();
+    auto hal = device->boot1_1();
     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();
+    MergeStatus status = hal->getSnapshotMergeStatus();
     switch (status) {
         case MergeStatus::SNAPSHOTTED:
             *message = "snapshotted";
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7ce7c7c..cbd42b1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -399,6 +399,9 @@
             " 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"
+            " snapshot-update merge      On devices that support snapshot-based updates, finish\n"
+            "                            an in-progress update if it is in the \"merging\"\n"
+            "                            phase.\n"
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -2089,8 +2092,8 @@
             if (!args.empty()) {
                 arg = next_arg(&args);
             }
-            if (!arg.empty() && arg != "cancel") {
-                syntax_error("expected: snapshot-update [cancel]");
+            if (!arg.empty() && (arg != "cancel" && arg != "merge")) {
+                syntax_error("expected: snapshot-update [cancel|merge]");
             }
             fb->SnapshotUpdateCommand(arg);
         } else {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 6a5ad20..8d534ea 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -124,8 +124,11 @@
 
 RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
                                               std::vector<std::string>* info) {
+    prolog_(StringPrintf("Snapshot %s", command.c_str()));
     std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
-    return RawCommand(raw, response, info);
+    auto result = RawCommand(raw, response, info);
+    epilog_(result);
+    return result;
 }
 
 RetCode FastBootDriver::FlashPartition(const std::string& partition,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 8f24709..7411e5a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -224,7 +224,8 @@
     bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
 
     // This method should be called preceding any wipe or flash of metadata or
-    // userdata. It is only valid in recovery.
+    // userdata. It is only valid in recovery or fastbootd, and it ensures that
+    // a merge has been completed.
     //
     // When userdata will be wiped or flashed, it is necessary to clean up any
     // snapshot state. If a merge is in progress, the merge must be finished.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 1de7008..008ece7 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -2185,6 +2185,15 @@
         return true;
     }
 
+    // Check this early, so we don't accidentally start trying to populate
+    // the state file in recovery. Note we don't call GetUpdateState since
+    // we want errors in acquiring the lock to be propagated, instead of
+    // returning UpdateState::None.
+    auto state_file = GetStateFilePath();
+    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
+        return true;
+    }
+
     auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
     auto super_path = device_->GetSuperDevice(slot_number);
     if (!CreateLogicalAndSnapshotPartitions(super_path)) {
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 22cf43b..2886289 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -494,7 +494,7 @@
 
 // Cache miss, go to logd to acquire a public reference.
 // Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
-static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
+static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
   // call event tag service to arrange for a new tag
   char* buf = NULL;
   // Can not use android::base::StringPrintf, asprintf + free instead.
@@ -515,8 +515,9 @@
     } else {
       size = ret;
     }
+#ifdef __ANDROID__
     // Ask event log tag service for an existing entry
-    if (__send_log_msg(buf, size) >= 0) {
+    if (SendLogdControlMessage(buf, size) >= 0) {
       buf[size - 1] = '\0';
       char* ep;
       unsigned long val = strtoul(buf, &ep, 10);  // return size
@@ -529,6 +530,7 @@
         }
       }
     }
+#endif
     free(buf);
   }
   return NULL;
@@ -618,8 +620,9 @@
     } else {
       size = ret;
     }
+#ifdef __ANDROID__
     // Ask event log tag service for an allocation
-    if (__send_log_msg(buf, size) >= 0) {
+    if (SendLogdControlMessage(buf, size) >= 0) {
       buf[size - 1] = '\0';
       unsigned long val = strtoul(buf, &cp, 10);        // return size
       if ((buf != cp) && (val > 0) && (*cp == '\n')) {  // truncation OK
@@ -635,6 +638,7 @@
         }
       }
     }
+#endif
     free(buf);
   }
 
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index 428a482..f61bbdc 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -650,10 +650,6 @@
   return fd;
 }
 
-ssize_t __send_log_msg(char*, size_t) {
-  return -ENODEV;
-}
-
 int __android_log_is_loggable(int prio, const char*, int def) {
   int logLevel = def;
   return logLevel >= 0 && prio >= logLevel;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index ce54db2..bd2256c 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -29,7 +29,6 @@
 int fakeLogClose(int fd);
 ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
 
-ssize_t __send_log_msg(char*, size_t);
 int __android_log_is_loggable(int prio, const char*, int def);
 int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
 int __android_log_is_debuggable();
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index eaa157a..4e2dc66 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -40,39 +40,16 @@
 #include "logger.h"
 
 static int LogdAvailable(log_id_t LogId);
-static int LogdVersion(struct logger* logger, struct android_log_transport_context* transp);
 static int LogdRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
                     struct log_msg* log_msg);
-static int LogdPoll(struct logger_list* logger_list, struct android_log_transport_context* transp);
 static void LogdClose(struct logger_list* logger_list,
                       struct android_log_transport_context* transp);
-static int LogdClear(struct logger* logger, struct android_log_transport_context* transp);
-static ssize_t LogdSetSize(struct logger* logger, struct android_log_transport_context* transp,
-                           size_t size);
-static ssize_t LogdGetSize(struct logger* logger, struct android_log_transport_context* transp);
-static ssize_t LogdGetReadableSize(struct logger* logger,
-                                   struct android_log_transport_context* transp);
-static ssize_t LogdGetPrune(struct logger_list* logger,
-                            struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t LogdSetPrune(struct logger_list* logger,
-                            struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t LogdGetStats(struct logger_list* logger,
-                            struct android_log_transport_context* transp, char* buf, size_t len);
 
 struct android_log_transport_read logdLoggerRead = {
     .name = "logd",
     .available = LogdAvailable,
-    .version = LogdVersion,
     .close = LogdClose,
     .read = LogdRead,
-    .poll = LogdPoll,
-    .clear = LogdClear,
-    .setSize = LogdSetSize,
-    .getSize = LogdGetSize,
-    .getReadableSize = LogdGetReadableSize,
-    .getPrune = LogdGetPrune,
-    .setPrune = LogdSetPrune,
-    .getStats = LogdGetStats,
 };
 
 static int LogdAvailable(log_id_t logId) {
@@ -116,7 +93,7 @@
 }
 
 /* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct logger* logger, const char* msg, char* buf, size_t buf_size) {
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
   ssize_t ret;
   size_t len;
   char* cp;
@@ -126,10 +103,6 @@
     return sock;
   }
 
-  if (msg) {
-    snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned)-1);
-  }
-
   len = strlen(buf) + 1;
   ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
   if (ret <= 0) {
@@ -180,10 +153,6 @@
   return ret;
 }
 
-ssize_t __send_log_msg(char* buf, size_t buf_size) {
-  return send_log_msg(NULL, NULL, buf, buf_size);
-}
-
 static int check_log_success(char* buf, ssize_t ret) {
   if (ret < 0) {
     return ret;
@@ -197,17 +166,28 @@
   return 0;
 }
 
-static int LogdClear(struct logger* logger, struct android_log_transport_context*) {
+int android_logger_clear(struct logger* logger) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
+  uint32_t log_id = android_logger_get_id(logger);
   char buf[512];
+  snprintf(buf, sizeof(buf), "clear %" PRIu32, log_id);
 
-  return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+  return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
 }
 
 /* returns the total size of the log's ring buffer */
-static ssize_t LogdGetSize(struct logger* logger, struct android_log_transport_context*) {
-  char buf[512];
+long android_logger_get_log_size(struct logger* logger) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
 
-  ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+  uint32_t log_id = android_logger_get_id(logger);
+  char buf[512];
+  snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+
+  ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
   if (ret < 0) {
     return ret;
   }
@@ -219,23 +199,32 @@
   return atol(buf);
 }
 
-static ssize_t LogdSetSize(struct logger* logger, struct android_log_transport_context*,
-                           size_t size) {
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
+
+  uint32_t log_id = android_logger_get_id(logger);
   char buf[512];
+  snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
 
-  snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
-
-  return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+  return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
 }
 
 /*
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
  */
-static ssize_t LogdGetReadableSize(struct logger* logger, struct android_log_transport_context*) {
-  char buf[512];
+long android_logger_get_log_readable_size(struct logger* logger) {
+  if (!android_logger_is_logd(logger)) {
+    return -EINVAL;
+  }
 
-  ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+  uint32_t log_id = android_logger_get_id(logger);
+  char buf[512];
+  snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+
+  ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
   if (ret < 0) {
     return ret;
   }
@@ -247,20 +236,15 @@
   return atol(buf);
 }
 
-/*
- * returns the logger version
- */
-static int LogdVersion(struct logger*, struct android_log_transport_context*) {
-  uid_t uid = __android_log_uid();
-  return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
+int android_logger_get_log_version(struct logger*) {
+  return 4;
 }
 
-/*
- * returns statistics
- */
-static ssize_t LogdGetStats(struct logger_list* logger_list, struct android_log_transport_context*,
-                            char* buf, size_t len) {
-  struct logger* logger;
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
+
   char* cp = buf;
   size_t remaining = len;
   size_t n;
@@ -270,27 +254,35 @@
   remaining -= n;
   cp += n;
 
-  logger_for_each(logger, logger_list) {
-    n = snprintf(cp, remaining, " %d", logger->logId);
-    n = MIN(n, remaining);
-    remaining -= n;
-    cp += n;
+  for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+    if ((1 << log_id) & logger_list->log_mask) {
+      n = snprintf(cp, remaining, " %zu", log_id);
+      n = MIN(n, remaining);
+      remaining -= n;
+      cp += n;
+    }
   }
 
   if (logger_list->pid) {
     snprintf(cp, remaining, " pid=%u", logger_list->pid);
   }
 
-  return send_log_msg(NULL, NULL, buf, len);
+  return SendLogdControlMessage(buf, len);
+}
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
+
+  snprintf(buf, len, "getPruneList");
+  return SendLogdControlMessage(buf, len);
 }
 
-static ssize_t LogdGetPrune(struct logger_list*, struct android_log_transport_context*, char* buf,
-                            size_t len) {
-  return send_log_msg(NULL, "getPruneList", buf, len);
-}
+int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+  if (logger_list->mode & ANDROID_LOG_PSTORE) {
+    return -EINVAL;
+  }
 
-static ssize_t LogdSetPrune(struct logger_list*, struct android_log_transport_context*, char* buf,
-                            size_t len) {
   const char cmd[] = "setPruneList ";
   const size_t cmdlen = sizeof(cmd) - 1;
 
@@ -301,11 +293,10 @@
   buf[len - 1] = '\0';
   memcpy(buf, cmd, cmdlen);
 
-  return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+  return check_log_success(buf, SendLogdControlMessage(buf, len));
 }
 
 static int logdOpen(struct logger_list* logger_list, struct android_log_transport_context* transp) {
-  struct logger* logger;
   char buffer[256], *cp, c;
   int ret, remaining, sock;
 
@@ -333,12 +324,15 @@
   cp += 5;
   c = '=';
   remaining = sizeof(buffer) - (cp - buffer);
-  logger_for_each(logger, logger_list) {
-    ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
-    ret = MIN(ret, remaining);
-    remaining -= ret;
-    cp += ret;
-    c = ',';
+
+  for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+    if ((1 << log_id) & logger_list->log_mask) {
+      ret = snprintf(cp, remaining, "%c%zu", c, log_id);
+      ret = MIN(ret, remaining);
+      remaining -= ret;
+      cp += ret;
+      c = ',';
+    }
   }
 
   if (logger_list->tail) {
@@ -412,27 +406,6 @@
   return ret;
 }
 
-static int LogdPoll(struct logger_list* logger_list, struct android_log_transport_context* transp) {
-  struct pollfd p;
-
-  int ret = logdOpen(logger_list, transp);
-  if (ret < 0) {
-    return ret;
-  }
-
-  memset(&p, 0, sizeof(p));
-  p.fd = ret;
-  p.events = POLLIN;
-  ret = poll(&p, 1, 20);
-  if ((ret > 0) && !(p.revents & POLLIN)) {
-    ret = 0;
-  }
-  if ((ret == -1) && errno) {
-    return -errno;
-  }
-  return ret;
-}
-
 /* Close all the logs */
 static void LogdClose(struct logger_list*, struct android_log_transport_context* transp) {
   int sock = atomic_exchange(&transp->context.sock, -1);
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 7c53cbb..09f8627 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -22,6 +22,6 @@
 
 __BEGIN_DECLS
 
-ssize_t __send_log_msg(char* buf, size_t buf_size);
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size);
 
 __END_DECLS
diff --git a/liblog/logger.h b/liblog/logger.h
index 9c488b6..d2251e5 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -53,7 +53,6 @@
 
   /* Does not cause resources to be taken */
   int (*available)(log_id_t logId);
-  int (*version)(struct logger* logger, struct android_log_transport_context* transp);
   /* Release resources taken by the following interfaces */
   void (*close)(struct logger_list* logger_list, struct android_log_transport_context* transp);
   /*
@@ -62,53 +61,37 @@
    */
   int (*read)(struct logger_list* logger_list, struct android_log_transport_context* transp,
               struct log_msg* log_msg);
-  /* Must only be called if not ANDROID_LOG_NONBLOCK (blocking) */
-  int (*poll)(struct logger_list* logger_list, struct android_log_transport_context* transp);
-
-  int (*clear)(struct logger* logger, struct android_log_transport_context* transp);
-  ssize_t (*setSize)(struct logger* logger, struct android_log_transport_context* transp,
-                     size_t size);
-  ssize_t (*getSize)(struct logger* logger, struct android_log_transport_context* transp);
-  ssize_t (*getReadableSize)(struct logger* logger, struct android_log_transport_context* transp);
-
-  ssize_t (*getPrune)(struct logger_list* logger_list, struct android_log_transport_context* transp,
-                      char* buf, size_t len);
-  ssize_t (*setPrune)(struct logger_list* logger_list, struct android_log_transport_context* transp,
-                      char* buf, size_t len);
-  ssize_t (*getStats)(struct logger_list* logger_list, struct android_log_transport_context* transp,
-                      char* buf, size_t len);
 };
 
 struct android_log_transport_context {
   union android_log_context_union context; /* zero init per-transport context */
 
   struct android_log_transport_read* transport;
-  unsigned logMask;      /* mask of requested log buffers */
 };
 
 struct logger_list {
-  struct listnode logger;
   android_log_transport_context transport_context;
   bool transport_initialized;
   int mode;
   unsigned int tail;
   log_time start;
   pid_t pid;
+  uint32_t log_mask;
 };
 
-struct logger {
-  struct listnode node;
-  struct logger_list* parent;
+// Format for a 'logger' entry: uintptr_t where only the bottom 32 bits are used.
+// bit 31: Set if this 'logger' is for logd.
+// bit 30: Set if this 'logger' is for pmsg
+// bits 0-2: the decimal value of the log buffer.
+// Other bits are unused.
 
-  log_id_t logId;
-};
+#define LOGGER_LOGD (1 << 31)
+#define LOGGER_PMSG (1 << 30)
+#define LOGGER_LOG_ID_MASK ((1 << 3) - 1)
 
-/* assumes caller has structures read-locked, single threaded, or fenced */
-#define logger_for_each(logp, logger_list)                                      \
-  for ((logp) = node_to_item((logger_list)->logger.next, struct logger, node);  \
-       ((logp) != node_to_item(&(logger_list)->logger, struct logger, node)) && \
-       ((logp)->parent == (logger_list));                                       \
-       (logp) = node_to_item((logp)->node.next, struct logger, node))
+inline bool android_logger_is_logd(struct logger* logger) {
+  return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
+}
 
 /* OS specific dribs and drabs */
 
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 71362bb..0ce7a46 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -21,6 +21,7 @@
 #include <pthread.h>
 #include <sched.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -32,211 +33,53 @@
 #include "log_portability.h"
 #include "logger.h"
 
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger* logger) {
-  if (!logger) {
-    return;
-  }
-
-  list_remove(&logger->node);
-
-  free(logger);
-}
-
-/* android_logger_alloc unimplemented, no use case */
-
 /* method for getting the associated sublog id */
 log_id_t android_logger_get_id(struct logger* logger) {
-  return ((struct logger*)logger)->logId;
+  return static_cast<log_id_t>(reinterpret_cast<uintptr_t>(logger) & LOGGER_LOG_ID_MASK);
 }
 
-static int init_transport_context(struct logger_list* logger_list) {
+static struct logger_list* android_logger_list_alloc_internal(int mode, unsigned int tail,
+                                                              log_time start, pid_t pid) {
+  auto* logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(struct logger_list)));
   if (!logger_list) {
-    return -EINVAL;
+    return nullptr;
   }
 
-  if (list_empty(&logger_list->logger)) {
-    return -EINVAL;
-  }
-
-  if (logger_list->transport_initialized) {
-    return 0;
-  }
+  logger_list->mode = mode;
+  logger_list->start = start;
+  logger_list->tail = tail;
+  logger_list->pid = pid;
 
 #if (FAKE_LOG_DEVICE == 0)
   extern struct android_log_transport_read logdLoggerRead;
   extern struct android_log_transport_read pmsgLoggerRead;
 
-  struct android_log_transport_read* transport;
-  transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
-
-  struct logger* logger;
-  unsigned logMask = 0;
-
-  logger_for_each(logger, logger_list) {
-    log_id_t logId = logger->logId;
-
-    if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
-      continue;
-    }
-    if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
-      logMask |= 1 << logId;
-    }
-  }
-  if (!logMask) {
-    return -ENODEV;
-  }
-
-  logger_list->transport_context.transport = transport;
-  logger_list->transport_context.logMask = logMask;
+  logger_list->transport_context.transport =
+      (mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
 #endif
-  return 0;
-}
 
-#define LOGGER_FUNCTION(logger, def, func, args...)                                           \
-  ssize_t ret = -EINVAL;                                                                      \
-                                                                                              \
-  if (!logger) {                                                                              \
-    return ret;                                                                               \
-  }                                                                                           \
-  ret = init_transport_context(logger->parent);                                               \
-  if (ret < 0) {                                                                              \
-    return ret;                                                                               \
-  }                                                                                           \
-                                                                                              \
-  ret = (def);                                                                                \
-  android_log_transport_context* transport_context = &logger->parent->transport_context;      \
-  if (transport_context->logMask & (1 << logger->logId) && transport_context->transport &&    \
-      transport_context->transport->func) {                                                   \
-    ssize_t retval = (transport_context->transport->func)(logger, transport_context, ##args); \
-    if (ret >= 0 || ret == (def)) {                                                           \
-      ret = retval;                                                                           \
-    }                                                                                         \
-  }                                                                                           \
-  return ret
-
-int android_logger_clear(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, clear);
-}
-
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, getSize);
-}
-
-int android_logger_set_log_size(struct logger* logger, unsigned long size) {
-  LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger* logger) {
-  LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
-}
-
-/*
- * returns the logger version
- */
-int android_logger_get_log_version(struct logger* logger) {
-  LOGGER_FUNCTION(logger, 4, version);
-}
-
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                                      \
-  ssize_t ret = init_transport_context(logger_list);                                               \
-  if (ret < 0) {                                                                                   \
-    return ret;                                                                                    \
-  }                                                                                                \
-                                                                                                   \
-  ret = (def);                                                                                     \
-  android_log_transport_context* transport_context = &logger_list->transport_context;              \
-  if (transport_context->transport && transport_context->transport->func) {                        \
-    ssize_t retval = (transport_context->transport->func)(logger_list, transport_context, ##args); \
-    if (ret >= 0 || ret == (def)) {                                                                \
-      ret = retval;                                                                                \
-    }                                                                                              \
-  }                                                                                                \
-  return ret
-
-/*
- * returns statistics
- */
-ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
-}
-
-ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
-}
-
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
-  LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
+  return logger_list;
 }
 
 struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
-  struct logger_list* logger_list;
-
-  logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(*logger_list)));
-  if (!logger_list) {
-    return NULL;
-  }
-
-  list_init(&logger_list->logger);
-  logger_list->mode = mode;
-  logger_list->tail = tail;
-  logger_list->pid = pid;
-
-  return logger_list;
+  return android_logger_list_alloc_internal(mode, tail, log_time(0, 0), pid);
 }
 
 struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
-  struct logger_list* logger_list;
-
-  logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(*logger_list)));
-  if (!logger_list) {
-    return NULL;
-  }
-
-  list_init(&logger_list->logger);
-  logger_list->mode = mode;
-  logger_list->start = start;
-  logger_list->pid = pid;
-
-  return logger_list;
+  return android_logger_list_alloc_internal(mode, 0, start, pid);
 }
 
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
-
 /* Open the named log and add it to the logger list */
 struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
-  struct logger* logger;
-
   if (!logger_list || (logId >= LOG_ID_MAX)) {
     return nullptr;
   }
 
-  logger_for_each(logger, logger_list) {
-    if (logger->logId == logId) {
-      return reinterpret_cast<struct logger*>(logger);
-    }
-  }
+  logger_list->log_mask |= 1 << logId;
 
-  logger = static_cast<struct logger*>(calloc(1, sizeof(*logger)));
-  if (!logger) {
-    return nullptr;
-  }
-
-  logger->logId = logId;
-  list_add_tail(&logger_list->logger, &logger->node);
-  logger->parent = logger_list;
-
-  // Reset known transport to re-evaluate, since we added a new logger.
-  logger_list->transport_initialized = false;
-
-  return logger;
+  uintptr_t logger = logId;
+  logger |= (logger_list->mode & ANDROID_LOG_PSTORE) ? LOGGER_PMSG : LOGGER_LOGD;
+  return reinterpret_cast<struct logger*>(logger);
 }
 
 /* Open the single named log and make it part of a new logger list */
@@ -256,10 +99,14 @@
   return logger_list;
 }
 
-/* Validate log_msg packet, read function has already been null checked */
-static int android_transport_read(struct logger_list* logger_list,
-                                  struct android_log_transport_context* transp,
-                                  struct log_msg* log_msg) {
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
+  if (logger_list == nullptr || logger_list->transport_context.transport == nullptr ||
+      logger_list->log_mask == 0) {
+    return -EINVAL;
+  }
+
+  android_log_transport_context* transp = &logger_list->transport_context;
+
   int ret = (*transp->transport->read)(logger_list, transp, log_msg);
 
   if (ret <= 0) {
@@ -285,17 +132,6 @@
   return ret;
 }
 
-/* Read from the selected logs */
-int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
-  int ret = init_transport_context(logger_list);
-  if (ret < 0) {
-    return ret;
-  }
-
-  android_log_transport_context* transport_context = &logger_list->transport_context;
-  return android_transport_read(logger_list, transport_context, log_msg);
-}
-
 /* Close all the logs */
 void android_logger_list_free(struct logger_list* logger_list) {
   if (logger_list == NULL) {
@@ -308,11 +144,5 @@
     (*transport_context->transport->close)(logger_list, transport_context);
   }
 
-  while (!list_empty(&logger_list->logger)) {
-    struct listnode* node = list_head(&logger_list->logger);
-    struct logger* logger = node_to_item(node, struct logger, node);
-    android_logger_free((struct logger*)logger);
-  }
-
   free(logger_list);
 }
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index cd83161..9f603e9 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -27,27 +27,16 @@
 #include "logger.h"
 
 static int PmsgAvailable(log_id_t logId);
-static int PmsgVersion(struct logger* logger, struct android_log_transport_context* transp);
 static int PmsgRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
                     struct log_msg* log_msg);
 static void PmsgClose(struct logger_list* logger_list,
                       struct android_log_transport_context* transp);
-static int PmsgClear(struct logger* logger, struct android_log_transport_context* transp);
 
 struct android_log_transport_read pmsgLoggerRead = {
     .name = "pmsg",
     .available = PmsgAvailable,
-    .version = PmsgVersion,
     .close = PmsgClose,
     .read = PmsgRead,
-    .poll = NULL,
-    .clear = PmsgClear,
-    .setSize = NULL,
-    .getSize = NULL,
-    .getReadableSize = NULL,
-    .getPrune = NULL,
-    .setPrune = NULL,
-    .getStats = NULL,
 };
 
 static int PmsgAvailable(log_id_t logId) {
@@ -60,17 +49,6 @@
   return -EBADF;
 }
 
-static int PmsgClear(struct logger*, struct android_log_transport_context*) {
-  return unlink("/sys/fs/pstore/pmsg-ramoops-0");
-}
-
-/*
- * returns the logger version
- */
-static int PmsgVersion(struct logger*, struct android_log_transport_context*) {
-  return 4;
-}
-
 static int PmsgRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
                     struct log_msg* log_msg) {
   ssize_t ret;
@@ -134,7 +112,7 @@
     }
     preread_count = 0;
 
-    if ((transp->logMask & (1 << buf.l.id)) &&
+    if ((logger_list->log_mask & (1 << buf.l.id)) &&
         ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
          ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
           ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
@@ -232,12 +210,12 @@
   memset(&transp, 0, sizeof(transp));
 
   logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
-  transp.logMask = (unsigned)-1;
+  logger_list.log_mask = (unsigned)-1;
   if (logId != LOG_ID_ANY) {
-    transp.logMask = (1 << logId);
+    logger_list.log_mask = (1 << logId);
   }
-  transp.logMask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
-  if (!transp.logMask) {
+  logger_list.log_mask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+  if (!logger_list.log_mask) {
     return -EINVAL;
   }
 
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 56892a2..39ac7a5 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -913,7 +913,7 @@
 }
 BENCHMARK(BM_lookupEventTagNum);
 
-// Must be functionally identical to liblog internal __send_log_msg.
+// Must be functionally identical to liblog internal SendLogdControlMessage()
 static void send_to_control(char* buf, size_t len) {
   int sock =
       socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index e9787aa..2e8af20 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -103,6 +103,7 @@
   fn("sp", regs_[ARM64_REG_SP]);
   fn("lr", regs_[ARM64_REG_LR]);
   fn("pc", regs_[ARM64_REG_PC]);
+  fn("pst", regs_[ARM64_REG_PSTATE]);
 }
 
 Regs* RegsArm64::Read(void* remote_data) {
@@ -113,6 +114,7 @@
   uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
   reg_data[ARM64_REG_PC] = user->pc;
   reg_data[ARM64_REG_SP] = user->sp;
+  reg_data[ARM64_REG_PSTATE] = user->pstate;
   return regs;
 }
 
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index e8b778b..e953335 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -55,6 +55,7 @@
   ARM64_REG_R30,
   ARM64_REG_R31,
   ARM64_REG_PC,
+  ARM64_REG_PSTATE,
   ARM64_REG_LAST,
 
   ARM64_REG_SP = ARM64_REG_R31,
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 7e36953..bc95851 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -114,6 +114,7 @@
   result.push_back({"sp", ARM64_REG_SP});
   result.push_back({"lr", ARM64_REG_LR});
   result.push_back({"pc", ARM64_REG_PC});
+  result.push_back({"pst", ARM64_REG_PSTATE});
   return result;
 }
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 70ccb80..2d14bf3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -969,6 +969,16 @@
         }
     }
 
+    if (mode & ANDROID_LOG_PSTORE) {
+        if (clearLog) {
+            unlink("/sys/fs/pstore/pmsg-ramoops-0");
+            return EXIT_SUCCESS;
+        }
+        if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+            LogcatPanic(HELP_TRUE, "-L is incompatible with -g/-G, -S, and -p/-P");
+        }
+    }
+
     std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
             nullptr, &android_logger_list_free};
     if (tail_time != log_time::EPOCH) {
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 33da1f1..f4a846f 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -375,9 +375,11 @@
       {"audio_hal.period_size", "u:object_r:default_prop:s0"},
       {"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
       {"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.boot-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
@@ -388,6 +390,7 @@
       {"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},