Add a util class for getting system properties.
System properties affect artd's behavior on app compilation. This class
makes system properties mockable so that artd can be unit-tested. It
also provides fallback lookup support, which is useful for artd.
This class is added to libarttools because it can potentially be shared
with other code, such as odrefresh.
Bug: 229268202
Test: m test-art-host-gtest-art_libarttools_tests
Ignore-AOSP-First: ART Services
Change-Id: Ibb9322db8124ca6d8a54a3ee65ad9a4ffc730845
diff --git a/artd/artd.cc b/artd/artd.cc
index 392f587..8f520e6 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -30,7 +30,6 @@
#include "aidl/com/android/server/art/BnArtd.h"
#include "android-base/errors.h"
#include "android-base/logging.h"
-#include "android-base/properties.h"
#include "android-base/result.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -52,7 +51,6 @@
using ::aidl::com::android::server::art::ArtifactsPath;
using ::aidl::com::android::server::art::GetOptimizationStatusResult;
using ::android::base::Error;
-using ::android::base::GetBoolProperty;
using ::android::base::Result;
using ::android::base::Split;
using ::android::base::StringPrintf;
@@ -60,9 +58,6 @@
constexpr const char* kServiceName = "artd";
-constexpr const char* kPhenotypeFlagPrefix = "persist.device_config.runtime_native_boot.";
-constexpr const char* kDalvikVmFlagPrefix = "dalvik.vm.";
-
Result<std::vector<std::string>> GetBootClassPath() {
const char* env_value = getenv("BOOTCLASSPATH");
if (env_value == nullptr || strlen(env_value) == 0) {
@@ -82,22 +77,6 @@
return Split(location_str, ":");
}
-bool UseJitZygote() {
- bool profile_boot_class_path_phenotype =
- GetBoolProperty(std::string(kPhenotypeFlagPrefix) + "profilebootclasspath",
- /*default_value=*/false);
-
- bool profile_boot_class_path =
- GetBoolProperty(std::string(kDalvikVmFlagPrefix) + "profilebootclasspath",
- /*default_value=*/profile_boot_class_path_phenotype);
-
- return profile_boot_class_path;
-}
-
-bool DenyArtApexDataFiles() {
- return !GetBoolProperty("odsign.verification.success", /*default_value=*/false);
-}
-
// Deletes a file. Returns the size of the deleted file, or 0 if the deleted file is empty or an
// error occurs.
int64_t GetSizeAndDeleteFile(const std::string& path) {
@@ -233,5 +212,15 @@
bool Artd::HasRuntimeOptionsCache() const { return !cached_boot_image_locations_.empty(); }
+bool Artd::UseJitZygote() const {
+ return props_->GetBool("dalvik.vm.profilebootclasspath",
+ "persist.device_config.runtime_native_boot.profilebootclasspath",
+ /*default_value=*/false);
+}
+
+bool Artd::DenyArtApexDataFiles() const {
+ return !props_->GetBool("odsign.verification.success", /*default_value=*/false);
+}
+
} // namespace artd
} // namespace art
diff --git a/artd/artd.h b/artd/artd.h
index 2a05267..cb7a12e 100644
--- a/artd/artd.h
+++ b/artd/artd.h
@@ -24,12 +24,17 @@
#include "android-base/result.h"
#include "android/binder_auto_utils.h"
#include "oat_file_assistant.h"
+#include "tools/system_properties.h"
namespace art {
namespace artd {
class Artd : public aidl::com::android::server::art::BnArtd {
public:
+ explicit Artd(std::unique_ptr<art::tools::SystemProperties> props =
+ std::make_unique<art::tools::SystemProperties>())
+ : props_(std::move(props)) {}
+
ndk::ScopedAStatus isAlive(bool* _aidl_return) override;
ndk::ScopedAStatus deleteArtifacts(
@@ -51,10 +56,15 @@
bool HasRuntimeOptionsCache() const;
+ bool UseJitZygote() const;
+
+ bool DenyArtApexDataFiles() const;
+
std::vector<std::string> cached_boot_image_locations_;
std::vector<std::string> cached_boot_class_path_;
std::string cached_apex_versions_;
bool cached_deny_art_apex_data_files_;
+ std::unique_ptr<art::tools::SystemProperties> props_;
};
} // namespace artd
diff --git a/libarttools/Android.bp b/libarttools/Android.bp
index 3df40a5..ff3f4d1 100644
--- a/libarttools/Android.bp
+++ b/libarttools/Android.bp
@@ -49,12 +49,16 @@
art_cc_defaults {
name: "art_libarttools_tests_defaults",
srcs: [
+ "tools/system_properties_test.cc",
"tools/tools_test.cc",
],
shared_libs: [
"libbase",
"libarttools",
],
+ static_libs: [
+ "libgmock",
+ ],
}
// Version of ART gtest `art_libarttools_tests` bundled with the ART APEX on target.
diff --git a/libarttools/tools/system_properties.h b/libarttools/tools/system_properties.h
new file mode 100644
index 0000000..06b7bcb
--- /dev/null
+++ b/libarttools/tools/system_properties.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 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_LIBARTTOOLS_TOOLS_SYSTEM_PROPERTIES_H_
+#define ART_LIBARTTOOLS_TOOLS_SYSTEM_PROPERTIES_H_
+
+#include <string>
+
+#include "android-base/parsebool.h"
+#include "android-base/properties.h"
+
+namespace art {
+namespace tools {
+
+// A class for getting system properties with fallback lookup support. Different from
+// android::base::GetProperty, this class is mockable.
+class SystemProperties {
+ public:
+ virtual ~SystemProperties() = default;
+
+ // Returns the current value of the system property `key`, or `default_value` if the property
+ // doesn't have a value.
+ std::string Get(const std::string& key, const std::string& default_value) const {
+ std::string value = GetProperty(key);
+ if (!value.empty()) {
+ return value;
+ }
+ return default_value;
+ }
+
+ // Same as above, but allows specifying one or more fallback keys. The last argument is a string
+ // default value that will be used if none of the given keys has a value.
+ //
+ // Usage:
+ //
+ // Look up for "key_1", then "key_2", then "key_3". If none of them has a value, return "default":
+ // Get("key_1", "key_2", "key_3", /*default_value=*/"default")
+ template <typename... Args>
+ std::string Get(const std::string& key, const std::string& fallback_key, Args... args) const {
+ return Get(key, Get(fallback_key, args...));
+ }
+
+ // Returns the current value of the system property `key` with zero or more fallback keys, or an
+ // empty string if none of the given keys has a value.
+ //
+ // Usage:
+ //
+ // Look up for "key_1". If it doesn't have a value, return an empty string:
+ // GetOrEmpty("key_1")
+ //
+ // Look up for "key_1", then "key_2", then "key_3". If none of them has a value, return an empty
+ // string:
+ // GetOrEmpty("key_1", "key_2", "key_3")
+ template <typename... Args>
+ std::string GetOrEmpty(const std::string& key, Args... fallback_keys) const {
+ return Get(key, fallback_keys..., /*default_value=*/"");
+ }
+
+ // Returns the current value of the boolean system property `key`, or `default_value` if the
+ // property doesn't have a value. See `android::base::ParseBool` for how the value is parsed.
+ bool GetBool(const std::string& key, bool default_value) const {
+ android::base::ParseBoolResult result = android::base::ParseBool(GetProperty(key));
+ if (result != android::base::ParseBoolResult::kError) {
+ return result == android::base::ParseBoolResult::kTrue;
+ }
+ return default_value;
+ }
+
+ // Same as above, but allows specifying one or more fallback keys. The last argument is a bool
+ // default value that will be used if none of the given keys has a value.
+ //
+ // Usage:
+ //
+ // Look up for "key_1", then "key_2", then "key_3". If none of them has a value, return true:
+ // Get("key_1", "key_2", "key_3", /*default_value=*/true)
+ template <typename... Args>
+ bool GetBool(const std::string& key, const std::string& fallback_key, Args... args) const {
+ return GetBool(key, GetBool(fallback_key, args...));
+ }
+
+ protected:
+ // The single source of truth of system properties. Can be mocked in unit tests.
+ virtual std::string GetProperty(const std::string& key) const {
+ return android::base::GetProperty(key, /*default_value=*/"");
+ }
+};
+
+} // namespace tools
+} // namespace art
+
+#endif // ART_LIBARTTOOLS_TOOLS_SYSTEM_PROPERTIES_H_
diff --git a/libarttools/tools/system_properties_test.cc b/libarttools/tools/system_properties_test.cc
new file mode 100644
index 0000000..80300f0
--- /dev/null
+++ b/libarttools/tools/system_properties_test.cc
@@ -0,0 +1,97 @@
+/*
+ * 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 "system_properties.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace tools {
+namespace {
+
+using ::testing::Return;
+
+class MockSystemProperties : public SystemProperties {
+ public:
+ MOCK_METHOD(std::string, GetProperty, (const std::string& key), (const, override));
+};
+
+class SystemPropertiesTest : public testing::Test {
+ protected:
+ MockSystemProperties system_properties_;
+};
+
+TEST_F(SystemPropertiesTest, Get) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("value_1"));
+ EXPECT_EQ(system_properties_.Get("key_1", /*default_value=*/"default"), "value_1");
+}
+
+TEST_F(SystemPropertiesTest, GetWithFallback) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_CALL(system_properties_, GetProperty("key_2")).WillOnce(Return("value_2"));
+ EXPECT_CALL(system_properties_, GetProperty("key_3")).WillOnce(Return("value_3"));
+ EXPECT_EQ(system_properties_.Get("key_1", "key_2", "key_3", /*default_value=*/"default"),
+ "value_2");
+}
+
+TEST_F(SystemPropertiesTest, GetDefault) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_EQ(system_properties_.Get("key_1", /*default_value=*/"default"), "default");
+}
+
+TEST_F(SystemPropertiesTest, GetOrEmpty) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("value_1"));
+ EXPECT_EQ(system_properties_.GetOrEmpty("key_1"), "value_1");
+}
+
+TEST_F(SystemPropertiesTest, GetOrEmptyWithFallback) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_CALL(system_properties_, GetProperty("key_2")).WillOnce(Return("value_2"));
+ EXPECT_CALL(system_properties_, GetProperty("key_3")).WillOnce(Return("value_3"));
+ EXPECT_EQ(system_properties_.GetOrEmpty("key_1", "key_2", "key_3"), "value_2");
+}
+
+TEST_F(SystemPropertiesTest, GetOrEmptyDefault) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_EQ(system_properties_.GetOrEmpty("key_1"), "");
+}
+
+TEST_F(SystemPropertiesTest, GetBoolTrue) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("true"));
+ EXPECT_EQ(system_properties_.GetBool("key_1", /*default_value=*/false), true);
+}
+
+TEST_F(SystemPropertiesTest, GetBoolFalse) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("false"));
+ EXPECT_EQ(system_properties_.GetBool("key_1", /*default_value=*/true), false);
+}
+
+TEST_F(SystemPropertiesTest, GetBoolWithFallback) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_CALL(system_properties_, GetProperty("key_2")).WillOnce(Return("true"));
+ EXPECT_CALL(system_properties_, GetProperty("key_3")).WillOnce(Return("false"));
+ EXPECT_EQ(system_properties_.GetBool("key_1", "key_2", "key_3", /*default_value=*/false), true);
+}
+
+TEST_F(SystemPropertiesTest, GetBoolDefault) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_EQ(system_properties_.GetBool("key_1", /*default_value=*/true), true);
+}
+
+} // namespace
+} // namespace tools
+} // namespace art