• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2021 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	"fmt"
19	"strconv"
20	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24
25	"android/soong/android"
26)
27
28func init() {
29	android.RegisterModuleType("bootimg", BootimgFactory)
30}
31
32type bootimg struct {
33	android.ModuleBase
34
35	properties BootimgProperties
36
37	output     android.Path
38	installDir android.InstallPath
39
40	bootImageType bootImageType
41}
42
43type BootimgProperties struct {
44	// Set the name of the output. Defaults to <module_name>.img.
45	Stem *string
46
47	// Path to the linux kernel prebuilt file
48	Kernel_prebuilt *string `android:"arch_variant,path"`
49
50	// Filesystem module that is used as ramdisk
51	Ramdisk_module *string
52
53	// Path to the device tree blob (DTB) prebuilt file to add to this boot image
54	Dtb_prebuilt *string `android:"arch_variant,path"`
55
56	// Header version number. Must be set to one of the version numbers that are currently
57	// supported. Refer to
58	// https://source.android.com/devices/bootloader/boot-image-header
59	Header_version *string
60
61	// Determines the specific type of boot image this module is building. Can be boot,
62	// vendor_boot or init_boot. Defaults to boot.
63	// Refer to https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
64	// for vendor_boot.
65	// Refer to https://source.android.com/docs/core/architecture/partitions/generic-boot for
66	// init_boot.
67	Boot_image_type *string
68
69	// Optional kernel commandline arguments
70	Cmdline []string `android:"arch_variant"`
71
72	// File that contains bootconfig parameters. This can be set only when `vendor_boot` is true
73	// and `header_version` is greater than or equal to 4.
74	Bootconfig *string `android:"arch_variant,path"`
75
76	// The size of the partition on the device. It will be a build error if this built partition
77	// image exceeds this size.
78	Partition_size *int64
79
80	// When set to true, sign the image with avbtool. Default is false.
81	Use_avb *bool
82
83	// This can either be "default", or "make_legacy". "make_legacy" will sign the boot image
84	// like how build/make/core/Makefile does, to get bit-for-bit backwards compatibility. But
85	// we may want to reconsider if it's necessary to have two modes in the future. The default
86	// is "default"
87	Avb_mode *string
88
89	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
90	Partition_name *string
91
92	// Path to the private key that avbtool will use to sign this filesystem image.
93	// TODO(jiyong): allow apex_key to be specified here
94	Avb_private_key *string `android:"path_device_first"`
95
96	// Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
97	Avb_algorithm *string
98
99	// The index used to prevent rollback of the image on device.
100	Avb_rollback_index *int64
101
102	// Rollback index location of this image. Must be 0, 1, 2, etc.
103	Avb_rollback_index_location *int64
104
105	// The security patch passed to as the com.android.build.<type>.security_patch avb property.
106	// Replacement for the make variables BOOT_SECURITY_PATCH / INIT_BOOT_SECURITY_PATCH.
107	Security_patch *string
108}
109
110type bootImageType int
111
112const (
113	unsupported bootImageType = iota
114	boot
115	vendorBoot
116	initBoot
117)
118
119func toBootImageType(ctx android.ModuleContext, bootImageType string) bootImageType {
120	switch bootImageType {
121	case "boot":
122		return boot
123	case "vendor_boot":
124		return vendorBoot
125	case "init_boot":
126		return initBoot
127	default:
128		ctx.ModuleErrorf("Unknown boot_image_type %s. Must be one of \"boot\", \"vendor_boot\", or \"init_boot\"", bootImageType)
129	}
130	return unsupported
131}
132
133func (b bootImageType) String() string {
134	switch b {
135	case boot:
136		return "boot"
137	case vendorBoot:
138		return "vendor_boot"
139	case initBoot:
140		return "init_boot"
141	default:
142		panic("unknown boot image type")
143	}
144}
145
146func (b bootImageType) isBoot() bool {
147	return b == boot
148}
149
150func (b bootImageType) isVendorBoot() bool {
151	return b == vendorBoot
152}
153
154func (b bootImageType) isInitBoot() bool {
155	return b == initBoot
156}
157
158// bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb.
159func BootimgFactory() android.Module {
160	module := &bootimg{}
161	module.AddProperties(&module.properties)
162	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
163	return module
164}
165
166type bootimgDep struct {
167	blueprint.BaseDependencyTag
168	kind string
169}
170
171var bootimgRamdiskDep = bootimgDep{kind: "ramdisk"}
172
173func (b *bootimg) DepsMutator(ctx android.BottomUpMutatorContext) {
174	ramdisk := proptools.String(b.properties.Ramdisk_module)
175	if ramdisk != "" {
176		ctx.AddDependency(ctx.Module(), bootimgRamdiskDep, ramdisk)
177	}
178}
179
180func (b *bootimg) installFileName() string {
181	return proptools.StringDefault(b.properties.Stem, b.BaseModuleName()+".img")
182}
183
184func (b *bootimg) partitionName() string {
185	return proptools.StringDefault(b.properties.Partition_name, b.BaseModuleName())
186}
187
188func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
189	b.bootImageType = toBootImageType(ctx, proptools.StringDefault(b.properties.Boot_image_type, "boot"))
190	if b.bootImageType == unsupported {
191		return
192	}
193
194	kernelProp := proptools.String(b.properties.Kernel_prebuilt)
195	if b.bootImageType.isVendorBoot() && kernelProp != "" {
196		ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel")
197		return
198	}
199	if b.bootImageType.isBoot() && kernelProp == "" {
200		ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
201		return
202	}
203
204	kernelPath := b.getKernelPath(ctx)
205	unsignedOutput := b.buildBootImage(ctx, kernelPath)
206
207	output := unsignedOutput
208	if proptools.Bool(b.properties.Use_avb) {
209		// This bootimg module supports 2 modes of avb signing. It is not clear to this author
210		// why there are differences, but one of them is to match the behavior of make-built boot
211		// images.
212		switch proptools.StringDefault(b.properties.Avb_mode, "default") {
213		case "default":
214			output = b.signImage(ctx, unsignedOutput)
215		case "make_legacy":
216			output = b.addAvbFooter(ctx, unsignedOutput, kernelPath)
217		default:
218			ctx.PropertyErrorf("avb_mode", `Unknown value for avb_mode, expected "default" or "make_legacy", got: %q`, *b.properties.Avb_mode)
219		}
220	}
221
222	b.installDir = android.PathForModuleInstall(ctx, "etc")
223	ctx.InstallFile(b.installDir, b.installFileName(), output)
224
225	ctx.SetOutputFiles([]android.Path{output}, "")
226	b.output = output
227
228	// Set the Filesystem info of the ramdisk dependency.
229	// `android_device` will use this info to package `target_files.zip`
230	if ramdisk := proptools.String(b.properties.Ramdisk_module); ramdisk != "" {
231		ramdiskModule := ctx.GetDirectDepWithTag(ramdisk, bootimgRamdiskDep)
232		fsInfo, _ := android.OtherModuleProvider(ctx, ramdiskModule, FilesystemProvider)
233		android.SetProvider(ctx, FilesystemProvider, fsInfo)
234	} else {
235		setCommonFilesystemInfo(ctx, b)
236	}
237
238	// Set BootimgInfo for building target_files.zip
239	dtbPath := b.getDtbPath(ctx)
240	android.SetProvider(ctx, BootimgInfoProvider, BootimgInfo{
241		Cmdline:             b.properties.Cmdline,
242		Kernel:              kernelPath,
243		Dtb:                 dtbPath,
244		Bootconfig:          b.getBootconfigPath(ctx),
245		Output:              output,
246		PropFileForMiscInfo: b.buildPropFileForMiscInfo(ctx),
247		HeaderVersion:       proptools.String(b.properties.Header_version),
248	})
249
250	extractedPublicKey := android.PathForModuleOut(ctx, b.partitionName()+".avbpubkey")
251	if b.properties.Avb_private_key != nil {
252		key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key))
253		ctx.Build(pctx, android.BuildParams{
254			Rule:   extractPublicKeyRule,
255			Input:  key,
256			Output: extractedPublicKey,
257		})
258	}
259	var ril int
260	if b.properties.Avb_rollback_index_location != nil {
261		ril = proptools.Int(b.properties.Avb_rollback_index_location)
262	}
263
264	android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{
265		Name:                  b.bootImageType.String(),
266		RollbackIndexLocation: ril,
267		PublicKey:             extractedPublicKey,
268		Output:                output,
269	})
270
271	// Dump compliance metadata
272	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
273	prebuiltFilesCopied := make([]string, 0)
274	if kernelPath != nil {
275		prebuiltFilesCopied = append(prebuiltFilesCopied, kernelPath.String()+":kernel")
276	}
277	if dtbPath != nil {
278		prebuiltFilesCopied = append(prebuiltFilesCopied, dtbPath.String()+":dtb.img")
279	}
280	complianceMetadataInfo.SetPrebuiltFilesCopied(prebuiltFilesCopied)
281
282	if ramdisk := proptools.String(b.properties.Ramdisk_module); ramdisk != "" {
283		buildComplianceMetadata(ctx, bootimgRamdiskDep)
284	}
285}
286
287var BootimgInfoProvider = blueprint.NewProvider[BootimgInfo]()
288
289type BootimgInfo struct {
290	Cmdline             []string
291	Kernel              android.Path
292	Dtb                 android.Path
293	Bootconfig          android.Path
294	Output              android.Path
295	PropFileForMiscInfo android.Path
296	HeaderVersion       string
297}
298
299func (b *bootimg) getKernelPath(ctx android.ModuleContext) android.Path {
300	var kernelPath android.Path
301	kernelName := proptools.String(b.properties.Kernel_prebuilt)
302	if kernelName != "" {
303		kernelPath = android.PathForModuleSrc(ctx, kernelName)
304	}
305	return kernelPath
306}
307
308func (b *bootimg) getDtbPath(ctx android.ModuleContext) android.Path {
309	var dtbPath android.Path
310	dtbName := proptools.String(b.properties.Dtb_prebuilt)
311	if dtbName != "" {
312		dtbPath = android.PathForModuleSrc(ctx, dtbName)
313	}
314	return dtbPath
315}
316
317func (b *bootimg) getBootconfigPath(ctx android.ModuleContext) android.Path {
318	var bootconfigPath android.Path
319	bootconfigName := proptools.String(b.properties.Bootconfig)
320	if bootconfigName != "" {
321		bootconfigPath = android.PathForModuleSrc(ctx, bootconfigName)
322	}
323	return bootconfigPath
324}
325
326func (b *bootimg) buildBootImage(ctx android.ModuleContext, kernel android.Path) android.Path {
327	output := android.PathForModuleOut(ctx, "unsigned", b.installFileName())
328
329	builder := android.NewRuleBuilder(pctx, ctx)
330	cmd := builder.Command().BuiltTool("mkbootimg")
331
332	if kernel != nil {
333		cmd.FlagWithInput("--kernel ", kernel)
334	}
335
336	// These arguments are passed for boot.img and init_boot.img generation
337	if b.bootImageType.isBoot() || b.bootImageType.isInitBoot() {
338		cmd.FlagWithArg("--os_version ", ctx.Config().PlatformVersionLastStable())
339		cmd.FlagWithArg("--os_patch_level ", ctx.Config().PlatformSecurityPatch())
340	}
341
342	if b.getDtbPath(ctx) != nil {
343		cmd.FlagWithInput("--dtb ", b.getDtbPath(ctx))
344	}
345
346	cmdline := strings.Join(b.properties.Cmdline, " ")
347	if cmdline != "" {
348		flag := "--cmdline "
349		if b.bootImageType.isVendorBoot() {
350			flag = "--vendor_cmdline "
351		}
352		cmd.FlagWithArg(flag, proptools.ShellEscapeIncludingSpaces(cmdline))
353	}
354
355	headerVersion := proptools.String(b.properties.Header_version)
356	if headerVersion == "" {
357		ctx.PropertyErrorf("header_version", "must be set")
358		return output
359	}
360	verNum, err := strconv.Atoi(headerVersion)
361	if err != nil {
362		ctx.PropertyErrorf("header_version", "%q is not a number", headerVersion)
363		return output
364	}
365	if verNum < 3 {
366		ctx.PropertyErrorf("header_version", "must be 3 or higher for vendor_boot")
367		return output
368	}
369	cmd.FlagWithArg("--header_version ", headerVersion)
370
371	ramdiskName := proptools.String(b.properties.Ramdisk_module)
372	if ramdiskName != "" {
373		ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
374		if filesystem, ok := ramdisk.(*filesystem); ok {
375			flag := "--ramdisk "
376			if b.bootImageType.isVendorBoot() {
377				flag = "--vendor_ramdisk "
378			}
379			cmd.FlagWithInput(flag, filesystem.OutputPath())
380		} else {
381			ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
382			return output
383		}
384	}
385
386	bootconfig := proptools.String(b.properties.Bootconfig)
387	if bootconfig != "" {
388		if !b.bootImageType.isVendorBoot() {
389			ctx.PropertyErrorf("bootconfig", "requires vendor_boot: true")
390			return output
391		}
392		if verNum < 4 {
393			ctx.PropertyErrorf("bootconfig", "requires header_version: 4 or later")
394			return output
395		}
396		cmd.FlagWithInput("--vendor_bootconfig ", android.PathForModuleSrc(ctx, bootconfig))
397	}
398
399	// Output flag for boot.img and init_boot.img
400	flag := "--output "
401	if b.bootImageType.isVendorBoot() {
402		flag = "--vendor_boot "
403	}
404	cmd.FlagWithOutput(flag, output)
405
406	if b.properties.Partition_size != nil {
407		assertMaxImageSize(builder, output, *b.properties.Partition_size, proptools.Bool(b.properties.Use_avb))
408	}
409
410	builder.Build("build_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName()))
411	return output
412}
413
414func (b *bootimg) addAvbFooter(ctx android.ModuleContext, unsignedImage android.Path, kernel android.Path) android.Path {
415	output := android.PathForModuleOut(ctx, b.installFileName())
416	builder := android.NewRuleBuilder(pctx, ctx)
417	builder.Command().Text("cp").Input(unsignedImage).Output(output)
418	cmd := builder.Command().BuiltTool("avbtool").
419		Text("add_hash_footer").
420		FlagWithInput("--image ", output)
421
422	if b.properties.Partition_size != nil {
423		cmd.FlagWithArg("--partition_size ", strconv.FormatInt(*b.properties.Partition_size, 10))
424	} else {
425		cmd.Flag("--dynamic_partition_size")
426	}
427
428	// If you don't provide a salt, avbtool will use random bytes for the salt.
429	// This is bad for determinism (cached builds and diff tests are affected), so instead,
430	// we try to provide a salt. The requirements for a salt are not very clear, one aspect of it
431	// is that if it's unpredictable, attackers trying to change the contents of a partition need
432	// to find a new hash collision every release, because the salt changed.
433	if kernel != nil {
434		cmd.Textf(`--salt $(sha256sum "%s" | cut -d " " -f 1)`, kernel.String())
435		cmd.Implicit(kernel)
436	} else {
437		cmd.Textf(`--salt $(sha256sum "%s" "%s" | cut -d " " -f 1 | tr -d '\n')`, ctx.Config().BuildNumberFile(ctx), ctx.Config().Getenv("BUILD_DATETIME_FILE"))
438		cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx))
439	}
440
441	cmd.FlagWithArg("--partition_name ", b.bootImageType.String())
442
443	if b.properties.Avb_algorithm != nil {
444		cmd.FlagWithArg("--algorithm ", proptools.NinjaAndShellEscape(*b.properties.Avb_algorithm))
445	}
446
447	if b.properties.Avb_private_key != nil {
448		key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key))
449		cmd.FlagWithInput("--key ", key)
450	}
451
452	if !b.bootImageType.isVendorBoot() {
453		cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf(
454			"com.android.build.%s.os_version:%s", b.bootImageType.String(), ctx.Config().PlatformVersionLastStable())))
455	}
456
457	fingerprintFile := ctx.Config().BuildFingerprintFile(ctx)
458	cmd.FlagWithArg("--prop ", fmt.Sprintf("com.android.build.%s.fingerprint:$(cat %s)", b.bootImageType.String(), fingerprintFile.String()))
459	cmd.OrderOnly(fingerprintFile)
460
461	if b.properties.Security_patch != nil {
462		cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf(
463			"com.android.build.%s.security_patch:%s", b.bootImageType.String(), *b.properties.Security_patch)))
464	}
465
466	if b.properties.Avb_rollback_index != nil {
467		cmd.FlagWithArg("--rollback_index ", strconv.FormatInt(*b.properties.Avb_rollback_index, 10))
468	}
469
470	builder.Build("add_avb_footer", fmt.Sprintf("Adding avb footer to %s", b.BaseModuleName()))
471	return output
472}
473
474func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.Path) android.Path {
475	propFile, toolDeps := b.buildPropFile(ctx)
476
477	output := android.PathForModuleOut(ctx, b.installFileName())
478	builder := android.NewRuleBuilder(pctx, ctx)
479	builder.Command().Text("cp").Input(unsignedImage).Output(output)
480	builder.Command().BuiltTool("verity_utils").
481		Input(propFile).
482		Implicits(toolDeps).
483		Output(output)
484
485	builder.Build("sign_bootimg", fmt.Sprintf("Signing %s", b.BaseModuleName()))
486	return output
487}
488
489func (b *bootimg) buildPropFile(ctx android.ModuleContext) (android.Path, android.Paths) {
490	var sb strings.Builder
491	var deps android.Paths
492	addStr := func(name string, value string) {
493		fmt.Fprintf(&sb, "%s=%s\n", name, value)
494	}
495	addPath := func(name string, path android.Path) {
496		addStr(name, path.String())
497		deps = append(deps, path)
498	}
499
500	addStr("avb_hash_enable", "true")
501	addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool"))
502	algorithm := proptools.StringDefault(b.properties.Avb_algorithm, "SHA256_RSA4096")
503	addStr("avb_algorithm", algorithm)
504	key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key))
505	addPath("avb_key_path", key)
506	addStr("avb_add_hash_footer_args", "") // TODO(jiyong): add --rollback_index
507	partitionName := proptools.StringDefault(b.properties.Partition_name, b.Name())
508	addStr("partition_name", partitionName)
509
510	propFile := android.PathForModuleOut(ctx, "prop")
511	android.WriteFileRule(ctx, propFile, sb.String())
512	return propFile, deps
513}
514
515func (b *bootimg) getAvbHashFooterArgs(ctx android.ModuleContext) string {
516	ret := ""
517	if !b.bootImageType.isVendorBoot() {
518		ret += "--prop " + fmt.Sprintf("com.android.build.%s.os_version:%s", b.bootImageType.String(), ctx.Config().PlatformVersionLastStable())
519	}
520
521	fingerprintFile := ctx.Config().BuildFingerprintFile(ctx)
522	ret += " --prop " + fmt.Sprintf("com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", b.bootImageType.String(), fingerprintFile.String())
523
524	if b.properties.Security_patch != nil {
525		ret += " --prop " + fmt.Sprintf("com.android.build.%s.security_patch:%s", b.bootImageType.String(), *b.properties.Security_patch)
526	}
527
528	if b.properties.Avb_rollback_index != nil {
529		ret += " --rollback_index " + strconv.FormatInt(*b.properties.Avb_rollback_index, 10)
530	}
531	return strings.TrimSpace(ret)
532}
533
534func (b *bootimg) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
535	var sb strings.Builder
536	addStr := func(name string, value string) {
537		fmt.Fprintf(&sb, "%s=%s\n", name, value)
538	}
539
540	bootImgType := proptools.String(b.properties.Boot_image_type)
541	addStr("avb_"+bootImgType+"_add_hash_footer_args", b.getAvbHashFooterArgs(ctx))
542	if ramdisk := proptools.String(b.properties.Ramdisk_module); ramdisk != "" {
543		ramdiskModule := ctx.GetDirectDepWithTag(ramdisk, bootimgRamdiskDep)
544		fsInfo, _ := android.OtherModuleProvider(ctx, ramdiskModule, FilesystemProvider)
545		if fsInfo.HasOrIsRecovery {
546			// Create a dup entry for recovery
547			addStr("avb_recovery_add_hash_footer_args", strings.ReplaceAll(b.getAvbHashFooterArgs(ctx), bootImgType, "recovery"))
548		}
549	}
550	if b.properties.Avb_private_key != nil {
551		addStr("avb_"+bootImgType+"_algorithm", proptools.StringDefault(b.properties.Avb_algorithm, "SHA256_RSA4096"))
552		addStr("avb_"+bootImgType+"_key_path", android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key)).String())
553		addStr("avb_"+bootImgType+"_rollback_index_location", strconv.Itoa(proptools.Int(b.properties.Avb_rollback_index_location)))
554	}
555	if b.properties.Partition_size != nil {
556		addStr(bootImgType+"_size", strconv.FormatInt(*b.properties.Partition_size, 10))
557	}
558	if bootImgType != "boot" {
559		addStr(bootImgType, "true")
560	}
561
562	propFilePreProcessing := android.PathForModuleOut(ctx, "prop_for_misc_info_pre_processing")
563	android.WriteFileRuleVerbatim(ctx, propFilePreProcessing, sb.String())
564	propFile := android.PathForModuleOut(ctx, "prop_file_for_misc_info")
565	ctx.Build(pctx, android.BuildParams{
566		Rule:   textFileProcessorRule,
567		Input:  propFilePreProcessing,
568		Output: propFile,
569	})
570
571	return propFile
572}
573
574var _ android.AndroidMkEntriesProvider = (*bootimg)(nil)
575
576// Implements android.AndroidMkEntriesProvider
577func (b *bootimg) AndroidMkEntries() []android.AndroidMkEntries {
578	return []android.AndroidMkEntries{android.AndroidMkEntries{
579		Class:      "ETC",
580		OutputFile: android.OptionalPathForPath(b.output),
581		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
582			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
583				entries.SetString("LOCAL_MODULE_PATH", b.installDir.String())
584				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", b.installFileName())
585			},
586		},
587	}}
588}
589
590var _ Filesystem = (*bootimg)(nil)
591
592func (b *bootimg) OutputPath() android.Path {
593	return b.output
594}
595
596func (b *bootimg) SignedOutputPath() android.Path {
597	if proptools.Bool(b.properties.Use_avb) {
598		return b.OutputPath()
599	}
600	return nil
601}
602