Merge "Remove service defined in an APEX during userspace reboot"
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index e7a3ff2..824ee43 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,6 +20,7 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 
+#include <chrono>
 #include <functional>
 #include <thread>
 
@@ -79,14 +80,24 @@
     return true;
 }
 
-bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name,
+                                        const std::chrono::milliseconds& timeout_ms) {
     if (GetState(name) == DmDeviceState::INVALID) {
         return true;
     }
-    return DeleteDevice(name);
+    return DeleteDevice(name, timeout_ms);
 }
 
-bool DeviceMapper::DeleteDevice(const std::string& name) {
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+    return DeleteDeviceIfExists(name, 0ms);
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name,
+                                const std::chrono::milliseconds& timeout_ms) {
+    std::string unique_path;
+    if (!GetDeviceUniquePath(name, &unique_path)) {
+        LOG(ERROR) << "Failed to get unique path for device " << name;
+    }
     struct dm_ioctl io;
     InitIo(&io, name);
 
@@ -100,9 +111,23 @@
     CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
             << "Didn't generate uevent for [" << name << "] removal";
 
+    if (timeout_ms <= std::chrono::milliseconds::zero()) {
+        return true;
+    }
+    if (unique_path.empty()) {
+        return false;
+    }
+    if (!WaitForFileDeleted(unique_path, timeout_ms)) {
+        LOG(ERROR) << "Timeout out waiting for " << unique_path << " to be deleted";
+        return false;
+    }
     return true;
 }
 
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+    return DeleteDevice(name, 0ms);
+}
+
 static std::string GenerateUuid() {
     uuid_t uuid_bytes;
     uuid_generate(uuid_bytes);
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index ed2fa83..1695953 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -520,3 +520,27 @@
     ASSERT_TRUE(target.Valid());
     ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
 }
+
+TEST(libdm, DeleteDeviceWithTimeout) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    std::string path;
+    ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
+    ASSERT_EQ(0, access(path.c_str(), F_OK));
+
+    ASSERT_TRUE(dm.DeleteDevice("libdm-test-dm-linear", 5s));
+    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
+    ASSERT_NE(0, access(path.c_str(), F_OK));
+    ASSERT_EQ(ENOENT, errno);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index e25ce7f..830c5e8 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -90,6 +90,10 @@
     // Returns 'true' on success, false otherwise.
     bool DeleteDevice(const std::string& name);
     bool DeleteDeviceIfExists(const std::string& name);
+    // Removes a device mapper device with the given name and waits for |timeout_ms| milliseconds
+    // for the corresponding block device to be deleted.
+    bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+    bool DeleteDeviceIfExists(const std::string& name, const std::chrono::milliseconds& timeout_ms);
 
     // Fetches and returns the complete state of the underlying device mapper
     // device with given name.
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
index eccf2fb..f252565 100644
--- a/fs_mgr/libdm/utility.cpp
+++ b/fs_mgr/libdm/utility.cpp
@@ -52,5 +52,15 @@
     return WaitForCondition(condition, timeout_ms);
 }
 
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
+    auto condition = [&]() -> WaitResult {
+        if (access(path.c_str(), F_OK) == 0 || errno != ENOENT) {
+            return WaitResult::Wait;
+        }
+        return WaitResult::Done;
+    };
+    return WaitForCondition(condition, timeout_ms);
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/utility.h b/fs_mgr/libdm/utility.h
index f1dce9e..58fa96b 100644
--- a/fs_mgr/libdm/utility.h
+++ b/fs_mgr/libdm/utility.h
@@ -23,6 +23,7 @@
 enum class WaitResult { Wait, Done, Fail };
 
 bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms);
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms);
 bool WaitForCondition(const std::function<WaitResult()>& condition,
                       const std::chrono::milliseconds& timeout_ms);
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 8e3875f..7450d19 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -277,6 +277,7 @@
     friend class SnapshotTest;
     friend class SnapshotUpdateTest;
     friend class FlashAfterUpdateTest;
+    friend class LockTestConsumer;
     friend struct AutoDeleteCowImage;
     friend struct AutoDeleteSnapshot;
     friend struct PartitionCowCreator;
@@ -304,9 +305,6 @@
         LockedFile(const std::string& path, android::base::unique_fd&& fd, int lock_mode)
             : path_(path), fd_(std::move(fd)), lock_mode_(lock_mode) {}
         ~LockedFile();
-
-        const std::string& path() const { return path_; }
-        int fd() const { return fd_; }
         int lock_mode() const { return lock_mode_; }
 
       private:
@@ -314,8 +312,7 @@
         android::base::unique_fd fd_;
         int lock_mode_;
     };
-    std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
-    bool Truncate(LockedFile* file);
+    static std::unique_ptr<LockedFile> OpenFile(const std::string& file, int lock_flags);
 
     // Create a new snapshot record. This creates the backing COW store and
     // persists information needed to map the device. The device can be mapped
@@ -381,10 +378,13 @@
     // set the update state to None.
     bool RemoveAllUpdateState(LockedFile* lock);
 
-    // Interact with /metadata/ota/state.
-    std::unique_ptr<LockedFile> OpenStateFile(int open_flags, int lock_flags);
+    // Interact with /metadata/ota.
+    std::unique_ptr<LockedFile> OpenLock(int lock_flags);
     std::unique_ptr<LockedFile> LockShared();
     std::unique_ptr<LockedFile> LockExclusive();
+    std::string GetLockPath() const;
+
+    // Interact with /metadata/ota/state.
     UpdateState ReadUpdateState(LockedFile* file);
     bool WriteUpdateState(LockedFile* file, UpdateState state);
     std::string GetStateFilePath() const;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 80d73c3..830495c 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -219,7 +219,7 @@
     // we can tell whether or not we performed a rollback.
     auto contents = device_->GetSlotSuffix();
     auto boot_file = GetSnapshotBootIndicatorPath();
-    if (!android::base::WriteStringToFile(contents, boot_file)) {
+    if (!WriteStringToFileAtomic(contents, boot_file)) {
         PLOG(ERROR) << "write failed: " << boot_file;
         return false;
     }
@@ -1220,12 +1220,12 @@
         return UpdateState::None;
     }
 
-    auto file = LockShared();
-    if (!file) {
+    auto lock = LockShared();
+    if (!lock) {
         return UpdateState::None;
     }
 
-    auto state = ReadUpdateState(file.get());
+    auto state = ReadUpdateState(lock.get());
     if (progress) {
         *progress = 0.0;
         if (state == UpdateState::Merging) {
@@ -1597,9 +1597,9 @@
     return true;
 }
 
-auto SnapshotManager::OpenFile(const std::string& file, int open_flags, int lock_flags)
+auto SnapshotManager::OpenFile(const std::string& file, int lock_flags)
         -> std::unique_ptr<LockedFile> {
-    unique_fd fd(open(file.c_str(), open_flags | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0660));
+    unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
     if (fd < 0) {
         PLOG(ERROR) << "Open failed: " << file;
         return nullptr;
@@ -1624,29 +1624,28 @@
     return metadata_dir_ + "/state"s;
 }
 
-std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::OpenStateFile(int open_flags,
-                                                                            int lock_flags) {
-    auto state_file = GetStateFilePath();
-    return OpenFile(state_file, open_flags, lock_flags);
+std::string SnapshotManager::GetLockPath() const {
+    return metadata_dir_;
+}
+
+std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::OpenLock(int lock_flags) {
+    auto lock_file = GetLockPath();
+    return OpenFile(lock_file, lock_flags);
 }
 
 std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockShared() {
-    return OpenStateFile(O_RDONLY, LOCK_SH);
+    return OpenLock(LOCK_SH);
 }
 
 std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockExclusive() {
-    return OpenStateFile(O_RDWR | O_CREAT, LOCK_EX);
+    return OpenLock(LOCK_EX);
 }
 
-UpdateState SnapshotManager::ReadUpdateState(LockedFile* file) {
-    // Reset position since some calls read+write.
-    if (lseek(file->fd(), 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek state file failed";
-        return UpdateState::None;
-    }
+UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
+    CHECK(lock);
 
     std::string contents;
-    if (!android::base::ReadFdToString(file->fd(), &contents)) {
+    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
         PLOG(ERROR) << "Read state file failed";
         return UpdateState::None;
     }
@@ -1693,14 +1692,15 @@
     }
 }
 
-bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
+bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
+    CHECK(lock);
+    CHECK(lock->lock_mode() == LOCK_EX);
+
     std::stringstream ss;
     ss << state;
     std::string contents = ss.str();
     if (contents.empty()) return false;
 
-    if (!Truncate(file)) return false;
-
 #ifdef LIBSNAPSHOT_USE_HAL
     auto merge_status = MergeStatus::UNKNOWN;
     switch (state) {
@@ -1733,7 +1733,7 @@
     }
 #endif
 
-    if (!android::base::WriteStringToFd(contents, file->fd())) {
+    if (!WriteStringToFileAtomic(contents, GetStateFilePath())) {
         PLOG(ERROR) << "Could not write to state file";
         return false;
     }
@@ -1782,14 +1782,14 @@
     CHECK(!status.name().empty());
 
     auto path = GetSnapshotStatusFilePath(status.name());
-    unique_fd fd(
-            open(path.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW | O_CREAT | O_SYNC | O_TRUNC, 0660));
-    if (fd < 0) {
-        PLOG(ERROR) << "Open failed: " << path;
+
+    std::string content;
+    if (!status.SerializeToString(&content)) {
+        LOG(ERROR) << "Unable to serialize SnapshotStatus for " << status.name();
         return false;
     }
 
-    if (!status.SerializeToFileDescriptor(fd.get())) {
+    if (!WriteStringToFileAtomic(content, path)) {
         PLOG(ERROR) << "Unable to write SnapshotStatus to " << path;
         return false;
     }
@@ -1797,18 +1797,6 @@
     return true;
 }
 
-bool SnapshotManager::Truncate(LockedFile* file) {
-    if (lseek(file->fd(), 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek file failed: " << file->path();
-        return false;
-    }
-    if (ftruncate(file->fd(), 0) < 0) {
-        PLOG(ERROR) << "truncate failed: " << file->path();
-        return false;
-    }
-    return true;
-}
-
 std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
                                                    const SnapshotStatus& status) {
     if (status.device_size() != status.snapshot_size()) {
@@ -2166,7 +2154,7 @@
 bool SnapshotManager::Dump(std::ostream& os) {
     // Don't actually lock. Dump() is for debugging purposes only, so it is okay
     // if it is racy.
-    auto file = OpenStateFile(O_RDONLY, 0);
+    auto file = OpenLock(0 /* lock flag */);
     if (!file) return false;
 
     std::stringstream ss;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 1d2a1f1..9e5fef3 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -20,6 +20,8 @@
 #include <sys/types.h>
 
 #include <chrono>
+#include <deque>
+#include <future>
 #include <iostream>
 
 #include <android-base/file.h>
@@ -142,7 +144,7 @@
     }
 
     bool AcquireLock() {
-        lock_ = sm->OpenStateFile(O_RDWR, LOCK_EX);
+        lock_ = sm->LockExclusive();
         return !!lock_;
     }
 
@@ -595,6 +597,154 @@
     ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
 }
 
+enum class Request { UNKNOWN, LOCK_SHARED, LOCK_EXCLUSIVE, UNLOCK, EXIT };
+std::ostream& operator<<(std::ostream& os, Request request) {
+    switch (request) {
+        case Request::LOCK_SHARED:
+            return os << "LOCK_SHARED";
+        case Request::LOCK_EXCLUSIVE:
+            return os << "LOCK_EXCLUSIVE";
+        case Request::UNLOCK:
+            return os << "UNLOCK";
+        case Request::EXIT:
+            return os << "EXIT";
+        case Request::UNKNOWN:
+            [[fallthrough]];
+        default:
+            return os << "UNKNOWN";
+    }
+}
+
+class LockTestConsumer {
+  public:
+    AssertionResult MakeRequest(Request new_request) {
+        {
+            std::unique_lock<std::mutex> ulock(mutex_);
+            requests_.push_back(new_request);
+        }
+        cv_.notify_all();
+        return AssertionSuccess() << "Request " << new_request << " successful";
+    }
+
+    template <typename R, typename P>
+    AssertionResult WaitFulfill(std::chrono::duration<R, P> timeout) {
+        std::unique_lock<std::mutex> ulock(mutex_);
+        if (cv_.wait_for(ulock, timeout, [this] { return requests_.empty(); })) {
+            return AssertionSuccess() << "All requests_ fulfilled.";
+        }
+        return AssertionFailure() << "Timeout waiting for fulfilling " << requests_.size()
+                                  << " request(s), first one is "
+                                  << (requests_.empty() ? Request::UNKNOWN : requests_.front());
+    }
+
+    void StartHandleRequestsInBackground() {
+        future_ = std::async(std::launch::async, &LockTestConsumer::HandleRequests, this);
+    }
+
+  private:
+    void HandleRequests() {
+        static constexpr auto consumer_timeout = 3s;
+
+        auto next_request = Request::UNKNOWN;
+        do {
+            // Peek next request.
+            {
+                std::unique_lock<std::mutex> ulock(mutex_);
+                if (cv_.wait_for(ulock, consumer_timeout, [this] { return !requests_.empty(); })) {
+                    next_request = requests_.front();
+                } else {
+                    next_request = Request::EXIT;
+                }
+            }
+
+            // Handle next request.
+            switch (next_request) {
+                case Request::LOCK_SHARED: {
+                    lock_ = sm->LockShared();
+                } break;
+                case Request::LOCK_EXCLUSIVE: {
+                    lock_ = sm->LockExclusive();
+                } break;
+                case Request::EXIT:
+                    [[fallthrough]];
+                case Request::UNLOCK: {
+                    lock_.reset();
+                } break;
+                case Request::UNKNOWN:
+                    [[fallthrough]];
+                default:
+                    break;
+            }
+
+            // Pop next request. This thread is the only thread that
+            // pops from the front of the requests_ deque.
+            {
+                std::unique_lock<std::mutex> ulock(mutex_);
+                if (next_request == Request::EXIT) {
+                    requests_.clear();
+                } else {
+                    requests_.pop_front();
+                }
+            }
+            cv_.notify_all();
+        } while (next_request != Request::EXIT);
+    }
+
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::deque<Request> requests_;
+    std::unique_ptr<SnapshotManager::LockedFile> lock_;
+    std::future<void> future_;
+};
+
+class LockTest : public ::testing::Test {
+  public:
+    void SetUp() {
+        first_consumer.StartHandleRequestsInBackground();
+        second_consumer.StartHandleRequestsInBackground();
+    }
+
+    void TearDown() {
+        EXPECT_TRUE(first_consumer.MakeRequest(Request::EXIT));
+        EXPECT_TRUE(second_consumer.MakeRequest(Request::EXIT));
+    }
+
+    static constexpr auto request_timeout = 500ms;
+    LockTestConsumer first_consumer;
+    LockTestConsumer second_consumer;
+};
+
+TEST_F(LockTest, SharedShared) {
+    ASSERT_TRUE(first_consumer.MakeRequest(Request::LOCK_SHARED));
+    ASSERT_TRUE(first_consumer.WaitFulfill(request_timeout));
+    ASSERT_TRUE(second_consumer.MakeRequest(Request::LOCK_SHARED));
+    ASSERT_TRUE(second_consumer.WaitFulfill(request_timeout));
+}
+
+using LockTestParam = std::pair<Request, Request>;
+class LockTestP : public LockTest, public ::testing::WithParamInterface<LockTestParam> {};
+TEST_P(LockTestP, Test) {
+    ASSERT_TRUE(first_consumer.MakeRequest(GetParam().first));
+    ASSERT_TRUE(first_consumer.WaitFulfill(request_timeout));
+    ASSERT_TRUE(second_consumer.MakeRequest(GetParam().second));
+    ASSERT_FALSE(second_consumer.WaitFulfill(request_timeout))
+            << "Should not be able to " << GetParam().second << " while separate thread "
+            << GetParam().first;
+    ASSERT_TRUE(first_consumer.MakeRequest(Request::UNLOCK));
+    ASSERT_TRUE(second_consumer.WaitFulfill(request_timeout))
+            << "Should be able to hold lock that is released by separate thread";
+}
+INSTANTIATE_TEST_SUITE_P(
+        LockTest, LockTestP,
+        testing::Values(LockTestParam{Request::LOCK_EXCLUSIVE, Request::LOCK_EXCLUSIVE},
+                        LockTestParam{Request::LOCK_EXCLUSIVE, Request::LOCK_SHARED},
+                        LockTestParam{Request::LOCK_SHARED, Request::LOCK_EXCLUSIVE}),
+        [](const testing::TestParamInfo<LockTestP::ParamType>& info) {
+            std::stringstream ss;
+            ss << info.param.first << "_" << info.param.second;
+            return ss.str();
+        });
+
 class SnapshotUpdateTest : public SnapshotTest {
   public:
     void SetUp() override {
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 1b2f528..fa1d7f0 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -140,5 +140,17 @@
     }
 }
 
+bool WriteStringToFileAtomic(const std::string& content, const std::string& path) {
+    std::string tmp_path = path + ".tmp";
+    if (!android::base::WriteStringToFile(content, tmp_path)) {
+        return false;
+    }
+    if (rename(tmp_path.c_str(), path.c_str()) == -1) {
+        PLOG(ERROR) << "rename failed from " << tmp_path << " to " << path;
+        return false;
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 0c08ed2..5cc572e 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -112,5 +112,12 @@
 // Initialize a device before using it as the COW device for a dm-snapshot device.
 bool InitializeCow(const std::string& device);
 
+// "Atomically" write string to file. This is done by a series of actions:
+// 1. Write to path + ".tmp"
+// 2. Move temporary file to path using rename()
+// Note that rename() is an atomic operation. This function may not work properly if there
+// is an open fd to |path|, because that fd has an old view of the file.
+bool WriteStringToFileAtomic(const std::string& content, const std::string& path);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/init/README.md b/init/README.md
index e8df4ec..b8300fa 100644
--- a/init/README.md
+++ b/init/README.md
@@ -583,6 +583,7 @@
   Note that this is _not_ synchronous, and even if it were, there is
   no guarantee that the operating system's scheduler will execute the
   service sufficiently to guarantee anything about the service's status.
+  See the `exec_start` command for a synchronous version of `start`.
 
 > This creates an important consequence that if the service offers
   functionality to other services, such as providing a
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1a98f4e..6bf3997 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -84,6 +84,7 @@
 using namespace std::literals::string_literals;
 
 using android::base::Basename;
+using android::base::SetProperty;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -561,16 +562,16 @@
             LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
             trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
         }
-        property_set("ro.crypto.state", "encrypted");
-        property_set("ro.crypto.type", "block");
+        SetProperty("ro.crypto.state", "encrypted");
+        SetProperty("ro.crypto.type", "block");
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
         return {};
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
-        property_set("ro.crypto.state", "unencrypted");
+        SetProperty("ro.crypto.state", "unencrypted");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return {};
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
-        property_set("ro.crypto.state", "unsupported");
+        SetProperty("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return {};
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
@@ -586,8 +587,8 @@
         if (!FscryptInstallKeyring()) {
             return Error() << "FscryptInstallKeyring() failed";
         }
-        property_set("ro.crypto.state", "encrypted");
-        property_set("ro.crypto.type", "file");
+        SetProperty("ro.crypto.state", "encrypted");
+        SetProperty("ro.crypto.type", "file");
 
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
@@ -597,8 +598,8 @@
         if (!FscryptInstallKeyring()) {
             return Error() << "FscryptInstallKeyring() failed";
         }
-        property_set("ro.crypto.state", "encrypted");
-        property_set("ro.crypto.type", "file");
+        SetProperty("ro.crypto.state", "encrypted");
+        SetProperty("ro.crypto.type", "file");
 
         // Although encrypted, vold has already set the device up, so we do not need to
         // do anything different from the nonencrypted case.
@@ -608,8 +609,8 @@
         if (!FscryptInstallKeyring()) {
             return Error() << "FscryptInstallKeyring() failed";
         }
-        property_set("ro.crypto.state", "encrypted");
-        property_set("ro.crypto.type", "file");
+        SetProperty("ro.crypto.state", "encrypted");
+        SetProperty("ro.crypto.type", "file");
 
         // Although encrypted, vold has already set the device up, so we do not need to
         // do anything different from the nonencrypted case.
@@ -662,7 +663,7 @@
     }
 
     auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
-    property_set(prop_name, std::to_string(t.duration().count()));
+    SetProperty(prop_name, std::to_string(t.duration().count()));
 
     if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
@@ -718,7 +719,7 @@
                        << "' from init; use the restorecon builtin directly";
     }
 
-    property_set(args[1], args[2]);
+    SetProperty(args[1], args[2]);
     return {};
 }
 
@@ -832,7 +833,7 @@
         // To be consistent in vboot 1.0 and vboot 2.0 (AVB), use "system" for the partition even
         // for system as root, so it has property [partition.system.verified].
         std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
-        property_set("partition." + partition + ".verified", std::to_string(mode));
+        SetProperty("partition." + partition + ".verified", std::to_string(mode));
     }
 
     return {};
@@ -1221,8 +1222,8 @@
 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()));
+    SetProperty("sys.init.userspace_reboot.last_finished",
+                std::to_string(now.time_since_epoch().count()));
     if (!android::sysprop::InitProperties::userspace_reboot_in_progress(false)) {
         return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
     }
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 30d3129..caa8f8d 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -39,15 +39,6 @@
 inline bool CanReadProperty(const std::string&, const std::string&) {
     return true;
 }
-inline uint32_t SetProperty(const std::string& key, const std::string& value) {
-    android::base::SetProperty(key, value);
-    return 0;
-}
-inline uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
-inline uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&,
-                                  const ucred&, std::string*) {
-    return 0;
-}
 
 // reboot_utils.h
 inline void SetFatalRebootTarget() {}
diff --git a/init/init.cpp b/init/init.cpp
index 736beb1..6ba64ee 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -81,6 +81,7 @@
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::ReadFileToString;
+using android::base::SetProperty;
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::Trim;
@@ -91,8 +92,6 @@
 
 static int property_triggers_enabled = 0;
 
-static char qemu[32];
-
 static int signal_fd = -1;
 static int property_fd = -1;
 
@@ -101,7 +100,6 @@
 static std::string wait_prop_value;
 static std::string shutdown_command;
 static bool do_shutdown = false;
-static bool load_debug_prop = false;
 
 static std::unique_ptr<Subcontext> subcontext;
 
@@ -209,7 +207,7 @@
     // to wait.
     if (name == kColdBootDoneProp) {
         auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
-        property_set("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+        SetProperty("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
     }
 
     if (waiting_for_prop) {
@@ -365,85 +363,15 @@
     return {};
 }
 
-static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
-    if (key.empty()) return;
-
-    if (for_emulator) {
-        // In the emulator, export any kernel option with the "ro.kernel." prefix.
-        property_set("ro.kernel." + key, value);
-        return;
-    }
-
-    if (key == "qemu") {
-        strlcpy(qemu, value.c_str(), sizeof(qemu));
-    } else if (android::base::StartsWith(key, "androidboot.")) {
-        property_set("ro.boot." + key.substr(12), value);
-    }
-}
-
 static void export_oem_lock_status() {
     if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
         return;
     }
-    import_kernel_cmdline(
-            false, [](const std::string& key, const std::string& value, bool in_qemu) {
-                if (key == "androidboot.verifiedbootstate") {
-                    property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
-                }
-            });
-}
-
-static void export_kernel_boot_props() {
-    constexpr const char* UNSET = "";
-    struct {
-        const char *src_prop;
-        const char *dst_prop;
-        const char *default_value;
-    } prop_map[] = {
-        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
-        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
-        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
-        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
-        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
-        { "ro.boot.revision",   "ro.revision",   "0", },
-    };
-    for (const auto& prop : prop_map) {
-        std::string value = GetProperty(prop.src_prop, prop.default_value);
-        if (value != UNSET)
-            property_set(prop.dst_prop, value);
-    }
-}
-
-static void process_kernel_dt() {
-    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
-        return;
-    }
-
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
-    if (!dir) return;
-
-    std::string dt_file;
-    struct dirent *dp;
-    while ((dp = readdir(dir.get())) != NULL) {
-        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
-            continue;
+    ImportKernelCmdline([](const std::string& key, const std::string& value) {
+        if (key == "androidboot.verifiedbootstate") {
+            SetProperty("ro.boot.flash.locked", value == "orange" ? "0" : "1");
         }
-
-        std::string file_name = get_android_dt_dir() + dp->d_name;
-
-        android::base::ReadFileToString(file_name, &dt_file);
-        std::replace(dt_file.begin(), dt_file.end(), ',', '.');
-
-        property_set("ro.boot."s + dp->d_name, dt_file);
-    }
-}
-
-static void process_kernel_cmdline() {
-    // The first pass does the common stuff, and finds if we are in qemu.
-    // The second pass is only necessary for qemu to export all kernel params
-    // as properties.
-    import_kernel_cmdline(false, import_kernel_nv);
-    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
+    });
 }
 
 static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
@@ -469,7 +397,7 @@
     while ((dp = readdir(dir.get())) != nullptr) {
         if (dp->d_name[0] == '.') continue;
 
-        property_set("sys.usb.controller", dp->d_name);
+        SetProperty("sys.usb.controller", dp->d_name);
         break;
     }
 }
@@ -592,7 +520,7 @@
     int64_t first_stage_start_time_ns = -1;
     if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);
         first_stage_start_time_str) {
-        property_set("ro.boottime.init", first_stage_start_time_str);
+        SetProperty("ro.boottime.init", first_stage_start_time_str);
         android::base::ParseInt(first_stage_start_time_str, &first_stage_start_time_ns);
     }
     unsetenv(kEnvFirstStageStartedAt);
@@ -606,11 +534,11 @@
     if (selinux_start_time_ns == -1) return;
     if (first_stage_start_time_ns == -1) return;
 
-    property_set("ro.boottime.init.first_stage",
-                 std::to_string(selinux_start_time_ns - first_stage_start_time_ns));
-    property_set("ro.boottime.init.selinux",
-                 std::to_string(second_stage_start_time.time_since_epoch().count() -
-                                selinux_start_time_ns));
+    SetProperty("ro.boottime.init.first_stage",
+                std::to_string(selinux_start_time_ns - first_stage_start_time_ns));
+    SetProperty("ro.boottime.init.selinux",
+                std::to_string(second_stage_start_time.time_since_epoch().count() -
+                               selinux_start_time_ns));
 }
 
 void SendLoadPersistentPropertiesMessage() {
@@ -707,34 +635,27 @@
     // Indicate that booting is in progress to background fw loaders, etc.
     close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
 
-    property_init();
-
-    // If arguments are passed both on the command line and in DT,
-    // properties set in DT always have priority over the command-line ones.
-    process_kernel_dt();
-    process_kernel_cmdline();
-
-    // Propagate the kernel variables to internal variables
-    // used by init as well as the current required properties.
-    export_kernel_boot_props();
-
-    // Make the time that init stages started available for bootstat to log.
-    RecordStageBoottimes(start_time);
-
-    // Set libavb version for Framework-only OTA match in Treble build.
-    const char* avb_version = getenv("INIT_AVB_VERSION");
-    if (avb_version) property_set("ro.boot.avb_version", avb_version);
-
     // See if need to load debug props to allow adb root, when the device is unlocked.
     const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+    bool load_debug_prop = false;
     if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
         load_debug_prop = "true"s == force_debuggable_env;
     }
-
-    // Clean up our environment.
-    unsetenv("INIT_AVB_VERSION");
     unsetenv("INIT_FORCE_DEBUGGABLE");
 
+    // Umount the debug ramdisk so property service doesn't read .prop files from there, when it
+    // is not meant to.
+    if (!load_debug_prop) {
+        UmountDebugRamdisk();
+    }
+
+    PropertyInit();
+
+    // Umount the debug ramdisk after property service has read the .prop files when it means to.
+    if (load_debug_prop) {
+        UmountDebugRamdisk();
+    }
+
     // Now set up SELinux for second stage.
     SelinuxSetupKernelLogging();
     SelabelInitialize();
@@ -747,16 +668,22 @@
 
     InstallSignalFdHandler(&epoll);
 
-    property_load_boot_defaults(load_debug_prop);
-    UmountDebugRamdisk();
-    fs_mgr_vendor_overlay_mount_all();
-    export_oem_lock_status();
-
     StartPropertyService(&property_fd);
     if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result) {
         LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
     }
 
+    // Make the time that init stages started available for bootstat to log.
+    RecordStageBoottimes(start_time);
+
+    // Set libavb version for Framework-only OTA match in Treble build.
+    if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
+        SetProperty("ro.boot.avb_version", avb_version);
+    }
+    unsetenv("INIT_AVB_VERSION");
+
+    fs_mgr_vendor_overlay_mount_all();
+    export_oem_lock_status();
     MountHandler mount_handler(&epoll);
     set_usb_controller();
 
@@ -780,9 +707,9 @@
 
     // Make the GSI status available before scripts start running.
     if (android::gsi::IsGsiRunning()) {
-        property_set("ro.gsid.image_running", "1");
+        SetProperty("ro.gsid.image_running", "1");
     } else {
-        property_set("ro.gsid.image_running", "0");
+        SetProperty("ro.gsid.image_running", "0");
     }
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 791a019..0e4e024 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -38,7 +38,6 @@
 #include <libdm/dm.h>
 
 #include "epoll.h"
-#include "property_service.h"
 
 namespace android {
 namespace init {
@@ -96,7 +95,7 @@
     // handling, except for clearing non-existent or already clear property.
     // Goal is reduction of empty properties and associated triggers.
     if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
-    property_set(mount_prop, value);
+    android::base::SetProperty(mount_prop, value);
 }
 
 }  // namespace
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 7d707cc..adf8929 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -97,8 +97,6 @@
 
 static PropertyInfoAreaFile property_info_area;
 
-void CreateSerializedPropertyInfo();
-
 struct PropertyAuditData {
     const ucred* cr;
     const char* name;
@@ -117,21 +115,6 @@
     return 0;
 }
 
-void property_init() {
-    selinux_callback cb;
-    cb.func_audit = PropertyAuditCallback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
-    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
-    CreateSerializedPropertyInfo();
-    if (__system_property_area_init()) {
-        LOG(FATAL) << "Failed to initialize property area";
-    }
-    if (!property_info_area.LoadDefaultPath()) {
-        LOG(FATAL) << "Failed to load serialized property info file";
-    }
-}
-
 bool CanReadProperty(const std::string& source_context, const std::string& name) {
     const char* target_context = nullptr;
     property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
@@ -527,20 +510,6 @@
     return PropertySet(name, value, error);
 }
 
-uint32_t InitPropertySet(const std::string& name, const std::string& value) {
-    uint32_t result = 0;
-    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
-    std::string error;
-    result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
-    if (result != PROP_SUCCESS) {
-        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
-    }
-
-    return result;
-}
-
-uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
-
 static void handle_property_set_fd() {
     static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
 
@@ -634,6 +603,18 @@
     }
 }
 
+uint32_t InitPropertySet(const std::string& name, const std::string& value) {
+    uint32_t result = 0;
+    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+    std::string error;
+    result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
+    if (result != PROP_SUCCESS) {
+        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
+    }
+
+    return result;
+}
+
 static bool load_properties_from_file(const char*, const char*,
                                       std::map<std::string, std::string>*);
 
@@ -765,11 +746,11 @@
     bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
     std::string config = android::base::GetProperty("persist.sys.usb.config", "");
     if (config.empty()) {
-        property_set("persist.sys.usb.config", is_debuggable ? "adb" : "none");
+        InitPropertySet("persist.sys.usb.config", is_debuggable ? "adb" : "none");
     } else if (is_debuggable && config.find("adb") == std::string::npos &&
                config.length() + 4 < PROP_VALUE_MAX) {
         config.append(",adb");
-        property_set("persist.sys.usb.config", config);
+        InitPropertySet("persist.sys.usb.config", config);
     }
 }
 
@@ -890,7 +871,7 @@
     }
 }
 
-void property_load_boot_defaults(bool load_debug_prop) {
+void PropertyLoadBootDefaults() {
     // TODO(b/117892318): merge prop.default and build.prop files into one
     // We read the properties and their values into a map, in order to always allow properties
     // loaded in the later property files to override the properties in loaded in the earlier
@@ -916,7 +897,7 @@
     load_properties_from_file("/product/build.prop", nullptr, &properties);
     load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
 
-    if (load_debug_prop) {
+    if (access(kDebugRamdiskProp, R_OK) == 0) {
         LOG(INFO) << "Loading " << kDebugRamdiskProp;
         load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
     }
@@ -1009,6 +990,97 @@
     selinux_android_restorecon(kPropertyInfosPath, 0);
 }
 
+static void ExportKernelBootProps() {
+    constexpr const char* UNSET = "";
+    struct {
+        const char* src_prop;
+        const char* dst_prop;
+        const char* default_value;
+    } prop_map[] = {
+            // clang-format off
+        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
+        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
+        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
+        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
+        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
+        { "ro.boot.revision",   "ro.revision",   "0", },
+            // clang-format on
+    };
+    for (const auto& prop : prop_map) {
+        std::string value = GetProperty(prop.src_prop, prop.default_value);
+        if (value != UNSET) InitPropertySet(prop.dst_prop, value);
+    }
+}
+
+static void ProcessKernelDt() {
+    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
+        return;
+    }
+
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
+    if (!dir) return;
+
+    std::string dt_file;
+    struct dirent* dp;
+    while ((dp = readdir(dir.get())) != NULL) {
+        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") ||
+            !strcmp(dp->d_name, "name")) {
+            continue;
+        }
+
+        std::string file_name = get_android_dt_dir() + dp->d_name;
+
+        android::base::ReadFileToString(file_name, &dt_file);
+        std::replace(dt_file.begin(), dt_file.end(), ',', '.');
+
+        InitPropertySet("ro.boot."s + dp->d_name, dt_file);
+    }
+}
+
+static void ProcessKernelCmdline() {
+    bool for_emulator = false;
+    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+        if (key == "qemu") {
+            for_emulator = true;
+        } else if (StartsWith(key, "androidboot.")) {
+            InitPropertySet("ro.boot." + key.substr(12), value);
+        }
+    });
+
+    if (for_emulator) {
+        ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+            // In the emulator, export any kernel option with the "ro.kernel." prefix.
+            InitPropertySet("ro.kernel." + key, value);
+        });
+    }
+}
+
+void PropertyInit() {
+    selinux_callback cb;
+    cb.func_audit = PropertyAuditCallback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
+    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
+    CreateSerializedPropertyInfo();
+    if (__system_property_area_init()) {
+        LOG(FATAL) << "Failed to initialize property area";
+    }
+    if (!property_info_area.LoadDefaultPath()) {
+        LOG(FATAL) << "Failed to load serialized property info file";
+    }
+
+    // If arguments are passed both on the command line and in DT,
+    // properties set in DT always have priority over the command-line ones.
+    ProcessKernelDt();
+    ProcessKernelCmdline();
+
+    // Propagate the kernel variables to internal variables
+    // used by init as well as the current required properties.
+    ExportKernelBootProps();
+
+    PropertyLoadBootDefaults();
+}
+
 static void HandleInitSocket() {
     auto message = ReadMessage(init_socket);
     if (!message) {
@@ -1075,7 +1147,7 @@
 }
 
 void StartPropertyService(int* epoll_socket) {
-    property_set("ro.property_service.version", "2");
+    InitPropertySet("ro.property_service.version", "2");
 
     int sockets[2];
     if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
@@ -1095,11 +1167,6 @@
     listen(property_set_fd, 8);
 
     std::thread{PropertyServiceThread}.detach();
-
-    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
-        android::base::SetProperty(key, value);
-        return 0;
-    };
 }
 
 }  // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 8f7d8d9..506d116 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -29,10 +29,7 @@
 
 bool CanReadProperty(const std::string& source_context, const std::string& name);
 
-extern uint32_t (*property_set)(const std::string& name, const std::string& value);
-
-void property_init();
-void property_load_boot_defaults(bool load_debug_prop);
+void PropertyInit();
 void StartPropertyService(int* epoll_socket);
 
 }  // namespace init
diff --git a/init/reboot.cpp b/init/reboot.cpp
index b987f7d..0e61234 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -59,7 +59,6 @@
 #include "builtin_arguments.h"
 #include "init.h"
 #include "mount_namespace.h"
-#include "property_service.h"
 #include "reboot_utils.h"
 #include "service.h"
 #include "service_list.h"
@@ -548,7 +547,7 @@
          reasons[1] == "hard" || reasons[1] == "warm")) {
         skip = strlen("reboot,");
     }
-    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+    SetProperty(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
     sync();
 
     bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
@@ -619,7 +618,7 @@
         bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
 
         if (do_shutdown_animation) {
-            property_set("service.bootanim.exit", "0");
+            SetProperty("service.bootanim.exit", "0");
             // Could be in the middle of animation. Stop and start so that it can pick
             // up the right mode.
             boot_anim->Stop();
@@ -733,8 +732,8 @@
 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()));
+    SetProperty("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();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index a9cd290..2fc8f6d 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -88,12 +88,11 @@
 EnforcingStatus StatusFromCmdline() {
     EnforcingStatus status = SELINUX_ENFORCING;
 
-    import_kernel_cmdline(false,
-                          [&](const std::string& key, const std::string& value, bool in_qemu) {
-                              if (key == "androidboot.selinux" && value == "permissive") {
-                                  status = SELINUX_PERMISSIVE;
-                              }
-                          });
+    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+        if (key == "androidboot.selinux" && value == "permissive") {
+            status = SELINUX_PERMISSIVE;
+        }
+    });
 
     return status;
 }
diff --git a/init/service.cpp b/init/service.cpp
index 05daec0..be46585 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -45,7 +45,6 @@
 #include <android/api-level.h>
 
 #include "mount_namespace.h"
-#include "property_service.h"
 #include "selinux.h"
 #else
 #include "host_init_stubs.h"
@@ -55,6 +54,7 @@
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::make_scope_guard;
+using android::base::SetProperty;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -165,13 +165,13 @@
     }
 
     std::string prop_name = "init.svc." + name_;
-    property_set(prop_name, new_state);
+    SetProperty(prop_name, new_state);
 
     if (new_state == "running") {
         uint64_t start_ns = time_started_.time_since_epoch().count();
         std::string boottime_property = "ro.boottime." + name_;
         if (GetProperty(boottime_property, "").empty()) {
-            property_set(boottime_property, std::to_string(start_ns));
+            SetProperty(boottime_property, std::to_string(start_ns));
         }
     }
 
@@ -179,9 +179,9 @@
     // on device for security checks.
     std::string pid_property = "init.svc_debug_pid." + name_;
     if (new_state == "running") {
-        property_set(pid_property, std::to_string(pid_));
+        SetProperty(pid_property, std::to_string(pid_));
     } else if (new_state == "stopped") {
-        property_set(pid_property, "");
+        SetProperty(pid_property, "");
     }
 }
 
@@ -325,7 +325,7 @@
                     LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
                                << (boot_completed ? "in 4 minutes" : "before boot completed");
                     // Notifies update_verifier and apexd
-                    property_set("sys.init.updatable_crashing", "1");
+                    SetProperty("sys.init.updatable_crashing", "1");
                 }
             }
         } else {
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index bebcc77..f3f759d 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -176,11 +176,6 @@
 
     SelabelInitialize();
 
-    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
-        android::base::SetProperty(key, value);
-        return 0;
-    };
-
     trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
 
     auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
diff --git a/init/util.cpp b/init/util.cpp
index e5254dd..0ca0da5 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -234,15 +234,14 @@
     return -1;
 }
 
-void import_kernel_cmdline(bool in_qemu,
-                           const std::function<void(const std::string&, const std::string&, bool)>& fn) {
+void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>& fn) {
     std::string cmdline;
     android::base::ReadFileToString("/proc/cmdline", &cmdline);
 
     for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
         std::vector<std::string> pieces = android::base::Split(entry, "=");
         if (pieces.size() == 2) {
-            fn(pieces[0], pieces[1], in_qemu);
+            fn(pieces[0], pieces[1]);
         }
     }
 }
@@ -359,12 +358,11 @@
     // Use the standard procfs-based path by default
     std::string android_dt_dir = kDefaultAndroidDtDir;
     // The platform may specify a custom Android DT path in kernel cmdline
-    import_kernel_cmdline(false,
-                          [&](const std::string& key, const std::string& value, bool in_qemu) {
-                              if (key == "androidboot.android_dt_dir") {
-                                  android_dt_dir = value;
-                              }
-                          });
+    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+        if (key == "androidboot.android_dt_dir") {
+            android_dt_dir = value;
+        }
+    });
     LOG(INFO) << "Using Android DT directory " << android_dt_dir;
     return android_dt_dir;
 }
diff --git a/init/util.h b/init/util.h
index 9d89ed7..ad322d9 100644
--- a/init/util.h
+++ b/init/util.h
@@ -47,8 +47,7 @@
 
 bool mkdir_recursive(const std::string& pathname, mode_t mode);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
-void import_kernel_cmdline(bool in_qemu,
-                           const std::function<void(const std::string&, const std::string&, bool)>&);
+void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
 Result<std::string> ExpandProps(const std::string& src);
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index ee3b250..6601072 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -114,7 +114,7 @@
   }
   char* msg() {
     unsigned short hdr_size = entry.hdr_size;
-    if (hdr_size != sizeof(entry)) {
+    if (hdr_size >= sizeof(struct log_msg) - sizeof(entry)) {
       return nullptr;
     }
     return reinterpret_cast<char*>(buf) + hdr_size;
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index c65501c..0d383ff 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -120,7 +120,8 @@
     return -EINVAL;
   }
 
-  if (log_msg->entry.hdr_size != sizeof(log_msg->entry)) {
+  if (log_msg->entry.hdr_size < sizeof(log_msg->entry) ||
+      log_msg->entry.hdr_size >= sizeof(struct log_msg) - sizeof(log_msg->entry)) {
     return -EINVAL;
   }
 
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 512c962..9cd3d65 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -251,6 +251,7 @@
         "tests/files/offline/shared_lib_in_apk_arm64/*",
         "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
         "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
+        "tests/files/offline/signal_load_bias_arm/*",
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 6627787..f01b092 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -173,7 +173,12 @@
   if (!valid_) {
     return false;
   }
-  return regs->StepIfSignalHandler(rel_pc, this, process_memory);
+
+  // Convert the rel_pc to an elf_offset.
+  if (rel_pc < static_cast<uint64_t>(load_bias_)) {
+    return false;
+  }
+  return regs->StepIfSignalHandler(rel_pc - load_bias_, this, process_memory);
 }
 
 // The relative pc is always relative to the start of the map from which it comes.
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 885dc94..1b1f7eb 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -127,12 +127,12 @@
   return regs;
 }
 
-bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+bool RegsArm::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
   uint32_t data;
   Memory* elf_memory = elf->memory();
   // Read from elf memory since it is usually more expensive to read from
   // process memory.
-  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+  if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
     return false;
   }
 
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 2e8af20..1df1dff 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -126,12 +126,12 @@
   return regs;
 }
 
-bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+bool RegsArm64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
   uint64_t data;
   Memory* elf_memory = elf->memory();
   // Read from elf memory since it is usually more expensive to read from
   // process memory.
-  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+  if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
     return false;
   }
 
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index f330fe0..ebefe42 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -129,13 +129,13 @@
   return regs;
 }
 
-bool RegsMips::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+bool RegsMips::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
   uint64_t data;
   uint64_t offset = 0;
   Memory* elf_memory = elf->memory();
   // Read from elf memory since it is usually more expensive to read from
   // process memory.
-  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+  if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
     return false;
   }
 
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 3f67d92..be2fd22 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -127,12 +127,12 @@
   return regs;
 }
 
-bool RegsMips64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+bool RegsMips64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
   uint64_t data;
   Memory* elf_memory = elf->memory();
   // Read from elf memory since it is usually more expensive to read from
   // process memory.
-  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+  if (!elf_memory->Read(elf_offset, &data, sizeof(data))) {
     return false;
   }
 
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index 9bb39d1..5538fc0 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -119,12 +119,12 @@
   return regs;
 }
 
-bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+bool RegsX86::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
   uint64_t data;
   Memory* elf_memory = elf->memory();
   // Read from elf memory since it is usually more expensive to read from
   // process memory.
-  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+  if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
     return false;
   }
 
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index 74cd1cb..5b9aa58 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -139,17 +139,17 @@
   return regs;
 }
 
-bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+bool RegsX86_64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
   uint64_t data;
   Memory* elf_memory = elf->memory();
   // Read from elf memory since it is usually more expensive to read from
   // process memory.
-  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
+  if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
     return false;
   }
 
   uint16_t data2;
-  if (!elf_memory->ReadFully(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+  if (!elf_memory->ReadFully(elf_offset + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
     return false;
   }
 
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 1c2a81c..4f761b4 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -66,7 +66,7 @@
 
   virtual uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) = 0;
 
-  virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
+  virtual bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) = 0;
 
   virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
 
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index 44f6744..aa029be 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -40,7 +40,7 @@
 
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+  bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
 
   void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index a72f91f..5cd7e5b 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -40,7 +40,7 @@
 
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+  bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
 
   void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index c9dd202..8164a15 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -40,7 +40,7 @@
 
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+  bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
 
   void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 7c42812..c982542 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -40,7 +40,7 @@
 
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+  bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
 
   void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
 
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index d19e449..2323a4f 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -41,7 +41,7 @@
 
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+  bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
 
   void SetFromUcontext(x86_ucontext_t* ucontext);
 
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index dc9a220..3e919a4 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -41,7 +41,7 @@
 
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+  bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
 
   void SetFromUcontext(x86_64_ucontext_t* ucontext);
 
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index d227b60..4866345 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -104,6 +104,8 @@
     memory_->SetMemory(0x100, &phdr, sizeof(phdr));
   }
 
+  void VerifyStepIfSignalHandler(uint64_t load_bias);
+
   MemoryFake* memory_;
 };
 
@@ -281,7 +283,7 @@
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 }
 
-TEST_F(ElfTest, step_in_signal_map) {
+void ElfTest::VerifyStepIfSignalHandler(uint64_t load_bias) {
   ElfFake elf(memory_);
 
   RegsArm regs;
@@ -290,6 +292,7 @@
 
   ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
   elf.FakeSetInterface(interface);
+  elf.FakeSetLoadBias(load_bias);
 
   memory_->SetData32(0x3000, 0xdf0027ad);
   MemoryFake process_memory;
@@ -299,12 +302,20 @@
   }
 
   elf.FakeSetValid(true);
-  ASSERT_TRUE(elf.StepIfSignalHandler(0x3000, &regs, &process_memory));
+  ASSERT_TRUE(elf.StepIfSignalHandler(0x3000 + load_bias, &regs, &process_memory));
   EXPECT_EQ(ERROR_NONE, elf.GetLastErrorCode());
   EXPECT_EQ(15U, regs.pc());
   EXPECT_EQ(13U, regs.sp());
 }
 
+TEST_F(ElfTest, step_in_signal_map) {
+  VerifyStepIfSignalHandler(0);
+}
+
+TEST_F(ElfTest, step_in_signal_map_non_zero_load_bias) {
+  VerifyStepIfSignalHandler(0x1000);
+}
+
 class ElfInterfaceMock : public ElfInterface {
  public:
   ElfInterfaceMock(Memory* memory) : ElfInterface(memory) {}
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 0d58c09..364101a 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1629,4 +1629,72 @@
   EXPECT_EQ(0xfffe1d74ULL, unwinder.frames()[10].sp);
 }
 
+TEST_F(UnwindOfflineTest, signal_load_bias_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("signal_load_bias_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0029ef9e  libunwindstack_unit_test (SignalInnerFunction+10)\n"
+      "  #01 pc 0029efa7  libunwindstack_unit_test (SignalMiddleFunction+2)\n"
+      "  #02 pc 0029efaf  libunwindstack_unit_test (SignalOuterFunction+2)\n"
+      "  #03 pc 002a280b  libunwindstack_unit_test (unwindstack::SignalCallerHandler(int, "
+      "siginfo*, void*)+10)\n"
+      "  #04 pc 00058bd4  libc.so (__restore)\n"
+      "  #05 pc 0029f01e  libunwindstack_unit_test (InnerFunction+106)\n"
+      "  #06 pc 0029f633  libunwindstack_unit_test (MiddleFunction+16)\n"
+      "  #07 pc 0029f64b  libunwindstack_unit_test (OuterFunction+16)\n"
+      "  #08 pc 002a1711  libunwindstack_unit_test (unwindstack::RemoteThroughSignal(int, unsigned "
+      "int)+260)\n"
+      "  #09 pc 002a1603  libunwindstack_unit_test "
+      "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+10)\n"
+      "  #10 pc 002c8fe3  libunwindstack_unit_test (testing::Test::Run()+130)\n"
+      "  #11 pc 002c9b25  libunwindstack_unit_test (testing::TestInfo::Run()+184)\n"
+      "  #12 pc 002c9e27  libunwindstack_unit_test (testing::TestSuite::Run()+202)\n"
+      "  #13 pc 002d193d  libunwindstack_unit_test "
+      "(testing::internal::UnitTestImpl::RunAllTests()+660)\n"
+      "  #14 pc 002d160b  libunwindstack_unit_test (testing::UnitTest::Run()+134)\n"
+      "  #15 pc 002de035  libunwindstack_unit_test (IsolateMain+680)\n"
+      "  #16 pc 00058155  libc.so (__libc_init+68)\n",
+      frame_info);
+
+  EXPECT_EQ(0xb6955f9eULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xf2790ce8ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xb6955fa7ULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xf2790ce8ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xb6955fafULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xf2790cf0ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xb695980bULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xf2790cf8ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf23febd4ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xf2790d10ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0xb695601eULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffe67798ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xb6956633ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffe67890ULL, unwinder.frames()[6].sp);
+  EXPECT_EQ(0xb695664bULL, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffe678a0ULL, unwinder.frames()[7].sp);
+  EXPECT_EQ(0xb6958711ULL, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xffe678b0ULL, unwinder.frames()[8].sp);
+  EXPECT_EQ(0xb6958603ULL, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xffe67ac8ULL, unwinder.frames()[9].sp);
+  EXPECT_EQ(0xb697ffe3ULL, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xffe67ad8ULL, unwinder.frames()[10].sp);
+  EXPECT_EQ(0xb6980b25ULL, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xffe67ae8ULL, unwinder.frames()[11].sp);
+  EXPECT_EQ(0xb6980e27ULL, unwinder.frames()[12].pc);
+  EXPECT_EQ(0xffe67b18ULL, unwinder.frames()[12].sp);
+  EXPECT_EQ(0xb698893dULL, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xffe67b48ULL, unwinder.frames()[13].sp);
+  EXPECT_EQ(0xb698860bULL, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xffe67bb0ULL, unwinder.frames()[14].sp);
+  EXPECT_EQ(0xb6995035ULL, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xffe67bd0ULL, unwinder.frames()[15].sp);
+  EXPECT_EQ(0xf23fe155ULL, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xffe67d10ULL, unwinder.frames()[16].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
new file mode 100644
index 0000000..f046624
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test b/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
new file mode 100644
index 0000000..f460dd6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt
new file mode 100644
index 0000000..165ae49
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt
@@ -0,0 +1,4 @@
+b66b7000-b670c000 r--p 0 00:00 0   libunwindstack_unit_test
+b670c000-b69a8000 r-xp 54000 00:00 0   libunwindstack_unit_test
+f23a6000-f23d0000 r--p 0 00:00 0   libc.so
+f23d0000-f2451000 r-xp 29000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt
new file mode 100644
index 0000000..e03f8fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: b69b7c84
+r1: 1
+r2: 1
+r3: 1
+r4: f1e52bd0
+r5: f1e11000
+r6: f1e52bd0
+r7: f1e52a38
+r8: f1e11000
+r9: 5de82a8f
+r10: f1e06030
+r11: f1e6d080
+ip: ffe67a88
+sp: f2790ce8
+lr: b6955fab
+pc: b6955f9e
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data
new file mode 100644
index 0000000..d9f23f8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data
new file mode 100644
index 0000000..6011883
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data
Binary files differ
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 02a61a5..c0e11d3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -282,72 +282,78 @@
 
     fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd);
 
-    fprintf(stderr, "options include:\n"
-                    "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
-                    "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
-                    "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
-                    "                  Rotate log every kbytes. Requires -f option\n"
-                    "  -n <count>, --rotate-count=<count>\n"
-                    "                  Sets max number of rotated logs to <count>, default 4\n"
-                    "  --id=<id>       If the signature id for logging to file changes, then clear\n"
-                    "                  the fileset and continue\n"
-                    "  -v <format>, --format=<format>\n"
-                    "                  Sets log print format verb and adverbs, where <format> is:\n"
-                    "                    brief help long process raw tag thread threadtime time\n"
-                    "                  and individually flagged modifying adverbs can be added:\n"
-                    "                    color descriptive epoch monotonic printable uid\n"
-                    "                    usec UTC year zone\n"
-                    "                  Multiple -v parameters or comma separated list of format and\n"
-                    "                  format modifiers are allowed.\n"
-                    // private and undocumented nsec, no signal, too much noise
-                    // useful for -T or -t <timestamp> accurate testing though.
-                    "  -D, --dividers  Print dividers between each log buffer\n"
-                    "  -c, --clear     Clear (flush) the entire log and exit\n"
-                    "                  if Log to File specified, clear fileset instead\n"
-                    "  -d              Dump the log and then exit (don't block)\n"
-                    "  -e <expr>, --regex=<expr>\n"
-                    "                  Only print lines where the log message matches <expr>\n"
-                    "                  where <expr> is a regular expression\n"
-                    // Leave --head undocumented as alias for -m
-                    "  -m <count>, --max-count=<count>\n"
-                    "                  Quit after printing <count> lines. This is meant to be\n"
-                    "                  paired with --regex, but will work on its own.\n"
-                    "  --print         Paired with --regex and --max-count to let content bypass\n"
-                    "                  regex filter but still stop at number of matches.\n"
-                    // Leave --tail undocumented as alias for -t
-                    "  -t <count>      Print only the most recent <count> lines (implies -d)\n"
-                    "  -t '<time>'     Print most recent lines since specified time (implies -d)\n"
-                    "  -T <count>      Print only the most recent <count> lines (does not imply -d)\n"
-                    "  -T '<time>'     Print most recent lines since specified time (not imply -d)\n"
-                    "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
-                    "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
-                    "  -g, --buffer-size                      Get the size of the ring buffer.\n"
-                    "  -G <size>, --buffer-size=<size>\n"
-                    "                  Set size of log ring buffer, may suffix with K or M.\n"
-                    "  -L, --last      Dump logs from prior to last reboot\n"
-                    "  -b <buffer>, --buffer=<buffer>         Request alternate ring buffer, 'main',\n"
-                    "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
-                    "                  Additionally, 'kernel' for userdebug and eng builds, and\n"
-                    "                  'security' for Device Owner installations.\n"
-                    "                  Multiple -b parameters or comma separated list of buffers are\n"
-                    "                  allowed. Buffers interleaved.\n"
-                    "                  Default -b main,system,crash,kernel.\n"
-                    "  -B, --binary    Output the log in binary.\n"
-                    "  -S, --statistics                       Output statistics.\n"
-                    "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
-                    "                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
-                    "                  with ~, otherwise weighed for longevity if unadorned. All\n"
-                    "                  other pruning activity is oldest first. Special case ~!\n"
-                    "                  represents an automatic quicker pruning for the noisiest\n"
-                    "                  UID as determined by the current statistics.\n"
-                    "  -P '<list> ...', --prune='<list> ...'\n"
-                    "                  Set prune white and ~black list, using same format as\n"
-                    "                  listed above. Must be quoted.\n"
-                    "  --pid=<pid>     Only prints logs from the given pid.\n"
-                    // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value for match to 2 hours
-                    "  --wrap          Sleep for 2 hours or when buffer about to wrap whichever\n"
-                    "                  comes first. Improves efficiency of polling by providing\n"
-                    "                  an about-to-wrap wakeup.\n");
+    fprintf(stderr, R"init(
+General options:
+  -b, --buffer=<buffer>       Request alternate ring buffer(s):
+                                main system radio events crash default all
+                              Additionally, 'kernel' for userdebug and eng builds, and
+                              'security' for Device Owner installations.
+                              Multiple -b parameters or comma separated list of buffers are
+                              allowed. Buffers are interleaved.
+                              Default -b main,system,crash,kernel.
+  -L, --last                  Dump logs from prior to last reboot from pstore.
+  -c, --clear                 Clear (flush) the entire log and exit.
+                              if -f is specified, clear the specified file and its related rotated
+                              log files instead.
+                              if -L is specified, clear pstore log instead.
+  -d                          Dump the log and then exit (don't block).
+  --pid=<pid>                 Only print logs from the given pid.
+  --wrap                      Sleep for 2 hours or when buffer about to wrap whichever
+                              comes first. Improves efficiency of polling by providing
+                              an about-to-wrap wakeup.
+
+Formatting:
+  -v, --format=<format>       Sets log print format verb and adverbs, where <format> is one of:
+                                brief help long process raw tag thread threadtime time
+                              Modifying adverbs can be added:
+                                color descriptive epoch monotonic printable uid usec UTC year zone
+                              Multiple -v parameters or comma separated list of format and format
+                              modifiers are allowed.
+  -D, --dividers              Print dividers between each log buffer.
+  -B, --binary                Output the log in binary.
+
+Outfile files:
+  -f, --file=<file>           Log to file instead of stdout.
+  -r, --rotate-kbytes=<n>     Rotate log every <n> kbytes. Requires -f option.
+  -n, --rotate-count=<count>  Sets max number of rotated logs to <count>, default 4.
+  --id=<id>                   If the signature <id> for logging to file changes, then clear the
+                              associated files and continue.
+
+Logd control:
+ These options send a control message to the logd daemon on device, print its return message if
+ applicable, then exit. They are incompatible with -L, as these attributes do not apply to pstore.
+  -g, --buffer-size           Get the size of the ring buffers within logd.
+  -G, --buffer-size=<size>    Set size of a ring buffer in logd. May suffix with K or M.
+                              This can individually control each buffer's size with -b.
+  -S, --statistics            Output statistics.
+                              --pid can be used to provide pid specific stats.
+  -p, --prune                 Print prune white and ~black list. Service is specified as UID,
+                              UID/PID or /PID. Weighed for quicker pruning if prefix with ~,
+                              otherwise weighed for longevity if unadorned. All other pruning
+                              activity is oldest first. Special case ~! represents an automatic
+                              quicker pruning for the noisiest UID as determined by the current
+                              statistics.
+  -P, --prune='<list> ...'    Set prune white and ~black list, using same format as listed above.
+                              Must be quoted.
+
+Filtering:
+  -s                          Set default filter to silent. Equivalent to filterspec '*:S'
+  -e, --regex=<expr>          Only print lines where the log message matches <expr> where <expr> is
+                              an ECMAScript regular expression.
+  -m, --max-count=<count>     Quit after printing <count> lines. This is meant to be paired with
+                              --regex, but will work on its own.
+  --print                     This option is only applicable when --regex is set and only useful if
+                              --max-count is also provided.
+                              With --print, logcat will print all messages even if they do not
+                              match the regex. Logcat will quit after printing the max-count number
+                              of lines that match the regex.
+  -t <count>                  Print only the most recent <count> lines (implies -d).
+  -t '<time>'                 Print the lines since specified time (implies -d).
+  -T <count>                  Print only the most recent <count> lines (does not imply -d).
+  -T '<time>'                 Print the lines since specified time (not imply -d).
+                              count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'
+                              'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format.
+)init");
 
     fprintf(stderr, "\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
@@ -1154,7 +1160,13 @@
         struct log_msg log_msg;
         int ret = android_logger_list_read(logger_list.get(), &log_msg);
         if (!ret) {
-            LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
+            LogcatPanic(HELP_FALSE, R"init(read: unexpected EOF!
+
+This means that either logd crashed, or more likely, this instance of logcat was unable to read log
+messages as quickly as they were being produced.
+
+If you have enabled significant logging, look into using the -G option to increase log buffer sizes.
+)init");
         }
 
         if (ret < 0) {