Add odm sepolicy support to selinux.cpp

init: support loading odm sepolicy

Currently init merges two sepolicy cil files:
    - /system/etc/selinux/plat_sepolicy.cil
    - /vendor/etc/selinux/nonplat_sepolicy.cil

This change replaces nonplat_sepolicy.cil with the following two files:
    - /vendor/etc/selinux/declaration/nonplat_declaration.cil
    - /vendor/etc/selinux/vender_sepolicy.cil

And support merging another default (but optional):
    - /odm/etc/selinux/odm_sepolicy.cil.

Bug: 64240127
Test: boot sailfish normally without odm.cil
Test: boot another device having odm.cil
Change-Id: I0b7f8c656c73ddb0fd46f2af3c625d7c81566f2f
diff --git a/init/selinux.cpp b/init/selinux.cpp
index ef59164..3f7ad13 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -198,9 +198,18 @@
 
 bool FindPrecompiledSplitPolicy(std::string* file) {
     file->clear();
-
-    static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
-    if (access(precompiled_sepolicy, R_OK) == -1) {
+    // If there is an odm partition, precompiled_sepolicy will be in
+    // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
+    static constexpr const char vendor_precompiled_sepolicy[] =
+        "/vendor/etc/selinux/precompiled_sepolicy";
+    static constexpr const char odm_precompiled_sepolicy[] =
+        "/odm/etc/selinux/precompiled_sepolicy";
+    if (access(odm_precompiled_sepolicy, R_OK) == 0) {
+        *file = odm_precompiled_sepolicy;
+    } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
+        *file = vendor_precompiled_sepolicy;
+    } else {
+        PLOG(INFO) << "No precompiled sepolicy";
         return false;
     }
     std::string actual_plat_id;
@@ -209,19 +218,18 @@
                       "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
         return false;
     }
+
     std::string precompiled_plat_id;
-    if (!ReadFirstLine("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
-                       &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/vendor/etc/selinux/"
-                      "precompiled_sepolicy.plat_and_mapping.sha256";
+    std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_sha256;
+        file->clear();
         return false;
     }
     if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+        file->clear();
         return false;
     }
-
-    *file = precompiled_sepolicy;
     return true;
 }
 
@@ -293,24 +301,55 @@
         return false;
     }
     std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+
+    // vendor_sepolicy.cil and nonplat_declaration.cil are the new design to replace
+    // nonplat_sepolicy.cil.
+    std::string nonplat_declaration_cil_file("/vendor/etc/selinux/nonplat_declaration.cil");
+    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
+
+    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
+        // For backward compatibility.
+        // TODO: remove this after no device is using nonplat_sepolicy.cil.
+        vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
+        nonplat_declaration_cil_file.clear();
+    } else if (access(nonplat_declaration_cil_file.c_str(), F_OK) == -1) {
+        LOG(ERROR) << "Missing " << nonplat_declaration_cil_file;
+        return false;
+    }
+
+    // odm_sepolicy.cil is default but optional.
+    std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");
+    if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
+        odm_policy_cil_file.clear();
+    }
     const std::string version_as_string = std::to_string(max_policy_version);
 
     // clang-format off
-    const char* compile_args[] = {
+    std::vector<const char*> compile_args {
         "/system/bin/secilc",
         plat_policy_cil_file,
         "-M", "true", "-G", "-N",
         // Target the highest policy language version supported by the kernel
         "-c", version_as_string.c_str(),
         mapping_file.c_str(),
-        "/vendor/etc/selinux/nonplat_sepolicy.cil",
         "-o", compiled_sepolicy,
         // We don't care about file_contexts output by the compiler
         "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
-        nullptr};
+    };
     // clang-format on
 
-    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args)) {
+    if (!nonplat_declaration_cil_file.empty()) {
+        compile_args.push_back(nonplat_declaration_cil_file.c_str());
+    }
+    if (!vendor_policy_cil_file.empty()) {
+        compile_args.push_back(vendor_policy_cil_file.c_str());
+    }
+    if (!odm_policy_cil_file.empty()) {
+        compile_args.push_back(odm_policy_cil_file.c_str());
+    }
+    compile_args.push_back(nullptr);
+
+    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
         unlink(compiled_sepolicy);
         return false;
     }