Merge "liblog: return 0 from android_logger_list_read() when recv() returns 0"
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 9256a16..1d72c70 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -72,6 +72,7 @@
     name: "libsnapshot_sources",
     srcs: [
         "android/snapshot/snapshot.proto",
+        "device_info.cpp",
         "snapshot.cpp",
         "snapshot_metadata_updater.cpp",
         "partition_cow_creator.cpp",
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
new file mode 100644
index 0000000..bacb41c
--- /dev/null
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -0,0 +1,123 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "device_info.h"
+
+#include <android-base/logging.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
+
+namespace android {
+namespace snapshot {
+
+#ifdef LIBSNAPSHOT_USE_HAL
+using android::hardware::boot::V1_0::CommandResult;
+#endif
+
+using namespace std::string_literals;
+
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
+std::string DeviceInfo::GetGsidDir() const {
+    return "ota"s;
+}
+
+std::string DeviceInfo::GetMetadataDir() const {
+    return "/metadata/ota"s;
+}
+
+std::string DeviceInfo::GetSlotSuffix() const {
+    return fs_mgr_get_slot_suffix();
+}
+
+std::string DeviceInfo::GetOtherSlotSuffix() const {
+    return fs_mgr_get_other_slot_suffix();
+}
+
+const android::fs_mgr::IPartitionOpener& DeviceInfo::GetPartitionOpener() const {
+    return opener_;
+}
+
+std::string DeviceInfo::GetSuperDevice(uint32_t slot) const {
+    return fs_mgr_get_super_partition_name(slot);
+}
+
+bool DeviceInfo::IsOverlayfsSetup() const {
+    return fs_mgr_overlayfs_is_setup();
+}
+
+#ifdef LIBSNAPSHOT_USE_HAL
+bool DeviceInfo::EnsureBootHal() {
+    if (!boot_control_) {
+        auto hal = android::hardware::boot::V1_0::IBootControl::getService();
+        if (!hal) {
+            LOG(ERROR) << "Could not find IBootControl HAL";
+            return false;
+        }
+        boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
+        if (!boot_control_) {
+            LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
+            return false;
+        }
+    }
+    return true;
+}
+#endif
+
+bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
+#ifdef LIBSNAPSHOT_USE_HAL
+    if (!EnsureBootHal()) {
+        return false;
+    }
+    if (!boot_control_->setSnapshotMergeStatus(status)) {
+        LOG(ERROR) << "Unable to set the snapshot merge status";
+        return false;
+    }
+    return true;
+#else
+    LOG(ERROR) << "HAL support not enabled.";
+    return false;
+#endif
+}
+
+bool DeviceInfo::IsRecovery() const {
+    return kIsRecovery;
+}
+
+bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
+#ifdef LIBSNAPSHOT_USE_HAL
+    if (!EnsureBootHal()) {
+        return false;
+    }
+
+    CommandResult result = {};
+    auto cb = [&](CommandResult r) -> void { result = r; };
+    boot_control_->setSlotAsUnbootable(slot, cb);
+    if (!result.success) {
+        LOG(ERROR) << "Error setting slot " << slot << " unbootable: " << result.errMsg;
+        return false;
+    }
+    return true;
+#else
+    LOG(ERROR) << "HAL support not enabled.";
+    return false;
+#endif
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
new file mode 100644
index 0000000..d8d3d91
--- /dev/null
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <string>
+
+#ifdef LIBSNAPSHOT_USE_HAL
+#include <android/hardware/boot/1.1/IBootControl.h>
+#endif
+#include <liblp/partition_opener.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+class DeviceInfo final : public SnapshotManager::IDeviceInfo {
+    using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+
+  public:
+    std::string GetGsidDir() const override;
+    std::string GetMetadataDir() const override;
+    std::string GetSlotSuffix() const override;
+    std::string GetOtherSlotSuffix() const override;
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override;
+    std::string GetSuperDevice(uint32_t slot) const override;
+    bool IsOverlayfsSetup() const override;
+    bool SetBootControlMergeStatus(MergeStatus status) override;
+    bool SetSlotAsUnbootable(unsigned int slot) override;
+    bool IsRecovery() const override;
+
+  private:
+    bool EnsureBootHal();
+
+    android::fs_mgr::PartitionOpener opener_;
+#ifdef LIBSNAPSHOT_USE_HAL
+    android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
+#endif
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
index d5ceb0e..73450db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
@@ -27,6 +27,8 @@
     virtual ~AutoDevice(){};
     void Release();
 
+    bool HasDevice() const { return !name_.empty(); }
+
   protected:
     AutoDevice(const std::string& name) : name_(name) {}
     std::string name_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index c820395..8f24709 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -122,6 +122,7 @@
         virtual const IPartitionOpener& GetPartitionOpener() const = 0;
         virtual bool IsOverlayfsSetup() const = 0;
         virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+        virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
         virtual bool IsRecovery() const = 0;
     };
 
@@ -133,7 +134,7 @@
     static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
 
     // This is similar to New(), except designed specifically for first-stage
-    // init.
+    // init or recovery.
     static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
 
     // Helper function for first-stage init to check whether a SnapshotManager
@@ -180,7 +181,10 @@
     //
     //   MergeCompleted indicates that the update has fully completed.
     //   GetUpdateState will return None, and a new update can begin.
-    UpdateState ProcessUpdateState();
+    //
+    // The optional callback allows the caller to periodically check the
+    // progress with GetUpdateState().
+    UpdateState ProcessUpdateState(const std::function<void()>& callback = {});
 
   public:
     // Initiate the merge if necessary, then wait for the merge to finish.
@@ -219,6 +223,18 @@
     // call to CreateLogicalPartitions when snapshots are present.
     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.
+    //
+    // 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.
+    // If a snapshot is present but not yet merged, the slot must be marked as
+    // unbootable.
+    //
+    // Returns true on success (or nothing to do), false on failure. The
+    // optional callback fires periodically to query progress via GetUpdateState.
+    bool HandleImminentDataWipe(const std::function<void()>& callback = {});
+
     // Dump debug information.
     bool Dump(std::ostream& os);
 
@@ -231,7 +247,7 @@
     // is not found in fstab.
     // Note: if this function is called the second time before the AutoDevice returned from the
     // first call is destroyed, the device will be unmounted when any of these AutoDevices is
-    // destroyed. FOr example:
+    // destroyed. For example:
     //   auto a = mgr->EnsureMetadataMounted(); // mounts
     //   auto b = mgr->EnsureMetadataMounted(); // does nothing
     //   b.reset() // unmounts
@@ -250,7 +266,10 @@
     FRIEND_TEST(SnapshotTest, Merge);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+    FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
+    FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
     FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
+    FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     friend class SnapshotTest;
     friend class SnapshotUpdateTest;
@@ -464,6 +483,10 @@
             const LpMetadata* exported_target_metadata, const std::string& target_suffix,
             const std::map<std::string, SnapshotStatus>& all_snapshot_status);
 
+    // Unmap all partitions that were mapped by CreateLogicalAndSnapshotPartitions.
+    // This should only be called in recovery.
+    bool UnmapAllPartitions();
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 2c516a2..1de7008 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -29,19 +29,16 @@
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#ifdef LIBSNAPSHOT_USE_HAL
-#include <android/hardware/boot/1.1/IBootControl.h>
-#endif
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
-#include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 #include <liblp/liblp.h>
 
 #include <android/snapshot/snapshot.pb.h>
+#include "device_info.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
 #include "utility.h"
@@ -77,57 +74,6 @@
 
 static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
 
-#ifdef __ANDROID_RECOVERY__
-constexpr bool kIsRecovery = true;
-#else
-constexpr bool kIsRecovery = false;
-#endif
-
-class DeviceInfo final : public SnapshotManager::IDeviceInfo {
-  public:
-    std::string GetGsidDir() const override { return "ota"s; }
-    std::string GetMetadataDir() const override { return "/metadata/ota"s; }
-    std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
-    std::string GetOtherSlotSuffix() const override { return fs_mgr_get_other_slot_suffix(); }
-    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
-    std::string GetSuperDevice(uint32_t slot) const override {
-        return fs_mgr_get_super_partition_name(slot);
-    }
-    bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
-    bool SetBootControlMergeStatus(MergeStatus status) override;
-    bool IsRecovery() const override { return kIsRecovery; }
-
-  private:
-    android::fs_mgr::PartitionOpener opener_;
-#ifdef LIBSNAPSHOT_USE_HAL
-    android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
-#endif
-};
-
-bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
-#ifdef LIBSNAPSHOT_USE_HAL
-    if (!boot_control_) {
-        auto hal = android::hardware::boot::V1_0::IBootControl::getService();
-        if (!hal) {
-            LOG(ERROR) << "Could not find IBootControl HAL";
-            return false;
-        }
-        boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
-        if (!boot_control_) {
-            LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
-            return false;
-        }
-    }
-    if (!boot_control_->setSnapshotMergeStatus(status)) {
-        LOG(ERROR) << "Unable to set the snapshot merge status";
-        return false;
-    }
-    return true;
-#else
-    return false;
-#endif
-}
-
 // Note: IImageManager is an incomplete type in the header, so the default
 // destructor doesn't work.
 SnapshotManager::~SnapshotManager() {}
@@ -512,6 +458,14 @@
         return false;
     }
 
+    // We can't delete snapshots in recovery. The only way we'd try is it we're
+    // completing or canceling a merge in preparation for a data wipe, in which
+    // case, we don't care if the file sticks around.
+    if (device_->IsRecovery()) {
+        LOG(INFO) << "Skipping delete of snapshot " << name << " in recovery.";
+        return true;
+    }
+
     auto cow_image_name = GetCowImageDeviceName(name);
     if (images_->BackingImageExists(cow_image_name)) {
         if (!images_->DeleteBackingImage(cow_image_name)) {
@@ -744,7 +698,7 @@
 // Note that when a merge fails, we will *always* try again to complete the
 // merge each time the device boots. There is no harm in doing so, and if
 // the problem was transient, we might manage to get a new outcome.
-UpdateState SnapshotManager::ProcessUpdateState() {
+UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback) {
     while (true) {
         UpdateState state = CheckMergeState();
         if (state == UpdateState::MergeFailed) {
@@ -756,6 +710,10 @@
             return state;
         }
 
+        if (callback) {
+            callback();
+        }
+
         // This wait is not super time sensitive, so we have a relatively
         // low polling frequency.
         std::this_thread::sleep_for(2s);
@@ -1718,9 +1676,9 @@
         case UpdateState::None:
         case UpdateState::MergeNeedsReboot:
         case UpdateState::MergeCompleted:
+        case UpdateState::Initiated:
             merge_status = MergeStatus::NONE;
             break;
-        case UpdateState::Initiated:
         case UpdateState::Unverified:
             merge_status = MergeStatus::SNAPSHOTTED;
             break;
@@ -2119,6 +2077,27 @@
     return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
 }
 
+bool SnapshotManager::UnmapAllPartitions() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    const auto& opener = device_->GetPartitionOpener();
+    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_device = device_->GetSuperDevice(slot);
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
+        return false;
+    }
+
+    bool ok = true;
+    for (const auto& partition : metadata->partitions) {
+        auto partition_name = GetPartitionName(partition);
+        ok &= UnmapPartitionWithSnapshot(lock.get(), partition_name);
+    }
+    return ok;
+}
+
 bool SnapshotManager::Dump(std::ostream& os) {
     // Don't actually lock. Dump() is for debugging purposes only, so it is okay
     // if it is racy.
@@ -2192,5 +2171,66 @@
     return state;
 }
 
+bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
+    if (!device_->IsRecovery()) {
+        LOG(ERROR) << "Data wipes are only allowed in recovery.";
+        return false;
+    }
+
+    auto mount = EnsureMetadataMounted();
+    if (!mount || !mount->HasDevice()) {
+        // We allow the wipe to continue, because if we can't mount /metadata,
+        // it is unlikely the device would have booted anyway. If there is no
+        // metadata partition, then the device predates Virtual A/B.
+        return true;
+    }
+
+    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_path = device_->GetSuperDevice(slot_number);
+    if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+        LOG(ERROR) << "Unable to map partitions to complete merge.";
+        return false;
+    }
+
+    UpdateState state = ProcessUpdateState(callback);
+    LOG(INFO) << "Update state in recovery: " << state;
+    switch (state) {
+        case UpdateState::MergeFailed:
+            LOG(ERROR) << "Unrecoverable merge failure detected.";
+            return false;
+        case UpdateState::Unverified: {
+            // If an OTA was just applied but has not yet started merging, we
+            // have no choice but to revert slots, because the current slot will
+            // immediately become unbootable. Rather than wait for the device
+            // to reboot N times until a rollback, we proactively disable the
+            // new slot instead.
+            //
+            // Since the rollback is inevitable, we don't treat a HAL failure
+            // as an error here.
+            std::string old_slot;
+            auto boot_file = GetSnapshotBootIndicatorPath();
+            if (android::base::ReadFileToString(boot_file, &old_slot) &&
+                device_->GetSlotSuffix() != old_slot) {
+                LOG(ERROR) << "Reverting to slot " << old_slot << " since update will be deleted.";
+                device_->SetSlotAsUnbootable(slot_number);
+            }
+            break;
+        }
+        case UpdateState::MergeNeedsReboot:
+            // We shouldn't get here, because nothing is depending on
+            // logical partitions.
+            LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
+            break;
+        default:
+            break;
+    }
+
+    // Nothing should be depending on partitions now, so unmap them all.
+    if (!UnmapAllPartitions()) {
+        LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index af268f9..8783526 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1230,6 +1230,80 @@
     EXPECT_FALSE(IsMetadataMounted());
 }
 
+// Test that during a merge, we can wipe data in recovery.
+TEST_F(SnapshotUpdateTest, MergeInRecovery) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    init = nullptr;
+
+    // Initiate the merge and then immediately stop it to simulate a reboot.
+    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_TRUE(new_sm->InitiateMerge());
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a reboot into recovery.
+    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    test_device->set_recovery(true);
+    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+    ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+}
+
+// Test that after an OTA, before a merge, we can wipe data in recovery.
+TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a reboot into recovery.
+    auto test_device = new TestDeviceInfo(fake_super, "_b");
+    test_device->set_recovery(true);
+    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
+    EXPECT_TRUE(test_device->IsSlotUnbootable(1));
+    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+}
+
+// Test that after an OTA and a bootloader rollback with no merge, we can wipe
+// data in recovery.
+TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a rollback, with reboot into recovery.
+    auto test_device = new TestDeviceInfo(fake_super, "_a");
+    test_device->set_recovery(true);
+    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 0f70afe..9083843 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -16,6 +16,7 @@
 
 #include <optional>
 #include <string>
+#include <unordered_set>
 
 #include <android/hardware/boot/1.1/IBootControl.h>
 #include <gmock/gmock.h>
@@ -89,6 +90,12 @@
     }
     bool IsOverlayfsSetup() const override { return false; }
     bool IsRecovery() const override { return recovery_; }
+    bool SetSlotAsUnbootable(unsigned int slot) override {
+        unbootable_slots_.insert(slot);
+        return true;
+    }
+
+    bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
 
     void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
     void set_fake_super(const std::string& path) {
@@ -102,6 +109,7 @@
     std::unique_ptr<TestPartitionOpener> opener_;
     MergeStatus merge_status_;
     bool recovery_ = false;
+    std::unordered_set<uint32_t> unbootable_slots_;
 };
 
 class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
diff --git a/init/action.cpp b/init/action.cpp
index 69e40d0..f05fa7c 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -180,21 +180,24 @@
 // It takes an optional (name, value) pair, which if provided must
 // be present in property_triggers_; it skips the check of the current
 // property value for this pair.
-bool Action::CheckPropertyTriggers(const std::string& name,
-                                   const std::string& value) const {
+bool Action::CheckPropertyTriggers(const std::string& name, const std::string& value) const {
     if (property_triggers_.empty()) {
         return true;
     }
 
-    bool found = name.empty();
+    if (!name.empty()) {
+        auto it = property_triggers_.find(name);
+        if (it == property_triggers_.end()) {
+            return false;
+        }
+        const auto& trigger_value = it->second;
+        if (trigger_value != "*" && trigger_value != value) {
+            return false;
+        }
+    }
+
     for (const auto& [trigger_name, trigger_value] : property_triggers_) {
-        if (trigger_name == name) {
-            if (trigger_value != "*" && trigger_value != value) {
-                return false;
-            } else {
-                found = true;
-            }
-        } else {
+        if (trigger_name != name) {
             std::string prop_value = android::base::GetProperty(trigger_name, "");
             if (trigger_value == "*" && !prop_value.empty()) {
                 continue;
@@ -202,7 +205,7 @@
             if (trigger_value != prop_value) return false;
         }
     }
-    return found;
+    return true;
 }
 
 bool Action::CheckEvent(const EventTrigger& event_trigger) const {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a55514b..8f58145 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -65,6 +65,7 @@
 
 #include "action_manager.h"
 #include "bootchart.h"
+#include "builtin_arguments.h"
 #include "fscrypt_init_extensions.h"
 #include "init.h"
 #include "mount_namespace.h"
@@ -1216,6 +1217,15 @@
     }
 }
 
+static Result<void> do_finish_userspace_reboot(const BuiltinArguments&) {
+    LOG(INFO) << "Userspace reboot successfully finished";
+    boot_clock::time_point now = boot_clock::now();
+    property_set("sys.init.userspace_reboot.last_finished",
+                 std::to_string(now.time_since_epoch().count()));
+    property_set(kUserspaceRebootInProgress, "0");
+    return {};
+}
+
 // Builtin-function-map start
 const BuiltinFunctionMap& GetBuiltinFunctionMap() {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1237,6 +1247,7 @@
         {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
         {"exec_start",              {1,     1,    {false,  do_exec_start}}},
         {"export",                  {2,     2,    {false,  do_export}}},
+        {"finish_userspace_reboot", {0,     0,    {false,  do_finish_userspace_reboot}}},
         {"hostname",                {1,     1,    {true,   do_hostname}}},
         {"ifup",                    {1,     1,    {true,   do_ifup}}},
         {"init_user0",              {0,     0,    {false,  do_init_user0}}},
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 64ec1fb..7167672 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -69,10 +69,13 @@
 
 using namespace std::literals;
 
+using android::base::boot_clock;
 using android::base::GetBoolProperty;
+using android::base::SetProperty;
 using android::base::Split;
 using android::base::Timer;
 using android::base::unique_fd;
+using android::base::WaitForProperty;
 using android::base::WriteStringToFile;
 
 namespace android {
@@ -728,16 +731,21 @@
 
 static Result<void> DoUserspaceReboot() {
     LOG(INFO) << "Userspace reboot initiated";
+    boot_clock::time_point now = boot_clock::now();
+    property_set("sys.init.userspace_reboot.last_started",
+                 std::to_string(now.time_since_epoch().count()));
     auto guard = android::base::make_scope_guard([] {
         // Leave shutdown so that we can handle a full reboot.
         LeaveShutdown();
         trigger_shutdown("reboot,abort-userspace-reboot");
     });
-    // Triggering userspace-reboot-requested will result in a bunch of set_prop
+    // Triggering userspace-reboot-requested will result in a bunch of setprop
     // actions. We should make sure, that all of them are propagated before
-    // proceeding with userspace reboot.
-    // TODO(b/135984674): implement proper synchronization logic.
-    std::this_thread::sleep_for(500ms);
+    // proceeding with userspace reboot. Synchronously setting kUserspaceRebootInProgress property
+    // is not perfect, but it should do the trick.
+    if (property_set(kUserspaceRebootInProgress, "1") != 0) {
+        return Error() << "Failed to set property " << kUserspaceRebootInProgress;
+    }
     EnterShutdown();
     std::vector<Service*> stop_first;
     // Remember the services that were enabled. We will need to manually enable them again otherwise
@@ -752,6 +760,12 @@
             were_enabled.push_back(s);
         }
     }
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() before terminating services...";
+        sync();
+        LOG(INFO) << "sync() took " << sync_timer;
+    }
     // TODO(b/135984674): do we need shutdown animation for userspace reboot?
     // TODO(b/135984674): control userspace timeout via read-only property?
     StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */);
@@ -767,6 +781,12 @@
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " debugging services are still running";
     }
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() after stopping services...";
+        sync();
+        LOG(INFO) << "sync() took " << sync_timer;
+    }
     if (auto result = UnmountAllApexes(); !result) {
         return result;
     }
@@ -784,7 +804,38 @@
     return {};
 }
 
+static void UserspaceRebootWatchdogThread() {
+    if (!WaitForProperty("sys.init.userspace_reboot_in_progress", "1", 20s)) {
+        // TODO(b/135984674): should we reboot instead?
+        LOG(WARNING) << "Userspace reboot didn't start in 20 seconds. Stopping watchdog";
+        return;
+    }
+    LOG(INFO) << "Starting userspace reboot watchdog";
+    // TODO(b/135984674): this should be configured via a read-only sysprop.
+    std::chrono::milliseconds timeout = 60s;
+    if (!WaitForProperty("sys.boot_completed", "1", timeout)) {
+        LOG(ERROR) << "Failed to boot in " << timeout.count() << "ms. Switching to full reboot";
+        // In this case device is in a boot loop. Only way to recover is to do dirty reboot.
+        RebootSystem(ANDROID_RB_RESTART2, "userspace-reboot-watchdog-triggered");
+    }
+    LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
+}
+
 static void HandleUserspaceReboot() {
+    // Spinnig up a separate thread will fail the setns call later in the boot sequence.
+    // Fork a new process to monitor userspace reboot while we are investigating a better solution.
+    pid_t pid = fork();
+    if (pid < 0) {
+        PLOG(ERROR) << "Failed to fork process for userspace reboot watchdog. Switching to full "
+                    << "reboot";
+        trigger_shutdown("reboot,userspace-reboot-failed-to-fork");
+        return;
+    }
+    if (pid == 0) {
+        // Child
+        UserspaceRebootWatchdogThread();
+        _exit(EXIT_SUCCESS);
+    }
     LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
     auto& am = ActionManager::GetInstance();
     am.ClearQueue();
diff --git a/init/reboot.h b/init/reboot.h
index 81c3edc..cdfa024 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -22,6 +22,8 @@
 namespace android {
 namespace init {
 
+static const constexpr char* kUserspaceRebootInProgress = "sys.init.userspace_reboot.in_progress";
+
 // Parses and handles a setprop sys.powerctl message.
 void HandlePowerctlMessage(const std::string& command);
 
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index 7882c96..e9f4a32 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -47,9 +47,6 @@
   uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
 };
 
-// TODO(tomcherry): real C++ structs.
-typedef struct android_log_context_internal android_log_context_internal;
-
 static void init_context(android_log_context_internal* context, uint32_t tag) {
   context->tag = tag;
   context->read_write_flag = kAndroidLoggerWrite;
@@ -110,11 +107,9 @@
   return 0;
 }
 
-int android_log_reset(android_log_context ctx) {
-  android_log_context_internal* context;
+int android_log_reset(android_log_context context) {
   uint32_t tag;
 
-  context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -126,10 +121,7 @@
   return 0;
 }
 
-int android_log_parser_reset(android_log_context ctx, const char* msg, size_t len) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+int android_log_parser_reset(android_log_context context, const char* msg, size_t len) {
   if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
     return -EBADF;
   }
@@ -140,10 +132,7 @@
   return 0;
 }
 
-int android_log_write_list_begin(android_log_context ctx) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+int android_log_write_list_begin(android_log_context context) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -174,8 +163,7 @@
   return 0;
 }
 
-int android_log_write_int32(android_log_context ctx, int32_t value) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_int32(android_log_context context, int32_t value) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -195,8 +183,7 @@
   return 0;
 }
 
-int android_log_write_int64(android_log_context ctx, int64_t value) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_int64(android_log_context context, int64_t value) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -216,8 +203,7 @@
   return 0;
 }
 
-int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_string8_len(android_log_context context, const char* value, size_t maxlen) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -252,8 +238,7 @@
   return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
 }
 
-int android_log_write_float32(android_log_context ctx, float value) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_float32(android_log_context context, float value) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -273,10 +258,7 @@
   return 0;
 }
 
-int android_log_write_list_end(android_log_context ctx) {
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+int android_log_write_list_end(android_log_context context) {
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -303,8 +285,7 @@
 /*
  * Logs the list of elements to the event log.
  */
-int android_log_write_list(android_log_context ctx, log_id_t id) {
-  android_log_context_internal* context;
+int android_log_write_list(android_log_context context, log_id_t id) {
   const char* msg;
   ssize_t len;
 
@@ -312,7 +293,6 @@
     return -EINVAL;
   }
 
-  context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -337,12 +317,10 @@
                                      : __android_log_security_bwrite(context->tag, msg, len));
 }
 
-int android_log_write_list_buffer(android_log_context ctx, const char** buffer) {
-  android_log_context_internal* context;
+int android_log_write_list_buffer(android_log_context context, const char** buffer) {
   const char* msg;
   ssize_t len;
 
-  context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -375,12 +353,10 @@
  * this and continues to call this function, the behavior is undefined
  * (although it won't crash).
  */
-static android_log_list_element android_log_read_next_internal(android_log_context ctx, int peek) {
+static android_log_list_element android_log_read_next_internal(android_log_context context,
+                                                               int peek) {
   android_log_list_element elem;
   unsigned pos;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
 
   memset(&elem, 0, sizeof(elem));
 
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 619cf8c..eaa157a 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -39,47 +39,43 @@
 #include "logd_reader.h"
 #include "logger.h"
 
-static int logdAvailable(log_id_t LogId);
-static int logdVersion(struct android_log_logger* logger,
-                       struct android_log_transport_context* transp);
-static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg);
-static int logdPoll(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp);
-static void logdClose(struct android_log_logger_list* logger_list,
+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 android_log_logger* logger,
-                     struct android_log_transport_context* transp);
-static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp, size_t size);
-static ssize_t logdGetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp);
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
+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 android_log_logger_list* logger,
+static ssize_t LogdGetPrune(struct logger_list* logger,
                             struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t logdSetPrune(struct android_log_logger_list* logger,
+static ssize_t LogdSetPrune(struct logger_list* logger,
                             struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t logdGetStats(struct android_log_logger_list* logger,
+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,
+    .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) {
+static int LogdAvailable(log_id_t logId) {
   if (logId >= LOG_ID_MAX) {
     return -EINVAL;
   }
@@ -120,8 +116,7 @@
 }
 
 /* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
-                            size_t buf_size) {
+static ssize_t send_log_msg(struct logger* logger, const char* msg, char* buf, size_t buf_size) {
   ssize_t ret;
   size_t len;
   char* cp;
@@ -202,16 +197,14 @@
   return 0;
 }
 
-static int logdClear(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp __unused) {
+static int LogdClear(struct logger* logger, struct android_log_transport_context*) {
   char buf[512];
 
   return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
 }
 
 /* returns the total size of the log's ring buffer */
-static ssize_t logdGetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp __unused) {
+static ssize_t LogdGetSize(struct logger* logger, struct android_log_transport_context*) {
   char buf[512];
 
   ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
@@ -226,8 +219,8 @@
   return atol(buf);
 }
 
-static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp __unused, size_t size) {
+static ssize_t LogdSetSize(struct logger* logger, struct android_log_transport_context*,
+                           size_t size) {
   char buf[512];
 
   snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
@@ -239,8 +232,7 @@
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
  */
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp __unused) {
+static ssize_t LogdGetReadableSize(struct logger* logger, struct android_log_transport_context*) {
   char buf[512];
 
   ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
@@ -258,8 +250,7 @@
 /*
  * returns the logger version
  */
-static int logdVersion(struct android_log_logger* logger __unused,
-                       struct android_log_transport_context* transp __unused) {
+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;
 }
@@ -267,10 +258,9 @@
 /*
  * returns statistics
  */
-static ssize_t logdGetStats(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp __unused, char* buf,
-                            size_t len) {
-  struct android_log_logger* logger;
+static ssize_t LogdGetStats(struct logger_list* logger_list, struct android_log_transport_context*,
+                            char* buf, size_t len) {
+  struct logger* logger;
   char* cp = buf;
   size_t remaining = len;
   size_t n;
@@ -294,14 +284,12 @@
   return send_log_msg(NULL, NULL, buf, len);
 }
 
-static ssize_t logdGetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused, char* buf,
+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);
 }
 
-static ssize_t logdSetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused, char* buf,
+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;
@@ -316,9 +304,8 @@
   return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
 }
 
-static int logdOpen(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp) {
-  struct android_log_logger* logger;
+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;
 
@@ -404,8 +391,8 @@
 }
 
 /* Read from the selected logs */
-static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
+static int LogdRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
+                    struct log_msg* log_msg) {
   int ret = logdOpen(logger_list, transp);
   if (ret < 0) {
     return ret;
@@ -425,8 +412,7 @@
   return ret;
 }
 
-static int logdPoll(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp) {
+static int LogdPoll(struct logger_list* logger_list, struct android_log_transport_context* transp) {
   struct pollfd p;
 
   int ret = logdOpen(logger_list, transp);
@@ -448,8 +434,7 @@
 }
 
 /* Close all the logs */
-static void logdClose(struct android_log_logger_list* logger_list __unused,
-                      struct android_log_transport_context* transp) {
+static void LogdClose(struct logger_list*, struct android_log_transport_context* transp) {
   int sock = atomic_exchange(&transp->context.sock, -1);
   if (sock > 0) {
     close(sock);
diff --git a/liblog/logger.h b/liblog/logger.h
index 02cad22..9c488b6 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -46,49 +46,37 @@
                size_t nr);
 };
 
-struct android_log_logger_list;
 struct android_log_transport_context;
-struct android_log_logger;
 
 struct android_log_transport_read {
   const char* name; /* human name to describe the transport */
 
   /* Does not cause resources to be taken */
   int (*available)(log_id_t logId);
-  int (*version)(struct android_log_logger* logger,
-                 struct android_log_transport_context* transp);
+  int (*version)(struct logger* logger, struct android_log_transport_context* transp);
   /* Release resources taken by the following interfaces */
-  void (*close)(struct android_log_logger_list* logger_list,
-                struct android_log_transport_context* transp);
+  void (*close)(struct logger_list* logger_list, struct android_log_transport_context* transp);
   /*
    * Expect all to instantiate open automagically on any call,
    * so we do not have an explicit open call.
    */
-  int (*read)(struct android_log_logger_list* logger_list,
-              struct android_log_transport_context* transp,
+  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 android_log_logger_list* logger_list,
-              struct android_log_transport_context* transp);
+  int (*poll)(struct logger_list* logger_list, struct android_log_transport_context* transp);
 
-  int (*clear)(struct android_log_logger* logger,
-               struct android_log_transport_context* transp);
-  ssize_t (*setSize)(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp, size_t size);
-  ssize_t (*getSize)(struct android_log_logger* logger,
-                     struct android_log_transport_context* transp);
-  ssize_t (*getReadableSize)(struct android_log_logger* logger,
-                             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 android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp, char* buf,
-                      size_t len);
-  ssize_t (*setPrune)(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp, char* buf,
-                      size_t len);
-  ssize_t (*getStats)(struct android_log_logger_list* logger_list,
-                      struct android_log_transport_context* transp, char* buf,
-                      size_t len);
+  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 {
@@ -98,7 +86,7 @@
   unsigned logMask;      /* mask of requested log buffers */
 };
 
-struct android_log_logger_list {
+struct logger_list {
   struct listnode logger;
   android_log_transport_context transport_context;
   bool transport_initialized;
@@ -108,22 +96,19 @@
   pid_t pid;
 };
 
-struct android_log_logger {
+struct logger {
   struct listnode node;
-  struct android_log_logger_list* parent;
+  struct logger_list* parent;
 
   log_id_t logId;
 };
 
 /* 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 android_log_logger, node);      \
-       ((logp) != node_to_item(&(logger_list)->logger,              \
-                               struct android_log_logger, node)) && \
-       ((logp)->parent == (logger_list));                           \
-       (logp) =                                                     \
-           node_to_item((logp)->node.next, struct android_log_logger, node))
+#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))
 
 /* OS specific dribs and drabs */
 
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index e8a89eb..71362bb 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -35,25 +35,23 @@
 /* android_logger_alloc unimplemented, no use case */
 /* android_logger_free not exported */
 static void android_logger_free(struct logger* logger) {
-  struct android_log_logger* logger_internal = (struct android_log_logger*)logger;
-
-  if (!logger_internal) {
+  if (!logger) {
     return;
   }
 
-  list_remove(&logger_internal->node);
+  list_remove(&logger->node);
 
-  free(logger_internal);
+  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 android_log_logger*)logger)->logId;
+  return ((struct logger*)logger)->logId;
 }
 
-static int init_transport_context(struct android_log_logger_list* logger_list) {
+static int init_transport_context(struct logger_list* logger_list) {
   if (!logger_list) {
     return -EINVAL;
   }
@@ -73,7 +71,7 @@
   struct android_log_transport_read* transport;
   transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
 
-  struct android_log_logger* logger;
+  struct logger* logger;
   unsigned logMask = 0;
 
   logger_for_each(logger, logger_list) {
@@ -96,28 +94,26 @@
   return 0;
 }
 
-#define LOGGER_FUNCTION(logger, def, func, args...)                                               \
-  ssize_t ret = -EINVAL;                                                                          \
-  android_log_logger* logger_internal = reinterpret_cast<android_log_logger*>(logger);            \
-                                                                                                  \
-  if (!logger_internal) {                                                                         \
-    return ret;                                                                                   \
-  }                                                                                               \
-  ret = init_transport_context(logger_internal->parent);                                          \
-  if (ret < 0) {                                                                                  \
-    return ret;                                                                                   \
-  }                                                                                               \
-                                                                                                  \
-  ret = (def);                                                                                    \
-  android_log_transport_context* transport_context = &logger_internal->parent->transport_context; \
-  if (transport_context->logMask & (1 << logger_internal->logId) &&                               \
-      transport_context->transport && transport_context->transport->func) {                       \
-    ssize_t retval =                                                                              \
-        (transport_context->transport->func)(logger_internal, transport_context, ##args);         \
-    if (ret >= 0 || ret == (def)) {                                                               \
-      ret = retval;                                                                               \
-    }                                                                                             \
-  }                                                                                               \
+#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) {
@@ -148,24 +144,20 @@
   LOGGER_FUNCTION(logger, 4, version);
 }
 
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                                  \
-  android_log_logger_list* logger_list_internal =                                              \
-      reinterpret_cast<android_log_logger_list*>(logger_list);                                 \
-                                                                                               \
-  ssize_t ret = init_transport_context(logger_list_internal);                                  \
-  if (ret < 0) {                                                                               \
-    return ret;                                                                                \
-  }                                                                                            \
-                                                                                               \
-  ret = (def);                                                                                 \
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context; \
-  if (transport_context->transport && transport_context->transport->func) {                    \
-    ssize_t retval =                                                                           \
-        (transport_context->transport->func)(logger_list_internal, transport_context, ##args); \
-    if (ret >= 0 || ret == (def)) {                                                            \
-      ret = retval;                                                                            \
-    }                                                                                          \
-  }                                                                                            \
+#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
 
 /*
@@ -184,9 +176,9 @@
 }
 
 struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
-  struct android_log_logger_list* logger_list;
+  struct logger_list* logger_list;
 
-  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
+  logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(*logger_list)));
   if (!logger_list) {
     return NULL;
   }
@@ -196,13 +188,13 @@
   logger_list->tail = tail;
   logger_list->pid = pid;
 
-  return (struct logger_list*)logger_list;
+  return logger_list;
 }
 
 struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
-  struct android_log_logger_list* logger_list;
+  struct logger_list* logger_list;
 
-  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
+  logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(*logger_list)));
   if (!logger_list) {
     return NULL;
   }
@@ -212,7 +204,7 @@
   logger_list->start = start;
   logger_list->pid = pid;
 
-  return (struct logger_list*)logger_list;
+  return logger_list;
 }
 
 /* android_logger_list_register unimplemented, no use case */
@@ -220,33 +212,31 @@
 
 /* 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 android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-  struct android_log_logger* logger;
+  struct logger* logger;
 
-  if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
+  if (!logger_list || (logId >= LOG_ID_MAX)) {
     return nullptr;
   }
 
-  logger_for_each(logger, logger_list_internal) {
+  logger_for_each(logger, logger_list) {
     if (logger->logId == logId) {
       return reinterpret_cast<struct logger*>(logger);
     }
   }
 
-  logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
+  logger = static_cast<struct logger*>(calloc(1, sizeof(*logger)));
   if (!logger) {
     return nullptr;
   }
 
   logger->logId = logId;
-  list_add_tail(&logger_list_internal->logger, &logger->node);
-  logger->parent = logger_list_internal;
+  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_internal->transport_initialized = false;
+  logger_list->transport_initialized = false;
 
-  return (struct logger*)logger;
+  return logger;
 }
 
 /* Open the single named log and make it part of a new logger list */
@@ -267,7 +257,7 @@
 }
 
 /* Validate log_msg packet, read function has already been null checked */
-static int android_transport_read(struct android_log_logger_list* logger_list,
+static int android_transport_read(struct logger_list* logger_list,
                                   struct android_log_transport_context* transp,
                                   struct log_msg* log_msg) {
   int ret = (*transp->transport->read)(logger_list, transp, log_msg);
@@ -297,38 +287,32 @@
 
 /* Read from the selected logs */
 int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-
-  int ret = init_transport_context(logger_list_internal);
+  int ret = init_transport_context(logger_list);
   if (ret < 0) {
     return ret;
   }
 
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
-  return android_transport_read(logger_list_internal, transport_context, log_msg);
+  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) {
-  struct android_log_logger_list* logger_list_internal =
-      (struct android_log_logger_list*)logger_list;
-
-  if (logger_list_internal == NULL) {
+  if (logger_list == NULL) {
     return;
   }
 
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
+  android_log_transport_context* transport_context = &logger_list->transport_context;
 
   if (transport_context->transport && transport_context->transport->close) {
-    (*transport_context->transport->close)(logger_list_internal, transport_context);
+    (*transport_context->transport->close)(logger_list, transport_context);
   }
 
-  while (!list_empty(&logger_list_internal->logger)) {
-    struct listnode* node = list_head(&logger_list_internal->logger);
-    struct android_log_logger* logger = node_to_item(node, struct android_log_logger, node);
+  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_internal);
+  free(logger_list);
 }
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index f43ce3a..cd83161 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -26,24 +26,22 @@
 
 #include "logger.h"
 
-static int pmsgAvailable(log_id_t logId);
-static int pmsgVersion(struct android_log_logger* logger,
-                       struct android_log_transport_context* transp);
-static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg);
-static void pmsgClose(struct android_log_logger_list* logger_list,
+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 android_log_logger* logger,
-                     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,
+    .available = PmsgAvailable,
+    .version = PmsgVersion,
+    .close = PmsgClose,
+    .read = PmsgRead,
     .poll = NULL,
-    .clear = pmsgClear,
+    .clear = PmsgClear,
     .setSize = NULL,
     .getSize = NULL,
     .getReadableSize = NULL,
@@ -52,7 +50,7 @@
     .getStats = NULL,
 };
 
-static int pmsgAvailable(log_id_t logId) {
+static int PmsgAvailable(log_id_t logId) {
   if (logId > LOG_ID_SECURITY) {
     return -EINVAL;
   }
@@ -62,21 +60,19 @@
   return -EBADF;
 }
 
-static int pmsgClear(struct android_log_logger* logger __unused,
-                     struct android_log_transport_context* transp __unused) {
+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 android_log_logger* logger __unused,
-                       struct android_log_transport_context* transp __unused) {
+static int PmsgVersion(struct logger*, struct android_log_transport_context*) {
   return 4;
 }
 
-static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
+static int PmsgRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
+                    struct log_msg* log_msg) {
   ssize_t ret;
   off_t current, next;
   struct __attribute__((__packed__)) {
@@ -192,8 +188,7 @@
   }
 }
 
-static void pmsgClose(struct android_log_logger_list* logger_list __unused,
-                      struct android_log_transport_context* transp) {
+static void PmsgClose(struct logger_list*, struct android_log_transport_context* transp) {
   int fd = atomic_exchange(&transp->context.fd, 0);
   if (fd > 0) {
     close(fd);
@@ -211,7 +206,7 @@
 ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
                                      __android_log_pmsg_file_read_fn fn, void* arg) {
   ssize_t ret;
-  struct android_log_logger_list logger_list;
+  struct logger_list logger_list;
   struct android_log_transport_context transp;
   struct content {
     struct listnode node;
@@ -268,7 +263,7 @@
 
   /* Read the file content */
   log_msg log_msg;
-  while (pmsgRead(&logger_list, &transp, &log_msg) > 0) {
+  while (PmsgRead(&logger_list, &transp, &log_msg) > 0) {
     const char* cp;
     size_t hdr_size = log_msg.entry.hdr_size;
 
@@ -426,7 +421,7 @@
     }
     list_add_head(node, &content->node);
   }
-  pmsgClose(&logger_list, &transp);
+  PmsgClose(&logger_list, &transp);
 
   /* Progress through all the collected files */
   list_for_each_safe(node, n, &name_list) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 674b737..6b61472 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -935,7 +935,6 @@
 
 on userspace-reboot-requested
   # TODO(b/135984674): reset all necessary properties here.
-  setprop sys.init.userspace_reboot_in_progress 1
   setprop sys.boot_completed 0
   setprop sys.init.updatable_crashing 0
   setprop apexd.status ""
@@ -955,3 +954,6 @@
   trigger zygote-start
   trigger early-boot
   trigger boot
+
+on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
+  finish_userspace_reboot