Merge "Fix 32-bit userspace on 64-bit kernel quota2 compatibility."
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
index 8f84346..1e3c559 100644
--- a/debuggerd/libdebuggerd/test/UnwinderMock.h
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <memory>
+
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Unwinder.h>
@@ -31,7 +33,7 @@
   }
 
   void MockSetBuildID(uint64_t offset, const std::string& build_id) {
-    unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
+    std::shared_ptr<unwindstack::MapInfo> map_info = GetMaps()->Find(offset);
     if (map_info != nullptr) {
       map_info->SetBuildID(std::string(build_id));
     }
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 534d7be..1835f0e 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -107,7 +107,7 @@
     // In this case, the sp will be in either an invalid map if triggered
     // on the main thread, or in a guard map if in another thread, which
     // will be the first case or second case from below.
-    unwindstack::MapInfo* map_info = maps->Find(sp);
+    auto map_info = maps->Find(sp);
     if (map_info == nullptr) {
       return "stack pointer is in a non-existent map; likely due to stack overflow.";
     } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
@@ -158,7 +158,7 @@
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
     uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
-    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+    auto map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
     } else {
@@ -396,7 +396,7 @@
   regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
     std::string label{"memory near "s + reg_name};
     if (maps) {
-      unwindstack::MapInfo* map_info = maps->Find(untag_address(reg_value));
+      auto map_info = maps->Find(untag_address(reg_value));
       if (map_info != nullptr && !map_info->name().empty()) {
         label += " (" + map_info->name() + ")";
       }
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 6c380a1..b1c4ef3 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -103,7 +103,7 @@
     // In this case, the sp will be in either an invalid map if triggered
     // on the main thread, or in a guard map if in another thread, which
     // will be the first case or second case from below.
-    unwindstack::MapInfo* map_info = maps->Find(sp);
+    std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(sp);
     if (map_info == nullptr) {
       return "stack pointer is in a non-existent map; likely due to stack overflow.";
     } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
@@ -226,7 +226,7 @@
       cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
-    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+    auto map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
     } else {
@@ -342,8 +342,8 @@
 
   f->set_file_map_offset(frame.map_elf_start_offset);
 
-  unwindstack::MapInfo* map_info = maps->Find(frame.map_start);
-  if (map_info) {
+  auto map_info = maps->Find(frame.map_start);
+  if (map_info.get() != nullptr) {
     f->set_build_id(map_info->GetPrintableBuildID());
   }
 }
@@ -370,7 +370,7 @@
           MemoryDump dump;
 
           dump.set_register_name(name);
-          unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
+          std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
           if (map_info) {
             dump.set_mapping_name(map_info->name());
           }
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 0e918a3..322fe5c 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,10 +18,10 @@
 # Package fastboot-related executables.
 #
 
-my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs_casefold
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
+my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
+my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
 $(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
 my_dist_files :=
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 9b6c3dd..609bd11 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -130,10 +130,12 @@
             if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
                 const auto arg = flag.substr(equal_sign + 1);
                 if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
-                    if (!ParseInt(arg, &entry->reserved_size)) {
+                    off64_t size_in_4k_blocks;
+                    if (!ParseInt(arg, &size_in_4k_blocks, static_cast<off64_t>(0),
+                                  std::numeric_limits<off64_t>::max() >> 12)) {
                         LWARNING << "Warning: reserve_root= flag malformed: " << arg;
                     } else {
-                        entry->reserved_size <<= 12;
+                        entry->reserved_size = size_in_4k_blocks << 12;
                     }
                 } else if (StartsWith(flag, "lowerdir=")) {
                     entry->lowerdir = std::move(arg);
@@ -442,7 +444,81 @@
     return "";
 }
 
-bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ *   /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
+    std::set<std::string> boot_devices;
+
+    for (const auto& entry : fstab) {
+        std::string blk_device = entry.blk_device;
+        // Skips blk_device that doesn't conform to the format.
+        if (!android::base::StartsWith(blk_device, "/dev/block") ||
+            android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+            android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+            continue;
+        }
+        // Skips non-by_name blk_device.
+        // /dev/block/<type>/<device>/by-name/<partition>
+        //                           ^ slash_by_name
+        auto slash_by_name = blk_device.find("/by-name");
+        if (slash_by_name == std::string::npos) continue;
+        blk_device.erase(slash_by_name);  // erases /by-name/<partition>
+
+        // Erases /dev/block/, now we have <type>/<device>
+        blk_device.erase(0, std::string("/dev/block/").size());
+
+        // <type>/<device>
+        //       ^ first_slash
+        auto first_slash = blk_device.find('/');
+        if (first_slash == std::string::npos) continue;
+
+        auto boot_device = blk_device.substr(first_slash + 1);
+        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+    }
+
+    return boot_devices;
+}
+
+FstabEntry BuildDsuUserdataFstabEntry() {
+    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
+
+    FstabEntry userdata = {
+            .blk_device = "userdata_gsi",
+            .mount_point = "/data",
+            .fs_type = "ext4",
+            .flags = kFlags,
+            .reserved_size = 128 * 1024 * 1024,
+    };
+    userdata.fs_mgr_flags.wait = true;
+    userdata.fs_mgr_flags.check = true;
+    userdata.fs_mgr_flags.logical = true;
+    userdata.fs_mgr_flags.quota = true;
+    userdata.fs_mgr_flags.late_mount = true;
+    userdata.fs_mgr_flags.formattable = true;
+    return userdata;
+}
+
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+    auto iter = std::remove_if(fstab->begin(), fstab->end(),
+                               [&](const auto& entry) { return entry.mount_point == mount_point; });
+    if (iter != fstab->end()) {
+        fstab->erase(iter, fstab->end());
+        return true;
+    }
+    return false;
+}
+
+}  // namespace
+
+bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
     ssize_t len;
     size_t alloc_len = 0;
     char *line = NULL;
@@ -528,80 +604,6 @@
     return false;
 }
 
-/* Extracts <device>s from the by-name symlinks specified in a fstab:
- *   /dev/block/<type>/<device>/by-name/<partition>
- *
- * <type> can be: platform, pci or vbd.
- *
- * For example, given the following entries in the input fstab:
- *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
- *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
- * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
- */
-std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
-    std::set<std::string> boot_devices;
-
-    for (const auto& entry : fstab) {
-        std::string blk_device = entry.blk_device;
-        // Skips blk_device that doesn't conform to the format.
-        if (!android::base::StartsWith(blk_device, "/dev/block") ||
-            android::base::StartsWith(blk_device, "/dev/block/by-name") ||
-            android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
-            continue;
-        }
-        // Skips non-by_name blk_device.
-        // /dev/block/<type>/<device>/by-name/<partition>
-        //                           ^ slash_by_name
-        auto slash_by_name = blk_device.find("/by-name");
-        if (slash_by_name == std::string::npos) continue;
-        blk_device.erase(slash_by_name);  // erases /by-name/<partition>
-
-        // Erases /dev/block/, now we have <type>/<device>
-        blk_device.erase(0, std::string("/dev/block/").size());
-
-        // <type>/<device>
-        //       ^ first_slash
-        auto first_slash = blk_device.find('/');
-        if (first_slash == std::string::npos) continue;
-
-        auto boot_device = blk_device.substr(first_slash + 1);
-        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
-    }
-
-    return boot_devices;
-}
-
-FstabEntry BuildDsuUserdataFstabEntry() {
-    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
-
-    FstabEntry userdata = {
-            .blk_device = "userdata_gsi",
-            .mount_point = "/data",
-            .fs_type = "ext4",
-            .flags = kFlags,
-            .reserved_size = 128 * 1024 * 1024,
-    };
-    userdata.fs_mgr_flags.wait = true;
-    userdata.fs_mgr_flags.check = true;
-    userdata.fs_mgr_flags.logical = true;
-    userdata.fs_mgr_flags.quota = true;
-    userdata.fs_mgr_flags.late_mount = true;
-    userdata.fs_mgr_flags.formattable = true;
-    return userdata;
-}
-
-bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
-    auto iter = std::remove_if(fstab->begin(), fstab->end(),
-                               [&](const auto& entry) { return entry.mount_point == mount_point; });
-    if (iter != fstab->end()) {
-        fstab->erase(iter, fstab->end());
-        return true;
-    }
-    return false;
-}
-
-}  // namespace
-
 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
                           const std::vector<std::string>& dsu_partitions) {
     static constexpr char kDsuKeysDir[] = "/avb";
@@ -707,7 +709,7 @@
     bool is_proc_mounts = path == "/proc/mounts";
 
     Fstab fstab;
-    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
+    if (!ReadFstabFromFp(fstab_file.get(), is_proc_mounts, &fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
@@ -762,7 +764,7 @@
         return false;
     }
 
-    if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
+    if (!ReadFstabFromFp(fstab_file.get(), false, fstab)) {
         if (verbose) {
             LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
                    << fstab_buf;
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
new file mode 100644
index 0000000..f0afd28
--- /dev/null
+++ b/fs_mgr/fuzz/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+cc_fuzz {
+  name: "libfstab_fuzzer",
+  srcs: [
+    "fs_mgr_fstab_fuzzer.cpp",
+  ],
+  static_libs: [
+    "libfstab",
+  ],
+  shared_libs: [
+    "libbase",
+  ],
+
+  dictionary: "fstab.dict",
+  fuzz_config: {
+    cc: [
+      "yochiang@google.com",
+    ],
+  },
+}
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
new file mode 100644
index 0000000..1fddbf8
--- /dev/null
+++ b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -0,0 +1,30 @@
+//
+// 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.
+//
+
+#include <cstdio>
+
+#include <fstab/fstab.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+            fmemopen(static_cast<void*>(const_cast<uint8_t*>(data)), size, "r"), fclose);
+    if (fstab_file == nullptr) {
+        return 0;
+    }
+    android::fs_mgr::Fstab fstab;
+    android::fs_mgr::ReadFstabFromFp(fstab_file.get(), /* proc_mounts= */ false, &fstab);
+    return 0;
+}
diff --git a/fs_mgr/fuzz/fstab.dict b/fs_mgr/fuzz/fstab.dict
new file mode 100644
index 0000000..84dddf7
--- /dev/null
+++ b/fs_mgr/fuzz/fstab.dict
@@ -0,0 +1,70 @@
+"#"
+"="
+","
+"f2fs"
+
+# mount flags
+"noatime"
+"noexec"
+"nosuid"
+"nodev"
+"nodiratime"
+"ro"
+"rw"
+"sync"
+"remount"
+"bind"
+"rec"
+"unbindable"
+"private"
+"slave"
+"shared"
+"defaults"
+
+# fs_mgr flags
+"wait"
+"check"
+"nonremovable"
+"recoveryonly"
+"noemulatedsd"
+"notrim"
+"verify"
+"formattable"
+"slotselect"
+"latemount"
+"nofail"
+"verifyatboot"
+"quota"
+"avb"
+"logical"
+"checkpoint=block"
+"checkpoint=fs"
+"first_stage_mount"
+"slotselect_other"
+"fsverity"
+"metadata_csum"
+"fscompress"
+"overlayfs_remove_missing_lowerdir"
+
+# fs_mgr flags that expect an argument
+"reserve_root="
+"lowerdir="
+"encryptable="
+"voldmanaged="
+"length="
+"swapprio="
+"zramsize="
+"forceencrypt="
+"fileencryption="
+"forcefdeorfbe="
+"max_comp_streams="
+"reservedsize="
+"readahead_size_kb="
+"eraseblk="
+"logicalblk="
+"avb_keys="
+"avb="
+"keydirectory="
+"metadata_encryption="
+"sysfs_path="
+"zram_backingdev_size="
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 9a4ed46..d0f32a3 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -99,6 +99,9 @@
 // Unless explicitly requested, a lookup on mount point should always return the 1st one.
 using Fstab = std::vector<FstabEntry>;
 
+// Exported for testability. Regular users should use ReadFstabFromfile().
+bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out);
+
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
 bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
 bool ReadDefaultFstab(Fstab* fstab);
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 801c446..37319fe 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 30545
 balsini@google.com
 dvander@google.com
 elsk@google.com
diff --git a/init/README.md b/init/README.md
index 64c6b1c..6c29b07 100644
--- a/init/README.md
+++ b/init/README.md
@@ -733,7 +733,10 @@
 `verity_update_state`
 > Internal implementation detail used to update dm-verity state and
   set the partition._mount-point_.verified properties used by adb remount
-  because fs\_mgr can't set them directly itself.
+  because fs\_mgr can't set them directly itself. This is required since
+  Android 12, because CtsNativeVerifiedBootTestCases will read property
+  "partition.${partition}.verified.hash_alg" to check that sha1 is not used.
+  See https://r.android.com/1546980 for more details.
 
 `wait <path> [ <timeout> ]`
 > Poll for the existence of the given file and return when found,
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index ac6b7b2..5f34cc4 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -99,7 +99,7 @@
 
     std::string vbmeta_digest = GetProperty("ro.boot.vbmeta.digest", "");
     ASSERT_GE(vbmeta_digest.size(), 8u);
-    std::string build_id = GetProperty("ro.boot.build.id", "");
+    std::string build_id = GetProperty("ro.build.id", "");
     // Check that the build id is constructed with the prefix of vbmeta digest
     std::string expected_build_id = legacy_build_id + "." + vbmeta_digest.substr(0, 8);
     ASSERT_EQ(expected_build_id, build_id);
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 449a505..45d3c7c 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -183,7 +183,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpu",
+            "Path": "dex2oat"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
@@ -638,7 +650,7 @@
     },
     {
       "Name": "Dex2OatBootComplete",
-      "Profiles": [ "SCHED_SP_BACKGROUND" ]
+      "Profiles": [ "Dex2oatPerformance", "LowIoPriority", "TimerSlackHigh" ]
     }
   ]
 }
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
index 56053e0..e7be548 100644
--- a/libprocessgroup/profiles/task_profiles_28.json
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -117,7 +117,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
index 52279b8..6174c8d 100644
--- a/libprocessgroup/profiles/task_profiles_29.json
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -117,7 +117,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
index 56053e0..e7be548 100644
--- a/libprocessgroup/profiles/task_profiles_30.json
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -117,7 +117,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index cf74e65..e935f99 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -194,22 +194,39 @@
     fd_.reset(FDS_NOT_CACHED);
 }
 
-bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
     if (tid <= 0) {
         return true;
     }
 
     std::string value = std::to_string(tid);
 
-    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
-        // If the thread is in the process of exiting, don't flag an error
-        if (errno != ESRCH) {
-            PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
-            return false;
-        }
+    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) {
+        return true;
     }
 
-    return true;
+    // If the thread is in the process of exiting, don't flag an error
+    if (errno == ESRCH) {
+        return true;
+    }
+
+    // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
+    if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
+        // This is an abnormal case happening only in testing, so report it only once
+        static bool empty_cpuset_reported = false;
+
+        if (empty_cpuset_reported) {
+            return true;
+        }
+
+        LOG(ERROR) << "Failed to add task '" << value
+                   << "' into cpuset because all cpus in that cpuset are offline";
+        empty_cpuset_reported = true;
+    } else {
+        PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+    }
+
+    return false;
 }
 
 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
@@ -219,7 +236,7 @@
         PLOG(WARNING) << "Failed to open " << procs_path;
         return false;
     }
-    if (!AddTidToCgroup(pid, tmp_fd)) {
+    if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
@@ -231,7 +248,7 @@
     std::lock_guard<std::mutex> lock(fd_mutex_);
     if (IsFdValid()) {
         // fd is cached, reuse it
-        if (!AddTidToCgroup(tid, fd_)) {
+        if (!AddTidToCgroup(tid, fd_, controller()->name())) {
             LOG(ERROR) << "Failed to add task into cgroup";
             return false;
         }
@@ -256,7 +273,7 @@
         PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
         return false;
     }
-    if (!AddTidToCgroup(tid, tmp_fd)) {
+    if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 25a84b0..97c38f4 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -134,7 +134,7 @@
     mutable std::mutex fd_mutex_;
 
     static bool IsAppDependentPath(const std::string& path);
-    static bool AddTidToCgroup(int tid, int fd);
+    static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
 
     bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
 };
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3831693..27fa059 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -155,6 +155,7 @@
     mkdir /dev/cpuctl/rt
     mkdir /dev/cpuctl/system
     mkdir /dev/cpuctl/system-background
+    mkdir /dev/cpuctl/dex2oat
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/foreground
     chown system system /dev/cpuctl/background
@@ -162,6 +163,7 @@
     chown system system /dev/cpuctl/rt
     chown system system /dev/cpuctl/system
     chown system system /dev/cpuctl/system-background
+    chown system system /dev/cpuctl/dex2oat
     chown system system /dev/cpuctl/tasks
     chown system system /dev/cpuctl/foreground/tasks
     chown system system /dev/cpuctl/background/tasks
@@ -169,6 +171,7 @@
     chown system system /dev/cpuctl/rt/tasks
     chown system system /dev/cpuctl/system/tasks
     chown system system /dev/cpuctl/system-background/tasks
+    chown system system /dev/cpuctl/dex2oat/tasks
     chmod 0664 /dev/cpuctl/tasks
     chmod 0664 /dev/cpuctl/foreground/tasks
     chmod 0664 /dev/cpuctl/background/tasks
@@ -176,6 +179,7 @@
     chmod 0664 /dev/cpuctl/rt/tasks
     chmod 0664 /dev/cpuctl/system/tasks
     chmod 0664 /dev/cpuctl/system-background/tasks
+    chmod 0664 /dev/cpuctl/dex2oat/tasks
 
     # Create a cpu group for NNAPI HAL processes
     mkdir /dev/cpuctl/nnapi-hal
@@ -1097,6 +1101,9 @@
     # Define default initial receive window size in segments.
     setprop net.tcp_def_init_rwnd 60
 
+    # Update dm-verity state and set partition.*.verified properties.
+    verity_update_state
+
     # Start standard binderized HAL daemons
     class_start hal