Setup ART experiments infra

Add a flexible class for ART Flags that can take their
values from cmdline arguments, system properties or
device config settings.
(the flags concept is evolved from Eric's previous CL
3dba023d4fb47882fa215715c196cfa3be30c098)

Plumb the loading/re-loading of flags in the runtime
initialization and after forking from zygote (so that
we don't require restarts to refresh the flags).

If multiple values are given for the same flag the
evaluation order is:
1) device config (configurable from server side)
2) system properties
3) cmdline arguments
4) the default value

The existing cmdline arguments are not migrated to the
new infra and will be done only on a per need basis.

Test: gtest & manual
Bug: 181748174
Change-Id: If3940393493af1052367d725a3f2aa94eee927c2
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h
index 22eb44c..7e343f8 100644
--- a/cmdline/cmdline_parser.h
+++ b/cmdline/cmdline_parser.h
@@ -210,6 +210,24 @@
       return parent_;
     }
 
+    // Write the results of this argument into a variable pointed to by destination.
+    // An optional is used to tell whether the command line argument was present.
+    CmdlineParser::Builder& IntoLocation(std::optional<TArg>* destination) {
+      save_value_ = [destination](TArg& value) {
+        *destination = value;
+      };
+
+      load_value_ = [destination]() -> TArg& {
+        return destination->value();
+      };
+
+      save_value_specified_ = true;
+      load_value_specified_ = true;
+
+      CompleteArgument();
+      return parent_;
+    }
+
     // Ensure we always move this when returning a new builder.
     ArgumentBuilder(ArgumentBuilder&&) = default;
 
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 43d3022..964e238 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -21,6 +21,7 @@
 #include <list>
 #include <ostream>
 
+#include "android-base/parsebool.h"
 #include "android-base/stringprintf.h"
 #include "cmdline_type_parser.h"
 #include "detail/cmdline_debug_detail.h"
@@ -67,6 +68,22 @@
 };
 
 template <>
+struct CmdlineType<bool> : CmdlineTypeParser<bool> {
+  Result Parse(const std::string& args) {
+    switch (::android::base::ParseBool(args)) {
+      case ::android::base::ParseBoolResult::kError:
+        return Result::Failure("Could not parse '" + args + "' as boolean");
+      case ::android::base::ParseBoolResult::kTrue:
+        return Result::Success(true);
+      case ::android::base::ParseBoolResult::kFalse:
+        return Result::Success(false);
+    }
+  }
+
+  static const char* DescribeType() { return "true|false|1|0|y|n|yes|no|on|off"; }
+};
+
+template <>
 struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> {
   /*
    * Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 1b02119..9dcba99 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -37,6 +37,7 @@
         "base/enums.cc",
         "base/file_magic.cc",
         "base/file_utils.cc",
+        "base/flags.cc",
         "base/hex_dump.cc",
         "base/hiddenapi_flags.cc",
         "base/logging.cc",
@@ -260,6 +261,7 @@
         "base/bit_vector_test.cc",
         "base/compiler_filter_test.cc",
         "base/file_utils_test.cc",
+        "base/flags_test.cc",
         "base/hash_map_test.cc",
         "base/hash_set_test.cc",
         "base/hex_dump_test.cc",
diff --git a/libartbase/base/flags.cc b/libartbase/base/flags.cc
new file mode 100644
index 0000000..46de9c0
--- /dev/null
+++ b/libartbase/base/flags.cc
@@ -0,0 +1,147 @@
+/*
+ * 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 "flags.h"
+
+#include <algorithm>
+
+#include "android-base/parsebool.h"
+#include "android-base/parseint.h"
+#include "android-base/properties.h"
+
+#include "base/utils.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wconversion"
+
+namespace {
+constexpr const char* kPhenotypeFlagPrefix = "persist.device_config.runtime_native.";
+constexpr const char* kSysPropertyFlagPrefix = "dalvik.vm.";
+constexpr const char* kUndefinedValue = "";
+
+// The various ParseValue functions store the parsed value into *destination. If parsing fails for
+// some reason, ParseValue makes no changes to *destination.
+
+void ParseValue(const std::string_view value, std::optional<bool>* destination) {
+  switch (::android::base::ParseBool(value)) {
+    case ::android::base::ParseBoolResult::kError:
+      return;
+    case ::android::base::ParseBoolResult::kTrue:
+      *destination = true;
+      return;
+    case ::android::base::ParseBoolResult::kFalse:
+      *destination = false;
+      return;
+  }
+}
+
+void ParseValue(const std::string_view value, std::optional<int>* destination) {
+  int parsed_value = 0;
+  if (::android::base::ParseInt(std::string{value}, &parsed_value)) {
+    *destination = parsed_value;
+  }
+}
+
+void ParseValue(const std::string_view value, std::optional<std::string>* destination) {
+  *destination = value;
+}
+
+}  // namespace
+
+namespace art {
+
+template <>
+std::forward_list<FlagBase*> FlagBase::ALL_FLAGS{};
+
+// gFlags must be defined after FlagBase::ALL_FLAGS so the constructors run in the right order.
+Flags gFlags;
+
+static std::string GenerateCmdLineArgName(const std::string& name) {
+  std::string result = "-X" + name + ":_";
+  std::replace(result.begin(), result.end(), '.', '-');
+  return result;
+}
+
+static std::string GenerateSysPropName(const std::string& name) {
+  return kSysPropertyFlagPrefix + name;
+}
+
+static std::string GeneratePhenotypeName(const std::string& name) {
+  return kPhenotypeFlagPrefix + name;
+}
+
+template <typename Value>
+Flag<Value>::Flag(const std::string& name, Value default_value) :
+    FlagBase(GenerateCmdLineArgName(name),
+             GenerateSysPropName(name),
+             GeneratePhenotypeName(name)),
+    initialized_{false},
+    default_{default_value} {
+  ALL_FLAGS.push_front(this);
+}
+
+template <typename Value>
+void Flag<Value>::Reload() {
+  // The cmdline flags are loaded by the parsed_options infra.
+
+  // Load system properties.
+  from_system_property_ = std::nullopt;
+  const std::string sysprop = ::android::base::GetProperty(system_property_name_, kUndefinedValue);
+  if (sysprop != kUndefinedValue) {
+    ParseValue(sysprop, &from_system_property_);
+  }
+
+  // Load the server-side configuration.
+  from_server_setting_ = std::nullopt;
+  const std::string server_config =
+      ::android::base::GetProperty(server_setting_name_, kUndefinedValue);
+  if (server_config != kUndefinedValue) {
+    ParseValue(server_config, &from_server_setting_);
+  }
+
+  initialized_ = true;
+}
+
+template <typename Value>
+void DumpValue(std::ostream& oss, const std::optional<Value>& val) {
+  if (val.has_value()) {
+    oss << val.value();
+  } else {
+    oss << kUndefinedValue;
+  }
+}
+
+template <typename Value>
+void Flag<Value>::Dump(std::ostream& oss) const {
+  std::pair<Value, std::string> valueLoc = GetValueLocation();
+  oss << "value: " << std::get<0>(valueLoc) << " (from " << std::get<1>(valueLoc) << ")";
+
+  oss << "\n default: " << default_;
+  oss << "\n " << command_line_argument_name_ << ": ";
+  DumpValue(oss, from_command_line_);
+  oss << "\n " << system_property_name_ << ": ";
+  DumpValue(oss, from_system_property_);
+  oss << "\n " << server_setting_name_ << ": ";
+  DumpValue(oss, from_server_setting_);
+}
+
+template class Flag<bool>;
+template class Flag<int>;
+template class Flag<std::string>;
+
+}  // namespace art
+
+#pragma clang diagnostic pop  // -Wconversion
diff --git a/libartbase/base/flags.h b/libartbase/base/flags.h
new file mode 100644
index 0000000..ae166e6
--- /dev/null
+++ b/libartbase/base/flags.h
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_FLAGS_H_
+#define ART_LIBARTBASE_BASE_FLAGS_H_
+
+#include <forward_list>
+#include <optional>
+#include <string>
+#include <variant>
+
+#include "logging.h"
+
+// This file defines a set of flags that can be used to enable/disable features within ART or
+// otherwise tune ART's behavior. Flags can be set through command line options, server side
+// configuration, system properties, or default values. This flexibility enables easier development
+// and also larger experiments.
+//
+// The value is retrieved in the following oder:
+//   1) server side (device config) property
+//   2) system property
+//   3) cmdline flag
+//   4) default value
+//
+// The flags are defined in the Flags struct near the bottom of the file. To define a new flag, add
+// a Flag field to the struct. Then to read the value of the flag, use gFlag.MyNewFlag().
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wconversion"
+
+namespace art {
+
+// FlagMetaBase handles automatically adding flags to the command line parser. It is parameterized
+// by all supported flag types. In general, this should be treated as though it does not exist and
+// FlagBase, which is already specialized to the types we support, should be used instead.
+template <typename... T>
+class FlagMetaBase {
+ public:
+  FlagMetaBase(const std::string&& command_line_argument_name,
+               const std::string&& system_property_name,
+               const std::string&& server_setting_name) :
+      command_line_argument_name_(command_line_argument_name),
+      system_property_name_(system_property_name),
+      server_setting_name_(server_setting_name) {}
+  virtual ~FlagMetaBase() {}
+
+  template <typename Builder>
+  static void AddFlagsToCmdlineParser(Builder* builder) {
+    for (auto* flag : ALL_FLAGS) {
+      // Each flag can return a pointer to where its command line value is stored. Because these can
+      // be different types, the return value comes as a variant. The cases list below contains a
+      // lambda that is specialized to handle each branch of the variant and call the correct
+      // methods on the command line parser builder.
+      FlagValuePointer location = flag->GetCmdLineLocation();
+      auto cases = {std::function<void()>([&]() {
+        if (std::holds_alternative<std::optional<T>*>(location)) {
+          builder = &builder->Define(flag->command_line_argument_name_.c_str())
+                         .template WithType<T>()
+                         .IntoLocation(std::get<std::optional<T>*>(location));
+        }
+      })...};
+      for (auto c : cases) {
+        c();
+      }
+    }
+  }
+
+  // Reload the value of the flags.
+  //
+  // DO NOT CALL this outside Runtime Init or Zygote post fork.
+  // This is a convention, as we should strive to have a constant view
+  // of the flags and not change the runtime behaviour midway during execution.
+  static void ReloadAllFlags(const std::string& caller) {
+    // Check the caller. This is a simple workaround to attract the attention
+    // to a possible dangerous call to ReloadAllFlags, while avoid building
+    // a lot of infra for it or having a complex friend definition.
+    DCHECK(caller == "Init"
+        || caller == "ZygoteHooks_nativePostForkChild"
+        || caller == "ZygoteHooks_nativePostForkSystemServer"
+        || caller == "test") << caller;
+    for (auto* flag : ALL_FLAGS) {
+      flag->Reload();
+    }
+
+    if (VLOG_IS_ON(startup)) {
+      VLOG_STREAM(startup) << "Dumping flags for " << caller;
+      DumpFlags(VLOG_STREAM(startup));
+    }
+  }
+
+  // Dump all the flags info to the given stream.
+  static void DumpFlags(std::ostream& oss) {
+    for (auto* flag : ALL_FLAGS) {
+      oss << "\n{\n";
+      flag->Dump(oss);
+      oss << "\n}";
+    }
+  }
+
+ protected:
+  using FlagValuePointer = std::variant<std::optional<T>*...>;
+  // Return the pointer to the value holder associated with the cmd line location.
+  virtual FlagValuePointer GetCmdLineLocation() = 0;
+  // Reloads the flag values.
+  virtual void Reload() = 0;
+  // Dumps the flags info to the given stream.
+  virtual void Dump(std::ostream& oss) const = 0;
+
+  static std::forward_list<FlagMetaBase<T...>*> ALL_FLAGS;
+
+  const std::string command_line_argument_name_;
+  const std::string system_property_name_;
+  const std::string server_setting_name_;
+};
+
+using FlagBase = FlagMetaBase<bool, int, std::string>;
+
+template <>
+std::forward_list<FlagBase*> FlagBase::ALL_FLAGS;
+
+class FlagsTests;
+
+// This class defines a flag with a value of a particular type.
+template <typename Value>
+class Flag : public FlagBase {
+ public:
+  // Create a new Flag. The name parameter is used to generate the names from the various parameter
+  // sources. See the documentation on the Flags struct for an example.
+  explicit Flag(const std::string& name, Value default_value = {});
+  virtual ~Flag() {}
+
+
+  // Returns the flag value.
+  //
+  // The value is retrieved in the following oder:
+  //   1) server side (device config) property
+  //   2) system property
+  //   3) cmdline flag
+  //   4) default value
+  ALWAYS_INLINE Value GetValue() const {
+    return std::get<0>(GetValueLocation());
+  }
+
+  ALWAYS_INLINE Value operator()() const {
+    return GetValue();
+  }
+
+  // Returns the value and the location of that value for the given flag.
+  ALWAYS_INLINE std::pair<Value, std::string> GetValueLocation() const {
+    DCHECK(initialized_);
+    if (from_server_setting_.has_value()) {
+      return std::pair{from_server_setting_.value(), server_setting_name_};
+    }
+    if (from_system_property_.has_value()) {
+      return std::pair{from_system_property_.value(), system_property_name_};
+    }
+    if (from_command_line_.has_value()) {
+      return std::pair{from_command_line_.value(), command_line_argument_name_};
+    }
+    return std::pair{default_, "default_value"};
+  }
+
+  void Dump(std::ostream& oss) const override;
+
+ protected:
+  FlagValuePointer GetCmdLineLocation() override { return &from_command_line_; }
+
+
+  // Reload the server-configured value and system property values. In general this should not be
+  // used directly, but it can be used to support reloading the value without restarting the device.
+  void Reload() override;
+
+ private:
+  bool initialized_;
+  const Value default_;
+  std::optional<Value> from_command_line_;
+  std::optional<Value> from_system_property_;
+  std::optional<Value> from_server_setting_;
+
+  friend class FlagsTests;
+};
+
+// This struct contains the list of ART flags. Flags are parameterized by the type of value they
+// support (bool, int, string, etc.). In addition to field name, flags have a name for the parameter
+// as well.
+//
+// Example:
+//
+//     Flag<int> WriteMetricsToLog{"my-feature-test.flag", 42};
+//
+// This creates a boolean flag that can be read through gFlags.WriteMetricsToLog(). The default
+// value is false. Note that the default value can be left unspecified, in which the value of the
+// type's default constructor will be used.
+//
+// The flag can be set through the following generated means:
+//
+// Command Line:
+//
+//     -Xmy-feature-test-flag=1
+//
+// Server Side (Phenotype) Configuration:
+//
+//     persist.device_config.runtime_native.my-feature-test.flag
+//
+// System Property:
+//
+//     setprop dalvik.vm.metrics.my-feature-test.flag 2
+struct Flags {
+  // Flag used to test the infra.
+  // TODO: can be removed once we add real flags.
+  Flag<int> MyFeatureTestFlag{"my-feature-test.flag", /*default_value=*/ 42};
+};
+
+// This is the actual instance of all the flags.
+extern Flags gFlags;
+
+}  // namespace art
+
+#pragma clang diagnostic pop  // -Wconversion
+
+#endif  // ART_LIBARTBASE_BASE_FLAGS_H_
diff --git a/libartbase/base/flags_test.cc b/libartbase/base/flags_test.cc
new file mode 100644
index 0000000..cfbf2a9
--- /dev/null
+++ b/libartbase/base/flags_test.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 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 "base/flags.h"
+
+#include <optional>
+
+#include "android-base/properties.h"
+#include "common_runtime_test.h"
+
+
+namespace art {
+
+class FlagsTests : public CommonRuntimeTest {
+ protected:
+  void assertCmdlineValue(bool has_value, int expected) {
+    ASSERT_EQ(gFlags.MyFeatureTestFlag.from_command_line_.has_value(), has_value);
+    if (has_value) {
+      ASSERT_EQ(gFlags.MyFeatureTestFlag.from_command_line_.value(), expected);
+    }
+  }
+
+  void assertSysPropValue(bool has_value, int expected) {
+    ASSERT_EQ(gFlags.MyFeatureTestFlag.from_system_property_.has_value(), has_value);
+    if (has_value) {
+      ASSERT_EQ(gFlags.MyFeatureTestFlag.from_system_property_.value(), expected);
+    }
+  }
+
+  void assertServerSettingValue(bool has_value, int expected) {
+    ASSERT_EQ(gFlags.MyFeatureTestFlag.from_server_setting_.has_value(), has_value);
+    if (has_value) {
+      ASSERT_EQ(gFlags.MyFeatureTestFlag.from_server_setting_.value(), expected);
+    }
+  }
+
+  void assertDefaultValue(int expected) {
+    ASSERT_EQ(gFlags.MyFeatureTestFlag.default_, expected);
+  }
+};
+
+class FlagsTestsWithCmdLine : public FlagsTests {
+ public:
+  ~FlagsTestsWithCmdLine() {
+    android::base::SetProperty("dalvik.vm.my-feature-test.flag", "");
+    android::base::SetProperty("persist.device_config.runtime_native.my-feature-test.flag", "");
+  }
+
+ protected:
+  void SetUpRuntimeOptions(RuntimeOptions* options) override {
+    // Disable implicit dex2oat invocations when loading image spaces.
+    options->emplace_back("-Xmy-feature-test-flag:1", nullptr);
+  }
+};
+
+// Validate that when no flag is set, the default is taken and none of the other
+// locations are populated
+TEST_F(FlagsTests, ValidateDefaultValue) {
+  FlagBase::ReloadAllFlags("test");
+
+  assertCmdlineValue(false, 1);
+  assertSysPropValue(false, 2);
+  assertServerSettingValue(false, 3);
+  assertDefaultValue(42);
+
+  ASSERT_EQ(gFlags.MyFeatureTestFlag(), 42);
+}
+
+// Validate that the server side config is picked when it is set.
+TEST_F(FlagsTestsWithCmdLine, FlagsTestsGetValueServerSetting) {
+  android::base::SetProperty("dalvik.vm.my-feature-test.flag", "2");
+  android::base::SetProperty("persist.device_config.runtime_native.my-feature-test.flag", "3");
+
+  FlagBase::ReloadAllFlags("test");
+
+  assertCmdlineValue(true, 1);
+  assertSysPropValue(true, 2);
+  assertServerSettingValue(true, 3);
+  assertDefaultValue(42);
+
+  ASSERT_EQ(gFlags.MyFeatureTestFlag(), 3);
+}
+
+// Validate that the system property value is picked when the server one is not set.
+TEST_F(FlagsTestsWithCmdLine, FlagsTestsGetValueSysProperty) {
+  android::base::SetProperty("dalvik.vm.my-feature-test.flag", "2");
+
+  FlagBase::ReloadAllFlags("test");
+
+  assertCmdlineValue(true, 1);
+  assertSysPropValue(true, 2);
+  assertServerSettingValue(false, 3);
+  assertDefaultValue(42);
+
+  ASSERT_EQ(gFlags.MyFeatureTestFlag(), 2);
+}
+
+// Validate that the cmdline value is picked when no properties are set.
+TEST_F(FlagsTestsWithCmdLine, FlagsTestsGetValueCmdline) {
+  FlagBase::ReloadAllFlags("test");
+
+  assertCmdlineValue(true, 1);
+  assertSysPropValue(false, 2);
+  assertServerSettingValue(false, 3);
+  assertDefaultValue(42);
+
+  ASSERT_EQ(gFlags.MyFeatureTestFlag(), 1);
+}
+
+}  // namespace art
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 9f75a88..cdb63f3 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -269,6 +269,9 @@
 static void ZygoteHooks_nativePostForkSystemServer(JNIEnv* env ATTRIBUTE_UNUSED,
                                                    jclass klass ATTRIBUTE_UNUSED,
                                                    jint runtime_flags) {
+  // Reload the current flags first. In case we need to take actions based on them.
+  Runtime::Current()->ReloadAllFlags(__FUNCTION__);
+
   // Set the runtime state as the first thing, in case JIT and other services
   // start querying it.
   Runtime::Current()->SetAsSystemServer();
@@ -296,7 +299,9 @@
                                             jboolean is_zygote,
                                             jstring instruction_set) {
   DCHECK(!(is_system_server && is_zygote));
-  // Set the runtime state as the first thing, in case JIT and other services
+  // Reload the current flags first. In case we need to take any updated actions.
+  Runtime::Current()->ReloadAllFlags(__FUNCTION__);
+  // Then, set the runtime state, in case JIT and other services
   // start querying it.
   Runtime::Current()->SetAsZygoteChild(is_system_server, is_zygote);
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 471a3fa..e5e7c4f 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -23,6 +23,7 @@
 #include <android-base/strings.h>
 
 #include "base/file_utils.h"
+#include "base/flags.h"
 #include "base/indenter.h"
 #include "base/macros.h"
 #include "base/utils.h"
@@ -464,8 +465,11 @@
       .Define("-XX:PerfettoJavaHeapStackProf=_")
           .WithType<bool>()
           .WithValueMap({{"false", false}, {"true", true}})
-          .IntoKey(M::PerfettoJavaHeapStackProf)
-      .Ignore({
+          .IntoKey(M::PerfettoJavaHeapStackProf);
+
+      FlagBase::AddFlagsToCmdlineParser(parser_builder.get());
+
+      parser_builder->Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
           "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 97c1d6d..52a4ab3 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -56,6 +56,7 @@
 #include "base/dumpable.h"
 #include "base/enums.h"
 #include "base/file_utils.h"
+#include "base/flags.h"
 #include "base/malloc_arena_pool.h"
 #include "base/mem_map_arena_pool.h"
 #include "base/memory_tool.h"
@@ -1284,6 +1285,10 @@
   apex_versions_ = result;
 }
 
+void Runtime::ReloadAllFlags(const std::string& caller) {
+  FlagBase::ReloadAllFlags(caller);
+}
+
 bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
   // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc.
   // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc.
@@ -1294,6 +1299,9 @@
   ScopedTrace trace(__FUNCTION__);
   CHECK_EQ(static_cast<size_t>(sysconf(_SC_PAGE_SIZE)), kPageSize);
 
+  // Reload all the flags value (from system properties and device configs).
+  ReloadAllFlags(__FUNCTION__);
+
   // Early override for logging output.
   if (runtime_options.Exists(Opt::UseStderrLogger)) {
     android::base::SetLogger(android::base::StderrLogger);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 9664977..1436a5f 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -1005,6 +1005,17 @@
     return apex_versions_;
   }
 
+  // Trigger a flag reload from system properties or device congfigs.
+  //
+  // Should only be called from runtime init and zygote post fork as
+  // we don't want to change the runtime config midway during execution.
+  //
+  // The caller argument should be the name of the function making this call
+  // and will be used to enforce the appropriate names.
+  //
+  // See Flags::ReloadAllFlags as well.
+  static void ReloadAllFlags(const std::string& caller);
+
  private:
   static void InitPlatformSignalHandlers();