Merge "Move libipchecksum to frameworks/libs/net."
diff --git a/diagnose_usb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
index 35edb5e..5716323 100644
--- a/diagnose_usb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -19,7 +19,9 @@
 #include <errno.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <string>
+#include <vector>
 
 #include <android-base/stringprintf.h>
 
@@ -45,9 +47,25 @@
         return "";
     }
 
-    // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
-    // additionally just to be sure.
-    if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
+    int ngroups = getgroups(0, nullptr);
+    if (ngroups < 0) {
+        perror("failed to get groups list size");
+        return "";
+    }
+
+    std::vector<gid_t> groups(ngroups);
+    ngroups = getgroups(groups.size(), groups.data());
+    if (ngroups < 0) {
+        perror("failed to get groups list");
+        return "";
+    }
+
+    groups.resize(ngroups);
+
+    // getgroups(2) indicates that the egid may not be included so we check it additionally just
+    // to be sure.
+    if (std::find(groups.begin(), groups.end(), plugdev_group->gr_gid) != groups.end() ||
+        getegid() == plugdev_group->gr_gid) {
         // The user is in plugdev so the problem is likely with the udev rules.
         return "missing udev rules? user is in the plugdev group";
     }
diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp
index 14b217c..388dadc 100644
--- a/fs_mgr/blockdev.cpp
+++ b/fs_mgr/blockdev.cpp
@@ -30,7 +30,6 @@
 using android::base::ErrnoError;
 using android::base::Error;
 using android::base::Result;
-using android::base::ResultError;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -94,10 +93,8 @@
     std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
     LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
     if (blockdev.empty()) {
-        const std::string err_msg =
-                StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev),
-                             minor(statbuf.st_dev), file_path.c_str());
-        return ResultError(err_msg, 0);
+        return Errorf("Failed to convert {}:{} (path {})", major(statbuf.st_dev),
+                      minor(statbuf.st_dev), file_path.c_str());
     }
     auto& dm = DeviceMapper::Instance();
     for (;;) {
@@ -110,7 +107,7 @@
     }
     std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
     if (!maybe_blockdev) {
-        return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
+        return Errorf("Failed to remove /dev/block/ prefix from {}", blockdev);
     }
     blockdev = PartitionParent(*maybe_blockdev);
     LOG(DEBUG) << __func__ << ": "
@@ -119,7 +116,7 @@
             StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
     std::string nr_tags;
     if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
-        return ResultError("Failed to read " + nr_tags_path, 0);
+        return Errorf("Failed to read {}", nr_tags_path);
     }
     rtrim(nr_tags);
     LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
@@ -137,11 +134,9 @@
 
     const std::string loop_device_name = Basename(loop_device_path);
 
-    const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
+    const auto qd = BlockDeviceQueueDepth(file_path);
     if (!qd.ok()) {
-        LOG(DEBUG) << __func__ << ": "
-                   << "BlockDeviceQueueDepth() returned " << qd.error();
-        return ResultError(qd.error());
+        return qd.error();
     }
     const std::string nr_requests = StringPrintf("%u", *qd);
     const std::string sysfs_path =
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a320c0e..137b066 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2044,7 +2044,9 @@
 
     ConfigureIoScheduler(loop_device);
 
-    ConfigureQueueDepth(loop_device, "/");
+    if (auto ret = ConfigureQueueDepth(loop_device, "/"); !ret.ok()) {
+        LOG(DEBUG) << "Failed to config queue depth: " << ret.error().message();
+    }
 
     // set block size & direct IO
     unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 332fcf5..1057d7f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 #include <stdint.h>
 #include <sys/sysmacros.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <chrono>
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 5c1b291..a6d96ed 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -19,8 +19,6 @@
 #include <fs_mgr_overlayfs.h>
 #include <libfiemap/image_manager.h>
 
-#include "utility.h"
-
 namespace android {
 namespace snapshot {
 
@@ -145,9 +143,5 @@
     return android::dm::DeviceMapper::Instance();
 }
 
-bool DeviceInfo::UseUserspaceSnapshots() const {
-    return IsUserspaceSnapshotsEnabled();
-}
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index a07f554..8aefb85 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -41,7 +41,6 @@
     std::unique_ptr<IImageManager> OpenImageManager() const override;
     bool IsFirstStageInit() const override;
     android::dm::IDeviceMapper& GetDeviceMapper() override;
-    bool UseUserspaceSnapshots() const override;
 
     void set_first_stage_init(bool value) { first_stage_init_ = value; }
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
index 8c4161c..573a85b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -34,7 +34,6 @@
     MOCK_METHOD(bool, IsFirstStageInit, (), (const, override));
     MOCK_METHOD(std::unique_ptr<android::fiemap::IImageManager>, OpenImageManager, (),
                 (const, override));
-    MOCK_METHOD(bool, UseUserspaceSnapshots, (), (const, override));
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index f7e37bb..41c6ef5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -111,7 +111,6 @@
         virtual bool IsFirstStageInit() const = 0;
         virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
         virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
-        virtual bool UseUserspaceSnapshots() const = 0;
 
         // Helper method for implementing OpenImageManager.
         std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 07c3ec5..c3b40dc 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -107,7 +107,6 @@
     }
 
     bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
-    bool UseUserspaceSnapshots() const override;
 
     void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
     void set_fake_super(const std::string& path) {
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 5fcbdfe..5569da0 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -143,7 +143,7 @@
 }
 
 std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
-    if (compression_enabled || userspace_snapshots_enabled) {
+    if (compression_enabled) {
         if (update == nullptr || !update->has_estimate_cow_size()) {
             LOG(ERROR) << "Update manifest does not include a COW size";
             return std::nullopt;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 1f34177..34b39ca 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -58,7 +58,6 @@
     std::vector<ChromeOSExtent> extra_extents = {};
     // True if compression is enabled.
     bool compression_enabled = false;
-    bool userspace_snapshots_enabled = false;
     std::string compression_algorithm;
 
     struct Return {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c7c5da1..e6e17bd 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1329,7 +1329,7 @@
                                                         const SnapshotStatus& status) {
     CHECK(lock);
 
-    if (!status.compression_enabled() && !UpdateUsesUserSnapshots(lock)) {
+    if (!status.compression_enabled()) {
         // Do not try to verify old-style COWs yet.
         return MergeFailureCode::Ok;
     }
@@ -2345,15 +2345,13 @@
     remaining_time = GetRemainingTime(params.timeout_ms, begin);
     if (remaining_time.count() < 0) return false;
 
-    if (context == SnapshotContext::Update) {
-        if (UpdateUsesUserSnapshots(lock) || live_snapshot_status->compression_enabled()) {
-            // Stop here, we can't run dm-user yet, the COW isn't built.
-            created_devices.Release();
-            return true;
-        }
+    if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
+        // Stop here, we can't run dm-user yet, the COW isn't built.
+        created_devices.Release();
+        return true;
     }
 
-    if (UpdateUsesUserSnapshots(lock) || live_snapshot_status->compression_enabled()) {
+    if (live_snapshot_status->compression_enabled()) {
         // Get the source device (eg the view of the partition from before it was resized).
         std::string source_device_path;
         if (live_snapshot_status->old_partition_size() > 0) {
@@ -3134,53 +3132,6 @@
             .compression_algorithm = compression_algorithm,
     };
 
-    cow_creator.userspace_snapshots_enabled = device_->UseUserspaceSnapshots();
-    if (cow_creator.userspace_snapshots_enabled) {
-        LOG(INFO) << "User-space snapshots enabled, compression = " << compression_algorithm;
-    } else {
-        LOG(INFO) << "User-space snapshots disabled, compression = " << compression_algorithm;
-    }
-    is_snapshot_userspace_ = cow_creator.userspace_snapshots_enabled;
-
-    if ((use_compression || is_snapshot_userspace_) && !device()->IsTestDevice()) {
-        // Terminate stale daemon if any
-        std::unique_ptr<SnapuserdClient> snapuserd_client =
-                SnapuserdClient::Connect(kSnapuserdSocket, 10s);
-        if (snapuserd_client) {
-            snapuserd_client->DetachSnapuserd();
-            snapuserd_client->CloseConnection();
-            snapuserd_client = nullptr;
-        }
-
-        // Clear the cached client if any
-        if (snapuserd_client_) {
-            snapuserd_client_->CloseConnection();
-            snapuserd_client_ = nullptr;
-        }
-    }
-
-    // If compression is enabled, we need to retain a copy of the old metadata
-    // so we can access original blocks in case they are moved around. We do
-    // not want to rely on the old super metadata slot because we don't
-    // guarantee its validity after the slot switch is successful.
-    //
-    // Note that we do this for userspace merges even if compression is
-    // disabled, since the code path expects it even if the source device will
-    // be unused.
-    if (cow_creator.compression_enabled || cow_creator.userspace_snapshots_enabled) {
-        auto metadata = current_metadata->Export();
-        if (!metadata) {
-            LOG(ERROR) << "Could not export current metadata";
-            return Return::Error();
-        }
-
-        auto path = GetOldPartitionMetadataPath();
-        if (!android::fs_mgr::WriteToImageFile(path, *metadata.get())) {
-            LOG(ERROR) << "Cannot write old metadata to " << path;
-            return Return::Error();
-        }
-    }
-
     auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                              &all_snapshot_status);
     if (!ret.is_ok()) return ret;
@@ -3202,11 +3153,64 @@
         return Return::Error();
     }
 
+    // If compression is enabled, we need to retain a copy of the old metadata
+    // so we can access original blocks in case they are moved around. We do
+    // not want to rely on the old super metadata slot because we don't
+    // guarantee its validity after the slot switch is successful.
+    if (cow_creator.compression_enabled) {
+        auto metadata = current_metadata->Export();
+        if (!metadata) {
+            LOG(ERROR) << "Could not export current metadata";
+            return Return::Error();
+        }
+
+        auto path = GetOldPartitionMetadataPath();
+        if (!android::fs_mgr::WriteToImageFile(path, *metadata.get())) {
+            LOG(ERROR) << "Cannot write old metadata to " << path;
+            return Return::Error();
+        }
+    }
+
     SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
     status.set_state(update_state);
     status.set_compression_enabled(cow_creator.compression_enabled);
-    status.set_userspace_snapshots(cow_creator.userspace_snapshots_enabled);
+    if (cow_creator.compression_enabled) {
+        if (!device()->IsTestDevice()) {
+            // Userspace snapshots is enabled only if compression is enabled
+            status.set_userspace_snapshots(IsUserspaceSnapshotsEnabled());
+            if (IsUserspaceSnapshotsEnabled()) {
+                is_snapshot_userspace_ = true;
+                LOG(INFO) << "User-space snapshots enabled";
+            } else {
+                is_snapshot_userspace_ = false;
+                LOG(INFO) << "User-space snapshots disabled";
+            }
 
+            // Terminate stale daemon if any
+            std::unique_ptr<SnapuserdClient> snapuserd_client =
+                    SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+            if (snapuserd_client) {
+                snapuserd_client->DetachSnapuserd();
+                snapuserd_client->CloseConnection();
+                snapuserd_client = nullptr;
+            }
+
+            // Clear the cached client if any
+            if (snapuserd_client_) {
+                snapuserd_client_->CloseConnection();
+                snapuserd_client_ = nullptr;
+            }
+        } else {
+            status.set_userspace_snapshots(!IsDmSnapshotTestingEnabled());
+            if (IsDmSnapshotTestingEnabled()) {
+                is_snapshot_userspace_ = false;
+                LOG(INFO) << "User-space snapshots disabled for testing";
+            } else {
+                is_snapshot_userspace_ = true;
+                LOG(INFO) << "User-space snapshots enabled for testing";
+            }
+        }
+    }
     if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
         LOG(ERROR) << "Unable to write new update state";
         return Return::Error();
@@ -3370,8 +3374,6 @@
         const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
     CHECK(lock);
 
-    bool userspace_merges = UpdateUsesUserSnapshots(lock);
-
     CreateLogicalPartitionParams cow_params{
             .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
             .metadata = exported_target_metadata,
@@ -3401,7 +3403,7 @@
             return Return::Error();
         }
 
-        if (userspace_merges || it->second.compression_enabled()) {
+        if (it->second.compression_enabled()) {
             unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
             if (fd < 0) {
                 PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
@@ -3447,8 +3449,8 @@
     if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
         return false;
     }
-    if (status.compression_enabled() || UpdateUsesUserSnapshots(lock.get())) {
-        LOG(ERROR) << "Cannot use MapUpdateSnapshot with user snapshots";
+    if (status.compression_enabled()) {
+        LOG(ERROR) << "Cannot use MapUpdateSnapshot with compressed snapshots";
         return false;
     }
 
@@ -3505,7 +3507,7 @@
         return nullptr;
     }
 
-    if (status.compression_enabled() || UpdateUsesUserSnapshots(lock.get())) {
+    if (status.compression_enabled()) {
         return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
                                             status, paths);
     }
@@ -3645,7 +3647,6 @@
        << (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
        << std::endl;
     ss << "Source build fingerprint: " << update_status.source_build_fingerprint() << std::endl;
-    ss << "Using userspace snapshots: " << UpdateUsesUserSnapshots(file.get()) << std::endl;
 
     bool ok = true;
     std::vector<std::string> snapshots;
@@ -3846,10 +3847,6 @@
 bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
     CHECK(lock);
 
-    if (UpdateUsesUserSnapshots(lock)) {
-        return true;
-    }
-
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
         LOG(ERROR) << "Could not list snapshots.";
@@ -3862,7 +3859,6 @@
             return false;
         }
         if (status.compression_enabled()) {
-            // Compressed snapshots are never written through dm-snapshot.
             continue;
         }
 
@@ -4026,10 +4022,7 @@
     if (!lock) return false;
 
     auto status = ReadSnapshotUpdateStatus(lock.get());
-    if (status.state() != UpdateState::None && status.compression_enabled()) {
-        return true;
-    }
-    return UpdateUsesUserSnapshots(lock.get());
+    return status.state() != UpdateState::None && status.compression_enabled();
 }
 
 bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
@@ -4055,9 +4048,6 @@
 }
 
 MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
-    // Note: disabling compression disables move operations, so we don't need
-    // separate phases when compression is disabled (irrespective of userspace
-    // merges).
     if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
         return MergePhase::FIRST_PHASE;
     }
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 63159dc..c1a5af7 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -130,7 +130,6 @@
     std::unique_ptr<IImageManager> OpenImageManager() const {
         return env_->CheckCreateFakeImageManager();
     }
-    bool UseUserspaceSnapshots() const override { return false; }
 
     void SwitchSlot() { switched_slot_ = !switched_slot_; }
 
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index c70b353..14f2d45 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -357,7 +357,7 @@
         DeltaArchiveManifest manifest;
 
         auto dynamic_partition_metadata = manifest.mutable_dynamic_partition_metadata();
-        dynamic_partition_metadata->set_vabc_enabled(ShouldUseCompression());
+        dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
         dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
 
         auto group = dynamic_partition_metadata->add_groups();
@@ -396,7 +396,7 @@
             if (!res) {
                 return res;
             }
-        } else if (!ShouldUseUserspaceSnapshots()) {
+        } else if (!IsCompressionEnabled()) {
             std::string ignore;
             if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
                 return AssertionFailure() << "Failed to map test_partition_b";
@@ -1030,7 +1030,7 @@
     }
 
     AssertionResult MapOneUpdateSnapshot(const std::string& name) {
-        if (ShouldUseUserspaceSnapshots()) {
+        if (ShouldUseCompression()) {
             std::unique_ptr<ISnapshotWriter> writer;
             return MapUpdateSnapshot(name, &writer);
         } else {
@@ -1040,7 +1040,7 @@
     }
 
     AssertionResult WriteSnapshotAndHash(const std::string& name) {
-        if (ShouldUseUserspaceSnapshots()) {
+        if (ShouldUseCompression()) {
             std::unique_ptr<ISnapshotWriter> writer;
             auto res = MapUpdateSnapshot(name, &writer);
             if (!res) {
@@ -2072,7 +2072,7 @@
 
 // Test for overflow bit after update
 TEST_F(SnapshotUpdateTest, Overflow) {
-    if (ShouldUseUserspaceSnapshots()) {
+    if (ShouldUseCompression()) {
         GTEST_SKIP() << "No overflow bit set for userspace COWs";
     }
 
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 2cd13e0..e3e3af8 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -25,8 +25,6 @@
 #include <openssl/sha.h>
 #include <payload_consumer/file_descriptor.h>
 
-#include "utility.h"
-
 namespace android {
 namespace snapshot {
 
@@ -322,9 +320,5 @@
     return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
 }
 
-bool TestDeviceInfo::UseUserspaceSnapshots() const {
-    return !IsDmSnapshotTestingEnabled();
-}
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8045c71..cc445be 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -88,6 +88,7 @@
 using namespace std::literals::string_literals;
 
 using android::base::Basename;
+using android::base::ResultError;
 using android::base::SetProperty;
 using android::base::Split;
 using android::base::StartsWith;
@@ -116,7 +117,7 @@
                         android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
 
     template <typename T>
-    operator android::base::expected<T, ResultError>() {
+    operator android::base::expected<T, ResultError<int>>() {
         if (ignore_error_) {
             return {};
         }
@@ -130,7 +131,7 @@
     }
 
   private:
-    Error error_;
+    Error<> error_;
     bool ignore_error_;
 };
 
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 6eaa80f..7aa4a9d 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -297,7 +297,7 @@
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
         auto& failure = subcontext_reply->failure();
-        return ResultError(failure.error_string(), failure.error_errno());
+        return ResultError<>(failure.error_string(), failure.error_errno());
     }
 
     if (subcontext_reply->reply_case() != SubcontextReply::kSuccess) {
@@ -321,7 +321,7 @@
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
         auto& failure = subcontext_reply->failure();
-        return ResultError(failure.error_string(), failure.error_errno());
+        return ResultError<>(failure.error_string(), failure.error_errno());
     }
 
     if (subcontext_reply->reply_case() != SubcontextReply::kExpandArgsReply) {
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 1a4196a..169b1d3 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -165,27 +165,7 @@
     return 0;
 }
 
-int get_sched_policy(int tid, SchedPolicy* policy) {
-    if (tid == 0) {
-        tid = GetThreadId();
-    }
-
-    std::string group;
-    if (schedboost_enabled()) {
-        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
-            (getCGroupSubsys(tid, "cpu", group) < 0)) {
-                LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
-                return -1;
-        }
-    }
-    if (group.empty() && cpusets_enabled()) {
-        if (getCGroupSubsys(tid, "cpuset", group) < 0) {
-            LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
-            return -1;
-        }
-    }
-
-    // TODO: replace hardcoded directories
+static int get_sched_policy_from_group(const std::string& group, SchedPolicy* policy) {
     if (group.empty()) {
         *policy = SP_FOREGROUND;
     } else if (group == "foreground") {
@@ -205,6 +185,35 @@
     return 0;
 }
 
+int get_sched_policy(int tid, SchedPolicy* policy) {
+    if (tid == 0) {
+        tid = GetThreadId();
+    }
+
+    std::string group;
+    if (schedboost_enabled()) {
+        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
+            (getCGroupSubsys(tid, "cpu", group) < 0)) {
+            LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
+            return -1;
+        }
+        // Wipe invalid group to fallback to cpuset
+        if (!group.empty()) {
+            if (get_sched_policy_from_group(group, policy) < 0) {
+                group.clear();
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    if (cpusets_enabled() && getCGroupSubsys(tid, "cpuset", group) < 0) {
+        LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
+        return -1;
+    }
+    return get_sched_policy_from_group(group, policy);
+}
+
 #else
 
 /* Stubs for non-Android targets. */