Merge "Use property ro.product.enforce_debugfs_restrictions to enable debugfs restrictions"
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 700d4bd..dfed77e 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -673,7 +673,7 @@
     return fd;
 }
 
-static void CheckRequirement(const std::string& cur_product, const std::string& var,
+static bool CheckRequirement(const std::string& cur_product, const std::string& var,
                              const std::string& product, bool invert,
                              const std::vector<std::string>& options) {
     Status("Checking '" + var + "'");
@@ -685,7 +685,7 @@
             double split = now();
             fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
                     cur_product.c_str(), product.c_str(), (split - start));
-            return;
+            return true;
         }
     }
 
@@ -694,7 +694,7 @@
         fprintf(stderr, "FAILED\n\n");
         fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
                 fb->Error().c_str());
-        die("requirements not met!");
+        return false;
     }
 
     bool match = false;
@@ -714,7 +714,7 @@
     if (match) {
         double split = now();
         fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
-        return;
+        return true;
     }
 
     fprintf(stderr, "FAILED\n\n");
@@ -724,7 +724,7 @@
         fprintf(stderr, " or '%s'", it->c_str());
     }
     fprintf(stderr, ".\n\n");
-    die("requirements not met!");
+    return false;
 }
 
 bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
@@ -788,7 +788,7 @@
     }
 }
 
-static void CheckRequirements(const std::string& data) {
+static void CheckRequirements(const std::string& data, bool force_flash) {
     std::string cur_product;
     if (fb->GetVar("product", &cur_product) != fastboot::SUCCESS) {
         fprintf(stderr, "getvar:product FAILED (%s)\n", fb->Error().c_str());
@@ -812,7 +812,14 @@
         if (name == "partition-exists") {
             HandlePartitionExists(options);
         } else {
-            CheckRequirement(cur_product, name, product, invert, options);
+            bool met = CheckRequirement(cur_product, name, product, invert, options);
+            if (!met) {
+                if (!force_flash) {
+                  die("requirements not met!");
+                } else {
+                  fprintf(stderr, "requirements not met! but proceeding due to --force\n");
+                }
+            }
         }
     }
 }
@@ -1405,7 +1412,8 @@
 
 class FlashAllTool {
   public:
-    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe);
+    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
+                 bool wipe, bool force_flash);
 
     void Flash();
 
@@ -1421,16 +1429,19 @@
     std::string slot_override_;
     bool skip_secondary_;
     bool wipe_;
+    bool force_flash_;
     std::string secondary_slot_;
     std::vector<std::pair<const Image*, std::string>> boot_images_;
     std::vector<std::pair<const Image*, std::string>> os_images_;
 };
 
-FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe)
+FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
+                           bool skip_secondary, bool wipe, bool force_flash)
    : source_(source),
      slot_override_(slot_override),
      skip_secondary_(skip_secondary),
-     wipe_(wipe)
+     wipe_(wipe),
+     force_flash_(force_flash)
 {
 }
 
@@ -1478,7 +1489,7 @@
     if (!source_.ReadFile("android-info.txt", &contents)) {
         die("could not read android-info.txt");
     }
-    ::CheckRequirements({contents.data(), contents.size()});
+    ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
 }
 
 void FlashAllTool::DetermineSecondarySlot() {
@@ -1598,14 +1609,15 @@
     return unzip_to_file(zip_, name.c_str());
 }
 
-static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary,
+                      bool force_flash) {
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
-    FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false);
+    FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false, force_flash);
     tool.Flash();
 
     CloseArchive(zip);
@@ -1630,8 +1642,9 @@
     return unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
 }
 
-static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
-    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
+static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe,
+                        bool force_flash) {
+    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe, force_flash);
     tool.Flash();
 }
 
@@ -2179,9 +2192,9 @@
         } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(slot_override, true, wants_wipe);
+                do_flashall(slot_override, true, wants_wipe, force_flash);
             } else {
-                do_flashall(slot_override, skip_secondary, wants_wipe);
+                do_flashall(slot_override, skip_secondary, wants_wipe, force_flash);
             }
             wants_reboot = true;
         } else if (command == "update") {
@@ -2193,7 +2206,7 @@
             if (!args.empty()) {
                 filename = next_arg(&args);
             }
-            do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
+            do_update(filename.c_str(), slot_override, skip_secondary || slot_all, force_flash);
             wants_reboot = true;
         } else if (command == FB_CMD_SET_ACTIVE) {
             std::string slot = verify_slot(next_arg(&args), false);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6952cdf..bbbb7e8 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -647,6 +647,46 @@
     return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
 }
 
+static void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {
+    std::string block_device;
+    if (!Realpath(entry_block_device, &block_device)) {
+        PERROR << "Failed to realpath " << entry_block_device;
+        return;
+    }
+
+    static constexpr std::string_view kDevBlockPrefix("/dev/block/");
+    if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {
+        LWARNING << block_device << " is not a block device";
+        return;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    while (true) {
+        std::string block_name = block_device;
+        if (android::base::StartsWith(block_device, kDevBlockPrefix)) {
+            block_name = block_device.substr(kDevBlockPrefix.length());
+        }
+        std::string sys_partition =
+                android::base::StringPrintf("/sys/class/block/%s/partition", block_name.c_str());
+        struct stat info;
+        if (lstat(sys_partition.c_str(), &info) == 0) {
+            // it has a partition like "sda12".
+            block_name += "/..";
+        }
+        std::string sys_ra = android::base::StringPrintf("/sys/class/block/%s/queue/read_ahead_kb",
+                                                         block_name.c_str());
+        std::string size = android::base::StringPrintf("%llu", (long long)size_kb);
+        android::base::WriteStringToFile(size, sys_ra.c_str());
+        LINFO << "Set readahead_kb: " << size << " on " << sys_ra;
+
+        auto parent = dm.GetParentBlockDeviceByPath(block_device);
+        if (!parent) {
+            return;
+        }
+        block_device = *parent;
+    }
+}
+
 //
 // Prepare the filesystem on the given block device to be mounted.
 //
@@ -667,6 +707,11 @@
     }
     mkdir(mount_point.c_str(), 0755);
 
+    // Don't need to return error, since it's a salt
+    if (entry.readahead_size_kb != -1) {
+        SetReadAheadSize(blk_device, entry.readahead_size_kb);
+    }
+
     int fs_stat = 0;
 
     if (is_extfs(entry.fs_type)) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0c0862e..42bf356 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
+#include <fnmatch.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -254,6 +255,13 @@
             } else {
                 entry->reserved_size = static_cast<off64_t>(size);
             }
+        } else if (StartsWith(flag, "readahead_size_kb=")) {
+            int val;
+            if (ParseInt(arg, &val, 0, 16 * 1024)) {
+                entry->readahead_size_kb = val;
+            } else {
+                LWARNING << "Warning: readahead_size_kb= flag malformed (0 ~ 16MB): " << arg;
+            }
         } else if (StartsWith(flag, "eraseblk=")) {
             // The erase block size flag is followed by an = and the flash erase block size. Get it,
             // check that it is a power of 2 and at least 4096, and return it.
@@ -680,7 +688,7 @@
     }
 }
 
-bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (!fstab_file) {
         PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
@@ -689,41 +697,51 @@
 
     bool is_proc_mounts = path == "/proc/mounts";
 
-    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, fstab)) {
+    Fstab fstab;
+    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
-    if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
-        // This is expected to fail if host is android Q, since Q doesn't
-        // support DSU slotting. The DSU "active" indicator file would be
-        // non-existent or empty if DSU is enabled within the guest system.
-        // In that case, just use the default slot name "dsu".
-        std::string dsu_slot;
-        if (!android::gsi::GetActiveDsu(&dsu_slot)) {
-            PWARNING << __FUNCTION__ << "(): failed to get active dsu slot";
+    if (!is_proc_mounts) {
+        if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+            // This is expected to fail if host is android Q, since Q doesn't
+            // support DSU slotting. The DSU "active" indicator file would be
+            // non-existent or empty if DSU is enabled within the guest system.
+            // In that case, just use the default slot name "dsu".
+            std::string dsu_slot;
+            if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
+                PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
+                return false;
+            }
+            if (dsu_slot.empty()) {
+                dsu_slot = "dsu";
+                LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
+            }
+            // This file is non-existent on Q vendor.
+            std::string lp_names;
+            if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
+                PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
+                return false;
+            }
+            TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
+        } else if (errno != ENOENT) {
+            PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
+            return false;
         }
-        if (dsu_slot.empty()) {
-            dsu_slot = "dsu";
-        }
-
-        std::string lp_names;
-        ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
-        TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, ","));
     }
 
-#ifndef NO_SKIP_MOUNT
-    SkipMountingPartitions(fstab);
-#endif
-    EnableMandatoryFlags(fstab);
+    SkipMountingPartitions(&fstab, false /* verbose */);
+    EnableMandatoryFlags(&fstab);
 
+    *fstab_out = std::move(fstab);
     return true;
 }
 
 // Returns fstab entries parsed from the device tree if they exist
-bool ReadFstabFromDt(Fstab* fstab, bool log) {
+bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
     std::string fstab_buf = ReadFstabFromDt();
     if (fstab_buf.empty()) {
-        if (log) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
+        if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
         return false;
     }
 
@@ -731,34 +749,36 @@
         fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
                  fstab_buf.length(), "r"), fclose);
     if (!fstab_file) {
-        if (log) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+        if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
         return false;
     }
 
     if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
-        if (log) {
+        if (verbose) {
             LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
                    << fstab_buf;
         }
         return false;
     }
 
-#ifndef NO_SKIP_MOUNT
-    SkipMountingPartitions(fstab);
-#endif
+    SkipMountingPartitions(fstab, verbose);
 
     return true;
 }
 
-#ifndef NO_SKIP_MOUNT
+#ifdef NO_SKIP_MOUNT
+bool SkipMountingPartitions(Fstab*, bool) {
+    return true;
+}
+#else
 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
 // only common files for all targets can be put into system partition. It is under
 // /system/system_ext because GSI is a single system.img that includes the contents of system_ext
 // partition and product partition under /system/system_ext and /system/product, respectively.
-bool SkipMountingPartitions(Fstab* fstab) {
-    constexpr const char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
+bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
+    static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
 
     std::string skip_config;
     auto save_errno = errno;
@@ -767,29 +787,39 @@
         return true;
     }
 
-    for (const auto& skip_mount_point : Split(skip_config, "\n")) {
-        if (skip_mount_point.empty()) {
+    std::vector<std::string> skip_mount_patterns;
+    for (const auto& line : Split(skip_config, "\n")) {
+        if (line.empty() || StartsWith(line, "#")) {
             continue;
         }
-        auto it = std::remove_if(fstab->begin(), fstab->end(),
-                                 [&skip_mount_point](const auto& entry) {
-                                     return entry.mount_point == skip_mount_point;
-                                 });
-        if (it == fstab->end()) continue;
-        fstab->erase(it, fstab->end());
-        LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
+        skip_mount_patterns.push_back(line);
     }
 
+    // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry
+    // would be partitioned to the second group.
+    auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool {
+        for (const auto& pattern : skip_mount_patterns) {
+            if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) {
+                return false;
+            }
+        }
+        return true;
+    };
+    auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch);
+    if (verbose) {
+        for (auto it = remove_from; it != fstab->end(); ++it) {
+            LINFO << "Skip mounting mountpoint: " << it->mount_point;
+        }
+    }
+    fstab->erase(remove_from, fstab->end());
     return true;
 }
 #endif
 
 // Loads the fstab file and combines with fstab entries passed in from device tree.
 bool ReadDefaultFstab(Fstab* fstab) {
-    Fstab dt_fstab;
-    ReadFstabFromDt(&dt_fstab, false);
-
-    *fstab = std::move(dt_fstab);
+    fstab->clear();
+    ReadFstabFromDt(fstab, false /* verbose */);
 
     std::string default_fstab_path;
     // Use different fstab paths for normal boot and recovery boot, respectively
@@ -800,16 +830,14 @@
     }
 
     Fstab default_fstab;
-    if (!default_fstab_path.empty()) {
-        ReadFstabFromFile(default_fstab_path, &default_fstab);
+    if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
+        for (auto&& entry : default_fstab) {
+            fstab->emplace_back(std::move(entry));
+        }
     } else {
         LINFO << __FUNCTION__ << "(): failed to find device default fstab";
     }
 
-    for (auto&& entry : default_fstab) {
-        fstab->emplace_back(std::move(entry));
-    }
-
     return !fstab->empty();
 }
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 2d4de09..2704e47 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -47,6 +47,7 @@
     int max_comp_streams = 0;
     off64_t zram_size = 0;
     off64_t reserved_size = 0;
+    off64_t readahead_size_kb = -1;
     std::string encryption_options;
     off64_t erase_blk_size = 0;
     off64_t logical_blk_size = 0;
@@ -97,9 +98,9 @@
 using Fstab = std::vector<FstabEntry>;
 
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
-bool ReadFstabFromDt(Fstab* fstab, bool log = true);
+bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
 bool ReadDefaultFstab(Fstab* fstab);
-bool SkipMountingPartitions(Fstab* fstab);
+bool SkipMountingPartitions(Fstab* fstab, bool verbose = false);
 
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 // The Fstab can contain multiple entries for the same mount point with different configurations.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
new file mode 100644
index 0000000..ac2c787
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2021 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 <memory>
+
+#include <gmock/gmock.h>
+#include <libsnapshot/snapshot_stats.h>
+
+namespace android::snapshot {
+
+class MockSnapshotMergeStats final : public ISnapshotMergeStats {
+  public:
+    virtual ~MockSnapshotMergeStats() = default;
+    // Called when merge starts or resumes.
+    MOCK_METHOD(bool, Start, (), (override));
+    MOCK_METHOD(void, set_state, (android::snapshot::UpdateState, bool), (override));
+    MOCK_METHOD(void, set_cow_file_size, (uint64_t), ());
+    MOCK_METHOD(void, set_total_cow_size_bytes, (uint64_t), (override));
+    MOCK_METHOD(void, set_estimated_cow_size_bytes, (uint64_t), (override));
+    MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
+    MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
+    MOCK_METHOD(uint64_t, cow_file_size, (), (override));
+    MOCK_METHOD(uint64_t, total_cow_size_bytes, (), (override));
+    MOCK_METHOD(uint64_t, estimated_cow_size_bytes, (), (override));
+    MOCK_METHOD(uint32_t, boot_complete_time_ms, (), (override));
+    MOCK_METHOD(uint32_t, boot_complete_to_merge_start_time_ms, (), (override));
+    MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
+
+    using ISnapshotMergeStats::Result;
+    // Return nullptr if any failure.
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index 1dab361..280e857 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -69,6 +69,8 @@
     // must ONLY be called if the control device has already been deleted.
     bool WaitForDeviceDelete(const std::string& control_device);
 
+    void CloseConnection() { sockfd_ = {}; }
+
     // Detach snapuserd. This shuts down the listener socket, and will cause
     // snapuserd to gracefully exit once all handler threads have terminated.
     // This should only be used on first-stage instances of snapuserd.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 2c5bf75..a0a1e4f 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -2297,6 +2297,17 @@
             return false;
         }
     }
+
+    // Terminate the daemon and release the snapuserd_client_ object.
+    // If we need to re-connect with the daemon, EnsureSnapuserdConnected()
+    // will re-create the object and establish the socket connection.
+    if (snapuserd_client_) {
+        LOG(INFO) << "Shutdown snapuserd daemon";
+        snapuserd_client_->DetachSnapuserd();
+        snapuserd_client_->CloseConnection();
+        snapuserd_client_ = nullptr;
+    }
+
     return true;
 }
 
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6ed0129..8fae00b 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2019,6 +2019,8 @@
 
     // Read bytes back and verify they match the cache.
     ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+
+    ASSERT_TRUE(sm->UnmapAllSnapshots());
 }
 
 TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) {
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
index 9335364..87c5528 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -158,6 +158,7 @@
     bool CommitMerge(int num_merge_ops);
 
     void CloseFds() { cow_fd_ = {}; }
+    void FreeResources() { worker_threads_.clear(); }
     size_t GetMetadataAreaSize() { return vec_.size(); }
     void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
 
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 167895e..64332d1 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -219,7 +219,13 @@
         auto iter = FindHandler(&lock, handler->misc_name());
         if (iter == dm_users_.end()) {
             // RemoveAndJoinHandler() already removed us from the list, and is
-            // now waiting on a join(), so just return.
+            // now waiting on a join(), so just return. Additionally, release
+            // all the resources held by snapuserd object which are shared
+            // by worker threads. This should be done when the last reference
+            // of "handler" is released; but we will explicitly release here
+            // to make sure snapuserd object is freed as it is the biggest
+            // consumer of memory in the daemon.
+            handler->FreeResources();
             LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
             return;
         }
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h
index e9d575d..6699189 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd_server.h
@@ -49,7 +49,15 @@
   public:
     explicit DmUserHandler(std::shared_ptr<Snapuserd> snapuserd);
 
-    void FreeResources() { snapuserd_ = nullptr; }
+    void FreeResources() {
+        // Each worker thread holds a reference to snapuserd.
+        // Clear them so that all the resources
+        // held by snapuserd is released
+        if (snapuserd_) {
+            snapuserd_->FreeResources();
+            snapuserd_ = nullptr;
+        }
+    }
     const std::shared_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
     std::thread& thread() { return thread_; }
 
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 5887641..9adb6bd 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1097,3 +1097,59 @@
     ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
             << "/data wasn't mounted from default fstab";
 }
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      readahead_size_kb=blah
+source none1       swap   defaults      readahead_size_kb=128
+source none2       swap   defaults      readahead_size_kb=5%
+source none3       swap   defaults      readahead_size_kb=5kb
+source none4       swap   defaults      readahead_size_kb=16385
+source none5       swap   defaults      readahead_size_kb=-128
+source none6       swap   defaults      readahead_size_kb=0
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(7U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(128, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none4", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none5", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none6", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->readahead_size_kb);
+}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index a11bb28..3faf430 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -97,7 +97,6 @@
 
     bool MountPartitions();
     bool TrySwitchSystemAsRoot();
-    bool TrySkipMountingPartitions();
     bool IsDmLinearEnabled();
     void GetSuperDeviceName(std::set<std::string>* devices);
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
@@ -534,7 +533,7 @@
 bool FirstStageMount::MountPartitions() {
     if (!TrySwitchSystemAsRoot()) return false;
 
-    if (!SkipMountingPartitions(&fstab_)) return false;
+    if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
 
     for (auto current = fstab_.begin(); current != fstab_.end();) {
         // We've already mounted /system above.
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 73ef97a..c2eb73c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1176,7 +1176,7 @@
     } else if (qemu_key == "media.ccodec"sv) {
         return "debug.stagefright.ccodec"s;
     } else {
-        return ""s;  // TBD
+        return "qemu."s + std::string(qemu_key);
     }
 }
 
@@ -1200,6 +1200,22 @@
             // emulator specific, should be retired once emulator migrates to
             // androidboot.
             InitPropertySet("ro.boot." + key, value);
+        } else if (key == "android.bootanim" && value == "0") {
+            // emulator specific, should be retired once emulator migrates to
+            // androidboot.
+            InitPropertySet("ro.boot.debug.sf.nobootanimation", "1");
+        } else if (key == "android.checkjni") {
+            // emulator specific, should be retired once emulator migrates to
+            // androidboot.
+            std::string value_bool;
+            if (value == "0") {
+                value_bool = "false";
+            } else if (value == "1") {
+                value_bool = "true";
+            } else {
+                value_bool = value;
+            }
+            InitPropertySet("ro.boot.dalvik.vm.checkjni", value_bool);
         }
     });
 }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 62c4586..2d3e06e 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -658,7 +658,7 @@
         extra_fstab.emplace_back(std::move(entry));
     }
 
-    SkipMountingPartitions(&extra_fstab);
+    SkipMountingPartitions(&extra_fstab, true /* verbose */);
     if (extra_fstab.empty()) {
         return;
     }
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index cab988b..2221228 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -106,10 +106,10 @@
     }
 
     if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
-                     [&args](const auto& other) { return other.devpath == args[2]; }) !=
+                     [&args](const auto& other) { return other.devpath == args[1]; }) !=
         external_firmware_handlers->end()) {
         return Error() << "found a previous external_firmware_handler with the same devpath, '"
-                       << args[2] << "'";
+                       << args[1] << "'";
     }
 
     passwd* pwd = getpwnam(args[2].c_str());
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index b604c53..4e63ba5 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -45,6 +45,13 @@
     EXPECT_EQ(expected.attribute_, test.attribute_);
 }
 
+void TestExternalFirmwareHandler(const ExternalFirmwareHandler& expected,
+                                 const ExternalFirmwareHandler& test) {
+    EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
+    EXPECT_EQ(expected.uid, test.uid) << expected.uid;
+    EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
+}
+
 template <typename T, typename F>
 void TestVector(const T& expected, const T& test, F function) {
     ASSERT_EQ(expected.size(), test.size());
@@ -67,6 +74,8 @@
     TestVector(expected.sysfs_permissions, result.sysfs_permissions, TestSysfsPermissions);
     TestVector(expected.dev_permissions, result.dev_permissions, TestPermissions);
     EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
+    TestVector(expected.external_firmware_handlers, result.external_firmware_handlers,
+               TestExternalFirmwareHandler);
 }
 
 TEST(ueventd_parser, EmptyFile) {
@@ -144,7 +153,7 @@
     auto ueventd_file = R"(
 external_firmware_handler devpath root handler_path
 external_firmware_handler /devices/path/firmware/something001.bin system /vendor/bin/firmware_handler.sh
-external_firmware_handler /devices/path/firmware/something001.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
+external_firmware_handler /devices/path/firmware/something002.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
 )";
 
     auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
@@ -159,7 +168,7 @@
                     "/vendor/bin/firmware_handler.sh",
             },
             {
-                    "/devices/path/firmware/something001.bin",
+                    "/devices/path/firmware/something002.bin",
                     AID_RADIO,
                     "/vendor/bin/firmware_handler.sh --has --arguments",
             },
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index baee4f9..c934860 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -24,7 +24,8 @@
 
 class Modprobe {
   public:
-    Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load");
+    Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
+             bool use_blocklist = true);
 
     bool LoadListedModules(bool strict = true);
     bool LoadWithAliases(const std::string& module_name, bool strict,
@@ -36,7 +37,6 @@
                             std::vector<std::string>* post_dependencies);
     void ResetModuleCount() { module_count_ = 0; }
     int GetModuleCount() { return module_count_; }
-    void EnableBlocklist(bool enable);
 
   private:
     std::string MakeCanonical(const std::string& module_path);
@@ -48,6 +48,7 @@
     void AddOption(const std::string& module_name, const std::string& option_name,
                    const std::string& value);
     std::string GetKernelCmdline();
+    bool IsBlocklisted(const std::string& module_name);
 
     bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
     bool ParseAliasCallback(const std::vector<std::string>& args);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index b3ae937..1a9d364 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -313,7 +313,9 @@
     }
 }
 
-Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file) {
+Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file,
+                   bool use_blocklist)
+    : blocklist_enabled(use_blocklist) {
     using namespace std::placeholders;
 
     for (const auto& base_path : base_paths) {
@@ -339,10 +341,6 @@
     ParseKernelCmdlineOptions();
 }
 
-void Modprobe::EnableBlocklist(bool enable) {
-    blocklist_enabled = enable;
-}
-
 std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
     auto it = module_deps_.find(module);
     if (it == module_deps_.end()) {
@@ -427,10 +425,23 @@
     return true;
 }
 
+bool Modprobe::IsBlocklisted(const std::string& module_name) {
+    if (!blocklist_enabled) return false;
+
+    auto canonical_name = MakeCanonical(module_name);
+    auto dependencies = GetDependencies(canonical_name);
+    for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
+        if (module_blocklist_.count(MakeCanonical(*dep))) return true;
+    }
+
+    return module_blocklist_.count(canonical_name) > 0;
+}
+
 bool Modprobe::LoadListedModules(bool strict) {
     auto ret = true;
     for (const auto& module : module_load_) {
         if (!LoadWithAliases(module, true)) {
+            if (IsBlocklisted(module)) continue;
             ret = false;
             if (strict) break;
         }
@@ -440,16 +451,10 @@
 
 bool Modprobe::Remove(const std::string& module_name) {
     auto dependencies = GetDependencies(MakeCanonical(module_name));
-    if (dependencies.empty()) {
-        LOG(ERROR) << "Empty dependencies for module " << module_name;
-        return false;
-    }
-    if (!Rmmod(dependencies[0])) {
-        return false;
-    }
-    for (auto dep = dependencies.begin() + 1; dep != dependencies.end(); ++dep) {
+    for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
         Rmmod(*dep);
     }
+    Rmmod(module_name);
     return true;
 }
 
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index d50c10d..f960b61 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -78,6 +78,18 @@
             "/test13.ko",
     };
 
+    std::vector<std::string> expected_modules_blocklist_enabled = {
+            "/test1.ko option1=50 option2=60",
+            "/test6.ko",
+            "/test2.ko",
+            "/test5.ko option1=",
+            "/test8.ko",
+            "/test7.ko param1=4",
+            "/test12.ko",
+            "/test11.ko",
+            "/test13.ko",
+    };
+
     const std::string modules_dep =
             "test1.ko:\n"
             "test2.ko:\n"
@@ -146,7 +158,7 @@
         *i = dir.path + *i;
     }
 
-    Modprobe m({dir.path});
+    Modprobe m({dir.path}, "modules.load", false);
     EXPECT_TRUE(m.LoadListedModules());
 
     GTEST_LOG_(INFO) << "Expected modules loaded (in order):";
@@ -176,8 +188,22 @@
 
     EXPECT_TRUE(modules_loaded == expected_after_remove);
 
-    m.EnableBlocklist(true);
+    m = Modprobe({dir.path});
     EXPECT_FALSE(m.LoadWithAliases("test4", true));
+    while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front()));
+    EXPECT_TRUE(m.LoadListedModules());
+
+    GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):";
+    for (auto i = expected_modules_blocklist_enabled.begin();
+         i != expected_modules_blocklist_enabled.end(); ++i) {
+        *i = dir.path + *i;
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+    GTEST_LOG_(INFO) << "Actual modules loaded with blocklist enabled (in order):";
+    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+    EXPECT_TRUE(modules_loaded == expected_modules_blocklist_enabled);
 }
 
 TEST(libmodprobe, ModuleDepLineWithoutColonIsSkipped) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 5b57bdd..bd94621 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -70,11 +70,11 @@
       "Name": "Frozen",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "freezer",
-            "Path": ""
+            "Name": "FreezerState",
+            "Value": "1"
           }
         }
       ]
@@ -83,11 +83,11 @@
       "Name": "Unfrozen",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "freezer",
-            "Path": "../"
+            "Name": "FreezerState",
+            "Value": "0"
           }
         }
       ]
@@ -558,32 +558,6 @@
         }
       ]
     },
-    {
-      "Name": "FreezerDisabled",
-      "Actions": [
-        {
-          "Name": "SetAttribute",
-          "Params":
-          {
-            "Name": "FreezerState",
-            "Value": "0"
-          }
-        }
-      ]
-    },
-    {
-      "Name": "FreezerEnabled",
-      "Actions": [
-        {
-          "Name": "SetAttribute",
-          "Params":
-          {
-            "Name": "FreezerState",
-            "Value": "1"
-          }
-        }
-      ]
-    }
   ],
 
   "AggregateProfiles": [
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index f13a681..db00a49 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -518,10 +518,10 @@
                 std::string attr_filepath = params_val["FilePath"].asString();
                 std::string attr_value = params_val["Value"].asString();
                 if (!attr_filepath.empty() && !attr_value.empty()) {
-                    const Json::Value& logfailures = params_val["LogFailures"];
-                    bool attr_logfailures = logfailures.isNull() || logfailures.asBool();
+                    std::string attr_logfailures = params_val["LogFailures"].asString();
+                    bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
                     profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value,
-                                                                   attr_logfailures));
+                                                                   logfailures));
                 } else if (attr_filepath.empty()) {
                     LOG(WARNING) << "WriteFile: invalid parameter: "
                                  << "empty filepath";
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
new file mode 100644
index 0000000..354c7b3
--- /dev/null
+++ b/libstats/pull_rust/Android.bp
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2021 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.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_bindgen {
+    name: "libstatspull_bindgen",
+    wrapper_src: "statslog.h",
+    crate_name: "statspull_bindgen",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--whitelist-function=AStatsEventList_addStatsEvent",
+        "--whitelist-function=AStatsEvent_.*",
+        "--whitelist-function=AStatsManager_.*",
+        "--whitelist-var=AStatsManager_.*",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "libstatspull",
+                "libstatssocket",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libstatspull",
+                "libstatssocket",
+            ],
+        },
+    },
+}
+
+rust_library {
+    name: "libstatspull_rust",
+    crate_name: "statspull_rust",
+    srcs: ["stats_pull.rs"],
+    rustlibs: [
+        "liblazy_static",
+        "liblog_rust",
+        "libstatslog_rust_header",
+        "libstatspull_bindgen",
+    ],
+}
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
new file mode 100644
index 0000000..174125e
--- /dev/null
+++ b/libstats/pull_rust/stats_pull.rs
@@ -0,0 +1,170 @@
+// Copyright 2021, 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.
+
+//! A Rust interface for the StatsD pull API.
+
+use lazy_static::lazy_static;
+use statslog_rust_header::{Atoms, Stat, StatsError};
+use statspull_bindgen::*;
+use std::collections::HashMap;
+use std::convert::TryInto;
+use std::os::raw::c_void;
+use std::sync::Mutex;
+
+/// The return value of callbacks.
+pub type StatsPullResult = Vec<Box<dyn Stat>>;
+
+/// A wrapper for AStatsManager_PullAtomMetadata.
+/// It calls AStatsManager_PullAtomMetadata_release on drop.
+pub struct Metadata {
+    metadata: *mut AStatsManager_PullAtomMetadata,
+}
+
+impl Metadata {
+    /// Calls AStatsManager_PullAtomMetadata_obtain.
+    pub fn new() -> Self {
+        // Safety: We panic if the memory allocation fails.
+        let metadata = unsafe { AStatsManager_PullAtomMetadata_obtain() };
+        if metadata.is_null() {
+            panic!("Cannot obtain pull atom metadata.");
+        } else {
+            Metadata { metadata }
+        }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_setCoolDownMillis.
+    pub fn set_cooldown_millis(&mut self, cooldown_millis: i64) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_setCoolDownMillis(self.metadata, cooldown_millis) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_getCoolDownMillis.
+    pub fn get_cooldown_millis(&self) -> i64 {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_getCoolDownMillis(self.metadata) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_setTimeoutMillis.
+    pub fn set_timeout_millis(&mut self, timeout_millis: i64) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_setTimeoutMillis(self.metadata, timeout_millis) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_getTimeoutMillis.
+    pub fn get_timeout_millis(&self) -> i64 {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_getTimeoutMillis(self.metadata) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_setAdditiveFields.
+    pub fn set_additive_fields(&mut self, additive_fields: &mut Vec<i32>) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe {
+            AStatsManager_PullAtomMetadata_setAdditiveFields(
+                self.metadata,
+                additive_fields.as_mut_ptr(),
+                additive_fields.len().try_into().expect("Cannot convert length to i32"),
+            )
+        }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_getAdditiveFields.
+    pub fn get_additive_fields(&self) -> Vec<i32> {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        // We call getNumAdditiveFields to ensure we pass getAdditiveFields a large enough array.
+        unsafe {
+            let num_fields = AStatsManager_PullAtomMetadata_getNumAdditiveFields(self.metadata)
+                .try_into()
+                .expect("Cannot convert num additive fields to usize");
+            let mut fields = vec![0; num_fields];
+            AStatsManager_PullAtomMetadata_getAdditiveFields(self.metadata, fields.as_mut_ptr());
+            fields
+        }
+    }
+}
+
+impl Drop for Metadata {
+    fn drop(&mut self) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_release(self.metadata) }
+    }
+}
+
+impl Default for Metadata {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+lazy_static! {
+    static ref COOKIES: Mutex<HashMap<i32, fn() -> StatsPullResult>> = Mutex::new(HashMap::new());
+}
+
+// Safety: We store our callbacks in the global so they are valid.
+unsafe extern "C" fn callback_wrapper(
+    atom_tag: i32,
+    data: *mut AStatsEventList,
+    _cookie: *mut c_void,
+) -> AStatsManager_PullAtomCallbackReturn {
+    if !data.is_null() {
+        let map = COOKIES.lock().unwrap();
+        let cb = map.get(&atom_tag);
+        match cb {
+            None => log::error!("No callback found for {}", atom_tag),
+            Some(cb) => {
+                let stats = cb();
+                let result = stats
+                    .iter()
+                    .map(|stat| stat.add_astats_event(&mut *data))
+                    .collect::<Result<Vec<()>, StatsError>>();
+                match result {
+                    Ok(_) => {
+                        return AStatsManager_PULL_SUCCESS as AStatsManager_PullAtomCallbackReturn
+                    }
+                    _ => log::error!("Error adding astats events: {:?}", result),
+                }
+            }
+        }
+    }
+    AStatsManager_PULL_SKIP as AStatsManager_PullAtomCallbackReturn
+}
+
+/// Rust wrapper for AStatsManager_setPullAtomCallback.
+pub fn set_pull_atom_callback(
+    atom: Atoms,
+    metadata: Option<&Metadata>,
+    callback: fn() -> StatsPullResult,
+) {
+    COOKIES.lock().unwrap().insert(atom as i32, callback);
+    let metadata_raw = match metadata {
+        Some(m) => m.metadata,
+        None => std::ptr::null_mut(),
+    };
+    // Safety: We pass a valid function as the callback.
+    unsafe {
+        AStatsManager_setPullAtomCallback(
+            atom as i32,
+            metadata_raw,
+            Some(callback_wrapper),
+            std::ptr::null_mut(),
+        );
+    }
+}
+
+/// Rust wrapper for AStatsManager_clearPullAtomCallback.
+pub fn clear_pull_atom_callback(atom: Atoms) {
+    COOKIES.lock().unwrap().remove(&(atom as i32));
+    // Safety: No memory allocations.
+    unsafe { AStatsManager_clearPullAtomCallback(atom as i32) }
+}
diff --git a/libstats/pull_rust/statslog.h b/libstats/pull_rust/statslog.h
new file mode 100644
index 0000000..983fb7b
--- /dev/null
+++ b/libstats/pull_rust/statslog.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "stats_pull_atom_callback.h"
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 8e45226..b57e287 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -443,6 +443,20 @@
     refs->mBase->onFirstRef();
 }
 
+void RefBase::incStrongRequireStrong(const void* id) const {
+    weakref_impl* const refs = mRefs;
+    refs->incWeak(id);
+
+    refs->addStrongRef(id);
+    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
+
+    LOG_ALWAYS_FATAL_IF(c <= 0 || c == INITIAL_STRONG_VALUE,
+                        "incStrongRequireStrong() called on %p which isn't already owned", refs);
+#if PRINT_REFS
+    ALOGD("incStrong (requiring strong) of %p from %p: cnt=%d\n", this, id, c);
+#endif
+}
+
 void RefBase::decStrong(const void* id) const
 {
     weakref_impl* const refs = mRefs;
@@ -521,6 +535,14 @@
     ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
 }
 
+void RefBase::weakref_type::incWeakRequireWeak(const void* id)
+{
+    weakref_impl* const impl = static_cast<weakref_impl*>(this);
+    impl->addWeakRef(id);
+    const int32_t c __unused = impl->mWeak.fetch_add(1,
+            std::memory_order_relaxed);
+    LOG_ALWAYS_FATAL_IF(c <= 0, "incWeakRequireWeak called on %p which has no weak refs", this);
+}
 
 void RefBase::weakref_type::decWeak(const void* id)
 {
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index c9b4894..93f9654 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -241,6 +241,30 @@
     ASSERT_FALSE(wp1 != wp2);
 }
 
+TEST(RefBase, AssertWeakRefExistsSuccess) {
+    bool isDeleted;
+    sp<Foo> foo = sp<Foo>::make(&isDeleted);
+    wp<Foo> weakFoo = foo;
+
+    EXPECT_EQ(weakFoo, wp<Foo>::fromExisting(foo.get()));
+    EXPECT_EQ(weakFoo.unsafe_get(), wp<Foo>::fromExisting(foo.get()).unsafe_get());
+
+    EXPECT_FALSE(isDeleted);
+    foo = nullptr;
+    EXPECT_TRUE(isDeleted);
+}
+
+TEST(RefBase, AssertWeakRefExistsDeath) {
+    // uses some other refcounting method, or none at all
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+
+    // can only get a valid wp<> object when you construct it from an sp<>
+    EXPECT_DEATH(wp<Foo>::fromExisting(foo), "");
+
+    delete foo;
+}
+
 // Set up a situation in which we race with visit2AndRremove() to delete
 // 2 strong references.  Bar destructor checks that there are no early
 // deletions and prior updates are visible to destructor.
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 70bf5a0..e3e5f11 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -390,28 +390,6 @@
     return static_cast<size_t>(*(p - 1));
 }
 
-status_t String16::makeLower()
-{
-    const size_t N = size();
-    const char16_t* str = string();
-    char16_t* edited = nullptr;
-    for (size_t i=0; i<N; i++) {
-        const char16_t v = str[i];
-        if (v >= 'A' && v <= 'Z') {
-            if (!edited) {
-                SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
-                if (!buf) {
-                    return NO_MEMORY;
-                }
-                edited = (char16_t*)buf->data();
-                mString = str = edited;
-            }
-            edited[i] = tolower((char)v);
-        }
-    }
-    return OK;
-}
-
 status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
 {
     const size_t N = size();
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
index 63c2800..defa0f5 100644
--- a/libutils/String16_fuzz.cpp
+++ b/libutils/String16_fuzz.cpp
@@ -34,11 +34,6 @@
                     str1.size();
                 }),
 
-                // Casing
-                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
-                    str1.makeLower();
-                }),
-
                 // Comparison
                 ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
                     str1.startsWith(str2);
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index 2505f44..c2e9b02 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -97,13 +97,6 @@
     EXPECT_STR16EQ(u" m", tmp);
 }
 
-TEST(String16Test, MakeLower) {
-    String16 tmp("Verify Me!");
-    tmp.makeLower();
-    EXPECT_EQ(10U, tmp.size());
-    EXPECT_STR16EQ(u"verify me!", tmp);
-}
-
 TEST(String16Test, ReplaceAll) {
     String16 tmp("Verify verify Verify");
     tmp.replaceAll(u'r', u'!');
@@ -176,14 +169,6 @@
     EXPECT_FALSE(tmp.isStaticString());
 }
 
-TEST(String16Test, StaticStringMakeLower) {
-    StaticString16 tmp(u"Verify me!");
-    tmp.makeLower();
-    EXPECT_EQ(10U, tmp.size());
-    EXPECT_STR16EQ(u"verify me!", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
 TEST(String16Test, StaticStringReplaceAll) {
     StaticString16 tmp(u"Verify verify Verify");
     tmp.replaceAll(u'r', u'!');
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index d37c1de..29f6bd4 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -21,8 +21,8 @@
 
 using namespace android;
 
-class SPFoo : public LightRefBase<SPFoo> {
-public:
+class SPFoo : virtual public RefBase {
+  public:
     explicit SPFoo(bool* deleted_check) : mDeleted(deleted_check) {
         *mDeleted = false;
     }
@@ -69,3 +69,14 @@
     ASSERT_NE(nullptr, foo);
     ASSERT_NE(foo, nullptr);
 }
+
+TEST(StrongPointer, AssertStrongRefExists) {
+    // uses some other refcounting method, or non at all
+    bool isDeleted;
+    SPFoo* foo = new SPFoo(&isDeleted);
+
+    // can only get a valid sp<> object when you construct it as an sp<> object
+    EXPECT_DEATH(sp<SPFoo>::fromExisting(foo), "");
+
+    delete foo;
+}
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e7acd17..7148949 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -140,7 +140,9 @@
 // count, and accidentally passed to f(sp<T>), a strong pointer to the object
 // will be temporarily constructed and destroyed, prematurely deallocating the
 // object, and resulting in heap corruption. None of this would be easily
-// visible in the source.
+// visible in the source. See below on
+// ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION for a compile time
+// option which helps avoid this case.
 
 // Extra Features:
 
@@ -167,6 +169,42 @@
 // to THE SAME sp<> or wp<>.  In effect, their thread-safety properties are
 // exactly like those of T*, NOT atomic<T*>.
 
+// Safety option: ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION
+//
+// This flag makes the semantics for using a RefBase object with wp<> and sp<>
+// much stricter by disabling implicit conversion from raw pointers to these
+// objects. In order to use this, apply this flag in Android.bp like so:
+//
+//    cflags: [
+//        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+//    ],
+//
+// REGARDLESS of whether this flag is on, best usage of sp<> is shown below. If
+// this flag is on, no other usage is possible (directly calling RefBase methods
+// is possible, but seeing code using 'incStrong' instead of 'sp<>', for
+// instance, should already set off big alarm bells. With carefully constructed
+// data structures, it should NEVER be necessary to directly use RefBase
+// methods). Proper RefBase usage:
+//
+//    class Foo : virtual public RefBase { ... };
+//
+//    // always construct an sp object with sp::make
+//    sp<Foo> myFoo = sp<Foo>::make(/*args*/);
+//
+//    // if you need a weak pointer, it must be constructed from a strong
+//    // pointer
+//    wp<Foo> weakFoo = myFoo; // NOT myFoo.get()
+//
+//    // If you are inside of a method of Foo and need access to a strong
+//    // explicitly call this function. This documents your intention to code
+//    // readers, and it will give a runtime error for what otherwise would
+//    // be potential double ownership
+//    .... Foo::someMethod(...) {
+//        // asserts if there is a memory issue
+//        sp<Foo> thiz = sp<Foo>::fromExisting(this);
+//    }
+//
+
 #ifndef ANDROID_REF_BASE_H
 #define ANDROID_REF_BASE_H
 
@@ -244,6 +282,7 @@
 {
 public:
             void            incStrong(const void* id) const;
+            void            incStrongRequireStrong(const void* id) const;
             void            decStrong(const void* id) const;
     
             void            forceIncStrong(const void* id) const;
@@ -257,6 +296,7 @@
         RefBase*            refBase() const;
 
         void                incWeak(const void* id);
+        void                incWeakRequireWeak(const void* id);
         void                decWeak(const void* id);
 
         // acquires a strong reference if there is already one.
@@ -365,10 +405,24 @@
 
     inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
 
+    // if nullptr, returns nullptr
+    //
+    // if a weak pointer is already available, this will retrieve it,
+    // otherwise, this will abort
+    static inline wp<T> fromExisting(T* other);
+
+    // for more information about this flag, see above
+#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
+    wp(std::nullptr_t) : wp() {}
+#else
     wp(T* other);  // NOLINT(implicit)
+#endif
     wp(const wp<T>& other);
     explicit wp(const sp<T>& other);
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> wp(U* other);  // NOLINT(implicit)
+#endif
     template<typename U> wp(const sp<U>& other);  // NOLINT(implicit)
     template<typename U> wp(const wp<U>& other);  // NOLINT(implicit)
 
@@ -376,11 +430,15 @@
 
     // Assignment
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     wp& operator = (T* other);
+#endif
     wp& operator = (const wp<T>& other);
     wp& operator = (const sp<T>& other);
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> wp& operator = (U* other);
+#endif
     template<typename U> wp& operator = (const wp<U>& other);
     template<typename U> wp& operator = (const sp<U>& other);
 
@@ -481,12 +539,27 @@
 // Note that the above comparison operations go out of their way to provide an ordering consistent
 // with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
 
+template <typename T>
+wp<T> wp<T>::fromExisting(T* other) {
+    if (!other) return nullptr;
+
+    auto refs = other->getWeakRefs();
+    refs->incWeakRequireWeak(other);
+
+    wp<T> ret;
+    ret.m_ptr = other;
+    ret.m_refs = refs;
+    return ret;
+}
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 wp<T>::wp(T* other)
     : m_ptr(other)
 {
     m_refs = other ? m_refs = other->createWeak(this) : nullptr;
 }
+#endif
 
 template<typename T>
 wp<T>::wp(const wp<T>& other)
@@ -502,12 +575,14 @@
     m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 wp<T>::wp(U* other)
     : m_ptr(other)
 {
     m_refs = other ? other->createWeak(this) : nullptr;
 }
+#endif
 
 template<typename T> template<typename U>
 wp<T>::wp(const wp<U>& other)
@@ -534,6 +609,7 @@
     if (m_ptr) m_refs->decWeak(this);
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 wp<T>& wp<T>::operator = (T* other)
 {
@@ -544,6 +620,7 @@
     m_refs = newRefs;
     return *this;
 }
+#endif
 
 template<typename T>
 wp<T>& wp<T>::operator = (const wp<T>& other)
@@ -569,6 +646,7 @@
     return *this;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 wp<T>& wp<T>::operator = (U* other)
 {
@@ -579,6 +657,7 @@
     m_refs = newRefs;
     return *this;
 }
+#endif
 
 template<typename T> template<typename U>
 wp<T>& wp<T>::operator = (const wp<U>& other)
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 1a4b47e..5ce48c6 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -85,8 +85,6 @@
 
             bool                contains(const char16_t* chrs) const;
 
-            status_t            makeLower();
-
             status_t            replaceAll(char16_t replaceThis,
                                            char16_t withThis);
 
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 11128f2..dd53b9e 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -32,30 +32,67 @@
 public:
     inline sp() : m_ptr(nullptr) { }
 
-    // TODO: switch everyone to using this over new, and make RefBase operator
-    // new private to that class so that we can avoid RefBase being used with
-    // other memory management mechanisms.
+    // The old way of using sp<> was like this. This is bad because it relies
+    // on implicit conversion to sp<>, which we would like to remove (if an
+    // object is being managed some other way, this is double-ownership). We
+    // want to move away from this:
+    //
+    //     sp<Foo> foo = new Foo(...); // DO NOT DO THIS
+    //
+    // Instead, prefer to do this:
+    //
+    //     sp<Foo> foo = sp<Foo>::make(...); // DO THIS
+    //
+    // Sometimes, in order to use this, when a constructor is marked as private,
+    // you may need to add this to your class:
+    //
+    //     friend class sp<Foo>;
     template <typename... Args>
     static inline sp<T> make(Args&&... args);
 
+    // if nullptr, returns nullptr
+    //
+    // if a strong pointer is already available, this will retrieve it,
+    // otherwise, this will abort
+    static inline sp<T> fromExisting(T* other);
+
+    // for more information about this macro and correct RefBase usage, see
+    // the comment at the top of utils/RefBase.h
+#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
+    sp(std::nullptr_t) : sp() {}
+#else
     sp(T* other);  // NOLINT(implicit)
+#endif
     sp(const sp<T>& other);
     sp(sp<T>&& other) noexcept;
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> sp(U* other);  // NOLINT(implicit)
+#endif
     template<typename U> sp(const sp<U>& other);  // NOLINT(implicit)
     template<typename U> sp(sp<U>&& other);  // NOLINT(implicit)
 
+    // Cast a strong pointer directly from one type to another. Constructors
+    // allow changing types, but only if they are pointer-compatible. This does
+    // a static_cast internally.
+    template <typename U>
+    static inline sp<T> cast(const sp<U>& other);
+
     ~sp();
 
     // Assignment
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     sp& operator = (T* other);
+#endif
     sp& operator = (const sp<T>& other);
     sp& operator=(sp<T>&& other) noexcept;
 
     template<typename U> sp& operator = (const sp<U>& other);
     template<typename U> sp& operator = (sp<U>&& other);
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> sp& operator = (U* other);
+#endif
 
     //! Special optimization for use by ProcessState (and nobody else).
     void force_set(T* other);
@@ -189,6 +226,19 @@
     return result;
 }
 
+template <typename T>
+sp<T> sp<T>::fromExisting(T* other) {
+    if (other) {
+        check_not_on_stack(other);
+        other->incStrongRequireStrong(other);
+        sp<T> result;
+        result.m_ptr = other;
+        return result;
+    }
+    return nullptr;
+}
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 sp<T>::sp(T* other)
         : m_ptr(other) {
@@ -197,6 +247,7 @@
         other->incStrong(this);
     }
 }
+#endif
 
 template<typename T>
 sp<T>::sp(const sp<T>& other)
@@ -210,6 +261,7 @@
     other.m_ptr = nullptr;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 sp<T>::sp(U* other)
         : m_ptr(other) {
@@ -218,6 +270,7 @@
         (static_cast<T*>(other))->incStrong(this);
     }
 }
+#endif
 
 template<typename T> template<typename U>
 sp<T>::sp(const sp<U>& other)
@@ -232,6 +285,12 @@
     other.m_ptr = nullptr;
 }
 
+template <typename T>
+template <typename U>
+sp<T> sp<T>::cast(const sp<U>& other) {
+    return sp<T>::fromExisting(static_cast<T*>(other.get()));
+}
+
 template<typename T>
 sp<T>::~sp() {
     if (m_ptr)
@@ -260,6 +319,7 @@
     return *this;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 sp<T>& sp<T>::operator =(T* other) {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
@@ -272,6 +332,7 @@
     m_ptr = other;
     return *this;
 }
+#endif
 
 template<typename T> template<typename U>
 sp<T>& sp<T>::operator =(const sp<U>& other) {
@@ -294,6 +355,7 @@
     return *this;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 sp<T>& sp<T>::operator =(U* other) {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
@@ -303,6 +365,7 @@
     m_ptr = other;
     return *this;
 }
+#endif
 
 template<typename T>
 void sp<T>::force_set(T* other) {
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 83cb6ff..d9f5526 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -1,8 +1,7 @@
 {
   "requireLibs": [
-    // Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-    "libdexfile_external.so",
-    "libdexfiled_external.so",
+    "libdexfile.so",
+    "libdexfiled.so",
     "libnativebridge.so",
     "libnativehelper.so",
     "libnativeloader.so",
diff --git a/toolbox/OWNERS b/toolbox/OWNERS
index 7529cb9..5e2c581 100644
--- a/toolbox/OWNERS
+++ b/toolbox/OWNERS
@@ -1 +1,2 @@
 include platform/system/core:/janitors/OWNERS
+per-file modprobe.c=willmcvicker@google.com,dvander@google.com
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 7df7b71..711586a 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -215,10 +215,7 @@
         return EXIT_FAILURE;
     }
 
-    Modprobe m(mod_dirs);
-    if (blocklist) {
-        m.EnableBlocklist(true);
-    }
+    Modprobe m(mod_dirs, "modules.load", blocklist);
 
     for (const auto& module : modules) {
         switch (mode) {
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 8ab6303..4aca375 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -96,13 +96,13 @@
 
     unique_fd file_fd(TEMP_FAILURE_RETRY(open(file_name, O_RDONLY)));
     if (!file_fd.ok()) {
-        fprintf(stderr, "Error opening file '%s': %s\n", file_name, strerror(errno));
+        PLOG(ERROR) << "Error opening file " << file_name;
         return {};
     }
 
     rc = fstat64(file_fd, &st);
     if (rc < 0) {
-        fprintf(stderr, "Error calling stat on file '%s': %s\n", file_name, strerror(errno));
+        PLOG(ERROR) << "Error calling stat on file '" << file_name << "'";
         return {};
     }
 
@@ -115,14 +115,14 @@
         file_page_offset = page_size - file_page_offset;
     }
     if (__builtin_add_overflow(file_size, file_page_offset, &file_page_size)) {
-        fprintf(stderr, "Failed to page-align file size\n");
+        LOG(ERROR) << "Failed to page-align file size";
         return {};
     }
 
     BufferAllocator alloc;
     unique_fd dmabuf_fd(alloc.Alloc(kDmabufSystemHeapName, file_page_size));
     if (!dmabuf_fd.ok()) {
-        fprintf(stderr, "Error creating dmabuf: %d\n", dmabuf_fd.get());
+        LOG(ERROR) << "Error creating dmabuf: " << dmabuf_fd.get();
         return dmabuf_fd;
     }
 
@@ -137,12 +137,12 @@
                 pread(file_fd, (char*)shm + file_offset, file_size - file_offset, file_offset));
 
         if (num_read < 0) {
-            fprintf(stderr, "Error reading package file '%s': %s\n", file_name, strerror(errno));
+            PLOG(ERROR) << "Error reading package file '" << file_name << "'";
             break;
         }
 
         if (num_read == 0) {
-            fprintf(stderr, "Unexpected end of file '%s'\n", file_name);
+            LOG(ERROR) << "Unexpected end of file '" << file_name << "'";
             break;
         }
 
@@ -182,17 +182,17 @@
     struct apploader_resp resp;
     ssize_t rc = read(tipc_fd, &resp, sizeof(resp));
     if (rc < 0) {
-        fprintf(stderr, "Failed to read response: %zd\n", rc);
+        PLOG(ERROR) << "Failed to read response";
         return rc;
     }
 
     if (rc < sizeof(resp)) {
-        fprintf(stderr, "Not enough data in response: %zd\n", rc);
+        LOG(ERROR) << "Not enough data in response: " << rc;
         return -EIO;
     }
 
     if (resp.hdr.cmd != (APPLOADER_CMD_LOAD_APPLICATION | APPLOADER_RESP_BIT)) {
-        fprintf(stderr, "Invalid command in response: %u\n", resp.hdr.cmd);
+        LOG(ERROR) << "Invalid command in response: " << resp.hdr.cmd;
         return -EINVAL;
     }
 
@@ -200,28 +200,28 @@
         case APPLOADER_NO_ERROR:
             break;
         case APPLOADER_ERR_UNKNOWN_CMD:
-            fprintf(stderr, "Error: unknown command\n");
+            LOG(ERROR) << "Error: unknown command";
             break;
         case APPLOADER_ERR_INVALID_CMD:
-            fprintf(stderr, "Error: invalid command arguments\n");
+            LOG(ERROR) << "Error: invalid command arguments";
             break;
         case APPLOADER_ERR_NO_MEMORY:
-            fprintf(stderr, "Error: out of Trusty memory\n");
+            LOG(ERROR) << "Error: out of Trusty memory";
             break;
         case APPLOADER_ERR_VERIFICATION_FAILED:
-            fprintf(stderr, "Error: failed to verify the package\n");
+            LOG(ERROR) << "Error: failed to verify the package";
             break;
         case APPLOADER_ERR_LOADING_FAILED:
-            fprintf(stderr, "Error: failed to load the package\n");
+            LOG(ERROR) << "Error: failed to load the package";
             break;
         case APPLOADER_ERR_ALREADY_EXISTS:
-            fprintf(stderr, "Error: application already exists\n");
+            LOG(ERROR) << "Error: application already exists";
             break;
         case APPLOADER_ERR_INTERNAL:
-            fprintf(stderr, "Error: internal apploader error\n");
+            LOG(ERROR) << "Error: internal apploader error";
             break;
         default:
-            fprintf(stderr, "Unrecognized error: %u\n", resp.error);
+            LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
     }
 
@@ -241,14 +241,14 @@
 
     tipc_fd = tipc_connect(dev_name, APPLOADER_PORT);
     if (tipc_fd < 0) {
-        fprintf(stderr, "Failed to connect to Trusty app loader: %s\n", strerror(-tipc_fd));
+        LOG(ERROR) << "Failed to connect to Trusty app loader: " << strerror(-tipc_fd);
         rc = tipc_fd;
         goto err_tipc_connect;
     }
 
     rc = send_load_message(tipc_fd, package_fd, package_size);
     if (rc < 0) {
-        fprintf(stderr, "Failed to send package: %zd\n", rc);
+        LOG(ERROR) << "Failed to send package: " << rc;
         goto err_send;
     }