Merge "Add ctl.apex_(un)load properties to (un)load apex"
diff --git a/init/init.cpp b/init/init.cpp
index 4955bc5..0658942 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -442,6 +442,19 @@
     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 @@
     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 @@
         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 0dc6ff6..5651a83 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 @@
     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";