Merge "Add partition size check to boot partitions" into main
diff --git a/apex/apex.go b/apex/apex.go
index 91d01b0..dd9195c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2083,7 +2083,7 @@
 				//
 				// Skip the dependency in unbundled builds where the device image is not
 				// being built.
-				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
+				if ch.IsStubsImplementationRequired() && !am.NotInPlatform() && !ctx.Config().UnbundledBuild() {
 					// we need a module name for Make
 					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
 					if !android.InList(name, a.makeModulesToInstall) {
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 5aa60d2..2249506 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -36,6 +36,8 @@
 
 	output     android.Path
 	installDir android.InstallPath
+
+	bootImageType bootImageType
 }
 
 type BootimgProperties struct {
@@ -56,9 +58,13 @@
 	// https://source.android.com/devices/bootloader/boot-image-header
 	Header_version *string
 
-	// Determines if this image is for the vendor_boot partition. Default is false. Refer to
-	// https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
-	Vendor_boot *bool
+	// Determines the specific type of boot image this module is building. Can be boot,
+	// vendor_boot or init_boot. Defaults to boot.
+	// Refer to https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
+	// for vendor_boot.
+	// Refer to https://source.android.com/docs/core/architecture/partitions/generic-boot for
+	// init_boot.
+	Boot_image_type *string
 
 	// Optional kernel commandline arguments
 	Cmdline []string `android:"arch_variant"`
@@ -85,6 +91,41 @@
 	Avb_algorithm *string
 }
 
+type bootImageType int
+
+const (
+	unsupported bootImageType = iota
+	boot
+	vendorBoot
+	initBoot
+)
+
+func toBootImageType(ctx android.ModuleContext, bootImageType string) bootImageType {
+	switch bootImageType {
+	case "boot":
+		return boot
+	case "vendor_boot":
+		return vendorBoot
+	case "init_boot":
+		return initBoot
+	default:
+		ctx.ModuleErrorf("Unknown boot_image_type %s. Must be one of \"boot\", \"vendor_boot\", or \"init_boot\"", bootImageType)
+	}
+	return unsupported
+}
+
+func (b bootImageType) isBoot() bool {
+	return b == boot
+}
+
+func (b bootImageType) isVendorBoot() bool {
+	return b == vendorBoot
+}
+
+func (b bootImageType) isInitBoot() bool {
+	return b == initBoot
+}
+
 // bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb.
 func BootimgFactory() android.Module {
 	module := &bootimg{}
@@ -116,8 +157,8 @@
 }
 
 func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	vendor := proptools.Bool(b.properties.Vendor_boot)
-	unsignedOutput := b.buildBootImage(ctx, vendor)
+	b.bootImageType = toBootImageType(ctx, proptools.StringDefault(b.properties.Boot_image_type, "boot"))
+	unsignedOutput := b.buildBootImage(ctx)
 
 	output := unsignedOutput
 	if proptools.Bool(b.properties.Use_avb) {
@@ -131,18 +172,19 @@
 	b.output = output
 }
 
-func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.Path {
+func (b *bootimg) buildBootImage(ctx android.ModuleContext) android.Path {
 	output := android.PathForModuleOut(ctx, "unsigned", b.installFileName())
 
 	builder := android.NewRuleBuilder(pctx, ctx)
 	cmd := builder.Command().BuiltTool("mkbootimg")
 
 	kernel := proptools.String(b.properties.Kernel_prebuilt)
-	if vendor && kernel != "" {
+	if b.bootImageType.isVendorBoot() && kernel != "" {
 		ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel")
 		return output
 	}
-	if !vendor && kernel == "" {
+
+	if b.bootImageType.isBoot() && kernel == "" {
 		ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
 		return output
 	}
@@ -150,7 +192,8 @@
 		cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel))
 	}
 
-	if !vendor {
+	// These arguments are passed for boot.img and init_boot.img generation
+	if b.bootImageType.isBoot() || b.bootImageType.isInitBoot() {
 		cmd.FlagWithArg("--os_version ", ctx.Config().PlatformVersionLastStable())
 		cmd.FlagWithArg("--os_patch_level ", ctx.Config().PlatformSecurityPatch())
 	}
@@ -164,7 +207,7 @@
 	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
 		flag := "--cmdline "
-		if vendor {
+		if b.bootImageType.isVendorBoot() {
 			flag = "--vendor_cmdline "
 		}
 		cmd.FlagWithArg(flag, proptools.ShellEscapeIncludingSpaces(cmdline))
@@ -191,7 +234,7 @@
 		ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
 		if filesystem, ok := ramdisk.(*filesystem); ok {
 			flag := "--ramdisk "
-			if vendor {
+			if b.bootImageType.isVendorBoot() {
 				flag = "--vendor_ramdisk "
 			}
 			cmd.FlagWithInput(flag, filesystem.OutputPath())
@@ -203,7 +246,7 @@
 
 	bootconfig := proptools.String(b.properties.Bootconfig)
 	if bootconfig != "" {
-		if !vendor {
+		if !b.bootImageType.isVendorBoot() {
 			ctx.PropertyErrorf("bootconfig", "requires vendor_boot: true")
 			return output
 		}
@@ -214,8 +257,9 @@
 		cmd.FlagWithInput("--vendor_bootconfig ", android.PathForModuleSrc(ctx, bootconfig))
 	}
 
+	// Output flag for boot.img and init_boot.img
 	flag := "--output "
-	if vendor {
+	if b.bootImageType.isVendorBoot() {
 		flag = "--vendor_boot "
 	}
 	cmd.FlagWithOutput(flag, output)
diff --git a/fsgen/boot_imgs.go b/fsgen/boot_imgs.go
index 6fd4b7b..630aaff 100644
--- a/fsgen/boot_imgs.go
+++ b/fsgen/boot_imgs.go
@@ -71,9 +71,30 @@
 	ctx.CreateModule(
 		filesystem.BootimgFactory,
 		&filesystem.BootimgProperties{
-			Vendor_boot:    proptools.BoolPtr(true),
-			Ramdisk_module: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")),
-			Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+			Boot_image_type: proptools.StringPtr("vendor_boot"),
+			Ramdisk_module:  proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")),
+			Header_version:  proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+		},
+		&struct {
+			Name *string
+		}{
+			Name: proptools.StringPtr(bootImageName),
+		},
+	)
+	return true
+}
+
+func createInitBootImage(ctx android.LoadHookContext) bool {
+	partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+
+	bootImageName := generatedModuleNameForPartition(ctx.Config(), "init_boot")
+
+	ctx.CreateModule(
+		filesystem.BootimgFactory,
+		&filesystem.BootimgProperties{
+			Boot_image_type: proptools.StringPtr("init_boot"),
+			Ramdisk_module:  proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk")),
+			Header_version:  proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
 		},
 		&struct {
 			Name *string
@@ -122,6 +143,23 @@
 	return false
 }
 
+// Derived from: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=480;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+func buildingInitBootImage(partitionVars android.PartitionVariables) bool {
+	if !partitionVars.ProductBuildInitBootImage {
+		if partitionVars.BoardUsesRecoveryAsBoot || len(partitionVars.BoardPrebuiltInitBootimage) > 0 {
+			return false
+		} else if len(partitionVars.BoardInitBootimagePartitionSize) > 0 {
+			return true
+		}
+	} else {
+		if partitionVars.BoardUsesRecoveryAsBoot {
+			panic("PRODUCT_BUILD_INIT_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT. Use only one option.")
+		}
+		return true
+	}
+	return false
+}
+
 func boardBootHeaderVersion(partitionVars android.PartitionVariables) (int, bool) {
 	if len(partitionVars.BoardBootHeaderVersion) == 0 {
 		return 0, false
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index a019087..6bfb097 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -50,6 +50,7 @@
 
 	Boot_image        string `blueprint:"mutated" android:"path_device_first"`
 	Vendor_boot_image string `blueprint:"mutated" android:"path_device_first"`
+	Init_boot_image   string `blueprint:"mutated" android:"path_device_first"`
 }
 
 type filesystemCreator struct {
@@ -137,6 +138,13 @@
 			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot")
 		}
 	}
+	if buildingInitBootImage(partitionVars) {
+		if createInitBootImage(ctx) {
+			f.properties.Init_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "init_boot")
+		} else {
+			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "init_boot")
+		}
+	}
 
 	for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
 		f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
@@ -737,12 +745,20 @@
 	}
 	if f.properties.Vendor_boot_image != "" {
 		diffTestFile := android.PathForModuleOut(ctx, "vendor_boot_diff_test.txt")
-		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Boot_image)
+		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Vendor_boot_image)
 		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/vendor_boot.img", ctx.Config().DeviceName()))
 		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
 		diffTestFiles = append(diffTestFiles, diffTestFile)
 		ctx.Phony("soong_generated_vendor_boot_filesystem_test", diffTestFile)
 	}
+	if f.properties.Init_boot_image != "" {
+		diffTestFile := android.PathForModuleOut(ctx, "init_boot_diff_test.txt")
+		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Init_boot_image)
+		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/init_boot.img", ctx.Config().DeviceName()))
+		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony("soong_generated_init_boot_filesystem_test", diffTestFile)
+	}
 	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
 }