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
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 {