From ed4f3bae3daad0d219187eab4d647cdb77bfbb1e Mon Sep 17 00:00:00 2001 From: Jiakai Zhang Date: Wed, 8 Jun 2022 16:22:07 +0100 Subject: Add artd unit tests. This change adds `art_artd_tests`, which contains the unit tests of artd. The change also splits the code into artd.h/artd.cc and artd_main.cc, where artd.h/artd.cc contains the implementation that is unit-testable, while artd_main.cc contains the main function of the artd binary. Bug: 177273468 Test: m test-art-host-gtest-art_artd_tests Test: ArtGtestsTargetChroot:ArtdTest Ignore-AOSP-First: Will cherry-pick later. Change-Id: I19701d7ca83f541becdcd413e740eff93d03037d --- build/apex/art_apex_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'build/apex/art_apex_test.py') diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py index 556bb78295..a13a7d3081 100755 --- a/build/apex/art_apex_test.py +++ b/build/apex/art_apex_test.py @@ -558,7 +558,6 @@ class ReleaseTargetChecker: # Check internal libraries for ART. self._checker.check_native_library('libartservice') self._checker.check_native_library('libperfetto_hprof') - self._checker.check_prefer64_library('artd-aidl-ndk') # Check internal Java libraries self._checker.check_java_library("service-art") @@ -672,6 +671,7 @@ class TestingTargetChecker: def run(self): # Check ART test binaries. + self._checker.check_art_test_executable('art_artd_tests') self._checker.check_art_test_executable('art_cmdline_tests') self._checker.check_art_test_executable('art_compiler_tests') self._checker.check_art_test_executable('art_dex2oat_tests') -- cgit v1.2.3-59-g8ed1b From 2b905c3ed4b86035da579af43ec895fb8f0b6f0d Mon Sep 17 00:00:00 2001 From: Jiakai Zhang Date: Wed, 20 Jul 2022 15:49:34 +0100 Subject: Add a wrapper binary that configures the process and executes a command. artd needs to configure the child process before it executes a command. However, this cannot be done between `fork` and `exec` because artd is a multi-threaded program, and doing things between `fork` and `exec` has a risk of deadlock. The wrapper binary is a workaround to this problem. Bug: 229268202 Test: adb shell pm art optimize-package -m speed-profile -f \ com.google.android.youtube Test: atest art_standalone_libarttools_tests Test: art/tools/run-gtests.sh \ /apex/com.android.art/bin/art/x86_64/art_libarttools_tests Test: atest ArtGtestsTargetChroot:ArtExecTest Ignore-AOSP-First: ART Services. Change-Id: Ifa32518afcb3802e2c9b893e9b4afba2f19572ba --- build/apex/Android.bp | 1 + build/apex/art_apex_test.py | 1 + libarttools/Android.bp | 36 ++++++++ libarttools/tools/art_exec.cc | 155 ++++++++++++++++++++++++++++++++ libarttools/tools/art_exec_test.cc | 180 +++++++++++++++++++++++++++++++++++++ 5 files changed, 373 insertions(+) create mode 100644 libarttools/tools/art_exec.cc create mode 100644 libarttools/tools/art_exec_test.cc (limited to 'build/apex/art_apex_test.py') diff --git a/build/apex/Android.bp b/build/apex/Android.bp index 80acd81e4a..d40f512e5b 100644 --- a/build/apex/Android.bp +++ b/build/apex/Android.bp @@ -270,6 +270,7 @@ apex_defaults { "libartservice", ], binaries: [ + "art_exec", "artd", ], multilib: { diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py index 1813913e10..efb4c3f921 100755 --- a/build/apex/art_apex_test.py +++ b/build/apex/art_apex_test.py @@ -549,6 +549,7 @@ class ReleaseTargetChecker: # removed in Android R. # Check binaries for ART. + self._checker.check_executable('art_exec') self._checker.check_executable('artd') self._checker.check_executable('oatdump') self._checker.check_executable("odrefresh") diff --git a/libarttools/Android.bp b/libarttools/Android.bp index 6746c8e394..5353285fba 100644 --- a/libarttools/Android.bp +++ b/libarttools/Android.bp @@ -49,6 +49,7 @@ cc_library { art_cc_defaults { name: "art_libarttools_tests_defaults", srcs: [ + "tools/art_exec_test.cc", "tools/cmdline_builder_test.cc", "tools/system_properties_test.cc", "tools/tools_test.cc", @@ -81,3 +82,38 @@ art_cc_test { "art_libarttools_tests_defaults", ], } + +// A defaults that contains libprocessgroup and all its dependencies. +cc_defaults { + name: "art_libprocessgroup_defaults", + shared_libs: [ + "libbase", + "libcgrouprc", + ], + static_libs: [ + "libjsoncpp", + "libprocessgroup", + ], +} + +cc_binary { + name: "art_exec", + defaults: [ + "art_defaults", + "art_libprocessgroup_defaults", + ], + srcs: [ + "tools/art_exec.cc", + ], + shared_libs: [ + "libartbase", + "libbase", + ], + static_libs: [ + "libcap", + ], + apex_available: [ + "com.android.art", + "com.android.art.debug", + ], +} diff --git a/libarttools/tools/art_exec.cc b/libarttools/tools/art_exec.cc new file mode 100644 index 0000000000..48dadb53c9 --- /dev/null +++ b/libarttools/tools/art_exec.cc @@ -0,0 +1,155 @@ +/* + * 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. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "android-base/logging.h" +#include "android-base/result.h" +#include "android-base/strings.h" +#include "base/macros.h" +#include "base/scoped_cap.h" +#include "processgroup/processgroup.h" +#include "system/thread_defs.h" + +namespace { + +using ::android::base::ConsumePrefix; +using ::android::base::Join; +using ::android::base::Result; +using ::android::base::Split; + +constexpr const char* kUsage = + R"(A wrapper binary that configures the process and executes a command. + +Usage: art_exec [OPTIONS]... -- [COMMAND]... + +Supported options: + --help: Print this text. + --set-task-profile=PROFILES: Apply a set of task profiles (see + https://source.android.com/devices/tech/perf/cgroups). Requires root access. PROFILES can be a + comma-separated list of task profile names. + --set-priority=PRIORITY: Apply the process priority. Currently, the only supported value of + PRIORITY is "background". + --drop-capabilities: Drop all root capabilities. Note that this has effect only if `art_exec` runs + with some root capabilities but not as the root user. +)"; + +constexpr int kErrorUsage = 100; +constexpr int kErrorOther = 101; + +struct Options { + int command_pos = -1; + std::vector task_profiles; + std::optional priority = std::nullopt; + bool drop_capabilities = false; +}; + +[[noreturn]] void Usage(const std::string& error_msg) { + LOG(ERROR) << error_msg; + std::cerr << error_msg << "\n" << kUsage << "\n"; + exit(kErrorUsage); +} + +Options ParseOptions(int argc, char** argv) { + Options options; + for (int i = 1; i < argc; i++) { + std::string_view arg = argv[i]; + if (arg == "--help") { + std::cerr << kUsage << "\n"; + exit(0); + } else if (ConsumePrefix(&arg, "--set-task-profile=")) { + options.task_profiles = Split(std::string(arg), ","); + if (options.task_profiles.empty()) { + Usage("Empty task profile list"); + } + } else if (ConsumePrefix(&arg, "--set-priority=")) { + if (arg == "background") { + options.priority = ANDROID_PRIORITY_BACKGROUND; + } else { + Usage("Unknown priority " + std::string(arg)); + } + } else if (arg == "--drop-capabilities") { + options.drop_capabilities = true; + } else if (arg == "--") { + if (i + 1 >= argc) { + Usage("Missing command after '--'"); + } + options.command_pos = i + 1; + return options; + } else { + Usage("Unknown option " + std::string(arg)); + } + } + Usage("Missing '--'"); +} + +Result DropInheritableCaps() { + art::ScopedCap cap(cap_get_proc()); + if (cap.Get() == nullptr) { + return ErrnoErrorf("Failed to call cap_get_proc"); + } + if (cap_clear_flag(cap.Get(), CAP_INHERITABLE) != 0) { + return ErrnoErrorf("Failed to call cap_clear_flag"); + } + if (cap_set_proc(cap.Get()) != 0) { + return ErrnoErrorf("Failed to call cap_set_proc"); + } + return {}; +} + +} // namespace + +int main(int argc, char** argv) { + android::base::InitLogging(argv); + + Options options = ParseOptions(argc, argv); + + if (!options.task_profiles.empty()) { + if (!SetTaskProfiles(/*tid=*/0, options.task_profiles)) { + LOG(ERROR) << "Failed to set task profile"; + return kErrorOther; + } + } + + if (options.priority.has_value()) { + if (setpriority(PRIO_PROCESS, /*who=*/0, options.priority.value()) != 0) { + PLOG(ERROR) << "Failed to setpriority"; + return kErrorOther; + } + } + + if (options.drop_capabilities) { + if (auto result = DropInheritableCaps(); !result.ok()) { + LOG(ERROR) << "Failed to drop inheritable capabilities: " << result.error(); + return kErrorOther; + } + } + + execv(argv[options.command_pos], argv + options.command_pos); + + std::vector command_args(argv + options.command_pos, argv + argc); + PLOG(FATAL) << "Failed to execute (" << Join(command_args, ' ') << ")"; + UNREACHABLE(); +} diff --git a/libarttools/tools/art_exec_test.cc b/libarttools/tools/art_exec_test.cc new file mode 100644 index 0000000000..ca55393583 --- /dev/null +++ b/libarttools/tools/art_exec_test.cc @@ -0,0 +1,180 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "android-base/file.h" +#include "android-base/logging.h" +#include "android-base/scopeguard.h" +#include "base/common_art_test.h" +#include "base/file_utils.h" +#include "base/globals.h" +#include "base/macros.h" +#include "base/os.h" +#include "base/scoped_cap.h" +#include "exec_utils.h" +#include "fmt/format.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "system/thread_defs.h" + +namespace art { +namespace { + +using ::android::base::make_scope_guard; +using ::android::base::ScopeGuard; +using ::testing::HasSubstr; + +// clang-tidy incorrectly complaints about the using declaration while the user-defined literal is +// actually being used. +using ::fmt::literals::operator""_format; // NOLINT + +constexpr uid_t kRoot = 0; +constexpr uid_t kNobody = 9999; + +std::string GetArtBin(const std::string& name) { return "{}/bin/{}"_format(GetArtRoot(), name); } + +std::string GetBin(const std::string& name) { return "{}/bin/{}"_format(GetAndroidRoot(), name); } + +// Executes the command, waits for it to finish, and keeps it in a waitable state until the current +// scope exits. +std::pair>> ScopedExecAndWait( + std::vector& args) { + std::vector execv_args; + execv_args.reserve(args.size() + 1); + for (std::string& arg : args) { + execv_args.push_back(arg.data()); + } + execv_args.push_back(nullptr); + + pid_t pid = fork(); + if (pid == 0) { + execv(execv_args[0], execv_args.data()); + UNREACHABLE(); + } else if (pid > 0) { + siginfo_t info; + CHECK_EQ(TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED | WNOWAIT)), 0); + CHECK_EQ(info.si_code, CLD_EXITED); + CHECK_EQ(info.si_status, 0); + std::function cleanup([=] { + siginfo_t info; + CHECK_EQ(TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)), 0); + }); + return std::make_pair(pid, make_scope_guard(std::move(cleanup))); + } else { + LOG(FATAL) << "Failed to call fork"; + UNREACHABLE(); + } +} + +// Grants the current process the given root capability. +void SetCap(cap_flag_t flag, cap_value_t value) { + ScopedCap cap(cap_get_proc()); + CHECK_NE(cap.Get(), nullptr); + cap_value_t caps[]{value}; + CHECK_EQ(cap_set_flag(cap.Get(), flag, /*ncap=*/1, caps, CAP_SET), 0); + CHECK_EQ(cap_set_proc(cap.Get()), 0); +} + +// Returns true if the given process has the given root capability. +bool GetCap(pid_t pid, cap_flag_t flag, cap_value_t value) { + ScopedCap cap(cap_get_pid(pid)); + CHECK_NE(cap.Get(), nullptr); + cap_flag_value_t flag_value; + CHECK_EQ(cap_get_flag(cap.Get(), value, flag, &flag_value), 0); + return flag_value == CAP_SET; +} + +class ArtExecTest : public testing::Test { + protected: + void SetUp() override { + testing::Test::SetUp(); + if (!kIsTargetAndroid) { + GTEST_SKIP() << "art_exec is for device only"; + } + if (getuid() != kRoot) { + GTEST_SKIP() << "art_exec requires root"; + } + art_exec_bin_ = GetArtBin("art_exec"); + } + + std::string art_exec_bin_; +}; + +TEST_F(ArtExecTest, Command) { + std::string error_msg; + int ret = ExecAndReturnCode({art_exec_bin_, "--", GetBin("sh"), "-c", "exit 123"}, &error_msg); + ASSERT_EQ(ret, 123) << error_msg; +} + +TEST_F(ArtExecTest, SetTaskProfiles) { + std::string filename = "/data/local/tmp/art-exec-test-XXXXXX"; + ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false)); + ASSERT_GE(scratch_file.GetFd(), 0); + + std::vector args{art_exec_bin_, + "--set-task-profile=Dex2oatPerformance", + "--", + GetBin("sh"), + "-c", + "cat /proc/self/cgroup > " + filename}; + auto [pid, scope_guard] = ScopedExecAndWait(args); + std::string cgroup; + ASSERT_TRUE(android::base::ReadFileToString(filename, &cgroup)); + EXPECT_THAT(cgroup, HasSubstr(":cpu:/dex2oat\n")); +} + +TEST_F(ArtExecTest, SetPriority) { + std::vector args{art_exec_bin_, "--set-priority=background", "--", GetBin("true")}; + auto [pid, scope_guard] = ScopedExecAndWait(args); + EXPECT_EQ(getpriority(PRIO_PROCESS, pid), ANDROID_PRIORITY_BACKGROUND); +} + +TEST_F(ArtExecTest, DropCapabilities) { + // Switch to a non-root user, but still keep the CAP_FOWNER capability available and inheritable. + // The order of the following calls matters. + CHECK_EQ(cap_setuid(kNobody), 0); + SetCap(CAP_INHERITABLE, CAP_FOWNER); + SetCap(CAP_EFFECTIVE, CAP_FOWNER); + ASSERT_EQ(cap_set_ambient(CAP_FOWNER, CAP_SET), 0); + + // Make sure the test is set up correctly (i.e., the child process should normally have the + // inherited root capability: CAP_FOWNER). + { + std::vector args{art_exec_bin_, "--", GetBin("true")}; + auto [pid, scope_guard] = ScopedExecAndWait(args); + ASSERT_TRUE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER)); + } + + { + std::vector args{art_exec_bin_, "--drop-capabilities", "--", GetBin("true")}; + auto [pid, scope_guard] = ScopedExecAndWait(args); + EXPECT_FALSE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER)); + } +} + +} // namespace +} // namespace art -- cgit v1.2.3-59-g8ed1b From 4cdcab58b438a91c91cd5b09235c703251c6e328 Mon Sep 17 00:00:00 2001 From: Martin Stjernholm Date: Wed, 19 Apr 2023 15:10:06 +0100 Subject: Propagate useartservice setting from experiment flag to global flag. Only propagate it if it turns off ART Service - if OEMs have disabled it with dalvik.vm.useartservice then we shouldn't enable it. Introduce a new art_boot binary to do it, which is only run at boot through a oneshot service. We cannot use setprop commands directly in the init script, because non-vendor APEXes are prohibited from using `on` actions (b/278862348). Test: boot -> Check that dalvik.vm.useartservice is not false adb root adb shell setprop persist.device_config.runtime_native_boot.useartservice false -> Check that dalvik.vm.useartservice is not false adb reboot -> Check that dalvik.vm.useartservice is false on udc-dev Test: Same as above, but with an unbundled ART module build installed on git_sc-release and git_tm-release. Test: boot -> Check that dalvik.vm.useartservice is unset adb root adb shell setprop persist.device_config.runtime_native_boot.useartservice true -> Check that dalvik.vm.useartservice is unset adb reboot -> Check that dalvik.vm.useartservice is unset on udc-dev with dalvik.vm.useartservice unset Bug: 272056722 Ignore-AOSP-First: Will cherry-pick to AOSP later Change-Id: Ifc552ed924818d7363aef822cd3060f7adfc92b6 --- artd/Android.bp | 7 ------ artd/artd.rc | 23 ------------------- build/apex/Android.bp | 10 ++++++++- build/apex/art.rc | 29 ++++++++++++++++++++++++ build/apex/art_apex_test.py | 1 + tools/Android.bp | 11 +++++++++ tools/art_boot.cc | 55 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 105 insertions(+), 31 deletions(-) delete mode 100644 artd/artd.rc create mode 100644 build/apex/art.rc create mode 100644 tools/art_boot.cc (limited to 'build/apex/art_apex_test.py') diff --git a/artd/Android.bp b/artd/Android.bp index b4518c91d0..1288ebcf25 100644 --- a/artd/Android.bp +++ b/artd/Android.bp @@ -61,13 +61,6 @@ art_cc_binary { ], } -prebuilt_etc { - name: "com.android.art.artd.init.rc", - src: "artd.rc", - filename: "init.rc", - installable: false, -} - art_cc_defaults { name: "art_artd_tests_defaults", defaults: ["artd_defaults"], diff --git a/artd/artd.rc b/artd/artd.rc deleted file mode 100644 index 5ddfcdc219..0000000000 --- a/artd/artd.rc +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -# A lazy service that is started and stopped dynamically as needed. -service artd /apex/com.android.art/bin/artd - interface aidl artd - disabled # Prevents the service from automatically starting at boot. - oneshot # Prevents the service from automatically restarting each time it is stopped. - class core - user artd - group artd - capabilities DAC_OVERRIDE DAC_READ_SEARCH FOWNER CHOWN diff --git a/build/apex/Android.bp b/build/apex/Android.bp index 2a76057e02..d5349b6229 100644 --- a/build/apex/Android.bp +++ b/build/apex/Android.bp @@ -252,6 +252,13 @@ art_module_apex_defaults { }, } +prebuilt_etc { + name: "com.android.art.init.rc", + src: "art.rc", + filename: "init.rc", + installable: false, +} + // Default values shared by device ART APEXes. apex_defaults { name: "com.android.art-device-defaults-minus-odrefresh", @@ -271,6 +278,7 @@ apex_defaults { "libartservice", ], binaries: [ + "art_boot", "art_exec", "artd", ], @@ -291,7 +299,7 @@ apex_defaults { ], prebuilts: [ "art-linker-config", - "com.android.art.artd.init.rc", + "com.android.art.init.rc", "current_sdkinfo", ], // ART APEXes depend on bouncycastle which is disabled for PDK builds. diff --git a/build/apex/art.rc b/build/apex/art.rc new file mode 100644 index 0000000000..6f3a5e878d --- /dev/null +++ b/build/apex/art.rc @@ -0,0 +1,29 @@ +# Copyright (C) 2023 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. + +# A lazy service that is started and stopped dynamically as needed. +service artd /apex/com.android.art/bin/artd + interface aidl artd + disabled # Prevents the service from automatically starting at boot. + oneshot # Prevents the service from automatically restarting each time it is stopped. + class core + user artd + group artd + capabilities DAC_OVERRIDE DAC_READ_SEARCH FOWNER CHOWN + +service art_boot /apex/com.android.art/bin/art_boot + oneshot + class core + seclabel u:r:su:s0 + user root diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py index 70f1da039b..c2549cc93e 100755 --- a/build/apex/art_apex_test.py +++ b/build/apex/art_apex_test.py @@ -550,6 +550,7 @@ class ReleaseTargetChecker: # removed in Android R. # Check binaries for ART. + self._checker.check_executable('art_boot') self._checker.check_executable('art_exec') self._checker.check_executable('artd') self._checker.check_executable('oatdump') diff --git a/tools/Android.bp b/tools/Android.bp index 7ffa672a14..207a121848 100644 --- a/tools/Android.bp +++ b/tools/Android.bp @@ -37,6 +37,17 @@ soong_config_module_type_import { ], } +cc_binary { + name: "art_boot", + defaults: ["art_defaults"], + srcs: ["art_boot.cc"], + shared_libs: ["libbase"], + apex_available: [ + "com.android.art", + "com.android.art.debug", + ], +} + // Copy the art shell script to the host and target's bin directory art_module_sh_binary { name: "art-script", diff --git a/tools/art_boot.cc b/tools/art_boot.cc new file mode 100644 index 0000000000..f725fc828e --- /dev/null +++ b/tools/art_boot.cc @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 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. + */ + +// This binary is run on boot as a oneshot service. It should not be run at any +// other point. + +#include + +#include "android-base/logging.h" +#include "android-base/properties.h" + +// Copies the value of one system property to another if it isn't empty and +// passes the predicate test_fn. +static void CopyPropertyIf(const char* src, const char* dst, bool (*test_fn)(const std::string&)) { + std::string prop = android::base::GetProperty(src, ""); + if (prop.empty()) { + LOG(INFO) << "Property " << src << " not set"; + } else if (!test_fn(prop)) { + LOG(INFO) << "Property " << src << " has ignored value " << prop; + } else { + if (android::base::SetProperty(dst, prop)) { + LOG(INFO) << "Set property " << dst << " to " << prop << " from " << src; + } else { + LOG(ERROR) << "Failed to set property " << dst << " to " << prop; + } + } +} + +int main(int, char** argv) { + android::base::InitLogging(argv); + + // Copy properties that must only be set at boot and not change value later. + // Note that P/H can change the properties in the experiment namespaces at any + // time. + CopyPropertyIf("persist.device_config.runtime_native_boot.useartservice", + "dalvik.vm.useartservice", + // If an OEM has set dalvik.vm.useartservice to false we + // shouldn't override it to true from the P/H property. + [](const std::string& prop) { return prop == "false"; }); + + return 0; +} -- cgit v1.2.3-59-g8ed1b