Merge changes from topic "bootconfig_args"

* changes:
  fs_mgr: handle more bootconfig parameters
  init: handle more bootconfig parameters
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 1efe793..6952cdf 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2203,7 +2203,8 @@
     // Devices upgrading to dynamic partitions are allowed to specify a super
     // partition name. This includes cuttlefish, which is a non-A/B device.
     std::string super_partition;
-    if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+    if (fs_mgr_get_boot_config_from_bootconfig_source("super_partition", &super_partition) ||
+        fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
         if (fs_mgr_get_slot_suffix().empty()) {
             return super_partition;
         }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 785a8e0..0c0862e 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -299,7 +299,8 @@
 std::string InitAndroidDtDir() {
     std::string android_dt_dir;
     // The platform may specify a custom Android DT path in kernel cmdline
-    if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
+    if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
+        !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
         // Fall back to the standard procfs-based path
         android_dt_dir = kDefaultAndroidDtDir;
     }
@@ -842,9 +843,22 @@
 }
 
 std::set<std::string> GetBootDevices() {
-    // First check the kernel commandline, then try the device tree otherwise
+    // First check bootconfig, then kernel commandline, then the device tree
     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
     std::string value;
+    if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
+        fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
+        std::set<std::string> boot_devices;
+        // remove quotes and split by spaces
+        auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
+        for (std::string_view device : boot_device_strings) {
+            // trim the trailing comma, keep the rest.
+            base::ConsumeSuffix(&device, ",");
+            boot_devices.emplace(device);
+        }
+        return boot_devices;
+    }
+
     if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
         ReadDtFile(dt_file_name, &value)) {
         auto boot_devices = Split(value, ",");
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 62a8d3b..bcd5180 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -121,6 +121,7 @@
 
 const std::string bootconfig =
         "androidboot.bootdevice  = \" \"1d84000.ufshc\"\n"
+        "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
         "androidboot.baseband = \"sdy\"\n"
         "androidboot.keymaster = \"1\"\n"
         "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
@@ -152,6 +153,7 @@
 
 const std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {
         {"androidboot.bootdevice", "1d84000.ufshc"},
+        {"androidboot.boot_devices", "dev1, dev2,withcomma, dev3"},
         {"androidboot.baseband", "sdy"},
         {"androidboot.keymaster", "1"},
         {"androidboot.serialno", "BLAHBLAHBLAH"},
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index 0f01166..e2ea0ab 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -105,8 +105,20 @@
     _exit(127);
 }
 
-int FirstStageConsole(const std::string& cmdline) {
-    auto pos = cmdline.find("androidboot.first_stage_console=");
+int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig) {
+    auto pos = bootconfig.find("androidboot.first_stage_console =");
+    if (pos != std::string::npos) {
+        int val = 0;
+        if (sscanf(bootconfig.c_str() + pos, "androidboot.first_stage_console = \"%d\"", &val) !=
+            1) {
+            return FirstStageConsoleParam::DISABLED;
+        }
+        if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+            return val;
+        }
+    }
+
+    pos = cmdline.find("androidboot.first_stage_console=");
     if (pos != std::string::npos) {
         int val = 0;
         if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index d5744df..4a30d35 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -29,7 +29,7 @@
 };
 
 void StartConsole(const std::string& cmdline);
-int FirstStageConsole(const std::string& cmdline);
+int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ff75aa3..b2ab550 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -102,8 +102,9 @@
     }
 }
 
-bool ForceNormalBoot(const std::string& cmdline) {
-    return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+bool ForceNormalBoot(const std::string& cmdline, const std::string& bootconfig) {
+    return bootconfig.find("androidboot.force_normal_boot = \"1\"") != std::string::npos ||
+           cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
 }  // namespace
@@ -211,6 +212,8 @@
     android::base::ReadFileToString("/proc/cmdline", &cmdline);
     // Don't expose the raw bootconfig to unprivileged processes.
     chmod("/proc/bootconfig", 0440);
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
     gid_t groups[] = {AID_READPROC};
     CHECKCALL(setgroups(arraysize(groups), groups));
     CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
@@ -278,11 +281,11 @@
         old_root_dir.reset();
     }
 
-    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
+    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
 
     boot_clock::time_point module_start_time = boot_clock::now();
     int module_count = 0;
-    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console,
+    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
                            module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
@@ -324,7 +327,7 @@
         LOG(INFO) << "Copied ramdisk prop to " << dest;
     }
 
-    if (ForceNormalBoot(cmdline)) {
+    if (ForceNormalBoot(cmdline, bootconfig)) {
         mkdir("/first_stage_ramdisk", 0755);
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.