Add a phenotype flag to force disable CMC GC.

This new flag (`runtime_native_boot.force_disable_uffd_gc`) has priority
over the flag that enables the CMC GC
(`runtime_native_boot.enable_uffd_gc_2`).

Bug: 251150519
Test: -
  1. Build with OVERRIDE_ENABLE_UFFD_GC=false
  2. See "Using CollectorTypeCC GC".
  3. adb shell device_config set_sync_disabled_for_tests persistent
  4. adb shell device_config put runtime_native_boot enable_uffd_gc_2 true
  5. adb reboot
  6. See odrefresh triggered on the reboot.
  7. See "Using CollectorTypeCMC GC".
  8. adb shell device_config put runtime_native_boot force_disable_uffd_gc true
  9. adb reboot
  10. See odrefresh triggered on the reboot.
  11. See "Using CollectorTypeCC GC".
Change-Id: I2a53ccadf7e2932969a80a0c86c1307a58abf47a
diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h
index 5a11b04..44b847c 100644
--- a/odrefresh/odr_config.h
+++ b/odrefresh/odr_config.h
@@ -74,6 +74,8 @@
 const android::base::NoDestructor<std::vector<SystemPropertyConfig>> kSystemProperties{
     {SystemPropertyConfig{.name = "persist.device_config.runtime_native_boot.enable_uffd_gc_2",
                           .default_value = "false"},
+     SystemPropertyConfig{.name = "persist.device_config.runtime_native_boot.force_disable_uffd_gc",
+                          .default_value = "false"},
      SystemPropertyConfig{.name = kPhDisableCompactDex, .default_value = "false"},
      SystemPropertyConfig{.name = kSystemPropertySystemServerCompilerFilterOverride,
                           .default_value = ""}}};
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index aee5b29..ad308f4 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -186,7 +186,7 @@
   return -1;
 }
 
-static bool GetCachedBoolProperty(const std::string& key, bool default_value) {
+static std::unordered_map<std::string, std::string> GetCachedProperties() {
   // For simplicity, we don't handle multiple calls because otherwise we would have to reset the fd.
   static bool called = false;
   CHECK(!called) << "GetCachedBoolProperty can be called only once";
@@ -197,7 +197,7 @@
   if (fd >= 0) {
     if (!android::base::ReadFdToString(fd, &cache_info_contents)) {
       PLOG(ERROR) << "Failed to read cache-info from fd " << fd;
-      return default_value;
+      return {};
     }
   } else {
     std::string path = GetApexDataDalvikCacheDirectory(InstructionSet::kNone) + "/cache-info.xml";
@@ -208,7 +208,7 @@
       if (errno != ENOENT) {
         PLOG(ERROR) << "Failed to read cache-info from the default path";
       }
-      return default_value;
+      return {};
     }
   }
 
@@ -217,37 +217,51 @@
   if (!cache_info.has_value()) {
     // This should never happen.
     LOG(ERROR) << "Failed to parse cache-info";
-    return default_value;
+    return {};
   }
   const com::android::art::KeyValuePairList* list = cache_info->getFirstSystemProperties();
   if (list == nullptr) {
     // This should never happen.
     LOG(ERROR) << "Missing system properties from cache-info";
-    return default_value;
+    return {};
   }
   const std::vector<com::android::art::KeyValuePair>& properties = list->getItem();
+  std::unordered_map<std::string, std::string> result;
   for (const com::android::art::KeyValuePair& pair : properties) {
-    if (pair.getK() == key) {
-      ParseBoolResult result = ParseBool(pair.getV());
-      switch (result) {
-        case ParseBoolResult::kTrue:
-          return true;
-        case ParseBoolResult::kFalse:
-          return false;
-        case ParseBoolResult::kError:
-          return default_value;
-      }
-    }
+    result[pair.getK()] = pair.getV();
   }
-  return default_value;
+  return result;
+}
+
+static bool GetCachedBoolProperty(
+    const std::unordered_map<std::string, std::string>& cached_properties,
+    const std::string& key,
+    bool default_value) {
+  auto it = cached_properties.find(key);
+  if (it == cached_properties.end()) {
+    return default_value;
+  }
+  ParseBoolResult result = ParseBool(it->second);
+  switch (result) {
+    case ParseBoolResult::kTrue:
+      return true;
+    case ParseBoolResult::kFalse:
+      return false;
+    case ParseBoolResult::kError:
+      return default_value;
+  }
 }
 
 static bool SysPropSaysUffdGc() {
   // The phenotype flag can change at time time after boot, but it shouldn't take effect until a
   // reboot. Therefore, we read the phenotype flag from the cache info, which is generated on boot.
-  return GetCachedBoolProperty("persist.device_config.runtime_native_boot.enable_uffd_gc_2",
-                               false) ||
-         GetBoolProperty("ro.dalvik.vm.enable_uffd_gc", false);
+  std::unordered_map<std::string, std::string> cached_properties = GetCachedProperties();
+  bool phenotype_enable = GetCachedBoolProperty(
+      cached_properties, "persist.device_config.runtime_native_boot.enable_uffd_gc_2", false);
+  bool phenotype_force_disable = GetCachedBoolProperty(
+      cached_properties, "persist.device_config.runtime_native_boot.force_disable_uffd_gc", false);
+  bool build_enable = GetBoolProperty("ro.dalvik.vm.enable_uffd_gc", false);
+  return (phenotype_enable || build_enable) && !phenotype_force_disable;
 }
 #else
 // Never called.