Merge "logd: add a test that logd ignores SIGPIPE"
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 6cd0430..a349408 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -7,7 +7,7 @@
       "name": "liblp_test"
     },
     {
-      "name": "fiemap_image_test_presubmit"
+      "name": "fiemap_image_test"
     },
     {
       "name": "fiemap_writer_test"
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 9f22be5..1462cc9 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1113,10 +1113,9 @@
                 // metadata-encrypted device with smaller blocks, we must not change this for
                 // devices shipped with Q or earlier unless they explicitly selected dm-default-key
                 // v2
-                constexpr unsigned int pre_gki_level = __ANDROID_API_Q__;
                 unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
                         "ro.crypto.dm_default_key.options_format.version",
-                        (android::fscrypt::GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
+                        (android::fscrypt::GetFirstApiLevel() <= __ANDROID_API_Q__ ? 1 : 2));
                 if (options_format_version > 1) {
                     bowTarget->SetBlockSize(4096);
                 }
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index cae43e6..a622110 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -110,30 +110,3 @@
     auto_gen_config: true,
     require_root: true,
 }
-
-/* BUG(148874852) temporary test */
-cc_test {
-    name: "fiemap_image_test_presubmit",
-    cppflags: [
-        "-DSKIP_TEST_IN_PRESUBMIT",
-    ],
-    static_libs: [
-        "libcrypto_utils",
-        "libdm",
-        "libext4_utils",
-        "libfs_mgr",
-        "liblp",
-    ],
-    shared_libs: [
-        "libbase",
-        "libcrypto",
-        "libcutils",
-        "liblog",
-    ],
-    srcs: [
-        "image_test.cpp",
-    ],
-    test_suites: ["device-tests"],
-    auto_gen_config: true,
-    require_root: true,
-}
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 3ee742f..f32e0eb 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -136,13 +136,13 @@
     return !!FindPartition(*metadata.get(), name);
 }
 
-static bool IsTestDir(const std::string& path) {
-    return android::base::StartsWith(path, kTestImageMetadataDir) ||
-           android::base::StartsWith(path, kOtaTestImageMetadataDir);
+bool ImageManager::MetadataDirIsTest() const {
+    return IsSubdir(metadata_dir_, kTestImageMetadataDir) ||
+           IsSubdir(metadata_dir_, kOtaTestImageMetadataDir);
 }
 
-static bool IsUnreliablePinningAllowed(const std::string& path) {
-    return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
+bool ImageManager::IsUnreliablePinningAllowed() const {
+    return IsSubdir(data_dir_, "/data/gsi/dsu/") || MetadataDirIsTest();
 }
 
 FiemapStatus ImageManager::CreateBackingImage(
@@ -159,7 +159,7 @@
     if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
         return FiemapStatus::Error();
     }
-    if (!reliable_pinning && !IsUnreliablePinningAllowed(data_path)) {
+    if (!reliable_pinning && !IsUnreliablePinningAllowed()) {
         // For historical reasons, we allow unreliable pinning for certain use
         // cases (DSUs, testing) because the ultimate use case is either
         // developer-oriented or ephemeral (the intent is to boot immediately
@@ -178,7 +178,7 @@
     // if device-mapper is stacked in some complex way not supported by
     // FiemapWriter.
     auto device_path = GetDevicePathForFile(fw.get());
-    if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
+    if (android::base::StartsWith(device_path, "/dev/block/dm-") && !MetadataDirIsTest()) {
         LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
 
         fw = {};
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6663391..6d09751 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -34,10 +34,13 @@
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 
+#include "utility.h"
+
 using namespace android::dm;
 using namespace std::literals;
 using android::base::unique_fd;
 using android::fiemap::ImageManager;
+using android::fiemap::IsSubdir;
 using android::fs_mgr::BlockDeviceInfo;
 using android::fs_mgr::PartitionOpener;
 using android::fs_mgr::WaitForFile;
@@ -131,6 +134,51 @@
     ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
 }
 
+namespace {
+
+struct IsSubdirTestParam {
+    std::string child;
+    std::string parent;
+    bool result;
+};
+
+class IsSubdirTest : public ::testing::TestWithParam<IsSubdirTestParam> {};
+
+TEST_P(IsSubdirTest, Test) {
+    const auto& param = GetParam();
+    EXPECT_EQ(param.result, IsSubdir(param.child, param.parent))
+            << "IsSubdir(child=\"" << param.child << "\", parent=\"" << param.parent
+            << "\") != " << (param.result ? "true" : "false");
+}
+
+std::vector<IsSubdirTestParam> IsSubdirTestValues() {
+    // clang-format off
+    std::vector<IsSubdirTestParam> base_cases{
+            {"/foo/bar",     "/foo",     true},
+            {"/foo/bar/baz", "/foo",     true},
+            {"/foo",         "/foo",     true},
+            {"/foo",         "/",        true},
+            {"/",            "/",        true},
+            {"/foo",         "/foo/bar", false},
+            {"/foo",         "/bar",     false},
+            {"/foo-bar",     "/foo",     false},
+            {"/",            "/foo",     false},
+    };
+    // clang-format on
+    std::vector<IsSubdirTestParam> ret;
+    for (const auto& e : base_cases) {
+        ret.push_back(e);
+        ret.push_back({e.child + "/", e.parent, e.result});
+        ret.push_back({e.child, e.parent + "/", e.result});
+        ret.push_back({e.child + "/", e.parent + "/", e.result});
+    }
+    return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
+
+}  // namespace
+
 bool Mkdir(const std::string& path) {
     if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
         std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 60b98dc..50f4f33 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -176,6 +176,8 @@
     bool MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
                          const std::chrono::milliseconds& timeout_ms, std::string* path);
     bool UnmapImageDevice(const std::string& name, bool force);
+    bool IsUnreliablePinningAllowed() const;
+    bool MetadataDirIsTest() const;
 
     ImageManager(const ImageManager&) = delete;
     ImageManager& operator=(const ImageManager&) = delete;
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index c189855..54cf183 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -167,5 +167,30 @@
     return F2fsPinBeforeAllocate(fd, supported);
 }
 
+bool IsSubdir(const std::string& child, const std::string& parent) {
+    // Precondition: both are absolute paths.
+    CHECK(android::base::StartsWith(child, "/")) << "Not an absolute path: " << child;
+    CHECK(android::base::StartsWith(parent, "/")) << "Not an absolute path: " << parent;
+
+    // Remove extraneous "/" at the end.
+    std::string_view child_sv = child;
+    while (child_sv != "/" && android::base::ConsumeSuffix(&child_sv, "/"))
+        ;
+
+    std::string_view parent_sv = parent;
+    while (parent_sv != "/" && android::base::ConsumeSuffix(&parent_sv, "/"))
+        ;
+
+    // IsSubdir(anything, "/") => true
+    if (parent_sv == "/") return true;
+
+    // IsSubdir("/foo", "/foo") => true
+    if (parent_sv == child_sv) return true;
+
+    // IsSubdir("/foo/bar", "/foo") => true
+    // IsSubdir("/foo-bar", "/foo") => false
+    return android::base::StartsWith(child_sv, std::string(parent_sv) + "/");
+}
+
 }  // namespace fiemap
 }  // namespace android
diff --git a/fs_mgr/libfiemap/utility.h b/fs_mgr/libfiemap/utility.h
index 4c0bc2b..aa40f79 100644
--- a/fs_mgr/libfiemap/utility.h
+++ b/fs_mgr/libfiemap/utility.h
@@ -51,5 +51,9 @@
 // cases (such as snapshots or adb remount).
 bool FilesystemHasReliablePinning(const std::string& file, bool* supported);
 
+// Crude implementation to check if |child| is a subdir of |parent|.
+// Assume both are absolute paths.
+bool IsSubdir(const std::string& child, const std::string& parent);
+
 }  // namespace fiemap
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 98bf56a..8e369b0 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -180,5 +180,22 @@
     uint64_t bsize_ = 0;
 };
 
+bool IsVirtualAbEnabled();
+
+#define SKIP_IF_NON_VIRTUAL_AB()                                                        \
+    do {                                                                                \
+        if (!IsVirtualAbEnabled()) GTEST_SKIP() << "Test for Virtual A/B devices only"; \
+    } while (0)
+
+#define RETURN_IF_NON_VIRTUAL_AB_MSG(msg) \
+    do {                                  \
+        if (!IsVirtualAbEnabled()) {      \
+            std::cerr << (msg);           \
+            return;                       \
+        }                                 \
+    } while (0)
+
+#define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG("")
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index adfb975..2970222 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -41,8 +41,14 @@
 
 class PartitionCowCreatorTest : public ::testing::Test {
   public:
-    void SetUp() override { SnapshotTestPropertyFetcher::SetUp(); }
-    void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
+    void SetUp() override {
+        SKIP_IF_NON_VIRTUAL_AB();
+        SnapshotTestPropertyFetcher::SetUp();
+    }
+    void TearDown() override {
+        RETURN_IF_NON_VIRTUAL_AB();
+        SnapshotTestPropertyFetcher::TearDown();
+    }
 };
 
 TEST_F(PartitionCowCreatorTest, IntersectSelf) {
@@ -223,6 +229,8 @@
 }
 
 TEST(DmSnapshotInternals, CowSizeCalculator) {
+    SKIP_IF_NON_VIRTUAL_AB();
+
     DmSnapCowSizeCalculator cc(512, 8);
     unsigned long int b;
 
@@ -286,7 +294,9 @@
     std::optional<InstallOperation> expected_output;
 };
 
-class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {};
+class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {
+    void SetUp() override { SKIP_IF_NON_VIRTUAL_AB(); }
+};
 TEST_P(OptimizeOperationTest, Test) {
     InstallOperation actual_output;
     EXPECT_EQ(GetParam().expected_output.has_value(),
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 5530e59..0a16c03 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -43,11 +43,11 @@
 
 class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {
   public:
-    SnapshotMetadataUpdaterTest() {
-        is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
-    }
+    SnapshotMetadataUpdaterTest() = default;
 
     void SetUp() override {
+        SKIP_IF_NON_VIRTUAL_AB();
+
         target_slot_ = GetParam();
         target_suffix_ = SlotSuffixForSlotNumber(target_slot_);
         SnapshotTestPropertyFetcher::SetUp(SlotSuffixForSlotNumber(1 - target_slot_));
@@ -68,7 +68,11 @@
         ASSERT_TRUE(FillFakeMetadata(builder_.get(), manifest_, target_suffix_));
     }
 
-    void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
+    void TearDown() override {
+        RETURN_IF_NON_VIRTUAL_AB();
+
+        SnapshotTestPropertyFetcher::TearDown();
+    }
 
     // Append suffix to name.
     std::string T(std::string_view name) { return std::string(name) + target_suffix_; }
@@ -127,7 +131,6 @@
                                   << ".";
     }
 
-    bool is_virtual_ab_;
     std::unique_ptr<MetadataBuilder> builder_;
     uint32_t target_slot_;
     std::string target_suffix_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 2bd0135..6ff935b 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -83,9 +83,7 @@
 
 class SnapshotTest : public ::testing::Test {
   public:
-    SnapshotTest() : dm_(DeviceMapper::Instance()) {
-        is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
-    }
+    SnapshotTest() : dm_(DeviceMapper::Instance()) {}
 
     // This is exposed for main.
     void Cleanup() {
@@ -95,7 +93,7 @@
 
   protected:
     void SetUp() override {
-        if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+        SKIP_IF_NON_VIRTUAL_AB();
 
         SnapshotTestPropertyFetcher::SetUp();
         InitializeState();
@@ -106,7 +104,7 @@
     }
 
     void TearDown() override {
-        if (!is_virtual_ab_) return;
+        RETURN_IF_NON_VIRTUAL_AB();
 
         lock_ = nullptr;
 
@@ -341,7 +339,6 @@
     }
 
     static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
-    bool is_virtual_ab_;
     DeviceMapper& dm_;
     std::unique_ptr<SnapshotManager::LockedFile> lock_;
     android::fiemap::IImageManager* image_manager_ = nullptr;
@@ -722,11 +719,13 @@
 class LockTest : public ::testing::Test {
   public:
     void SetUp() {
+        SKIP_IF_NON_VIRTUAL_AB();
         first_consumer.StartHandleRequestsInBackground();
         second_consumer.StartHandleRequestsInBackground();
     }
 
     void TearDown() {
+        RETURN_IF_NON_VIRTUAL_AB();
         EXPECT_TRUE(first_consumer.MakeRequest(Request::EXIT));
         EXPECT_TRUE(second_consumer.MakeRequest(Request::EXIT));
     }
@@ -770,7 +769,7 @@
 class SnapshotUpdateTest : public SnapshotTest {
   public:
     void SetUp() override {
-        if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+        SKIP_IF_NON_VIRTUAL_AB();
 
         SnapshotTest::SetUp();
         Cleanup();
@@ -832,7 +831,7 @@
         }
     }
     void TearDown() override {
-        if (!is_virtual_ab_) return;
+        RETURN_IF_NON_VIRTUAL_AB();
 
         Cleanup();
         SnapshotTest::TearDown();
@@ -1365,13 +1364,17 @@
     ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
 }
 
-class MetadataMountedTest : public SnapshotUpdateTest {
+class MetadataMountedTest : public ::testing::Test {
   public:
+    // This is so main() can instantiate this to invoke Cleanup.
+    virtual void TestBody() override {}
     void SetUp() override {
+        SKIP_IF_NON_VIRTUAL_AB();
         metadata_dir_ = test_device->GetMetadataDir();
         ASSERT_TRUE(ReadDefaultFstab(&fstab_));
     }
     void TearDown() override {
+        RETURN_IF_NON_VIRTUAL_AB();
         SetUp();
         // Remount /metadata
         test_device->set_recovery(false);
@@ -1702,8 +1705,6 @@
 };
 
 TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
-    if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
-
     // OTA client blindly unmaps all partitions that are possibly mapped.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
         ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
@@ -1803,14 +1804,13 @@
 class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
   protected:
     void SetUp() override {
-        if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
-
+        SKIP_IF_NON_VIRTUAL_AB();
         SnapshotTest::SetUp();
         userdata_ = std::make_unique<LowSpaceUserdata>();
         ASSERT_TRUE(userdata_->Init(GetParam()));
     }
     void TearDown() override {
-        if (!is_virtual_ab_) return;
+        RETURN_IF_NON_VIRTUAL_AB();
         return;  // BUG(149738928)
 
         EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
@@ -1852,11 +1852,6 @@
 
 INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
 
-}  // namespace snapshot
-}  // namespace android
-
-using namespace android::snapshot;
-
 bool Mkdir(const std::string& path) {
     if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
         std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
@@ -1865,8 +1860,21 @@
     return true;
 }
 
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
+class SnapshotTestEnvironment : public ::testing::Environment {
+  public:
+    ~SnapshotTestEnvironment() override {}
+    void SetUp() override;
+    void TearDown() override;
+
+  private:
+    std::unique_ptr<IImageManager> super_images_;
+};
+
+void SnapshotTestEnvironment::SetUp() {
+    // b/163082876: GTEST_SKIP in Environment will make atest report incorrect results. Until
+    // that is fixed, don't call GTEST_SKIP here, but instead call GTEST_SKIP in individual test
+    // suites.
+    RETURN_IF_NON_VIRTUAL_AB_MSG("Virtual A/B is not enabled, skipping global setup.\n");
 
     std::vector<std::string> paths = {
             // clang-format off
@@ -1879,18 +1887,13 @@
             // clang-format on
     };
     for (const auto& path : paths) {
-        if (!Mkdir(path)) {
-            return 1;
-        }
+        ASSERT_TRUE(Mkdir(path));
     }
 
     // Create this once, otherwise, gsid will start/stop between each test.
     test_device = new TestDeviceInfo();
     sm = SnapshotManager::New(test_device);
-    if (!sm) {
-        std::cerr << "Could not create snapshot manager\n";
-        return 1;
-    }
+    ASSERT_NE(nullptr, sm) << "Could not create snapshot manager";
 
     // Clean up previous run.
     MetadataMountedTest().TearDown();
@@ -1898,31 +1901,35 @@
     SnapshotTest().Cleanup();
 
     // Use a separate image manager for our fake super partition.
-    auto super_images = IImageManager::Open("ota/test/super", 10s);
-    if (!super_images) {
-        std::cerr << "Could not create image manager\n";
-        return 1;
-    }
+    super_images_ = IImageManager::Open("ota/test/super", 10s);
+    ASSERT_NE(nullptr, super_images_) << "Could not create image manager";
 
     // Clean up any old copy.
-    DeleteBackingImage(super_images.get(), "fake-super");
+    DeleteBackingImage(super_images_.get(), "fake-super");
 
     // Create and map the fake super partition.
     static constexpr int kImageFlags =
             IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
-    if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
-        std::cerr << "Could not create fake super partition\n";
-        return 1;
-    }
-    if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
-        std::cerr << "Could not map fake super partition\n";
-        return 1;
-    }
+    ASSERT_TRUE(super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags))
+            << "Could not create fake super partition";
+
+    ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super))
+            << "Could not map fake super partition";
     test_device->set_fake_super(fake_super);
+}
 
-    auto result = RUN_ALL_TESTS();
+void SnapshotTestEnvironment::TearDown() {
+    RETURN_IF_NON_VIRTUAL_AB();
+    if (super_images_ != nullptr) {
+        DeleteBackingImage(super_images_.get(), "fake-super");
+    }
+}
 
-    DeleteBackingImage(super_images.get(), "fake-super");
+}  // namespace snapshot
+}  // namespace android
 
-    return result;
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
+    return RUN_ALL_TESTS();
 }
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index de3d912..b07bf91 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
@@ -241,5 +242,9 @@
     return bsize_;
 }
 
+bool IsVirtualAbEnabled() {
+    return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 14d46b3..b3de9c4 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -240,3 +240,47 @@
     defaults: ["charger_defaults"],
     srcs: ["charger_test.cpp"],
 }
+
+cc_test {
+    name: "libhealthd_charger_test",
+    defaults: ["charger_defaults"],
+    srcs: [
+        "AnimationParser_test.cpp",
+        "healthd_mode_charger_test.cpp"
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    test_suites: [
+        "general-tests",
+        "device-tests",
+    ],
+    data: [
+        ":libhealthd_charger_test_data",
+    ],
+    require_root: true,
+}
+
+// /system/etc/res/images/charger/battery_fail.png
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery_fail.png",
+    src: "images/battery_fail.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery_fail.png",
+}
+
+// /system/etc/res/images/charger/battery_scale.png
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery_scale.png",
+    src: "images/battery_scale.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery_scale.png",
+}
+
+phony {
+    name: "charger_res_images",
+    required: [
+        "system_core_charger_res_images_battery_fail.png",
+        "system_core_charger_res_images_battery_scale.png",
+    ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
deleted file mode 100644
index 4b09cf8..0000000
--- a/healthd/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
-LOCAL_CHARGER_NO_UI := true
-endif
-
-### charger_res_images ###
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-define _add-charger-image
-include $$(CLEAR_VARS)
-LOCAL_MODULE := system_core_charger_res_images_$(notdir $(1))
-LOCAL_MODULE_STEM := $(notdir $(1))
-_img_modules += $$(LOCAL_MODULE)
-LOCAL_SRC_FILES := $1
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
-include $$(BUILD_PREBUILT)
-endef
-
-_img_modules :=
-_images :=
-$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
-  $(eval $(call _add-charger-image,$(_img))))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := charger_res_images
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := $(_img_modules)
-include $(BUILD_PHONY_PACKAGE)
-
-_add-charger-image :=
-_img_modules :=
-endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
index fde3b95..6b08570 100644
--- a/healthd/AnimationParser.cpp
+++ b/healthd/AnimationParser.cpp
@@ -37,8 +37,8 @@
     return true;
 }
 
-bool remove_prefix(const std::string& line, const char* prefix, const char** rest) {
-    const char* str = line.c_str();
+bool remove_prefix(std::string_view line, const char* prefix, const char** rest) {
+    const char* str = line.data();
     int start;
     char c;
 
diff --git a/healthd/AnimationParser.h b/healthd/AnimationParser.h
index bc00845..f55b563 100644
--- a/healthd/AnimationParser.h
+++ b/healthd/AnimationParser.h
@@ -17,6 +17,8 @@
 #ifndef HEALTHD_ANIMATION_PARSER_H
 #define HEALTHD_ANIMATION_PARSER_H
 
+#include <string_view>
+
 #include "animation.h"
 
 namespace android {
@@ -24,7 +26,7 @@
 bool parse_animation_desc(const std::string& content, animation* anim);
 
 bool can_ignore_line(const char* str);
-bool remove_prefix(const std::string& str, const char* prefix, const char** rest);
+bool remove_prefix(std::string_view str, const char* prefix, const char** rest);
 bool parse_text_field(const char* in, animation::text_field* field);
 }  // namespace android
 
diff --git a/healthd/tests/AnimationParser_test.cpp b/healthd/AnimationParser_test.cpp
similarity index 100%
rename from healthd/tests/AnimationParser_test.cpp
rename to healthd/AnimationParser_test.cpp
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
new file mode 100644
index 0000000..5893d10
--- /dev/null
+++ b/healthd/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libhealthd_charger_test"
+    }
+  ]
+}
diff --git a/healthd/animation.h b/healthd/animation.h
index d02d7a7..c2d5f1c 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -18,6 +18,7 @@
 #define HEALTHD_ANIMATION_H
 
 #include <inttypes.h>
+
 #include <string>
 
 class GRSurface;
@@ -52,20 +53,11 @@
     // - When treating paths as relative paths, it adds ".png" suffix.
     // - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix
     //   is added here.
-    void set_resource_root(const std::string& root) {
-        if (!animation_file.empty()) {
-            animation_file = root + animation_file + ".png";
-        }
-        if (!fail_file.empty()) {
-            fail_file = root + fail_file + ".png";
-        }
-        if (!text_clock.font_file.empty()) {
-            text_clock.font_file = root + text_clock.font_file + ".png";
-        }
-        if (!text_percent.font_file.empty()) {
-            text_percent.font_file = root + text_percent.font_file + ".png";
-        }
-    }
+    // If |backup_root| is provided, additionally check if file under |root| is accessbile or not.
+    // If not accessbile, use |backup_root| instead.
+    // Require that |root| starts and ends with "/". If |backup_root| is provided, require that
+    // |backup_root| starts and ends with "/".
+    void set_resource_root(const std::string& root, const std::string& backup_root = "");
 
     std::string animation_file;
     std::string fail_file;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 386ba1a..04a99a3 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -33,7 +33,9 @@
 #include <optional>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/strings.h>
 
 #include <linux/netlink.h>
 #include <sys/socket.h>
@@ -58,6 +60,7 @@
 #include <health2impl/Health.h>
 #include <healthd/healthd.h>
 
+using std::string_literals::operator""s;
 using namespace android;
 using android::hardware::Return;
 using android::hardware::health::GetHealthServiceOrDefault;
@@ -103,7 +106,13 @@
 
 namespace android {
 
-// Resources in /product/etc/res overrides resources in /res.
+// Legacy animation resources are loaded from this directory.
+static constexpr const char* legacy_animation_root = "/res/images/";
+
+// Built-in animation resources are loaded from this directory.
+static constexpr const char* system_animation_root = "/system/etc/res/images/";
+
+// Resources in /product/etc/res overrides resources in /res and /system/etc/res.
 // If the device is using the Generic System Image (GSI), resources may exist in
 // both paths.
 static constexpr const char* product_animation_desc_path =
@@ -625,6 +634,12 @@
         batt_anim_.set_resource_root(product_animation_root);
     } else if (base::ReadFileToString(animation_desc_path, &content)) {
         parse_success = parse_animation_desc(content, &batt_anim_);
+        // Fallback resources always exist in system_animation_root. On legacy devices with an old
+        // ramdisk image, resources may be overridden under root. For example,
+        // /res/images/charger/battery_fail.png may not be the same as
+        // system/core/healthd/images/battery_fail.png in the source tree, but is a device-specific
+        // image. Hence, load from /res, and fall back to /system/etc/res.
+        batt_anim_.set_resource_root(legacy_animation_root, system_animation_root);
     } else {
         LOGW("Could not open animation description at %s\n", animation_desc_path);
         parse_success = false;
@@ -633,13 +648,13 @@
     if (!parse_success) {
         LOGW("Could not parse animation description. Using default animation.\n");
         batt_anim_ = BASE_ANIMATION;
-        batt_anim_.animation_file.assign("charger/battery_scale");
+        batt_anim_.animation_file.assign(system_animation_root + "charger/battery_scale.png"s);
         InitDefaultAnimationFrames();
         batt_anim_.frames = owned_frames_.data();
         batt_anim_.num_frames = owned_frames_.size();
     }
     if (batt_anim_.fail_file.empty()) {
-        batt_anim_.fail_file.assign("charger/battery_fail");
+        batt_anim_.fail_file.assign(system_animation_root + "charger/battery_fail.png"s);
     }
 
     LOGV("Animation Description:\n");
@@ -678,10 +693,11 @@
 
     InitAnimation();
 
-    ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);
+    ret = CreateDisplaySurface(batt_anim_.fail_file.c_str(), &surf_unknown_);
     if (ret < 0) {
         LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
-        ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);
+        ret = CreateDisplaySurface((system_animation_root + "charger/battery_fail.png"s).c_str(),
+                                   &surf_unknown_);
         if (ret < 0) {
             LOGE("Cannot load built in battery_fail image\n");
             surf_unknown_ = NULL;
@@ -692,8 +708,8 @@
     int scale_count;
     int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
                     // chunk). We are using hard-coded frame.disp_time instead.
-    ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,
-                                           &scale_fps, &scale_frames);
+    ret = CreateMultiDisplaySurface(batt_anim_.animation_file.c_str(), &scale_count, &scale_fps,
+                                    &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
         batt_anim_.num_frames = 0;
@@ -722,6 +738,43 @@
     boot_min_cap_ = config->boot_min_cap;
 }
 
+int Charger::CreateDisplaySurface(const std::string& name, GRSurface** surface) {
+    return res_create_display_surface(name.c_str(), surface);
+}
+
+int Charger::CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
+                                       GRSurface*** surface) {
+    return res_create_multi_display_surface(name.c_str(), frames, fps, surface);
+}
+
+void set_resource_root_for(const std::string& root, const std::string& backup_root,
+                           std::string* value) {
+    if (value->empty()) {
+        return;
+    }
+
+    std::string new_value = root + *value + ".png";
+    // If |backup_root| is provided, additionally check whether the file under |root| is
+    // accessible or not. If not accessible, fallback to file under |backup_root|.
+    if (!backup_root.empty() && access(new_value.data(), F_OK) == -1) {
+        new_value = backup_root + *value + ".png";
+    }
+
+    *value = new_value;
+}
+
+void animation::set_resource_root(const std::string& root, const std::string& backup_root) {
+    CHECK(android::base::StartsWith(root, "/") && android::base::EndsWith(root, "/"))
+            << "animation root " << root << " must start and end with /";
+    CHECK(backup_root.empty() || (android::base::StartsWith(backup_root, "/") &&
+                                  android::base::EndsWith(backup_root, "/")))
+            << "animation backup root " << backup_root << " must start and end with /";
+    set_resource_root_for(root, backup_root, &animation_file);
+    set_resource_root_for(root, backup_root, &fail_file);
+    set_resource_root_for(root, backup_root, &text_clock.font_file);
+    set_resource_root_for(root, backup_root, &text_percent.font_file);
+}
+
 }  // namespace android
 
 int healthd_charger_main(int argc, char** argv) {
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 6e569ee..6f9ae8c 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -53,6 +53,11 @@
     // HalHealthLoop overrides
     void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
 
+    // Allowed to be mocked for testing.
+    virtual int CreateDisplaySurface(const std::string& name, GRSurface** surface);
+    virtual int CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
+                                          GRSurface*** surface);
+
   private:
     void InitDefaultAnimationFrames();
     void UpdateScreenState(int64_t now);
diff --git a/healthd/healthd_mode_charger_test.cpp b/healthd/healthd_mode_charger_test.cpp
new file mode 100644
index 0000000..f444f66
--- /dev/null
+++ b/healthd/healthd_mode_charger_test.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 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 <sysexits.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <health/utils.h>
+
+#include "healthd_mode_charger.h"
+
+using android::hardware::Return;
+using android::hardware::health::InitHealthdConfig;
+using std::string_literals::operator""s;
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::StrEq;
+using testing::Test;
+
+namespace android {
+
+// A replacement to ASSERT_* to be used in a forked process. When the condition is not met,
+// print a gtest message, then exit abnormally.
+class ChildAssertHelper : public std::stringstream {
+  public:
+    ChildAssertHelper(bool res, const char* expr, const char* file, int line) : res_(res) {
+        (*this) << file << ":" << line << ": `" << expr << "` evaluates to false\n";
+    }
+    ~ChildAssertHelper() {
+        EXPECT_TRUE(res_) << str();
+        if (!res_) exit(EX_SOFTWARE);
+    }
+
+  private:
+    bool res_;
+    DISALLOW_COPY_AND_ASSIGN(ChildAssertHelper);
+};
+#define CHILD_ASSERT_TRUE(expr) ChildAssertHelper(expr, #expr, __FILE__, __LINE__)
+
+// Run |test_body| in a chroot jail in a forked process. |subdir| is a sub-directory in testdata.
+// Within |test_body|,
+// - non-fatal errors may be reported using EXPECT_* macro as usual.
+// - fatal errors must be reported using CHILD_ASSERT_TRUE macro. ASSERT_* must not be used.
+void ForkTest(const std::string& subdir, const std::function<void(void)>& test_body) {
+    pid_t pid = fork();
+    ASSERT_GE(pid, 0) << "Fork fails: " << strerror(errno);
+    if (pid == 0) {
+        // child
+        CHILD_ASSERT_TRUE(
+                chroot((android::base::GetExecutableDirectory() + "/" + subdir).c_str()) != -1)
+                << "Failed to chroot to " << subdir << ": " << strerror(errno);
+        test_body();
+        // EXPECT_* macros may set the HasFailure bit without calling exit(). Set exit status
+        // accordingly.
+        exit(::testing::Test::HasFailure() ? EX_SOFTWARE : EX_OK);
+    }
+    // parent
+    int status;
+    ASSERT_NE(-1, waitpid(pid, &status, 0)) << "waitpid() fails: " << strerror(errno);
+    ASSERT_TRUE(WIFEXITED(status)) << "Test fails, waitpid() returns " << status;
+    ASSERT_EQ(EX_OK, WEXITSTATUS(status)) << "Test fails, child process returns " << status;
+}
+
+class MockHealth : public android::hardware::health::V2_1::IHealth {
+    MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, registerCallback,
+                (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
+    MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, unregisterCallback,
+                (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
+    MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, update, ());
+    MOCK_METHOD(Return<void>, getChargeCounter, (getChargeCounter_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getCurrentNow, (getCurrentNow_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getCurrentAverage, (getCurrentAverage_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getCapacity, (getCapacity_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getEnergyCounter, (getEnergyCounter_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getChargeStatus, (getChargeStatus_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getStorageInfo, (getStorageInfo_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getDiskStats, (getDiskStats_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getHealthInfo, (getHealthInfo_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getHealthConfig, (getHealthConfig_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, getHealthInfo_2_1, (getHealthInfo_2_1_cb _hidl_cb));
+    MOCK_METHOD(Return<void>, shouldKeepScreenOn, (shouldKeepScreenOn_cb _hidl_cb));
+};
+
+class TestCharger : public Charger {
+  public:
+    // Inherit constructor.
+    using Charger::Charger;
+    // Expose protected functions to be used in tests.
+    void Init(struct healthd_config* config) override { Charger::Init(config); }
+    MOCK_METHOD(int, CreateDisplaySurface, (const std::string& name, GRSurface** surface));
+    MOCK_METHOD(int, CreateMultiDisplaySurface,
+                (const std::string& name, int* frames, int* fps, GRSurface*** surface));
+};
+
+// Intentionally leak TestCharger instance to avoid calling ~HealthLoop() because ~HealthLoop()
+// should never be called. But still verify expected calls upon destruction.
+class VerifiedTestCharger {
+  public:
+    VerifiedTestCharger(TestCharger* charger) : charger_(charger) {
+        testing::Mock::AllowLeak(charger_);
+    }
+    TestCharger& operator*() { return *charger_; }
+    TestCharger* operator->() { return charger_; }
+    ~VerifiedTestCharger() { testing::Mock::VerifyAndClearExpectations(charger_); }
+
+  private:
+    TestCharger* charger_;
+};
+
+// Do not use SetUp and TearDown of a test suite, as they will be invoked in the parent process, not
+// the child process. In particular, if the test suite contains mocks, they will not be verified in
+// the child process. Instead, create mocks within closures in each tests.
+void ExpectChargerResAt(const std::string& root) {
+    sp<NiceMock<MockHealth>> health(new NiceMock<MockHealth>());
+    VerifiedTestCharger charger(new NiceMock<TestCharger>(health));
+
+    // Only one frame in all testdata/**/animation.txt
+    GRSurface* multi[] = {nullptr};
+
+    EXPECT_CALL(*charger, CreateDisplaySurface(StrEq(root + "charger/battery_fail.png"), _))
+            .WillRepeatedly(Invoke([](const auto&, GRSurface** surface) {
+                *surface = nullptr;
+                return 0;
+            }));
+    EXPECT_CALL(*charger,
+                CreateMultiDisplaySurface(StrEq(root + "charger/battery_scale.png"), _, _, _))
+            .WillRepeatedly(Invoke([&](const auto&, int* frames, int* fps, GRSurface*** surface) {
+                *frames = arraysize(multi);
+                *fps = 60;  // Unused fps value
+                *surface = multi;
+                return 0;
+            }));
+    struct healthd_config healthd_config;
+    InitHealthdConfig(&healthd_config);
+    charger->Init(&healthd_config);
+};
+
+// Test that if resources does not exist in /res or in /product/etc/res, load from /system.
+TEST(ChargerLoadAnimationRes, Empty) {
+    ForkTest("empty", std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
+}
+
+// Test loading everything from /res
+TEST(ChargerLoadAnimationRes, Legacy) {
+    ForkTest("legacy", std::bind(&ExpectChargerResAt, "/res/images/"));
+}
+
+// Test loading animation text from /res but images from /system if images does not exist under
+// /res.
+TEST(ChargerLoadAnimationRes, LegacyTextSystemImages) {
+    ForkTest("legacy_text_system_images",
+             std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
+}
+
+// Test loading everything from /product
+TEST(ChargerLoadAnimationRes, Product) {
+    ForkTest("product", std::bind(&ExpectChargerResAt, "/product/etc/res/images/"));
+}
+
+}  // namespace android
diff --git a/healthd/testdata/Android.bp b/healthd/testdata/Android.bp
new file mode 100644
index 0000000..110c79a
--- /dev/null
+++ b/healthd/testdata/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2020 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.
+//
+
+filegroup {
+    name: "libhealthd_charger_test_data",
+    srcs: ["**/*.*"],
+}
diff --git a/healthd/testdata/empty/ensure_directory_creation.txt b/healthd/testdata/empty/ensure_directory_creation.txt
new file mode 100644
index 0000000..36ceff4
--- /dev/null
+++ b/healthd/testdata/empty/ensure_directory_creation.txt
@@ -0,0 +1 @@
+File is placed to ensure directory is created on the device.
diff --git a/healthd/testdata/legacy/res/images/charger/battery_fail.png b/healthd/testdata/legacy/res/images/charger/battery_fail.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/legacy/res/images/charger/battery_fail.png
diff --git a/healthd/testdata/legacy/res/images/charger/battery_scale.png b/healthd/testdata/legacy/res/images/charger/battery_scale.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/legacy/res/images/charger/battery_scale.png
diff --git a/healthd/testdata/legacy/res/values/charger/animation.txt b/healthd/testdata/legacy/res/values/charger/animation.txt
new file mode 100644
index 0000000..0753336
--- /dev/null
+++ b/healthd/testdata/legacy/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt b/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
new file mode 100644
index 0000000..0753336
--- /dev/null
+++ b/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png b/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png b/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
diff --git a/healthd/testdata/product/product/etc/res/values/charger/animation.txt b/healthd/testdata/product/product/etc/res/values/charger/animation.txt
new file mode 100644
index 0000000..0753336
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/tests/Android.mk b/healthd/tests/Android.mk
deleted file mode 100644
index 87e8862..0000000
--- a/healthd/tests/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2016 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    AnimationParser_test.cpp \
-
-LOCAL_MODULE := healthd_test
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_LIBRARIES := \
-	libhealthd_internal \
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libbase \
-	libcutils \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/init/init.cpp b/init/init.cpp
index cb5bbba..7d00538 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -310,14 +310,14 @@
         // late_import is available only in Q and earlier release. As we don't
         // have system_ext in those versions, skip late_import for system_ext.
         parser.ParseConfig("/system_ext/etc/init");
-        if (!parser.ParseConfig("/product/etc/init")) {
-            late_import_paths.emplace_back("/product/etc/init");
+        if (!parser.ParseConfig("/vendor/etc/init")) {
+            late_import_paths.emplace_back("/vendor/etc/init");
         }
         if (!parser.ParseConfig("/odm/etc/init")) {
             late_import_paths.emplace_back("/odm/etc/init");
         }
-        if (!parser.ParseConfig("/vendor/etc/init")) {
-            late_import_paths.emplace_back("/vendor/etc/init");
+        if (!parser.ParseConfig("/product/etc/init")) {
+            late_import_paths.emplace_back("/product/etc/init");
         }
     } else {
         parser.ParseConfig(bootscript);
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 20cd659..233d400 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -212,13 +212,16 @@
 
     // fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
     if (fd < 0) {
+        int saved_errno = errno;
         fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
+        if (fd < 0) {
+            /* Q launching devices and newer must not reach here since they should have been
+             * able to open ashmem_device_path */
+            ALOGE("Unable to open ashmem device %s (error = %s) and /dev/ashmem(error = %s)",
+                  ashmem_device_path.c_str(), strerror(saved_errno), strerror(errno));
+            return fd;
+        }
     }
-
-    if (fd < 0) {
-        return fd;
-    }
-
     struct stat st;
     int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
     if (ret < 0) {
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 51c5e60..2c0156e 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -559,21 +559,6 @@
   return str->second.data();
 }
 
-// This function is deprecated and replaced with android_lookupEventTag_len
-// since it will cause the map to change from Shared and backed by a file,
-// to Private Dirty and backed up by swap, albeit highly compressible. By
-// deprecating this function everywhere, we save 100s of MB of memory space.
-const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag) {
-  size_t len;
-  const char* tagStr = android_lookupEventTag_len(map, &len, tag);
-
-  if (!tagStr) return tagStr;
-  char* cp = const_cast<char*>(tagStr);
-  cp += len;
-  if (*cp) *cp = '\0';  // Trigger copy on write :-( and why deprecated.
-  return tagStr;
-}
-
 // Look up tagname, generate one if necessary, and return a tag
 int android_lookupEventTagNum(EventTagMap* map, const char* tagname, const char* format, int prio) {
   const char* ep = endOfTag(tagname);
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index f7ec208..4d0ebf9 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -40,14 +40,6 @@
 void android_closeEventTagMap(EventTagMap* map);
 
 /*
- * Look up a tag by index.  Returns the tag string, or NULL if not found.
- */
-const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag)
-    __attribute__((
-        deprecated("use android_lookupEventTag_len() instead to minimize "
-                   "MAP_PRIVATE copy-on-write memory impact")));
-
-/*
  * Look up a tag by index.  Returns the tag string & string length, or NULL if
  * not found.  Returned string is not guaranteed to be nul terminated.
  */
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
index f3d8022..6229e7c 100644
--- a/libsparse/backed_block.cpp
+++ b/libsparse/backed_block.cpp
@@ -25,7 +25,7 @@
 
 struct backed_block {
   unsigned int block;
-  unsigned int len;
+  uint64_t len;
   enum backed_block_type type;
   union {
     struct {
@@ -60,7 +60,7 @@
   return bb->next;
 }
 
-unsigned int backed_block_len(struct backed_block* bb) {
+uint64_t backed_block_len(struct backed_block* bb) {
   return bb->len;
 }
 
@@ -270,7 +270,7 @@
 }
 
 /* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
                           unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
   if (bb == nullptr) {
@@ -287,7 +287,7 @@
 }
 
 /* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
                           unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
   if (bb == nullptr) {
@@ -305,7 +305,7 @@
 
 /* Queues a chunk of a file on disk to be written to the specified data blocks */
 int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
-                          unsigned int len, unsigned int block) {
+                          uint64_t len, unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
   if (bb == nullptr) {
     return -ENOMEM;
@@ -322,7 +322,7 @@
 }
 
 /* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
                         unsigned int block) {
   struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
   if (bb == nullptr) {
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 3a75460..71a8969 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -29,18 +29,18 @@
   BACKED_BLOCK_FILL,
 };
 
-int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
                           unsigned int block);
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
                           unsigned int block);
 int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
-                          unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+                          uint64_t len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
                         unsigned int block);
 
 struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
 struct backed_block* backed_block_iter_next(struct backed_block* bb);
-unsigned int backed_block_len(struct backed_block* bb);
+uint64_t backed_block_len(struct backed_block* bb);
 unsigned int backed_block_block(struct backed_block* bb);
 void* backed_block_data(struct backed_block* bb);
 const char* backed_block_filename(struct backed_block* bb);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 3d5fb0c..2f75349 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -75,8 +75,7 @@
  *
  * Returns 0 on success, negative errno on error.
  */
-int sparse_file_add_data(struct sparse_file *s,
-		void *data, unsigned int len, unsigned int block);
+int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block);
 
 /**
  * sparse_file_add_fill - associate a fill chunk with a sparse file
@@ -93,8 +92,8 @@
  *
  * Returns 0 on success, negative errno on error.
  */
-int sparse_file_add_fill(struct sparse_file *s,
-		uint32_t fill_val, unsigned int len, unsigned int block);
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
+                         unsigned int block);
 
 /**
  * sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -116,9 +115,8 @@
  *
  * Returns 0 on success, negative errno on error.
  */
-int sparse_file_add_file(struct sparse_file *s,
-		const char *filename, int64_t file_offset, unsigned int len,
-		unsigned int block);
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+                         uint64_t len, unsigned int block);
 
 /**
  * sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -143,8 +141,8 @@
  *
  * Returns 0 on success, negative errno on error.
  */
-int sparse_file_add_fd(struct sparse_file *s,
-		int fd, int64_t file_offset, unsigned int len, unsigned int block);
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
+                       unsigned int block);
 
 /**
  * sparse_file_write - write a sparse file to a file
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index b883c13..b2c5407 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -65,9 +65,9 @@
 };
 
 struct sparse_file_ops {
-  int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
-  int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
-  int (*write_skip_chunk)(struct output_file* out, int64_t len);
+  int (*write_data_chunk)(struct output_file* out, uint64_t len, void* data);
+  int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
+  int (*write_skip_chunk)(struct output_file* out, uint64_t len);
   int (*write_end_chunk)(struct output_file* out);
 };
 
@@ -316,7 +316,7 @@
   return 0;
 }
 
-static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
   chunk_header_t chunk_header;
   int ret;
 
@@ -340,9 +340,10 @@
   return 0;
 }
 
-static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+static int write_sparse_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
   chunk_header_t chunk_header;
-  int rnd_up_len, count;
+  uint64_t rnd_up_len;
+  int count;
   int ret;
 
   /* Round up the fill length to a multiple of the block size */
@@ -370,9 +371,9 @@
   return 0;
 }
 
-static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* data) {
   chunk_header_t chunk_header;
-  int rnd_up_len, zero_len;
+  uint64_t rnd_up_len, zero_len;
   int ret;
 
   /* Round up the data length to a multiple of the block size */
@@ -437,9 +438,9 @@
     .write_end_chunk = write_sparse_end_chunk,
 };
 
-static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
   int ret;
-  unsigned int rnd_up_len = ALIGN(len, out->block_size);
+  uint64_t rnd_up_len = ALIGN(len, out->block_size);
 
   ret = out->ops->write(out, data, len);
   if (ret < 0) {
@@ -453,10 +454,10 @@
   return ret;
 }
 
-static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
   int ret;
   unsigned int i;
-  unsigned int write_len;
+  uint64_t write_len;
 
   /* Initialize fill_buf with the fill_val */
   for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
@@ -464,7 +465,7 @@
   }
 
   while (len) {
-    write_len = std::min(len, out->block_size);
+    write_len = std::min(len, (uint64_t)out->block_size);
     ret = out->ops->write(out, out->fill_buf, write_len);
     if (ret < 0) {
       return ret;
@@ -476,7 +477,7 @@
   return 0;
 }
 
-static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
   return out->ops->skip(out, len);
 }
 
@@ -639,16 +640,16 @@
 }
 
 /* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+int write_data_chunk(struct output_file* out, uint64_t len, void* data) {
   return out->sparse_ops->write_data_chunk(out, len, data);
 }
 
 /* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
   return out->sparse_ops->write_fill_chunk(out, len, fill_val);
 }
 
-int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
   auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
   if (!m) return -errno;
 
@@ -656,7 +657,7 @@
 }
 
 /* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset) {
   int ret;
 
   int file_fd = open(file, O_RDONLY | O_BINARY);
@@ -671,6 +672,6 @@
   return ret;
 }
 
-int write_skip_chunk(struct output_file* out, int64_t len) {
+int write_skip_chunk(struct output_file* out, uint64_t len) {
   return out->sparse_ops->write_skip_chunk(out, len);
 }
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 278430b..ecbcdf3 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -30,11 +30,11 @@
 struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
                                               unsigned int block_size, int64_t len, int gz,
                                               int sparse, int chunks, int crc);
-int write_data_chunk(struct output_file* out, unsigned int len, void* data);
-int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
-int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
-int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
-int write_skip_chunk(struct output_file* out, int64_t len);
+int write_data_chunk(struct output_file* out, uint64_t len, void* data);
+int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, uint64_t len);
 void output_file_close(struct output_file* out);
 
 int read_all(int fd, void* buf, size_t len);
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 8622b4c..396e7eb 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -50,21 +50,21 @@
   free(s);
 }
 
-int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block) {
   return backed_block_add_data(s->backed_block_list, data, len, block);
 }
 
-int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
                          unsigned int block) {
   return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
 }
 
 int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
-                         unsigned int len, unsigned int block) {
+                         uint64_t len, unsigned int block) {
   return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
 }
 
-int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
                        unsigned int block) {
   return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
 }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 022dedf..9c012a9 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -205,6 +205,7 @@
     shared_libs: [
         "libutils",
         "libbase",
+        "liblog",
     ],
 }
 
@@ -238,6 +239,66 @@
     srcs: ["Vector_fuzz.cpp"],
 }
 
+cc_fuzz {
+    name: "libutils_fuzz_printer",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["Printer_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_callstack",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["CallStack_fuzz.cpp"],
+    shared_libs: [
+        "libutilscallstack",
+    ],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_process_callstack",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["ProcessCallStack_fuzz.cpp"],
+    shared_libs: [
+        "libutilscallstack",
+    ],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_stopwatch",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["StopWatch_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_propertymap",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["PropertyMap_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_rwlock",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["RWLock_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_refbase",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["RefBase_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_lrucache",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["LruCache_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_looper",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["Looper_fuzz.cpp"],
+}
+
 cc_test {
     name: "libutils_test",
     host_supported: true,
diff --git a/libutils/CallStack_fuzz.cpp b/libutils/CallStack_fuzz.cpp
new file mode 100644
index 0000000..e89b5b7
--- /dev/null
+++ b/libutils/CallStack_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 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 <memory.h>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/CallStack.h"
+
+static constexpr int MAX_STRING_SIZE = 500;
+static constexpr int MAX_IGNORE_DEPTH = 200;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    size_t ignoreDepth = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_IGNORE_DEPTH);
+    int logPriority = dataProvider.ConsumeIntegral<int>();
+    pid_t tid = dataProvider.ConsumeIntegral<pid_t>();
+    std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
+    std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
+
+    const char* logTagChars = logTag.c_str();
+    const char* prefixChars = prefix.c_str();
+
+    android::CallStack::CallStackUPtr callStack = android::CallStack::getCurrent(ignoreDepth);
+    android::CallStack* callstackPtr = callStack.get();
+    android::CallStack::logStack(logTagChars, callstackPtr,
+                                 static_cast<android_LogPriority>(logPriority));
+    android::CallStack::stackToString(prefixChars);
+
+    callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+    callstackPtr->clear();
+    callstackPtr->getCurrent(ignoreDepth);
+    callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+    callstackPtr->update(ignoreDepth, tid);
+    callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+
+    return 0;
+}
diff --git a/libutils/Looper_fuzz.cpp b/libutils/Looper_fuzz.cpp
new file mode 100644
index 0000000..c3ae54e
--- /dev/null
+++ b/libutils/Looper_fuzz.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020 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 <sys/select.h>
+
+#include <iostream>
+
+#include <utils/Looper.h>
+
+#include "Looper_test_pipe.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::Looper;
+using android::sp;
+
+// We don't want this to bog down fuzzing
+static constexpr int MAX_POLL_DELAY = 50;
+static constexpr int MAX_OPERATIONS = 500;
+
+void doNothing() {}
+void* doNothingPointer = reinterpret_cast<void*>(doNothing);
+
+static int noopCallback(int, int, void*) {
+    return 0;
+}
+
+std::vector<std::function<void(FuzzedDataProvider*, sp<Looper>, Pipe)>> operations = {
+        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
+            looper->pollOnce(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
+        },
+        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
+            looper->pollAll(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
+        },
+        // events and callback are nullptr
+        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+                          dataProvider->ConsumeIntegral<int>(), nullptr, nullptr);
+        },
+        // Events is nullptr
+        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+                          dataProvider->ConsumeIntegral<int>(), noopCallback, nullptr);
+        },
+        // callback is nullptr
+        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+                          dataProvider->ConsumeIntegral<int>(), nullptr, doNothingPointer);
+        },
+        // callback and events both set
+        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+                          dataProvider->ConsumeIntegral<int>(), noopCallback, doNothingPointer);
+        },
+
+        [](FuzzedDataProvider*, sp<Looper> looper, Pipe) -> void { looper->wake(); },
+        [](FuzzedDataProvider*, sp<Looper>, Pipe pipeObj) -> void { pipeObj.writeSignal(); }};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    Pipe pipeObj;
+    FuzzedDataProvider dataProvider(data, size);
+    sp<Looper> looper = new Looper(dataProvider.ConsumeBool());
+
+    size_t opsRun = 0;
+    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+        operations[op](&dataProvider, looper, pipeObj);
+    }
+    // Clear our pointer
+    looper.clear();
+    return 0;
+}
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 37bdf05..34f424b 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -2,12 +2,13 @@
 // Copyright 2010 The Android Open Source Project
 //
 
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
 #include <gtest/gtest.h>
-#include <unistd.h>
 #include <time.h>
+#include <unistd.h>
+#include <utils/Looper.h>
+#include <utils/StopWatch.h>
+#include <utils/Timers.h>
+#include "Looper_test_pipe.h"
 
 #include <utils/threads.h>
 
@@ -24,41 +25,6 @@
     MSG_TEST4 = 4,
 };
 
-class Pipe {
-public:
-    int sendFd;
-    int receiveFd;
-
-    Pipe() {
-        int fds[2];
-        ::pipe(fds);
-
-        receiveFd = fds[0];
-        sendFd = fds[1];
-    }
-
-    ~Pipe() {
-        if (sendFd != -1) {
-            ::close(sendFd);
-        }
-
-        if (receiveFd != -1) {
-            ::close(receiveFd);
-        }
-    }
-
-    status_t writeSignal() {
-        ssize_t nWritten = ::write(sendFd, "*", 1);
-        return nWritten == 1 ? 0 : -errno;
-    }
-
-    status_t readSignal() {
-        char buf[1];
-        ssize_t nRead = ::read(receiveFd, buf, 1);
-        return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
-    }
-};
-
 class DelayedTask : public Thread {
     int mDelayMillis;
 
diff --git a/libutils/Looper_test_pipe.h b/libutils/Looper_test_pipe.h
new file mode 100644
index 0000000..77b7b8b
--- /dev/null
+++ b/libutils/Looper_test_pipe.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 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 <unistd.h>
+/**
+ * A pipe class for use when testing or fuzzing Looper
+ */
+class Pipe {
+  public:
+    int sendFd;
+    int receiveFd;
+
+    Pipe() {
+        int fds[2];
+        ::pipe(fds);
+
+        receiveFd = fds[0];
+        sendFd = fds[1];
+    }
+
+    ~Pipe() {
+        if (sendFd != -1) {
+            ::close(sendFd);
+        }
+
+        if (receiveFd != -1) {
+            ::close(receiveFd);
+        }
+    }
+
+    android::status_t writeSignal() {
+        ssize_t nWritten = ::write(sendFd, "*", 1);
+        return nWritten == 1 ? 0 : -errno;
+    }
+
+    android::status_t readSignal() {
+        char buf[1];
+        ssize_t nRead = ::read(receiveFd, buf, 1);
+        return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+    }
+};
diff --git a/libutils/LruCache_fuzz.cpp b/libutils/LruCache_fuzz.cpp
new file mode 100644
index 0000000..f8bacfc
--- /dev/null
+++ b/libutils/LruCache_fuzz.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 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 <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/LruCache.h"
+#include "utils/StrongPointer.h"
+
+typedef android::LruCache<size_t, size_t> FuzzCache;
+
+static constexpr uint32_t MAX_CACHE_ENTRIES = 800;
+
+class NoopRemovedCallback : public android::OnEntryRemoved<size_t, size_t> {
+  public:
+    void operator()(size_t&, size_t&) {
+        // noop
+    }
+};
+
+static NoopRemovedCallback callback;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, FuzzCache*)>> operations = {
+        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->removeOldest(); },
+        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->peekOldestValue(); },
+        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->clear(); },
+        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->size(); },
+        [](FuzzedDataProvider*, FuzzCache* cache) -> void {
+            android::LruCache<size_t, size_t>::Iterator iter(*cache);
+            while (iter.next()) {
+                iter.key();
+                iter.value();
+            }
+        },
+        [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+            size_t key = dataProvider->ConsumeIntegral<size_t>();
+            size_t val = dataProvider->ConsumeIntegral<size_t>();
+            cache->put(key, val);
+        },
+        [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+            size_t key = dataProvider->ConsumeIntegral<size_t>();
+            cache->get(key);
+        },
+        [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+            size_t key = dataProvider->ConsumeIntegral<size_t>();
+            cache->remove(key);
+        },
+        [](FuzzedDataProvider*, FuzzCache* cache) -> void {
+            cache->setOnEntryRemovedListener(&callback);
+        }};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    FuzzCache cache(MAX_CACHE_ENTRIES);
+    while (dataProvider.remaining_bytes() > 0) {
+        uint8_t op = dataProvider.ConsumeIntegral<uint8_t>() % operations.size();
+        operations[op](&dataProvider, &cache);
+    }
+
+    return 0;
+}
diff --git a/libutils/Printer_fuzz.cpp b/libutils/Printer_fuzz.cpp
new file mode 100755
index 0000000..0180d41
--- /dev/null
+++ b/libutils/Printer_fuzz.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/file.h"
+#include "android/log.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/Printer.h"
+#include "utils/String8.h"
+static constexpr int MAX_STR_SIZE = 1000;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    android::String8 outStr = android::String8();
+    // Line indent/formatting
+    uint indent = dataProvider.ConsumeIntegral<uint>();
+    std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+    std::string line = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+
+    // Misc properties
+    std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+    android_LogPriority priority =
+            static_cast<android_LogPriority>(dataProvider.ConsumeIntegral<int>());
+    bool ignoreBlankLines = dataProvider.ConsumeBool();
+
+    TemporaryFile tf;
+    android::FdPrinter filePrinter = android::FdPrinter(tf.fd, indent, prefix.c_str());
+    android::String8Printer stringPrinter = android::String8Printer(&outStr);
+    android::PrefixPrinter printer = android::PrefixPrinter(stringPrinter, prefix.c_str());
+    android::LogPrinter logPrinter =
+            android::LogPrinter(logTag.c_str(), priority, prefix.c_str(), ignoreBlankLines);
+
+    printer.printLine(line.c_str());
+    printer.printFormatLine("%s", line.c_str());
+    logPrinter.printLine(line.c_str());
+    logPrinter.printFormatLine("%s", line.c_str());
+    filePrinter.printLine(line.c_str());
+    filePrinter.printFormatLine("%s", line.c_str());
+    return 0;
+}
diff --git a/libutils/ProcessCallStack_fuzz.cpp b/libutils/ProcessCallStack_fuzz.cpp
new file mode 100644
index 0000000..30136cd
--- /dev/null
+++ b/libutils/ProcessCallStack_fuzz.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 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 <atomic>
+#include <thread>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/ProcessCallStack.h"
+using android::ProcessCallStack;
+
+static constexpr int MAX_NAME_SIZE = 1000;
+static constexpr int MAX_LOG_META_SIZE = 1000;
+static constexpr uint8_t MAX_THREADS = 10;
+
+std::atomic_bool ranCallStackUpdate(false);
+void loop() {
+    while (!ranCallStackUpdate.load()) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+    }
+}
+
+void spawnThreads(FuzzedDataProvider* dataProvider) {
+    std::vector<std::thread> threads = std::vector<std::thread>();
+
+    // Get the number of threads to generate
+    uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
+
+    // Generate threads
+    for (uint8_t i = 0; i < count; i++) {
+        std::string threadName =
+                dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));
+        std::thread th = std::thread(loop);
+        pthread_setname_np(th.native_handle(), threadName.c_str());
+        threads.push_back(move(th));
+    }
+
+    // Collect thread information
+    ProcessCallStack callStack = ProcessCallStack();
+    callStack.update();
+
+    // Tell our patiently waiting threads they can be done now.
+    ranCallStackUpdate.store(true);
+
+    std::string logTag = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
+    std::string prefix = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
+    // Both of these, along with dump, all call print() under the hood,
+    // Which is covered by the Printer fuzzer.
+    callStack.log(logTag.c_str());
+    callStack.toString(prefix.c_str());
+
+    // Check size
+    callStack.size();
+
+    // wait for any remaining threads
+    for (auto& thread : threads) {
+        thread.join();
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    spawnThreads(&dataProvider);
+    return 0;
+}
diff --git a/libutils/PropertyMap_fuzz.cpp b/libutils/PropertyMap_fuzz.cpp
new file mode 100755
index 0000000..fd50729
--- /dev/null
+++ b/libutils/PropertyMap_fuzz.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/PropertyMap.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_FILE_SIZE = 256;
+static constexpr int MAX_STR_LEN = 2048;
+static constexpr int MAX_OPERATIONS = 1000;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+        operations = {
+                [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+                    propertyMap.getProperties();
+                },
+                [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+                    propertyMap.clear();
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    android::String8 key = android::String8(keyStr.c_str());
+                    propertyMap.hasProperty(key);
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    android::String8 key = android::String8(keyStr.c_str());
+                    android::String8 out;
+                    propertyMap.tryGetProperty(key, out);
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+                    TemporaryFile tf;
+                    // Generate file contents
+                    std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
+                    // If we have string contents, dump them into the file.
+                    // Otherwise, just leave it as an empty file.
+                    if (contents.length() > 0) {
+                        const char* bytes = contents.c_str();
+                        android::base::WriteStringToFd(bytes, tf.fd);
+                    }
+                    android::PropertyMap* mapPtr = &propertyMap;
+                    android::PropertyMap::load(android::String8(tf.path), &mapPtr);
+                },
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    android::String8 key = android::String8(keyStr.c_str());
+                    android::String8 val = android::String8(valStr.c_str());
+                    propertyMap.addProperty(key, val);
+                },
+};
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    android::PropertyMap proprtyMap = android::PropertyMap();
+
+    int opsRun = 0;
+    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+        operations[op](&dataProvider, proprtyMap);
+    }
+    return 0;
+}
diff --git a/libutils/RWLock_fuzz.cpp b/libutils/RWLock_fuzz.cpp
new file mode 100755
index 0000000..e075905
--- /dev/null
+++ b/libutils/RWLock_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 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 <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/RWLock.h"
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_NAME_LEN = 2048;
+
+static const std::vector<std::function<void(android::RWLock*)>> operations = {
+        [](android::RWLock* lock) -> void {
+            // This might return a non-zero value if already locked
+            // Either way we are definitely locked now.
+            lock->tryWriteLock();
+        },
+        [](android::RWLock* lock) -> void { lock->tryReadLock(); },
+        [](android::RWLock* lock) -> void { lock->unlock(); },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
+    int type = dataProvider.ConsumeIntegral<int>();
+    android::RWLock rwLock = android::RWLock(type, nameStr.c_str());
+    std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
+    int opsRun = 0;
+    for (auto it : opsToRun) {
+        if (opsRun++ >= MAX_OPERATIONS) {
+            break;
+        }
+        it = it % operations.size();
+        operations[it](&rwLock);
+    }
+    rwLock.unlock();
+    return 0;
+}
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
new file mode 100755
index 0000000..2a92531
--- /dev/null
+++ b/libutils/RefBase_fuzz.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2020 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 <atomic>
+#include <thread>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/RefBase.h"
+#include "utils/StrongPointer.h"
+using android::RefBase;
+using android::sp;
+using android::wp;
+
+static constexpr int REFBASE_INITIAL_STRONG_VALUE = (1 << 28);
+static constexpr int REFBASE_MAX_COUNT = 0xfffff;
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_THREADS = 10;
+
+bool canDecrementStrong(RefBase* ref) {
+    // There's an assert around decrementing the strong count too much that causes an artificial
+    // crash This is just running BAD_STRONG from RefBase
+    const int32_t count = ref->getStrongCount() - 1;
+    return !(count == 0 || ((count) & (~(REFBASE_MAX_COUNT | REFBASE_INITIAL_STRONG_VALUE))) != 0);
+}
+bool canDecrementWeak(RefBase* ref) {
+    const int32_t count = ref->getWeakRefs()->getWeakCount() - 1;
+    return !((count) == 0 || ((count) & (~REFBASE_MAX_COUNT)) != 0);
+}
+
+struct RefBaseSubclass : public RefBase {
+    RefBaseSubclass() {}
+    virtual ~RefBaseSubclass() {}
+};
+
+std::vector<std::function<void(RefBaseSubclass*)>> operations = {
+        [](RefBaseSubclass* ref) -> void { ref->getStrongCount(); },
+        [](RefBaseSubclass* ref) -> void { ref->printRefs(); },
+        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
+        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->getWeakCount(); },
+        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->refBase(); },
+        [](RefBaseSubclass* ref) -> void { ref->incStrong(nullptr); },
+        [](RefBaseSubclass* ref) -> void {
+            if (canDecrementStrong(ref)) {
+                ref->decStrong(nullptr);
+            }
+        },
+        [](RefBaseSubclass* ref) -> void { ref->forceIncStrong(nullptr); },
+        [](RefBaseSubclass* ref) -> void { ref->createWeak(nullptr); },
+        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncStrong(nullptr); },
+        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncWeak(nullptr); },
+        [](RefBaseSubclass* ref) -> void {
+            if (canDecrementWeak(ref)) {
+                ref->getWeakRefs()->decWeak(nullptr);
+            }
+        },
+        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->incWeak(nullptr); },
+        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
+};
+
+void loop(RefBaseSubclass* loopRef, const std::vector<uint8_t>& fuzzOps) {
+    for (auto op : fuzzOps) {
+        operations[op % operations.size()](loopRef);
+    }
+}
+
+void spawnThreads(FuzzedDataProvider* dataProvider) {
+    std::vector<std::thread> threads = std::vector<std::thread>();
+
+    // Get the number of threads to generate
+    uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
+
+    // Generate threads
+    for (uint8_t i = 0; i < count; i++) {
+        RefBaseSubclass* threadRef = new RefBaseSubclass();
+        uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_OPERATIONS);
+        std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
+        std::thread tmp = std::thread(loop, threadRef, threadOperations);
+        threads.push_back(move(tmp));
+    }
+
+    for (auto& th : threads) {
+        th.join();
+    }
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    spawnThreads(&dataProvider);
+    return 0;
+}
diff --git a/libutils/StopWatch_fuzz.cpp b/libutils/StopWatch_fuzz.cpp
new file mode 100644
index 0000000..63d8a28
--- /dev/null
+++ b/libutils/StopWatch_fuzz.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 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 "fuzzer/FuzzedDataProvider.h"
+#include "utils/StopWatch.h"
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_NAME_LEN = 2048;
+
+static const std::vector<std::function<void(android::StopWatch)>> operations = {
+        [](android::StopWatch stopWatch) -> void { stopWatch.reset(); },
+        [](android::StopWatch stopWatch) -> void { stopWatch.lap(); },
+        [](android::StopWatch stopWatch) -> void { stopWatch.elapsedTime(); },
+        [](android::StopWatch stopWatch) -> void { stopWatch.name(); },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
+    int clockVal = dataProvider.ConsumeIntegral<int>();
+    android::StopWatch stopWatch = android::StopWatch(nameStr.c_str(), clockVal);
+    std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
+    int opsRun = 0;
+    for (auto it : opsToRun) {
+        if (opsRun++ >= MAX_OPERATIONS) {
+            break;
+        }
+        it = it % operations.size();
+        operations[it](stopWatch);
+    }
+    return 0;
+}
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 73ec1be..9c71141 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -39,8 +39,15 @@
  */
 int64_t uptimeMillis()
 {
-    int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
-    return (int64_t) nanoseconds_to_milliseconds(when);
+    return nanoseconds_to_milliseconds(uptimeNanos());
+}
+
+/*
+ * public static native long uptimeNanos();
+ */
+int64_t uptimeNanos()
+{
+    return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
 /*
diff --git a/libutils/SystemClock_test.cpp b/libutils/SystemClock_test.cpp
index 5ad060b..7449dad 100644
--- a/libutils/SystemClock_test.cpp
+++ b/libutils/SystemClock_test.cpp
@@ -29,16 +29,24 @@
 
 TEST(SystemClock, SystemClock) {
     auto startUptimeMs = android::uptimeMillis();
+    auto startUptimeNs = android::uptimeNanos();
     auto startRealtimeMs = android::elapsedRealtime();
     auto startRealtimeNs = android::elapsedRealtimeNano();
 
     ASSERT_GT(startUptimeMs, 0)
             << "uptimeMillis() reported an impossible uptime";
+    ASSERT_GT(startUptimeNs, 0)
+            << "uptimeNanos() reported an impossible uptime";
     ASSERT_GE(startRealtimeMs, startUptimeMs)
             << "elapsedRealtime() thinks we've suspended for negative time";
-    ASSERT_GE(startRealtimeNs, startUptimeMs * MS_IN_NS)
+    ASSERT_GE(startRealtimeNs, startUptimeNs)
             << "elapsedRealtimeNano() thinks we've suspended for negative time";
 
+    ASSERT_GE(startUptimeNs, startUptimeMs * MS_IN_NS)
+            << "uptimeMillis() and uptimeNanos() are inconsistent";
+    ASSERT_LT(startUptimeNs, (startUptimeMs + SLACK_MS) * MS_IN_NS)
+            << "uptimeMillis() and uptimeNanos() are inconsistent";
+
     ASSERT_GE(startRealtimeNs, startRealtimeMs * MS_IN_NS)
             << "elapsedRealtime() and elapsedRealtimeNano() are inconsistent";
     ASSERT_LT(startRealtimeNs, (startRealtimeMs + SLACK_MS) * MS_IN_NS)
@@ -51,6 +59,7 @@
     ASSERT_EQ(nanosleepErr, 0) << "nanosleep() failed: " << strerror(errno);
 
     auto endUptimeMs = android::uptimeMillis();
+    auto endUptimeNs = android::uptimeNanos();
     auto endRealtimeMs = android::elapsedRealtime();
     auto endRealtimeNs = android::elapsedRealtimeNano();
 
@@ -58,6 +67,10 @@
             << "uptimeMillis() advanced too little after nanosleep()";
     EXPECT_LT(endUptimeMs - startUptimeMs, SLEEP_MS + SLACK_MS)
             << "uptimeMillis() advanced too much after nanosleep()";
+    EXPECT_GE(endUptimeNs - startUptimeNs, SLEEP_NS)
+            << "uptimeNanos() advanced too little after nanosleep()";
+    EXPECT_LT(endUptimeNs - startUptimeNs, SLEEP_NS + SLACK_NS)
+            << "uptimeNanos() advanced too much after nanosleep()";
     EXPECT_GE(endRealtimeMs - startRealtimeMs, SLEEP_MS)
             << "elapsedRealtime() advanced too little after nanosleep()";
     EXPECT_LT(endRealtimeMs - startRealtimeMs, SLEEP_MS + SLACK_MS)
@@ -67,6 +80,11 @@
     EXPECT_LT(endRealtimeNs - startRealtimeNs, SLEEP_NS + SLACK_NS)
             << "elapsedRealtimeNano() advanced too much after nanosleep()";
 
+    EXPECT_GE(endUptimeNs, endUptimeMs * MS_IN_NS)
+            << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
+    EXPECT_LT(endUptimeNs, (endUptimeMs + SLACK_MS) * MS_IN_NS)
+            << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
+
     EXPECT_GE(endRealtimeNs, endRealtimeMs * MS_IN_NS)
             << "elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()";
     EXPECT_LT(endRealtimeNs, (endRealtimeMs + SLACK_MS) * MS_IN_NS)
diff --git a/libutils/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
index f816fba..892104c 100644
--- a/libutils/include/utils/SystemClock.h
+++ b/libutils/include/utils/SystemClock.h
@@ -23,6 +23,7 @@
 namespace android {
 
 int64_t uptimeMillis();
+int64_t uptimeNanos();
 int64_t elapsedRealtime();
 int64_t elapsedRealtimeNano();
 
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index f2fe7ef..0ba1621 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -32,6 +32,7 @@
 #include <string>
 
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <cutils/sockets.h>
 #include <log/log_properties.h>
@@ -64,53 +65,58 @@
     }
 }
 
-int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc,
-                                          char** argv) {
+template <typename F>
+static int LogIdCommand(SocketClient* cli, int argc, char** argv, F&& function) {
     setname();
+    if (argc < 2) {
+        cli->sendMsg("Missing Argument");
+        return 0;
+    }
+
+    int log_id;
+    if (!android::base::ParseInt(argv[1], &log_id, static_cast<int>(LOG_ID_MAIN),
+                                 static_cast<int>(LOG_ID_KERNEL))) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    function(static_cast<log_id_t>(log_id));
+    return 0;
+}
+
+int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc, char** argv) {
     uid_t uid = cli->getUid();
     if (clientHasLogCredentials(cli)) {
         uid = AID_ROOT;
     }
 
-    if (argc < 2) {
-        cli->sendMsg("Missing Argument");
-        return 0;
-    }
-
-    int id = atoi(argv[1]);
-    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
-        cli->sendMsg("Range Error");
-        return 0;
-    }
-
-    cli->sendMsg(buf()->Clear((log_id_t)id, uid) ? "success" : "busy");
-    return 0;
+    return LogIdCommand(cli, argc, argv, [&](log_id_t id) {
+        cli->sendMsg(buf()->Clear(id, uid) ? "success" : "busy");
+    });
 }
 
-int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
-                                               char** argv) {
-    setname();
-    if (argc < 2) {
-        cli->sendMsg("Missing Argument");
-        return 0;
-    }
+template <typename F>
+static int LogSizeCommand(SocketClient* cli, int argc, char** argv, F&& size_function) {
+    return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
+        cli->sendMsg(std::to_string(size_function(log_id)).c_str());
+    });
+}
 
-    int id = atoi(argv[1]);
-    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
-        cli->sendMsg("Range Error");
-        return 0;
-    }
+int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+    return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return buf()->GetSize(id); });
+}
 
-    size_t size = buf()->GetSize(static_cast<log_id_t>(id));
-    char buf[512];
-    snprintf(buf, sizeof(buf), "%zu", size);
-    cli->sendMsg(buf);
-    return 0;
+int CommandListener::GetBufSizeReadableCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+    return LogSizeCommand(cli, argc, argv,
+                          [this](log_id_t id) { return stats()->SizeReadable(id); });
+}
+
+int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+    return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return stats()->Sizes(id); });
 }
 
 int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
                                                char** argv) {
-    setname();
     if (!clientHasLogCredentials(cli)) {
         cli->sendMsg("Permission Denied");
         return 0;
@@ -120,62 +126,11 @@
         cli->sendMsg("Missing Argument");
         return 0;
     }
-
-    int id = atoi(argv[1]);
-    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
-        cli->sendMsg("Range Error");
-        return 0;
-    }
-
     size_t size = atol(argv[2]);
-    if (!buf()->SetSize(static_cast<log_id_t>(id), size)) {
-        cli->sendMsg("Range Error");
-        return 0;
-    }
 
-    cli->sendMsg("success");
-    return 0;
-}
-
-int CommandListener::GetBufSizeReadableCmd::runCommand(SocketClient* cli, int argc, char** argv) {
-    setname();
-    if (argc < 2) {
-        cli->sendMsg("Missing Argument");
-        return 0;
-    }
-
-    int id = atoi(argv[1]);
-    if (id < LOG_ID_MIN || LOG_ID_MAX <= id) {
-        cli->sendMsg("Range Error");
-        return 0;
-    }
-
-    size_t size = stats()->SizeReadable(static_cast<log_id_t>(id));
-    char buf[512];
-    snprintf(buf, sizeof(buf), "%zu", size);
-    cli->sendMsg(buf);
-    return 0;
-}
-
-int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
-                                                   char** argv) {
-    setname();
-    if (argc < 2) {
-        cli->sendMsg("Missing Argument");
-        return 0;
-    }
-
-    int id = atoi(argv[1]);
-    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
-        cli->sendMsg("Range Error");
-        return 0;
-    }
-
-    size_t size = stats()->Sizes(static_cast<log_id_t>(id));
-    char buf[512];
-    snprintf(buf, sizeof(buf), "%zu", size);
-    cli->sendMsg(buf);
-    return 0;
+    return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
+        cli->sendMsg(buf()->SetSize(log_id, size) ? "success" : "busy");
+    });
 }
 
 // This returns a string with a length prefix with the format <length>\n<data>\n\f.  The length
@@ -200,8 +155,7 @@
     return android::base::StringPrintf("%zu\n%s\n\f", total_size, str.c_str());
 }
 
-int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
-                                                  char** argv) {
+int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc, char** argv) {
     setname();
     uid_t uid = cli->getUid();
     if (clientHasLogCredentials(cli)) {
@@ -266,8 +220,7 @@
     return 0;
 }
 
-int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
-                                                char** argv) {
+int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc, char** argv) {
     setname();
     uid_t uid = cli->getUid();
     if (clientHasLogCredentials(cli)) {
@@ -311,8 +264,7 @@
     return 0;
 }
 
-int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
-                                           char** /*argv*/) {
+int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int, char**) {
     setname();
 
     LOG(INFO) << "logd reinit";
@@ -335,8 +287,7 @@
     return 0;
 }
 
-int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
-                                         char** /*argv*/) {
+int CommandListener::ExitCmd::runCommand(SocketClient* cli, int, char**) {
     setname();
 
     cli->sendMsg("success");
diff --git a/logd/README.compression.md b/logd/README.compression.md
new file mode 100644
index 0000000..4ba634a
--- /dev/null
+++ b/logd/README.compression.md
@@ -0,0 +1,81 @@
+# Log Compression instead of Chatty in Android S
+
+## The problem
+
+* Log buffer space is precious, but suffers from the tragedy of the commons
+* Log spam fills the buffers making them less useful in logcat/bugreports
+* “Spam” is often in the eye of the beholder: which messages are important depends on what you’re trying to debug
+
+## The idea
+
+* Chatty isn’t helping as much as we’d hoped, and is surprisingly expensive
+* Compress logs to make more efficient use of the buffer
+* Address the root cause of log spam at its source:
+    * Do not hide log spam at runtime, which de-incentivize fixes
+    * Add presubmit coverage similar to SELinux violations to keep log spam down
+
+---
+
+## Chatty in Theory
+
+* Delete messages classified as spam to extend the range of logs from other sources
+* “Spam” defined as:
+    * Logs from UIDs whose logs consume over 12.5% of a log buffer
+    * Back-to-back exact duplicate messages
+
+## Chatty in Practice
+
+* Developer confusion about missing and de-duplicated logs
+* Lowered incentive to fix the root cause of bad logging behavior
+* High CPU overhead
+* Memory usage greatly exceeds configured buffer size
+* Only marginal increase in log range
+
+---
+
+## Log Compression in Theory
+
+* Store many more logs in the same log buffer size => better for diagnosis
+* Memory usage stays below configured log size => better system health
+* No gaps in logs, no de-duplicated logs => no developer confusion
+* No hiding bad behavior => increased accountability/incentive to fix root causes
+
+## Log Compression Preliminary Results
+
+* Captured 2, 5 day periods of full time personal usage of Pixel 4 and replayed the logs offline
+* Compression vs Chatty:
+    * **3.5x more log messages on average**
+    * **50% less CPU usage**
+    * **50% less memory usage**
+
+---
+
+## Log Messages in 1MB
+
+* The number of log messages still available in logcat after ‘Message Count’ messages have been logged to a 1MB log buffer
+* Note: ‘Simple’ is the Chatty code without log spam detection and without de-duplication.
+
+![Total Log Count](doc_images/total_log_count.png)
+
+---
+
+## CPU Time
+
+* Total CPU time on ARM64 (Walleye) and 32bit x86 (Cuttlefish)
+* X axis represents different log buffer size configurations.
+    * Chatty uses significantly more CPU time at 1MB (the default Pixel configuration)
+    * Chatty scales poorly with increased log buffer sizes
+* Note: “simple” isn’t “compression without actually compressing”, it’s “chatty without doing the chatty elimination”, which is why “simple” is more expensive than “compression” on walleye.
+
+![CPU Time Walleye](doc_images/cpu_walleye.png)
+![CPU Time Cuttlefish](doc_images/cpu_cuttlefish.png)
+
+---
+
+## Memory Usage
+
+* The memory used by ‘Message Count’ messages, on both Walleye and Cuttlefish
+* Note: Chatty does not consider the metadata (UID, PID, timestamp, etc) in its calculation of log buffer size, so a 1MB log buffer will consume more than 1MB.  Note that there are 8 log buffers, 5 of which are typically filled.
+
+![Memory Usage](doc_images/memory_usage.png)
+
diff --git a/logd/doc_images/cpu_cuttlefish.png b/logd/doc_images/cpu_cuttlefish.png
new file mode 100644
index 0000000..8d809ca
--- /dev/null
+++ b/logd/doc_images/cpu_cuttlefish.png
Binary files differ
diff --git a/logd/doc_images/cpu_walleye.png b/logd/doc_images/cpu_walleye.png
new file mode 100644
index 0000000..39c951b
--- /dev/null
+++ b/logd/doc_images/cpu_walleye.png
Binary files differ
diff --git a/logd/doc_images/memory_usage.png b/logd/doc_images/memory_usage.png
new file mode 100644
index 0000000..434d6d3
--- /dev/null
+++ b/logd/doc_images/memory_usage.png
Binary files differ
diff --git a/logd/doc_images/total_log_count.png b/logd/doc_images/total_log_count.png
new file mode 100644
index 0000000..e73c2c1
--- /dev/null
+++ b/logd/doc_images/total_log_count.png
Binary files differ