From 678f0b48286bccd3eeac9245b6ef0dabecc6df3b Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Thu, 7 Jul 2022 15:25:02 +0900 Subject: [PATCH] Add ctl.apex_(un)load properties to (un)load apex These props are supposed to be used by apexd when installing an apex without reboot. During the installation, apexd will unmount the current one and mount the new one. Since the path and its contents will be replaced, anything loaded from the apex should be unloaded before unmounting. After apexd mounts the apex again, then init should re-read .rc files from the apex. This change only addes required properties: - ctl.apex_load - ctl.apex_unload - init.apex. = loaded | unloaded Bug: 232114573 Bug: 232173613 Test: atest CtsInitTestCases Change-Id: I4a8942d67912e2f11acc51bec756c4e3c218179a --- init/init.cpp | 33 ++++++++++++++++++++++++++++++++- init/init_test.cpp | 18 ++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/init/init.cpp b/init/init.cpp index 4955bc5f2..06589422b 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -442,6 +442,19 @@ static Result DoControlRestart(Service* service) { return {}; } +static void DoUnloadApex(const std::string& apex_name) { + std::string prop_name = "init.apex." + apex_name; + // TODO(b/232114573) remove services and actions read from the apex + // TODO(b/232799709) kill services from the apex + SetProperty(prop_name, "unloaded"); +} + +static void DoLoadApex(const std::string& apex_name) { + std::string prop_name = "init.apex." + apex_name; + // TODO(b/232799709) read .rc files from the apex + SetProperty(prop_name, "loaded"); +} + enum class ControlTarget { SERVICE, // function gets called for the named service INTERFACE, // action gets called for every service that holds this interface @@ -465,6 +478,20 @@ static const std::map>& GetCont return control_message_functions; } +static bool HandleApexControlMessage(std::string_view action, const std::string& name, + std::string_view message) { + if (action == "load") { + DoLoadApex(name); + return true; + } else if (action == "unload") { + DoUnloadApex(name); + return true; + } else { + LOG(ERROR) << "Unknown control msg '" << message << "'"; + return false; + } +} + static bool HandleControlMessage(std::string_view message, const std::string& name, pid_t from_pid) { std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid); @@ -476,8 +503,12 @@ static bool HandleControlMessage(std::string_view message, const std::string& na process_cmdline = "unknown process"; } - Service* service = nullptr; auto action = message; + if (ConsumePrefix(&action, "apex_")) { + return HandleApexControlMessage(action, name, message); + } + + Service* service = nullptr; if (ConsumePrefix(&action, "interface_")) { service = ServiceList::GetInstance().FindInterface(name); } else { diff --git a/init/init_test.cpp b/init/init_test.cpp index 0dc6ff640..5651a835d 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -35,6 +35,10 @@ #include "util.h" using android::base::GetIntProperty; +using android::base::GetProperty; +using android::base::SetProperty; +using android::base::WaitForProperty; +using namespace std::literals; namespace android { namespace init { @@ -334,6 +338,20 @@ TEST(init, LazilyLoadedActionsCanBeTriggeredByTheNextTrigger) { EXPECT_EQ(2, num_executed); } +TEST(init, RespondToCtlApexMessages) { + if (getuid() != 0) { + GTEST_SKIP() << "Skipping test, must be run as root."; + return; + } + + std::string apex_name = "com.android.apex.cts.shim"; + SetProperty("ctl.apex_unload", apex_name); + EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "unloaded", 10s)); + + SetProperty("ctl.apex_load", apex_name); + EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "loaded", 10s)); +} + TEST(init, RejectsCriticalAndOneshotService) { if (GetIntProperty("ro.product.first_api_level", 10000) < 30) { GTEST_SKIP() << "Test only valid for devices launching with R or later";