init: generate apex-info-list.xml

If a device doesn't support unflattened APEXes, init activates flattened
APEXes instead of apexd.

Because apexd dumps apex-info-list.xml after it activates all APEXes,
init should do the same thing.

Bug: 159585065
Test: lunch aosp_cf_x86_phone_noapex & m & boot
      adb more /apex/apex-info-list.xml
      adb shell ls -lZ /apex/apex-info-list.xml
       -> -rw-rw-rw- 1 root root u:object_r:apex_info_file:s0 ..
Change-Id: I74a4d0ee363761564225097c5949e5ce335ac6dc
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index b9d5d67..f8359bc 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -27,10 +27,19 @@
 #include <android-base/properties.h>
 #include <android-base/result.h>
 #include <android-base/unique_fd.h>
-#include <apex_manifest.pb.h>
 
 #include "util.h"
 
+#ifndef RECOVERY
+#define ACTIVATE_FLATTENED_APEX 1
+#endif
+
+#ifdef ACTIVATE_FLATTENED_APEX
+#include <apex_manifest.pb.h>
+#include <com_android_apex.h>
+#include <selinux/android.h>
+#endif  // ACTIVATE_FLATTENED_APEX
+
 namespace android {
 namespace init {
 namespace {
@@ -106,6 +115,8 @@
     return updatable;
 }
 
+#ifdef ACTIVATE_FLATTENED_APEX
+
 static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
     if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
         return ErrnoError() << "Could not create mount point " << mount_path;
@@ -116,7 +127,7 @@
     return {};
 }
 
-static Result<std::string> GetApexName(const std::string& apex_dir) {
+static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
     const std::string manifest_path = apex_dir + "/apex_manifest.pb";
     std::string content;
     if (!android::base::ReadFileToString(manifest_path, &content)) {
@@ -126,11 +137,12 @@
     if (!manifest.ParseFromString(content)) {
         return Error() << "Can't parse manifest file: " << manifest_path;
     }
-    return manifest.name();
+    return manifest;
 }
 
+template <typename Fn>
 static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
-                                                const std::string& to_dir) {
+                                                const std::string& to_dir, Fn on_activate) {
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
     if (!dir) {
         return {};
@@ -140,15 +152,16 @@
         if (entry->d_name[0] == '.') continue;
         if (entry->d_type == DT_DIR) {
             const std::string apex_path = from_dir + "/" + entry->d_name;
-            const auto apex_name = GetApexName(apex_path);
-            if (!apex_name.ok()) {
-                LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
+            const auto apex_manifest = GetApexManifest(apex_path);
+            if (!apex_manifest.ok()) {
+                LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
                 continue;
             }
-            const std::string mount_path = to_dir + "/" + (*apex_name);
+            const std::string mount_path = to_dir + "/" + apex_manifest->name();
             if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
                 return result;
             }
+            on_activate(apex_path, *apex_manifest);
         }
     }
     return {};
@@ -167,15 +180,37 @@
             "/vendor/apex",
     };
 
+    std::vector<com::android::apex::ApexInfo> apex_infos;
+    auto on_activate = [&](const std::string& apex_path,
+                           const apex::proto::ApexManifest& apex_manifest) {
+        apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
+                                apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true);
+    };
+
     for (const auto& dir : kBuiltinDirsForApexes) {
-        if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop); !result.ok()) {
+        if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
             LOG(ERROR) << result.error();
             return false;
         }
     }
+
+    std::ostringstream oss;
+    com::android::apex::ApexInfoList apex_info_list(apex_infos);
+    com::android::apex::write(oss, apex_info_list);
+    const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
+    if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
+        PLOG(ERROR) << "Failed to write " << kApexInfoList;
+        return false;
+    }
+    if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
+        PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
+    }
+
     return true;
 }
 
+#endif  // ACTIVATE_FLATTENED_APEX
+
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -269,9 +304,9 @@
         default_ns_fd.reset(OpenMountNamespace());
         default_ns_id = GetMountNamespaceId();
     }
-
+#ifdef ACTIVATE_FLATTENED_APEX
     success &= ActivateFlattenedApexesIfPossible();
-
+#endif
     LOG(INFO) << "SetupMountNamespaces done";
     return success;
 }