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 <apex_name>
- ctl.apex_unload <apex_name>
- init.apex.<apex_name> = loaded | unloaded

Bug: 232114573
Bug: 232173613
Test: atest CtsInitTestCases
Change-Id: I4a8942d67912e2f11acc51bec756c4e3c218179a
This commit is contained in:
Jooyung Han 2022-07-07 15:25:02 +09:00
parent ff3fdf36ef
commit 678f0b4828
2 changed files with 50 additions and 1 deletions

View file

@ -442,6 +442,19 @@ static Result<void> 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<std::string, ControlMessageFunction, std::less<>>& 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 {

View file

@ -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";