• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package filesystem
16
17import (
18	"cmp"
19	"fmt"
20	"path/filepath"
21	"slices"
22	"sort"
23	"strings"
24	"sync/atomic"
25
26	"android/soong/android"
27	"android/soong/java"
28
29	"github.com/google/blueprint"
30	"github.com/google/blueprint/proptools"
31)
32
33var proguardDictToProto = pctx.AndroidStaticRule("proguard_dict_to_proto", blueprint.RuleParams{
34	Command:     `${symbols_map} -r8 $in -location $location -write_if_changed $out`,
35	Restat:      true,
36	CommandDeps: []string{"${symbols_map}"},
37}, "location")
38
39type PartitionNameProperties struct {
40	// Name of the super partition filesystem module
41	Super_partition_name *string
42	// Name of the boot partition filesystem module
43	Boot_partition_name *string
44	// Name of the vendor boot partition filesystem module
45	Vendor_boot_partition_name *string
46	// Name of the init boot partition filesystem module
47	Init_boot_partition_name *string
48	// Name of the system partition filesystem module
49	System_partition_name *string
50	// Name of the system_ext partition filesystem module
51	System_ext_partition_name *string
52	// Name of the product partition filesystem module
53	Product_partition_name *string
54	// Name of the vendor partition filesystem module
55	Vendor_partition_name *string
56	// Name of the odm partition filesystem module
57	Odm_partition_name *string
58	// Name of the recovery partition filesystem module
59	Recovery_partition_name *string
60	// The vbmeta partition and its "chained" partitions
61	Vbmeta_partitions []string
62	// Name of the userdata partition filesystem module
63	Userdata_partition_name *string
64	// Name of the system_dlkm partition filesystem module
65	System_dlkm_partition_name *string
66	// Name of the vendor_dlkm partition filesystem module
67	Vendor_dlkm_partition_name *string
68	// Name of the odm_dlkm partition filesystem module
69	Odm_dlkm_partition_name *string
70}
71
72type DeviceProperties struct {
73	// Path to the prebuilt bootloader that would be copied to PRODUCT_OUT
74	Bootloader *string `android:"path"`
75	// Path to android-info.txt file containing board specific info.
76	Android_info *string `android:"path"`
77	// If this is the "main" android_device target for the build, i.e. the one that gets built
78	// when running a plain `m` command. Currently, this is the autogenerated android_device module
79	// in soong-only builds, but in the future when we check in android_device modules, the main
80	// one will be determined based on the lunch product. TODO: Figure out how to make this
81	// blueprint:"mutated" and still set it from filesystem_creator
82	Main_device *bool
83
84	Ab_ota_updater            *bool
85	Ab_ota_partitions         []string
86	Ab_ota_keys               []string
87	Ab_ota_postinstall_config []string
88
89	Ramdisk_node_list      *string `android:"path"`
90	Releasetools_extension *string `android:"path"`
91	FastbootInfo           *string `android:"path"`
92
93	Partial_ota_update_partitions []string
94	Flash_block_size              *string
95	Bootloader_in_update_package  *bool
96
97	// The kernel version in the build. Will be verified against the actual kernel.
98	// If not provided, will attempt to extract it from the loose kernel or the kernel inside
99	// the boot image. The version is later used to decide whether or not to enable uffd_gc
100	// when dexpreopting apps. So setting this doesn't really do anything except enforce that the
101	// actual kernel version is as specified here.
102	Kernel_version *string
103}
104
105type androidDevice struct {
106	android.ModuleBase
107
108	partitionProps PartitionNameProperties
109
110	deviceProps DeviceProperties
111
112	allImagesZip android.Path
113
114	proguardDictZip             android.Path
115	proguardDictMapping         android.Path
116	proguardUsageZip            android.Path
117	kernelConfig                android.Path
118	kernelVersion               android.Path
119	miscInfo                    android.Path
120	rootDirForFsConfig          string
121	rootDirForFsConfigTimestamp android.Path
122	apkCertsInfo                android.Path
123	targetFilesZip              android.Path
124	updatePackage               android.Path
125}
126
127func AndroidDeviceFactory() android.Module {
128	module := &androidDevice{}
129	module.AddProperties(&module.partitionProps, &module.deviceProps)
130	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibFirst)
131	return module
132}
133
134var numMainAndroidDevicesOnceKey android.OnceKey = android.NewOnceKey("num_auto_generated_anroid_devices")
135
136type partitionDepTagType struct {
137	blueprint.BaseDependencyTag
138}
139
140type superPartitionDepTagType struct {
141	blueprint.BaseDependencyTag
142}
143type targetFilesMetadataDepTagType struct {
144	blueprint.BaseDependencyTag
145}
146
147var superPartitionDepTag superPartitionDepTagType
148var filesystemDepTag partitionDepTagType
149var targetFilesMetadataDepTag targetFilesMetadataDepTagType
150
151func (a *androidDevice) DepsMutator(ctx android.BottomUpMutatorContext) {
152	addDependencyIfDefined := func(dep *string) {
153		if dep != nil {
154			ctx.AddDependency(ctx.Module(), filesystemDepTag, proptools.String(dep))
155		}
156	}
157
158	if a.partitionProps.Super_partition_name != nil {
159		ctx.AddDependency(ctx.Module(), superPartitionDepTag, *a.partitionProps.Super_partition_name)
160	}
161	addDependencyIfDefined(a.partitionProps.Boot_partition_name)
162	addDependencyIfDefined(a.partitionProps.Init_boot_partition_name)
163	addDependencyIfDefined(a.partitionProps.Vendor_boot_partition_name)
164	addDependencyIfDefined(a.partitionProps.System_partition_name)
165	addDependencyIfDefined(a.partitionProps.System_ext_partition_name)
166	addDependencyIfDefined(a.partitionProps.Product_partition_name)
167	addDependencyIfDefined(a.partitionProps.Vendor_partition_name)
168	addDependencyIfDefined(a.partitionProps.Odm_partition_name)
169	addDependencyIfDefined(a.partitionProps.Userdata_partition_name)
170	addDependencyIfDefined(a.partitionProps.System_dlkm_partition_name)
171	addDependencyIfDefined(a.partitionProps.Vendor_dlkm_partition_name)
172	addDependencyIfDefined(a.partitionProps.Odm_dlkm_partition_name)
173	addDependencyIfDefined(a.partitionProps.Recovery_partition_name)
174	for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions {
175		ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition)
176	}
177	a.addDepsForTargetFilesMetadata(ctx)
178}
179
180func (a *androidDevice) addDepsForTargetFilesMetadata(ctx android.BottomUpMutatorContext) {
181	ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), targetFilesMetadataDepTag, "liblz4") // host variant
182}
183
184func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) {
185	if proptools.Bool(a.deviceProps.Main_device) {
186		numMainAndroidDevices := ctx.Config().Once(numMainAndroidDevicesOnceKey, func() interface{} {
187			return &atomic.Int32{}
188		}).(*atomic.Int32)
189		total := numMainAndroidDevices.Add(1)
190		if total > 1 {
191			// There should only be 1 main android_device module. That one will be
192			// made the default thing to build in soong-only builds.
193			ctx.ModuleErrorf("There cannot be more than 1 main android_device module")
194		}
195	}
196
197	allInstalledModules := a.allInstalledModules(ctx)
198
199	a.apkCertsInfo = a.buildApkCertsInfo(ctx, allInstalledModules)
200	a.kernelVersion, a.kernelConfig = a.extractKernelVersionAndConfigs(ctx)
201	a.miscInfo = a.addMiscInfo(ctx)
202	a.buildTargetFilesZip(ctx, allInstalledModules)
203	a.buildProguardZips(ctx, allInstalledModules)
204	a.buildUpdatePackage(ctx)
205
206	var deps []android.Path
207	if proptools.String(a.partitionProps.Super_partition_name) != "" {
208		superImage := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
209		if info, ok := android.OtherModuleProvider(ctx, superImage, SuperImageProvider); ok {
210			assertUnset := func(prop *string, propName string) {
211				if prop != nil && *prop != "" {
212					ctx.PropertyErrorf(propName, "Cannot be set because it's already part of the super image")
213				}
214			}
215			for _, subPartitionType := range android.SortedKeys(info.SubImageInfo) {
216				switch subPartitionType {
217				case "system":
218					assertUnset(a.partitionProps.System_partition_name, "system_partition_name")
219				case "system_ext":
220					assertUnset(a.partitionProps.System_ext_partition_name, "system_ext_partition_name")
221				case "system_dlkm":
222					assertUnset(a.partitionProps.System_dlkm_partition_name, "system_dlkm_partition_name")
223				case "system_other":
224					// TODO
225				case "product":
226					assertUnset(a.partitionProps.Product_partition_name, "product_partition_name")
227				case "vendor":
228					assertUnset(a.partitionProps.Vendor_partition_name, "vendor_partition_name")
229				case "vendor_dlkm":
230					assertUnset(a.partitionProps.Vendor_dlkm_partition_name, "vendor_dlkm_partition_name")
231				case "odm":
232					assertUnset(a.partitionProps.Odm_partition_name, "odm_partition_name")
233				case "odm_dlkm":
234					assertUnset(a.partitionProps.Odm_dlkm_partition_name, "odm_dlkm_partition_name")
235				default:
236					ctx.ModuleErrorf("Unsupported sub-partition of super partition: %q", subPartitionType)
237				}
238			}
239
240			deps = append(deps, info.SuperImage)
241		} else {
242			ctx.ModuleErrorf("Expected super image dep to provide SuperImageProvider")
243		}
244	}
245	ctx.VisitDirectDepsProxyWithTag(filesystemDepTag, func(m android.ModuleProxy) {
246		imageOutput, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider)
247		if !ok {
248			ctx.ModuleErrorf("Partition module %s doesn't set OutputfilesProvider", m.Name())
249		}
250		if len(imageOutput.DefaultOutputFiles) != 1 {
251			ctx.ModuleErrorf("Partition module %s should provide exact 1 output file", m.Name())
252		}
253		deps = append(deps, imageOutput.DefaultOutputFiles[0])
254	})
255
256	allImagesZip := android.PathForModuleOut(ctx, "all_images.zip")
257	allImagesZipBuilder := android.NewRuleBuilder(pctx, ctx)
258	cmd := allImagesZipBuilder.Command().BuiltTool("soong_zip")
259	for _, dep := range deps {
260		cmd.FlagWithArg("-e ", dep.Base())
261		cmd.FlagWithInput("-f ", dep)
262	}
263	cmd.FlagWithOutput("-o ", allImagesZip)
264	allImagesZipBuilder.Build("soong_all_images_zip", "all_images.zip")
265	a.allImagesZip = allImagesZip
266
267	allImagesStamp := android.PathForModuleOut(ctx, "all_images_stamp")
268	var validations android.Paths
269	if !ctx.Config().KatiEnabled() && proptools.Bool(a.deviceProps.Main_device) {
270		// In soong-only builds, build this module by default.
271		// This is the analogue to this make code:
272		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/main.mk;l=1396;drc=6595459cdd8164a6008335f6372c9f97b9094060
273		ctx.Phony("droidcore-unbundled", allImagesStamp)
274
275		deps = append(deps, a.copyFilesToProductOutForSoongOnly(ctx))
276	}
277
278	ctx.Build(pctx, android.BuildParams{
279		Rule:        android.Touch,
280		Output:      allImagesStamp,
281		Implicits:   deps,
282		Validations: validations,
283	})
284
285	// Checkbuilding it causes soong to make a phony, so you can say `m <module name>`
286	ctx.CheckbuildFile(allImagesStamp)
287
288	a.setVbmetaPhonyTargets(ctx)
289
290	a.distFiles(ctx)
291
292	android.SetProvider(ctx, android.AndroidDeviceInfoProvider, android.AndroidDeviceInfo{
293		Main_device: android.Bool(a.deviceProps.Main_device),
294	})
295
296	if proptools.String(a.partitionProps.Super_partition_name) != "" {
297		buildComplianceMetadata(ctx, superPartitionDepTag, filesystemDepTag)
298	} else {
299		buildComplianceMetadata(ctx, filesystemDepTag)
300	}
301}
302
303func buildComplianceMetadata(ctx android.ModuleContext, tags ...blueprint.DependencyTag) {
304	// Collect metadata from deps
305	filesContained := make([]string, 0)
306	prebuiltFilesCopied := make([]string, 0)
307	for _, tag := range tags {
308		ctx.VisitDirectDepsProxyWithTag(tag, func(m android.ModuleProxy) {
309			if complianceMetadataInfo, ok := android.OtherModuleProvider(ctx, m, android.ComplianceMetadataProvider); ok {
310				filesContained = append(filesContained, complianceMetadataInfo.GetFilesContained()...)
311				prebuiltFilesCopied = append(prebuiltFilesCopied, complianceMetadataInfo.GetPrebuiltFilesCopied()...)
312			}
313		})
314	}
315	// Merge to module's ComplianceMetadataInfo
316	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
317	filesContained = append(filesContained, complianceMetadataInfo.GetFilesContained()...)
318	sort.Strings(filesContained)
319	complianceMetadataInfo.SetFilesContained(filesContained)
320
321	prebuiltFilesCopied = append(prebuiltFilesCopied, complianceMetadataInfo.GetPrebuiltFilesCopied()...)
322	sort.Strings(prebuiltFilesCopied)
323	complianceMetadataInfo.SetPrebuiltFilesCopied(prebuiltFilesCopied)
324}
325
326// Returns a list of modules that are installed, which are collected from the dependency
327// filesystem and super_image modules.
328func (a *androidDevice) allInstalledModules(ctx android.ModuleContext) []android.Module {
329	fsInfoMap := a.getFsInfos(ctx)
330	allOwners := make(map[string][]string)
331	for _, partition := range android.SortedKeys(fsInfoMap) {
332		fsInfo := fsInfoMap[partition]
333		for _, owner := range fsInfo.Owners {
334			allOwners[owner.Name] = append(allOwners[owner.Name], owner.Variation)
335		}
336	}
337
338	ret := []android.Module{}
339	ctx.WalkDepsProxy(func(mod, _ android.ModuleProxy) bool {
340		if variations, ok := allOwners[ctx.OtherModuleName(mod)]; ok && android.InList(ctx.OtherModuleSubDir(mod), variations) {
341			ret = append(ret, mod)
342		}
343		return true
344	})
345
346	// Remove duplicates
347	ret = android.FirstUniqueFunc(ret, func(a, b android.Module) bool {
348		return a.String() == b.String()
349	})
350
351	// Sort the modules by their names and variants
352	slices.SortFunc(ret, func(a, b android.Module) int {
353		return cmp.Compare(a.String(), b.String())
354	})
355	return ret
356}
357
358func insertBeforeExtension(file, insertion string) string {
359	ext := filepath.Ext(file)
360	return strings.TrimSuffix(file, ext) + insertion + ext
361}
362
363func (a *androidDevice) distInstalledFiles(ctx android.ModuleContext) {
364	distInstalledFilesJsonAndTxt := func(installedFiles InstalledFilesStruct) {
365		if installedFiles.Json != nil {
366			ctx.DistForGoal("droidcore-unbundled", installedFiles.Json)
367		}
368		if installedFiles.Txt != nil {
369			ctx.DistForGoal("droidcore-unbundled", installedFiles.Txt)
370		}
371	}
372
373	fsInfoMap := a.getFsInfos(ctx)
374	for _, partition := range android.SortedKeys(fsInfoMap) {
375		// installed-files-*{.txt | .json} is not disted for userdata partition
376		if partition == "userdata" {
377			continue
378		}
379		fsInfo := fsInfoMap[partition]
380		for _, installedFiles := range fsInfo.InstalledFilesDepSet.ToList() {
381			distInstalledFilesJsonAndTxt(installedFiles)
382		}
383	}
384}
385
386func (a *androidDevice) distFiles(ctx android.ModuleContext) {
387	if !ctx.Config().KatiEnabled() && proptools.Bool(a.deviceProps.Main_device) {
388		a.distInstalledFiles(ctx)
389
390		namePrefix := ""
391		if ctx.Config().HasDeviceProduct() {
392			namePrefix = ctx.Config().DeviceProduct() + "-"
393		}
394		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardDictZip, namePrefix+insertBeforeExtension(a.proguardDictZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
395		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardDictMapping, namePrefix+insertBeforeExtension(a.proguardDictMapping.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
396		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardUsageZip, namePrefix+insertBeforeExtension(a.proguardUsageZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
397
398		if a.deviceProps.Android_info != nil {
399			ctx.DistForGoal("droidcore-unbundled", android.PathForModuleSrc(ctx, *a.deviceProps.Android_info))
400		}
401		if a.miscInfo != nil {
402			ctx.DistForGoal("droidcore-unbundled", a.miscInfo)
403			if a.partitionProps.Super_partition_name != nil {
404				ctx.DistForGoalWithFilename("dist_files", a.miscInfo, "super_misc_info.txt")
405			}
406		}
407		if a.targetFilesZip != nil {
408			ctx.DistForGoalWithFilename("target-files-package", a.targetFilesZip, namePrefix+insertBeforeExtension(a.targetFilesZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
409		}
410		if a.updatePackage != nil {
411			ctx.DistForGoalWithFilename("updatepackage", a.updatePackage, namePrefix+insertBeforeExtension(a.updatePackage.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
412		}
413
414	}
415}
416
417func (a *androidDevice) MakeVars(_ android.MakeVarsModuleContext) []android.ModuleMakeVarsValue {
418	if proptools.Bool(a.deviceProps.Main_device) {
419		return []android.ModuleMakeVarsValue{{"SOONG_ONLY_ALL_IMAGES_ZIP", a.allImagesZip.String()}}
420	}
421	return nil
422}
423
424func (a *androidDevice) buildProguardZips(ctx android.ModuleContext, allInstalledModules []android.Module) {
425	dictZip := android.PathForModuleOut(ctx, "proguard-dict.zip")
426	dictZipBuilder := android.NewRuleBuilder(pctx, ctx)
427	dictZipCmd := dictZipBuilder.Command().BuiltTool("soong_zip").Flag("-d").FlagWithOutput("-o ", dictZip)
428
429	dictMapping := android.PathForModuleOut(ctx, "proguard-dict-mapping.textproto")
430	dictMappingBuilder := android.NewRuleBuilder(pctx, ctx)
431	dictMappingCmd := dictMappingBuilder.Command().BuiltTool("symbols_map").Flag("-merge").Output(dictMapping)
432
433	protosDir := android.PathForModuleOut(ctx, "proguard_mapping_protos")
434
435	usageZip := android.PathForModuleOut(ctx, "proguard-usage.zip")
436	usageZipBuilder := android.NewRuleBuilder(pctx, ctx)
437	usageZipCmd := usageZipBuilder.Command().BuiltTool("merge_zips").Output(usageZip)
438
439	for _, mod := range allInstalledModules {
440		if proguardInfo, ok := android.OtherModuleProvider(ctx, mod, java.ProguardProvider); ok {
441			// Maintain these out/target/common paths for backwards compatibility. They may be able
442			// to be changed if tools look up file locations from the protobuf, but I'm not
443			// exactly sure how that works.
444			dictionaryFakePath := fmt.Sprintf("out/target/common/obj/%s/%s_intermediates/proguard_dictionary", proguardInfo.Class, proguardInfo.ModuleName)
445			dictZipCmd.FlagWithArg("-e ", dictionaryFakePath)
446			dictZipCmd.FlagWithInput("-f ", proguardInfo.ProguardDictionary)
447			dictZipCmd.Textf("-e out/target/common/obj/%s/%s_intermediates/classes.jar", proguardInfo.Class, proguardInfo.ModuleName)
448			dictZipCmd.FlagWithInput("-f ", proguardInfo.ClassesJar)
449
450			protoFile := protosDir.Join(ctx, filepath.Dir(dictionaryFakePath), "proguard_dictionary.textproto")
451			ctx.Build(pctx, android.BuildParams{
452				Rule:   proguardDictToProto,
453				Input:  proguardInfo.ProguardDictionary,
454				Output: protoFile,
455				Args: map[string]string{
456					"location": dictionaryFakePath,
457				},
458			})
459			dictMappingCmd.Input(protoFile)
460
461			usageZipCmd.Input(proguardInfo.ProguardUsageZip)
462		}
463	}
464
465	dictZipBuilder.Build("proguard_dict_zip", "Building proguard dictionary zip")
466	dictMappingBuilder.Build("proguard_dict_mapping_proto", "Building proguard mapping proto")
467	usageZipBuilder.Build("proguard_usage_zip", "Building proguard usage zip")
468
469	a.proguardDictZip = dictZip
470	a.proguardDictMapping = dictMapping
471	a.proguardUsageZip = usageZip
472}
473
474// Helper structs for target_files.zip creation
475type targetFilesZipCopy struct {
476	srcModule  *string
477	destSubdir string
478}
479
480type targetFilesystemZipCopy struct {
481	fsInfo     FilesystemInfo
482	destSubdir string
483}
484
485func (a *androidDevice) buildTargetFilesZip(ctx android.ModuleContext, allInstalledModules []android.Module) {
486	targetFilesDir := android.PathForModuleOut(ctx, "target_files_dir")
487	targetFilesZip := android.PathForModuleOut(ctx, "target_files.zip")
488
489	builder := android.NewRuleBuilder(pctx, ctx)
490	builder.Command().Textf("rm -rf %s", targetFilesDir.String())
491	builder.Command().Textf("mkdir -p %s", targetFilesDir.String())
492	toCopy := []targetFilesZipCopy{
493		targetFilesZipCopy{a.partitionProps.System_partition_name, "SYSTEM"},
494		targetFilesZipCopy{a.partitionProps.System_ext_partition_name, "SYSTEM_EXT"},
495		targetFilesZipCopy{a.partitionProps.Product_partition_name, "PRODUCT"},
496		targetFilesZipCopy{a.partitionProps.Vendor_partition_name, "VENDOR"},
497		targetFilesZipCopy{a.partitionProps.Odm_partition_name, "ODM"},
498		targetFilesZipCopy{a.partitionProps.System_dlkm_partition_name, "SYSTEM_DLKM"},
499		targetFilesZipCopy{a.partitionProps.Vendor_dlkm_partition_name, "VENDOR_DLKM"},
500		targetFilesZipCopy{a.partitionProps.Odm_dlkm_partition_name, "ODM_DLKM"},
501		targetFilesZipCopy{a.partitionProps.Init_boot_partition_name, "BOOT/RAMDISK"},
502		targetFilesZipCopy{a.partitionProps.Init_boot_partition_name, "INIT_BOOT/RAMDISK"},
503		targetFilesZipCopy{a.partitionProps.Vendor_boot_partition_name, "VENDOR_BOOT/RAMDISK"},
504	}
505
506	filesystemsToCopy := []targetFilesystemZipCopy{}
507	for _, zipCopy := range toCopy {
508		if zipCopy.srcModule == nil {
509			continue
510		}
511		filesystemsToCopy = append(
512			filesystemsToCopy,
513			targetFilesystemZipCopy{a.getFilesystemInfo(ctx, *zipCopy.srcModule), zipCopy.destSubdir},
514		)
515	}
516	// Get additional filesystems from super_partition dependency
517	if a.partitionProps.Super_partition_name != nil {
518		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
519		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
520			for _, partition := range android.SortedKeys(info.SubImageInfo) {
521				filesystemsToCopy = append(
522					filesystemsToCopy,
523					targetFilesystemZipCopy{info.SubImageInfo[partition], strings.ToUpper(partition)},
524				)
525			}
526		} else {
527			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
528		}
529	}
530
531	for _, toCopy := range filesystemsToCopy {
532		rootDirString := toCopy.fsInfo.RootDir.String()
533		if toCopy.destSubdir == "SYSTEM" {
534			rootDirString = rootDirString + "/system"
535		}
536		builder.Command().Textf("mkdir -p %s/%s", targetFilesDir.String(), toCopy.destSubdir)
537		builder.Command().
538			BuiltTool("acp").
539			Textf("-rd %s/. %s/%s", rootDirString, targetFilesDir, toCopy.destSubdir).
540			Implicit(toCopy.fsInfo.Output) // so that the staging dir is built
541		for _, extraRootDir := range toCopy.fsInfo.ExtraRootDirs {
542			builder.Command().
543				BuiltTool("acp").
544				Textf("-rd %s/. %s/%s", extraRootDir, targetFilesDir, toCopy.destSubdir).
545				Implicit(toCopy.fsInfo.Output) // so that the staging dir is built
546		}
547
548		if toCopy.destSubdir == "SYSTEM" {
549			// Create the ROOT partition in target_files.zip
550			builder.Command().Textf("rsync --links --exclude=system/* %s/ -r %s/ROOT", toCopy.fsInfo.RootDir, targetFilesDir.String())
551			// Add a duplicate rule to assemble the ROOT/ directory in separate intermediates.
552			// The output timestamp will be an input to a separate fs_config call.
553			a.rootDirForFsConfig = android.PathForModuleOut(ctx, "root_dir_for_fs_config").String()
554			rootDirBuilder := android.NewRuleBuilder(pctx, ctx)
555			rootDirForFsConfigTimestamp := android.PathForModuleOut(ctx, "root_dir_for_fs_config.timestamp")
556			rootDirBuilder.Command().Textf("rsync --links --exclude=system/* %s/ -r %s", toCopy.fsInfo.RootDir, a.rootDirForFsConfig).
557				Implicit(toCopy.fsInfo.Output).
558				Text("&& touch").
559				Output(rootDirForFsConfigTimestamp)
560			rootDirBuilder.Build("assemble_root_dir_for_fs_config", "Assemble ROOT/ for fs_config")
561			a.rootDirForFsConfigTimestamp = rootDirForFsConfigTimestamp
562		}
563	}
564	// Copy cmdline, kernel etc. files of boot images
565	if a.partitionProps.Vendor_boot_partition_name != nil {
566		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(a.partitionProps.Vendor_boot_partition_name), filesystemDepTag)
567		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
568		builder.Command().Textf("echo %s > %s/VENDOR_BOOT/cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
569		builder.Command().Textf("echo %s > %s/VENDOR_BOOT/vendor_cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
570		if bootImgInfo.Dtb != nil {
571			builder.Command().Textf("cp ").Input(bootImgInfo.Dtb).Textf(" %s/VENDOR_BOOT/dtb", targetFilesDir)
572		}
573		if bootImgInfo.Bootconfig != nil {
574			builder.Command().Textf("cp ").Input(bootImgInfo.Bootconfig).Textf(" %s/VENDOR_BOOT/vendor_bootconfig", targetFilesDir)
575		}
576	}
577	if a.partitionProps.Boot_partition_name != nil {
578		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(a.partitionProps.Boot_partition_name), filesystemDepTag)
579		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
580		builder.Command().Textf("echo %s > %s/BOOT/cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
581		if bootImgInfo.Dtb != nil {
582			builder.Command().Textf("cp ").Input(bootImgInfo.Dtb).Textf(" %s/BOOT/dtb", targetFilesDir)
583		}
584		if bootImgInfo.Kernel != nil {
585			builder.Command().Textf("cp ").Input(bootImgInfo.Kernel).Textf(" %s/BOOT/kernel", targetFilesDir)
586			// Even though kernel is not used to build vendor_boot, copy the kernel to VENDOR_BOOT to match the behavior of make packaging.
587			builder.Command().Textf("cp ").Input(bootImgInfo.Kernel).Textf(" %s/VENDOR_BOOT/kernel", targetFilesDir)
588		}
589		if bootImgInfo.Bootconfig != nil {
590			builder.Command().Textf("cp ").Input(bootImgInfo.Bootconfig).Textf(" %s/BOOT/bootconfig", targetFilesDir)
591		}
592	}
593
594	if a.deviceProps.Android_info != nil {
595		builder.Command().Textf("mkdir -p %s/OTA", targetFilesDir)
596		builder.Command().Textf("cp ").Input(android.PathForModuleSrc(ctx, *a.deviceProps.Android_info)).Textf(" %s/OTA/android-info.txt", targetFilesDir)
597	}
598
599	a.copyImagesToTargetZip(ctx, builder, targetFilesDir)
600	a.copyMetadataToTargetZip(ctx, builder, targetFilesDir, allInstalledModules)
601
602	a.targetFilesZip = targetFilesZip
603	builder.Command().
604		BuiltTool("soong_zip").
605		Text("-d").
606		FlagWithOutput("-o ", targetFilesZip).
607		FlagWithArg("-C ", targetFilesDir.String()).
608		FlagWithArg("-D ", targetFilesDir.String()).
609		Text("-sha256")
610	builder.Build("target_files_"+ctx.ModuleName(), "Build target_files.zip")
611}
612
613func (a *androidDevice) copyImagesToTargetZip(ctx android.ModuleContext, builder *android.RuleBuilder, targetFilesDir android.WritablePath) {
614	// Create an IMAGES/ subdirectory
615	builder.Command().Textf("mkdir -p %s/IMAGES", targetFilesDir.String())
616	if a.deviceProps.Bootloader != nil {
617		builder.Command().Textf("cp ").Input(android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Bootloader))).Textf(" %s/IMAGES/bootloader", targetFilesDir.String())
618	}
619	// Copy the filesystem ,boot and vbmeta img files to IMAGES/
620	ctx.VisitDirectDepsProxyWithTag(filesystemDepTag, func(child android.ModuleProxy) {
621		if strings.Contains(child.Name(), "recovery") {
622			return // skip recovery.img to match the make packaging behavior
623		}
624		if info, ok := android.OtherModuleProvider(ctx, child, BootimgInfoProvider); ok {
625			// Check Boot img first so that the boot.img is copied and not its dep ramdisk.img
626			builder.Command().Textf("cp ").Input(info.Output).Textf(" %s/IMAGES/", targetFilesDir.String())
627		} else if info, ok := android.OtherModuleProvider(ctx, child, FilesystemProvider); ok {
628			builder.Command().Textf("cp ").Input(info.Output).Textf(" %s/IMAGES/", targetFilesDir.String())
629		} else if info, ok := android.OtherModuleProvider(ctx, child, vbmetaPartitionProvider); ok {
630			builder.Command().Textf("cp ").Input(info.Output).Textf(" %s/IMAGES/", targetFilesDir.String())
631		} else {
632			ctx.ModuleErrorf("Module %s does not provide an .img file output for target_files.zip", child.Name())
633		}
634	})
635
636	if a.partitionProps.Super_partition_name != nil {
637		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
638		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
639			for _, partition := range android.SortedKeys(info.SubImageInfo) {
640				if info.SubImageInfo[partition].OutputHermetic != nil {
641					builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].OutputHermetic).Textf(" %s/IMAGES/", targetFilesDir.String())
642				}
643				if info.SubImageInfo[partition].MapFile != nil {
644					builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].MapFile).Textf(" %s/IMAGES/", targetFilesDir.String())
645				}
646			}
647			// super_empty.img
648			if info.SuperEmptyImage != nil {
649				builder.Command().Textf("cp ").Input(info.SuperEmptyImage).Textf(" %s/IMAGES/", targetFilesDir.String())
650			}
651		} else {
652			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
653		}
654	}
655}
656
657func (a *androidDevice) copyMetadataToTargetZip(ctx android.ModuleContext, builder *android.RuleBuilder, targetFilesDir android.WritablePath, allInstalledModules []android.Module) {
658	// Create a META/ subdirectory
659	builder.Command().Textf("mkdir -p %s/META", targetFilesDir.String())
660	if proptools.Bool(a.deviceProps.Ab_ota_updater) {
661		ctx.VisitDirectDepsProxyWithTag(targetFilesMetadataDepTag, func(child android.ModuleProxy) {
662			info, _ := android.OtherModuleProvider(ctx, child, android.OutputFilesProvider)
663			builder.Command().Textf("cp").Inputs(info.DefaultOutputFiles).Textf(" %s/META/", targetFilesDir.String())
664		})
665		builder.Command().Textf("cp").Input(android.PathForSource(ctx, "external/zucchini/version_info.h")).Textf(" %s/META/zucchini_config.txt", targetFilesDir.String())
666		builder.Command().Textf("cp").Input(android.PathForSource(ctx, "system/update_engine/update_engine.conf")).Textf(" %s/META/update_engine_config.txt", targetFilesDir.String())
667		if a.getFsInfos(ctx)["system"].ErofsCompressHints != nil {
668			builder.Command().Textf("cp").Input(a.getFsInfos(ctx)["system"].ErofsCompressHints).Textf(" %s/META/erofs_default_compress_hints.txt", targetFilesDir.String())
669		}
670		// ab_partitions.txt
671		abPartitionsSorted := android.SortedUniqueStrings(a.deviceProps.Ab_ota_partitions)
672		abPartitionsSortedString := proptools.ShellEscape(strings.Join(abPartitionsSorted, "\\n"))
673		builder.Command().Textf("echo -e").Flag(abPartitionsSortedString).Textf(" > %s/META/ab_partitions.txt", targetFilesDir.String())
674		// otakeys.txt
675		abOtaKeysSorted := android.SortedUniqueStrings(a.deviceProps.Ab_ota_keys)
676		abOtaKeysSortedString := proptools.ShellEscape(strings.Join(abOtaKeysSorted, "\\n"))
677		builder.Command().Textf("echo -e").Flag(abOtaKeysSortedString).Textf(" > %s/META/otakeys.txt", targetFilesDir.String())
678		// postinstall_config.txt
679		abOtaPostInstallConfigString := proptools.ShellEscape(strings.Join(a.deviceProps.Ab_ota_postinstall_config, "\\n"))
680		builder.Command().Textf("echo -e").Flag(abOtaPostInstallConfigString).Textf(" > %s/META/postinstall_config.txt", targetFilesDir.String())
681		// selinuxfc
682		if a.getFsInfos(ctx)["system"].SelinuxFc != nil {
683			builder.Command().Textf("cp").Input(a.getFsInfos(ctx)["system"].SelinuxFc).Textf(" %s/META/file_contexts.bin", targetFilesDir.String())
684		}
685	}
686	// Copy $partition_filesystem_config.txt
687	fsInfos := a.getFsInfos(ctx)
688	for _, partition := range android.SortedKeys(fsInfos) {
689		if fsInfos[partition].FilesystemConfig == nil {
690			continue
691		}
692		if android.InList(partition, []string{"userdata"}) {
693			continue
694		}
695		if partition != "vendor_ramdisk" {
696			// vendor_ramdisk will be handled separately.
697			builder.Command().Textf("cp").Input(fsInfos[partition].FilesystemConfig).Textf(" %s/META/%s", targetFilesDir.String(), a.filesystemConfigNameForTargetFiles(partition))
698		}
699		if partition == "ramdisk" {
700			// Create an additional copy at boot_filesystem_config.txt
701			builder.Command().Textf("cp").Input(fsInfos[partition].FilesystemConfig).Textf(" %s/META/boot_filesystem_config.txt", targetFilesDir.String())
702		}
703		if partition == "system" {
704			// Create root_filesystem_config from the assembled ROOT/ intermediates directory
705			a.generateFilesystemConfigForTargetFiles(ctx, builder, a.rootDirForFsConfigTimestamp, targetFilesDir.String(), a.rootDirForFsConfig, "root_filesystem_config.txt")
706		}
707		if partition == "vendor_ramdisk" {
708			// Create vendor_boot_filesystem_config from the assembled VENDOR_BOOT/RAMDISK intermediates directory
709			vendorRamdiskStagingDir := targetFilesDir.String() + "/VENDOR_BOOT/RAMDISK"
710			vendorRamdiskFsConfigOut := targetFilesDir.String() + "/META/vendor_boot_filesystem_config.txt"
711			fsConfigBin := ctx.Config().HostToolPath(ctx, "fs_config")
712			builder.Command().Textf(
713				`(cd %s; find . -type d | sed 's,$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,,' | %s -C -D %s -R \"\" > %s`,
714				vendorRamdiskStagingDir, fsConfigBin, vendorRamdiskStagingDir, vendorRamdiskFsConfigOut).
715				Implicit(fsConfigBin)
716		}
717	}
718	// Copy ramdisk_node_list
719	if ramdiskNodeList := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Ramdisk_node_list)); ramdiskNodeList != nil {
720		builder.Command().Textf("cp").Input(ramdiskNodeList).Textf(" %s/META/", targetFilesDir.String())
721	}
722	// Copy releasetools.py
723	if releaseTools := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Releasetools_extension)); releaseTools != nil {
724		builder.Command().Textf("cp").Input(releaseTools).Textf(" %s/META/", targetFilesDir.String())
725	}
726	// apexkeys.txt
727	var installedApexKeys []android.Path
728	for _, installedModule := range allInstalledModules {
729		if info, ok := android.OtherModuleProvider(ctx, installedModule, ApexKeyPathInfoProvider); ok {
730			installedApexKeys = append(installedApexKeys, info.ApexKeyPath)
731		}
732	}
733	installedApexKeys = android.SortedUniquePaths(installedApexKeys) // Sort by keypath to match make
734	builder.Command().Text("cat").Inputs(installedApexKeys).Textf(" >> %s/META/apexkeys.txt", targetFilesDir.String())
735	// apkcerts.txt
736	builder.Command().Textf("cp").Input(a.apkCertsInfo).Textf(" %s/META/", targetFilesDir.String())
737
738	// Copy fastboot-info.txt
739	if fastbootInfo := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.FastbootInfo)); fastbootInfo != nil {
740		// TODO (b/399788523): Autogenerate fastboot-info.txt if there is no source fastboot-info.txt
741		// https://cs.android.com/android/_/android/platform/build/+/80b9546f8f69e78b8fe1870e0e745d70fc18dfcd:core/Makefile;l=5831-5893;drc=077490384423dff9eac954da5c001c6f0be3fa6e;bpv=0;bpt=0
742		builder.Command().Textf("cp").Input(fastbootInfo).Textf(" %s/META/fastboot-info.txt", targetFilesDir.String())
743	}
744
745	// kernel_configs.txt and kernel_version.txt
746	if a.kernelConfig != nil {
747		builder.Command().Textf("cp").Input(a.kernelConfig).Textf(" %s/META/", targetFilesDir.String())
748	}
749	if a.kernelVersion != nil {
750		builder.Command().Textf("cp").Input(a.kernelVersion).Textf(" %s/META/", targetFilesDir.String())
751	}
752	// misc_info.txt
753	if a.miscInfo != nil {
754		builder.Command().Textf("cp").Input(a.miscInfo).Textf(" %s/META/", targetFilesDir.String())
755	}
756	// apex_info.pb, care_map.pb, vbmeta_digest.txt
757	a.addImgToTargetFiles(ctx, builder, targetFilesDir.String())
758
759	if a.partitionProps.Super_partition_name != nil {
760		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
761		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
762			// dynamic_partitions_info.txt
763			// TODO (b/390192334): Add `building_super_empty_partition=true`
764			builder.Command().Text("cp").Input(info.DynamicPartitionsInfo).Textf(" %s/META/", targetFilesDir.String())
765		} else {
766			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
767		}
768	}
769
770}
771
772var (
773	// https://cs.android.com/android/_/android/platform/build/+/30f05352c3e6f4333c77d4af66c253572d3ea6c9:core/Makefile;l=2111-2120;drc=519f75666431ee2926e0ec8991c682b28a4c9521;bpv=1;bpt=0
774	defaultTargetRecoveryFstypeMountOptions = "ext4=max_batch_time=0,commit=1,data=ordered,barrier=1,errors=panic,nodelalloc"
775)
776
777// A partial implementation of make's $PRODUCT_OUT/misc_info.txt
778// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=5894?q=misc_info.txt%20f:build%2Fmake%2Fcore%2FMakefile&ss=android%2Fplatform%2Fsuperproject%2Fmain
779// This file is subsequently used by add_img_to_target_files to create additioanl metadata files like apex_info.pb
780// TODO (b/399788119): Complete the migration of misc_info.txt
781func (a *androidDevice) addMiscInfo(ctx android.ModuleContext) android.Path {
782	buildType := func() string {
783		if ctx.Config().Debuggable() {
784			return "userdebug"
785		} else if ctx.Config().Eng() {
786			return "eng"
787		} else {
788			return "user"
789		}
790	}
791	defaultAppCertificate := func() string {
792		pem, _ := ctx.Config().DefaultAppCertificate(ctx)
793		return strings.TrimSuffix(pem.String(), ".x509.pem")
794	}
795
796	builder := android.NewRuleBuilder(pctx, ctx)
797	miscInfo := android.PathForModuleOut(ctx, "misc_info.txt")
798	builder.Command().
799		Textf("rm -f %s", miscInfo).
800		Textf("&& echo recovery_api_version=%s >> %s", ctx.Config().VendorConfig("recovery").String("recovery_api_version"), miscInfo).
801		Textf("&& echo fstab_version=%s >> %s", ctx.Config().VendorConfig("recovery").String("recovery_fstab_version"), miscInfo).
802		Textf("&& echo build_type=%s >> %s", buildType(), miscInfo).
803		Textf("&& echo default_system_dev_certificate=%s >> %s", defaultAppCertificate(), miscInfo).
804		Textf("&& echo root_dir=%s >> %s", android.PathForModuleInPartitionInstall(ctx, "root"), miscInfo).
805		ImplicitOutput(miscInfo)
806	if len(ctx.Config().ExtraOtaRecoveryKeys()) > 0 {
807		builder.Command().Textf(`echo "extra_recovery_keys=%s" >> %s`, strings.Join(ctx.Config().ExtraOtaRecoveryKeys(), ""), miscInfo)
808	} else {
809		if a.partitionProps.Boot_partition_name != nil {
810			builder.Command().
811				Textf("echo mkbootimg_args='--header_version %s' >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Boot_partition_name), miscInfo).
812				// TODO: Use boot's header version for recovery for now since cuttlefish does not set `BOARD_RECOVERY_MKBOOTIMG_ARGS`
813				Textf(" && echo recovery_mkbootimg_args='--header_version %s' >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Boot_partition_name), miscInfo)
814		}
815		if a.partitionProps.Init_boot_partition_name != nil {
816			builder.Command().
817				Textf("echo mkbootimg_init_args='--header_version' %s >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Init_boot_partition_name), miscInfo)
818		}
819		builder.Command().
820			Textf("echo mkbootimg_version_args='--os_version %s --os_patch_level %s' >> %s", ctx.Config().PlatformVersionLastStable(), ctx.Config().PlatformSecurityPatch(), miscInfo).
821			Textf(" && echo multistage_support=1 >> %s", miscInfo).
822			Textf(" && echo blockimgdiff_versions=3,4 >> %s", miscInfo)
823	}
824	fsInfos := a.getFsInfos(ctx)
825	if _, ok := fsInfos["vendor"]; ok {
826		builder.Command().Textf("echo board_uses_vendorimage=true >> %s", miscInfo)
827	}
828	if fsInfos["system"].ErofsCompressHints != nil {
829		builder.Command().Textf("echo erofs_default_compress_hints=%s >> %s", fsInfos["system"].ErofsCompressHints, miscInfo)
830	}
831	if releaseTools := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Releasetools_extension)); releaseTools != nil {
832		builder.Command().Textf("echo tool_extensions=%s >> %s", filepath.Dir(releaseTools.String()), miscInfo)
833	}
834	// ramdisk uses `compressed_cpio` fs_type
835	// https://cs.android.com/android/_/android/platform/build/+/30f05352c3e6f4333c77d4af66c253572d3ea6c9:core/Makefile;l=5923-5925;drc=519f75666431ee2926e0ec8991c682b28a4c9521;bpv=1;bpt=0
836	if _, ok := fsInfos["ramdisk"]; ok {
837		builder.Command().Textf("echo lz4_ramdisks=true >> %s", miscInfo)
838	}
839	// recovery_mount_options
840	// TODO: Add support for TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS which can be used to override the default
841	builder.Command().Textf("echo recovery_mount_options=%s >> %s", defaultTargetRecoveryFstypeMountOptions, miscInfo)
842
843	// vintf information
844	if proptools.Bool(ctx.Config().ProductVariables().Enforce_vintf_manifest) {
845		builder.Command().Textf("echo vintf_enforce=true >> %s", miscInfo)
846	}
847	if len(ctx.Config().DeviceManifestFiles()) > 0 {
848		builder.Command().Textf("echo vintf_include_empty_vendor_sku=true >> %s", miscInfo)
849	}
850
851	if a.partitionProps.Recovery_partition_name == nil {
852		builder.Command().Textf("echo no_recovery=true >> %s", miscInfo)
853	}
854	for _, partition := range android.SortedKeys(fsInfos) {
855		if fsInfos[partition].PropFileForMiscInfo != nil {
856			builder.Command().Text("cat").Input(fsInfos[partition].PropFileForMiscInfo).Textf(" >> %s", miscInfo)
857		}
858	}
859	if len(a.partitionProps.Vbmeta_partitions) > 0 {
860		builder.Command().
861			Textf("echo avb_enable=true >> %s", miscInfo).
862			Textf("&& echo avb_building_vbmeta_image=true >> %s", miscInfo).
863			Textf("&& echo avb_avbtool=avbtool >> %s", miscInfo)
864
865		var allChainedVbmetaPartitionTypes []string
866		for _, vbmetaPartitionName := range a.partitionProps.Vbmeta_partitions {
867			img := ctx.GetDirectDepProxyWithTag(vbmetaPartitionName, filesystemDepTag)
868			if provider, ok := android.OtherModuleProvider(ctx, img, vbmetaPartitionProvider); ok {
869				builder.Command().Text("cat").Input(provider.PropFileForMiscInfo).Textf(" >> %s", miscInfo)
870				if provider.FilesystemPartitionType != "" { // the top-level vbmeta.img
871					allChainedVbmetaPartitionTypes = append(allChainedVbmetaPartitionTypes, provider.FilesystemPartitionType)
872				}
873			} else {
874				ctx.ModuleErrorf("vbmeta dep %s does not set vbmetaPartitionProvider\n", vbmetaPartitionName)
875			}
876		}
877		// Determine the custom vbmeta partitions by removing system and vendor
878		customVbmetaPartitionTypes := android.RemoveListFromList(allChainedVbmetaPartitionTypes, []string{"system", "vendor"})
879		builder.Command().Textf("echo avb_custom_vbmeta_images_partition_list=%s >> %s",
880			strings.Join(android.SortedUniqueStrings(customVbmetaPartitionTypes), " "),
881			miscInfo,
882		)
883
884	}
885	if a.partitionProps.Boot_partition_name != nil {
886		builder.Command().Textf("echo boot_images=boot.img >> %s", miscInfo)
887	}
888
889	if a.partitionProps.Super_partition_name != nil {
890		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
891		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
892			// cat dynamic_partition_info.txt
893			builder.Command().Text("cat").Input(info.DynamicPartitionsInfo).Textf(" >> %s", miscInfo)
894			if info.AbUpdate {
895				builder.Command().Textf("echo ab_update=true >> %s", miscInfo)
896			}
897
898		} else {
899			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
900		}
901	}
902	bootImgNames := []*string{
903		a.partitionProps.Boot_partition_name,
904		a.partitionProps.Init_boot_partition_name,
905		a.partitionProps.Vendor_boot_partition_name,
906	}
907	for _, bootImgName := range bootImgNames {
908		if bootImgName == nil {
909			continue
910		}
911
912		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(bootImgName), filesystemDepTag)
913		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
914		// cat avb_ metadata of the boot images
915		builder.Command().Text("cat").Input(bootImgInfo.PropFileForMiscInfo).Textf(" >> %s", miscInfo)
916	}
917
918	builder.Command().Textf("echo blocksize=%s >> %s", proptools.String(a.deviceProps.Flash_block_size), miscInfo)
919	if proptools.Bool(a.deviceProps.Bootloader_in_update_package) {
920		builder.Command().Textf("echo bootloader_in_update_package=true >> %s", miscInfo)
921	}
922	if len(a.deviceProps.Partial_ota_update_partitions) > 0 {
923		builder.Command().Textf("echo partial_ota_update_partitions_list=%s >> %s", strings.Join(a.deviceProps.Partial_ota_update_partitions, " "), miscInfo)
924	}
925
926	// Sort and dedup
927	builder.Command().Textf("sort -u %s -o %s", miscInfo, miscInfo)
928
929	builder.Build("misc_info", "Building misc_info")
930
931	return miscInfo
932}
933
934func (a *androidDevice) getBootimgHeaderVersion(ctx android.ModuleContext, bootImgName *string) string {
935	bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(bootImgName), filesystemDepTag)
936	bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
937	return bootImgInfo.HeaderVersion
938}
939
940// addImgToTargetFiles invokes `add_img_to_target_files` and creates the following files in META/
941// - apex_info.pb
942// - care_map.pb
943// - vbmeta_digest.txt
944func (a *androidDevice) addImgToTargetFiles(ctx android.ModuleContext, builder *android.RuleBuilder, targetFilesDir string) {
945	mkbootimg := ctx.Config().HostToolPath(ctx, "mkbootimg")
946	builder.Command().
947		Textf("PATH=%s:$PATH", ctx.Config().HostToolDir()).
948		Textf("MKBOOTIMG=%s", mkbootimg).
949		Implicit(mkbootimg).
950		BuiltTool("add_img_to_target_files").
951		Flag("-a -v -p").
952		Flag(ctx.Config().HostToolDir()).
953		Text(targetFilesDir)
954}
955
956func (a *androidDevice) buildUpdatePackage(ctx android.ModuleContext) {
957	var exclusions []string
958	fsInfos := a.getFsInfos(ctx)
959	// Exclude the partitions that are not supported by flashall
960	for _, partition := range android.SortedKeys(fsInfos) {
961		if fsInfos[partition].NoFlashall {
962			exclusions = append(exclusions, fmt.Sprintf("IMAGES/%s.img", partition))
963			exclusions = append(exclusions, fmt.Sprintf("IMAGES/%s.map", partition))
964		}
965	}
966
967	updatePackage := android.PathForModuleOut(ctx, "img.zip")
968	rule := android.NewRuleBuilder(pctx, ctx)
969
970	buildSuperImage := ctx.Config().HostToolPath(ctx, "build_super_image")
971	zip2zip := ctx.Config().HostToolPath(ctx, "zip2zip")
972
973	rule.Command().
974		BuiltTool("img_from_target_files").
975		Text("--additional IMAGES/VerifiedBootParams.textproto:VerifiedBootParams.textproto").
976		FlagForEachArg("--exclude ", exclusions).
977		FlagWithArg("--build_super_image ", buildSuperImage.String()).
978		Implicit(buildSuperImage).
979		Implicit(zip2zip).
980		Input(a.targetFilesZip).
981		Output(updatePackage)
982
983	rule.Build("updatepackage", "Building updatepackage")
984
985	a.updatePackage = updatePackage
986}
987
988type ApexKeyPathInfo struct {
989	ApexKeyPath android.Path
990}
991
992var ApexKeyPathInfoProvider = blueprint.NewProvider[ApexKeyPathInfo]()
993
994func (a *androidDevice) generateFilesystemConfigForTargetFiles(ctx android.ModuleContext, builder *android.RuleBuilder, stagingDirTimestamp android.Path, targetFilesDir, stagingDir, filename string) {
995	fsConfigOut := android.PathForModuleOut(ctx, filename)
996	ctx.Build(pctx, android.BuildParams{
997		Rule:     fsConfigRule,
998		Implicit: stagingDirTimestamp,
999		Output:   fsConfigOut,
1000		Args: map[string]string{
1001			"rootDir": stagingDir,
1002			"prefix":  "",
1003		},
1004	})
1005	builder.Command().Textf("cp").Input(fsConfigOut).Textf(" %s/META/", targetFilesDir)
1006}
1007
1008// Filenames for the partition specific fs_config files.
1009// Hardcode the ramdisk files to their boot image prefix
1010func (a *androidDevice) filesystemConfigNameForTargetFiles(partition string) string {
1011	name := partition + "_filesystem_config.txt"
1012	if partition == "system" {
1013		name = "filesystem_config.txt"
1014	} else if partition == "ramdisk" {
1015		name = "init_boot_filesystem_config.txt"
1016	}
1017	return name
1018}
1019
1020func (a *androidDevice) getFilesystemInfo(ctx android.ModuleContext, depName string) FilesystemInfo {
1021	fsMod := ctx.GetDirectDepProxyWithTag(depName, filesystemDepTag)
1022	fsInfo, ok := android.OtherModuleProvider(ctx, fsMod, FilesystemProvider)
1023	if !ok {
1024		ctx.ModuleErrorf("Expected dependency %s to be a filesystem", depName)
1025	}
1026	return fsInfo
1027}
1028
1029func (a *androidDevice) setVbmetaPhonyTargets(ctx android.ModuleContext) {
1030	if !proptools.Bool(a.deviceProps.Main_device) {
1031		return
1032	}
1033
1034	if !ctx.Config().KatiEnabled() {
1035		for _, vbmetaPartitionName := range a.partitionProps.Vbmeta_partitions {
1036			img := ctx.GetDirectDepProxyWithTag(vbmetaPartitionName, filesystemDepTag)
1037			if provider, ok := android.OtherModuleProvider(ctx, img, vbmetaPartitionProvider); ok {
1038				// make generates `vbmetasystemimage` phony target instead of `vbmeta_systemimage` phony target.
1039				partitionName := strings.ReplaceAll(provider.Name, "_", "")
1040				ctx.Phony(fmt.Sprintf("%simage", partitionName), provider.Output)
1041			}
1042		}
1043	}
1044}
1045
1046func (a *androidDevice) getKernel(ctx android.ModuleContext) android.Path {
1047	if a.partitionProps.Boot_partition_name != nil {
1048		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(a.partitionProps.Boot_partition_name), filesystemDepTag)
1049		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
1050		return bootImgInfo.Kernel
1051	}
1052	return nil
1053}
1054
1055// Gets the kernel version and configs from the actual kernel file itself. Roughly equivalent to
1056// this make code: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=5443;drc=c0b66fc59de069e06ce0ffd703d4d21613be30c6
1057// However, it is a simplified version of that make code. Differences include:
1058//   - Not handling BOARD_KERNEL_CONFIG_FILE because BOARD_KERNEL_CONFIG_FILE was never used.
1059//   - Not unpacking the bootimage, as we should be able to just always export the kernel directly
1060//     in the BootimgInfo. We don't currently support prebuilt boot images, but even if we add that
1061//     in the future, it can be done in a prebuilt_bootimage module type that still exports the same
1062//     BootimgInfo.
1063//   - We don't print a warning and output '<unknown-kernel>' to kernel_version_for_uffd_gc.txt
1064//     because we expect the kernel to always be present. If it's not, we will get an error that
1065//     kernel_version_for_uffd_gc.txt doesn't exist. This may require later tweaking to the
1066//     dexpreopt rules so that they don't attempt to access that file in builds that don't have
1067//     a kernel.
1068func (a *androidDevice) extractKernelVersionAndConfigs(ctx android.ModuleContext) (android.Path, android.Path) {
1069	kernel := a.getKernel(ctx)
1070	// If there's no kernel, don't create kernel version / kernel config files. Reverse dependencies
1071	// on those files have to account for this, for example by disabling dexpreopt in unbundled
1072	// builds.
1073	if kernel == nil {
1074		return nil, nil
1075	}
1076
1077	lz4tool := ctx.Config().HostToolPath(ctx, "lz4")
1078
1079	extractedVersionFile := android.PathForModuleOut(ctx, "kernel_version.txt")
1080	extractedConfigsFile := android.PathForModuleOut(ctx, "kernel_configs.txt")
1081	builder := android.NewRuleBuilder(pctx, ctx)
1082	builder.Command().BuiltTool("extract_kernel").
1083		Flag("--tools lz4:"+lz4tool.String()).Implicit(lz4tool).
1084		FlagWithInput("--input ", kernel).
1085		FlagWithOutput("--output-release ", extractedVersionFile).
1086		FlagWithOutput("--output-configs ", extractedConfigsFile).
1087		Textf(`&& printf "\n" >> %s`, extractedVersionFile)
1088
1089	if specifiedVersion := proptools.String(a.deviceProps.Kernel_version); specifiedVersion != "" {
1090		specifiedVersionFile := android.PathForModuleOut(ctx, "specified_kernel_version.txt")
1091		android.WriteFileRule(ctx, specifiedVersionFile, specifiedVersion)
1092		builder.Command().Text("diff -q").
1093			Input(specifiedVersionFile).
1094			Input(extractedVersionFile).
1095			Textf(`|| (echo "Specified kernel version '$(cat %s)' does not match actual kernel version '$(cat %s)'"; exit 1)`, specifiedVersionFile, extractedVersionFile)
1096	}
1097
1098	builder.Build("extract_kernel_info", "Extract kernel version and configs")
1099
1100	if proptools.Bool(a.deviceProps.Main_device) && !ctx.Config().KatiEnabled() {
1101		if ctx.Config().EnableUffdGc() == "default" {
1102			kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")
1103			ctx.Build(pctx, android.BuildParams{
1104				Rule:   android.CpIfChanged,
1105				Input:  extractedVersionFile,
1106				Output: kernelVersionFile,
1107			})
1108		}
1109
1110		ctx.DistForGoal("droid_targets", extractedVersionFile)
1111	}
1112
1113	return extractedVersionFile, extractedConfigsFile
1114}
1115
1116func (a *androidDevice) buildApkCertsInfo(ctx android.ModuleContext, allInstalledModules []android.Module) android.Path {
1117	// TODO (spandandas): Add compressed
1118	formatLine := func(cert java.Certificate, name, partition string) string {
1119		pem := cert.AndroidMkString()
1120		var key string
1121		if cert.Key == nil {
1122			key = ""
1123		} else {
1124			key = cert.Key.String()
1125		}
1126		return fmt.Sprintf(`name="%s" certificate="%s" private_key="%s" partition="%s"`, name, pem, key, partition)
1127	}
1128
1129	apkCerts := []string{}
1130	var apkCertsFiles android.Paths
1131	for _, installedModule := range allInstalledModules {
1132		partition := ""
1133		if commonInfo, ok := android.OtherModuleProvider(ctx, installedModule, android.CommonModuleInfoProvider); ok {
1134			partition = commonInfo.PartitionTag
1135		} else {
1136			ctx.ModuleErrorf("%s does not set CommonModuleInfoKey", installedModule.Name())
1137		}
1138		if info, ok := android.OtherModuleProvider(ctx, installedModule, java.AppInfoProvider); ok {
1139			if info.AppSet {
1140				apkCertsFiles = append(apkCertsFiles, info.ApkCertsFile)
1141			} else {
1142				apkCerts = append(apkCerts, formatLine(info.Certificate, info.InstallApkName+".apk", partition))
1143			}
1144		} else if info, ok := android.OtherModuleProvider(ctx, installedModule, java.AppInfosProvider); ok {
1145			for _, certInfo := range info {
1146				// Partition information of apk-in-apex is not exported to the legacy Make packaging system.
1147				// Hardcode the partition to "system"
1148				apkCerts = append(apkCerts, formatLine(certInfo.Certificate, certInfo.InstallApkName+".apk", "system"))
1149			}
1150		} else if info, ok := android.OtherModuleProvider(ctx, installedModule, java.RuntimeResourceOverlayInfoProvider); ok {
1151			apkCerts = append(apkCerts, formatLine(info.Certificate, info.OutputFile.Base(), partition))
1152		}
1153	}
1154	slices.Sort(apkCerts) // sort by name
1155	fsInfos := a.getFsInfos(ctx)
1156	if fsInfos["system"].HasFsverity {
1157		defaultPem, defaultKey := ctx.Config().DefaultAppCertificate(ctx)
1158		apkCerts = append(apkCerts, formatLine(java.Certificate{Pem: defaultPem, Key: defaultKey}, "BuildManifest.apk", "system"))
1159		if info, ok := fsInfos["system_ext"]; ok && info.HasFsverity {
1160			apkCerts = append(apkCerts, formatLine(java.Certificate{Pem: defaultPem, Key: defaultKey}, "BuildManifestSystemExt.apk", "system_ext"))
1161		}
1162	}
1163
1164	apkCertsInfoWithoutAppSets := android.PathForModuleOut(ctx, "apkcerts_without_app_sets.txt")
1165	android.WriteFileRuleVerbatim(ctx, apkCertsInfoWithoutAppSets, strings.Join(apkCerts, "\n")+"\n")
1166	apkCertsInfo := android.PathForModuleOut(ctx, "apkcerts.txt")
1167	ctx.Build(pctx, android.BuildParams{
1168		Rule:        android.Cat,
1169		Description: "combine apkcerts.txt",
1170		Output:      apkCertsInfo,
1171		Inputs:      append(apkCertsFiles, apkCertsInfoWithoutAppSets),
1172	})
1173	return apkCertsInfo
1174}
1175