• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 Google Inc. All rights reserved.
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 java
16
17import (
18	"fmt"
19	"path/filepath"
20	"slices"
21	"strconv"
22	"strings"
23
24	"android/soong/android"
25	"android/soong/dexpreopt"
26
27	"github.com/google/blueprint"
28	"github.com/google/blueprint/proptools"
29)
30
31type AndroidLibraryDependency interface {
32	ExportPackage() android.Path
33	ResourcesNodeDepSet() *android.DepSet[*resourcesNode]
34	RRODirsDepSet() *android.DepSet[rroDir]
35	ManifestsDepSet() *android.DepSet[android.Path]
36	SetRROEnforcedForDependent(enforce bool)
37	IsRROEnforced(ctx android.BaseModuleContext) bool
38}
39
40func init() {
41	RegisterAARBuildComponents(android.InitRegistrationContext)
42}
43
44func RegisterAARBuildComponents(ctx android.RegistrationContext) {
45	ctx.RegisterModuleType("android_library_import", AARImportFactory)
46	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
47	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
48		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator)
49	})
50}
51
52//
53// AAR (android library)
54//
55
56type androidLibraryProperties struct {
57	BuildAAR bool `blueprint:"mutated"`
58}
59
60type aaptProperties struct {
61	// flags passed to aapt when creating the apk
62	Aaptflags []string
63
64	// include all resource configurations, not just the product-configured
65	// ones.
66	Aapt_include_all_resources *bool
67
68	// list of files to use as assets.
69	Assets []string `android:"path"`
70
71	// list of directories relative to the Blueprints file containing assets.
72	// Defaults to ["assets"] if a directory called assets exists.  Set to []
73	// to disable the default.
74	Asset_dirs []string
75
76	// list of directories relative to the Blueprints file containing
77	// Android resources.  Defaults to ["res"] if a directory called res exists.
78	// Set to [] to disable the default.
79	Resource_dirs []string
80
81	// list of zip files containing Android resources.
82	Resource_zips []string `android:"path"`
83
84	// path to AndroidManifest.xml.  If unset, defaults to "AndroidManifest.xml".
85	Manifest *string `android:"path"`
86
87	// paths to additional manifest files to merge with main manifest.
88	Additional_manifests []string `android:"path"`
89
90	// do not include AndroidManifest from dependent libraries
91	Dont_merge_manifests *bool
92
93	// If use_resource_processor is set, use Bazel's resource processor instead of aapt2 to generate R.class files.
94	// The resource processor produces more optimal R.class files that only list resources in the package of the
95	// library that provided them, as opposed to aapt2 which produces R.java files for every package containing
96	// every resource.  Using the resource processor can provide significant build time speedups, but requires
97	// fixing the module to use the correct package to reference each resource, and to avoid having any other
98	// libraries in the tree that use the same package name.  Defaults to false, but will default to true in the
99	// future.
100	Use_resource_processor *bool
101
102	// true if RRO is enforced for any of the dependent modules
103	RROEnforcedForDependent bool `blueprint:"mutated"`
104
105	// Filter only specified product and ignore other products
106	Filter_product *string `blueprint:"mutated"`
107
108	// Names of aconfig_declarations modules that specify aconfig flags that the module depends on.
109	Flags_packages []string
110}
111
112type aapt struct {
113	aaptSrcJar                         android.Path
114	transitiveAaptRJars                android.Paths
115	transitiveAaptResourcePackagesFile android.Path
116	exportPackage                      android.Path
117	manifestPath                       android.Path
118	proguardOptionsFile                android.Path
119	rTxt                               android.Path
120	rJar                               android.Path
121	extraAaptPackagesFile              android.Path
122	mergedManifestFile                 android.Path
123	noticeFile                         android.OptionalPath
124	assetPackage                       android.OptionalPath
125	isLibrary                          bool
126	defaultManifestVersion             string
127	useEmbeddedNativeLibs              bool
128	useEmbeddedDex                     bool
129	usesNonSdkApis                     bool
130	hasNoCode                          bool
131	LoggingParent                      string
132	resourceFiles                      android.Paths
133
134	splitNames []string
135	splits     []split
136
137	aaptProperties aaptProperties
138
139	resourcesNodesDepSet *android.DepSet[*resourcesNode]
140	rroDirsDepSet        *android.DepSet[rroDir]
141	manifestsDepSet      *android.DepSet[android.Path]
142
143	manifestValues struct {
144		applicationId string
145	}
146}
147
148type split struct {
149	name   string
150	suffix string
151	path   android.Path
152}
153
154// Propagate RRO enforcement flag to static lib dependencies transitively.
155func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) {
156	m := ctx.Module()
157	if d, ok := m.(AndroidLibraryDependency); ok && d.IsRROEnforced(ctx) {
158		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
159			if a, ok := d.(AndroidLibraryDependency); ok {
160				a.SetRROEnforcedForDependent(true)
161			}
162		})
163	}
164}
165
166func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
167	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) &&
168		// TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries.
169		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib")
170}
171
172func (a *aapt) filterProduct() string {
173	return String(a.aaptProperties.Filter_product)
174}
175
176func (a *aapt) ExportPackage() android.Path {
177	return a.exportPackage
178}
179func (a *aapt) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] {
180	return a.resourcesNodesDepSet
181}
182
183func (a *aapt) RRODirsDepSet() *android.DepSet[rroDir] {
184	return a.rroDirsDepSet
185}
186
187func (a *aapt) ManifestsDepSet() *android.DepSet[android.Path] {
188	return a.manifestsDepSet
189}
190
191func (a *aapt) SetRROEnforcedForDependent(enforce bool) {
192	a.aaptProperties.RROEnforcedForDependent = enforce
193}
194
195func (a *aapt) IsRROEnforced(ctx android.BaseModuleContext) bool {
196	// True if RRO is enforced for this module or...
197	return ctx.Config().EnforceRROForModule(ctx.ModuleName()) ||
198		// if RRO is enforced for any of its dependents.
199		a.aaptProperties.RROEnforcedForDependent
200}
201
202func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkContext,
203	manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths,
204	resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
205
206	hasVersionCode := android.PrefixInList(a.aaptProperties.Aaptflags, "--version-code")
207	hasVersionName := android.PrefixInList(a.aaptProperties.Aaptflags, "--version-name")
208
209	// Flags specified in Android.bp
210	linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...)
211
212	linkFlags = append(linkFlags, "--enable-compact-entries")
213
214	// Find implicit or explicit asset and resource dirs
215	assets := android.PathsRelativeToModuleSourceDir(android.SourceInput{
216		Context:     ctx,
217		Paths:       a.aaptProperties.Assets,
218		IncludeDirs: false,
219	})
220	assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
221	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
222	resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
223
224	// Glob directories into lists of paths
225	for _, dir := range resourceDirs {
226		resDirs = append(resDirs, globbedResourceDir{
227			dir:   dir,
228			files: androidResourceGlob(ctx, dir),
229		})
230		resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, a, dir)
231		overlayDirs = append(overlayDirs, resOverlayDirs...)
232		rroDirs = append(rroDirs, resRRODirs...)
233	}
234
235	var assetDeps android.Paths
236	for i, dir := range assetDirs {
237		// Add a dependency on every file in the asset directory.  This ensures the aapt2
238		// rule will be rerun if one of the files in the asset directory is modified.
239		assetDeps = append(assetDeps, androidResourceGlob(ctx, dir)...)
240
241		// Add a dependency on a file that contains a list of all the files in the asset directory.
242		// This ensures the aapt2 rule will be run if a file is removed from the asset directory,
243		// or a file is added whose timestamp is older than the output of aapt2.
244		assetFileListFile := android.PathForModuleOut(ctx, "asset_dir_globs", strconv.Itoa(i)+".glob")
245		androidResourceGlobList(ctx, dir, assetFileListFile)
246		assetDeps = append(assetDeps, assetFileListFile)
247	}
248
249	assetDirStrings := assetDirs.Strings()
250	if a.noticeFile.Valid() {
251		assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
252		assetDeps = append(assetDeps, a.noticeFile.Path())
253	}
254	if len(assets) > 0 {
255		// aapt2 doesn't support adding individual asset files. Create a temp directory to hold asset
256		// files and pass it to aapt2.
257		tmpAssetDir := android.PathForModuleOut(ctx, "tmp_asset_dir")
258
259		rule := android.NewRuleBuilder(pctx, ctx)
260		rule.Command().
261			Text("rm -rf").Text(tmpAssetDir.String()).
262			Text("&&").
263			Text("mkdir -p").Text(tmpAssetDir.String())
264
265		for _, asset := range assets {
266			output := tmpAssetDir.Join(ctx, asset.Rel())
267			assetDeps = append(assetDeps, output)
268			rule.Command().Text("mkdir -p").Text(filepath.Dir(output.String()))
269			rule.Command().Text("cp").Input(asset).Output(output)
270		}
271
272		rule.Build("tmp_asset_dir", "tmp_asset_dir")
273
274		assetDirStrings = append(assetDirStrings, tmpAssetDir.String())
275	}
276
277	linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
278	linkDeps = append(linkDeps, manifestPath)
279
280	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
281	linkDeps = append(linkDeps, assetDeps...)
282
283	// Returns the effective version for {min|target}_sdk_version
284	effectiveVersionString := func(sdkVersion android.SdkSpec, minSdkVersion android.ApiLevel) string {
285		// If {min|target}_sdk_version is current, use sdk_version to determine the effective level
286		// This is necessary for vendor modules.
287		// The effective version does not _only_ depend on {min|target}_sdk_version(level),
288		// but also on the sdk_version (kind+level)
289		if minSdkVersion.IsCurrent() {
290			ret, err := sdkVersion.EffectiveVersionString(ctx)
291			if err != nil {
292				ctx.ModuleErrorf("invalid sdk_version: %s", err)
293			}
294			return ret
295		}
296		ret, err := minSdkVersion.EffectiveVersionString(ctx)
297		if err != nil {
298			ctx.ModuleErrorf("invalid min_sdk_version: %s", err)
299		}
300		return ret
301	}
302	// SDK version flags
303	sdkVersion := sdkContext.SdkVersion(ctx)
304	minSdkVersion := effectiveVersionString(sdkVersion, sdkContext.MinSdkVersion(ctx))
305
306	linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
307	// Use minSdkVersion for target-sdk-version, even if `target_sdk_version` is set
308	// This behavior has been copied from Make.
309	linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
310
311	// Version code
312	if !hasVersionCode {
313		linkFlags = append(linkFlags, "--version-code", ctx.Config().PlatformSdkVersion().String())
314	}
315
316	if !hasVersionName {
317		var versionName string
318		if ctx.ModuleName() == "framework-res" {
319			// Some builds set AppsDefaultVersionName() to include the build number ("O-123456").  aapt2 copies the
320			// version name of framework-res into app manifests as compileSdkVersionCodename, which confuses things
321			// if it contains the build number.  Use the PlatformVersionName instead.
322			versionName = ctx.Config().PlatformVersionName()
323		} else {
324			versionName = ctx.Config().AppsDefaultVersionName()
325		}
326		versionName = proptools.NinjaEscape(versionName)
327		linkFlags = append(linkFlags, "--version-name ", versionName)
328	}
329
330	linkFlags, compileFlags = android.FilterList(linkFlags, []string{"--legacy"})
331
332	// Always set --pseudo-localize, it will be stripped out later for release
333	// builds that don't want it.
334	compileFlags = append(compileFlags, "--pseudo-localize")
335
336	return compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
337}
338
339func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkDep sdkDep) {
340	if sdkDep.frameworkResModule != "" {
341		ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
342	}
343}
344
345var extractAssetsRule = pctx.AndroidStaticRule("extractAssets",
346	blueprint.RuleParams{
347		Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} "assets/**/*"`,
348		CommandDeps: []string{"${config.Zip2ZipCmd}"},
349	})
350
351type aaptBuildActionOptions struct {
352	sdkContext                     android.SdkContext
353	classLoaderContexts            dexpreopt.ClassLoaderContextMap
354	excludedLibs                   []string
355	enforceDefaultTargetSdkVersion bool
356	forceNonFinalResourceIDs       bool
357	extraLinkFlags                 []string
358	aconfigTextFiles               android.Paths
359	usesLibrary                    *usesLibrary
360}
361
362func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptions) {
363
364	staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedExportPackages, libFlags :=
365		aaptLibs(ctx, opts.sdkContext, opts.classLoaderContexts, opts.usesLibrary)
366
367	// Exclude any libraries from the supplied list.
368	opts.classLoaderContexts = opts.classLoaderContexts.ExcludeLibs(opts.excludedLibs)
369
370	// App manifest file
371	manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
372	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
373
374	manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
375		SdkContext:                     opts.sdkContext,
376		ClassLoaderContexts:            opts.classLoaderContexts,
377		IsLibrary:                      a.isLibrary,
378		DefaultManifestVersion:         a.defaultManifestVersion,
379		UseEmbeddedNativeLibs:          a.useEmbeddedNativeLibs,
380		UsesNonSdkApis:                 a.usesNonSdkApis,
381		UseEmbeddedDex:                 a.useEmbeddedDex,
382		HasNoCode:                      a.hasNoCode,
383		LoggingParent:                  a.LoggingParent,
384		EnforceDefaultTargetSdkVersion: opts.enforceDefaultTargetSdkVersion,
385	})
386
387	staticDeps := transitiveAarDeps(staticResourcesNodesDepSet.ToList())
388	sharedDeps := transitiveAarDeps(sharedResourcesNodesDepSet.ToList())
389
390	// Add additional manifest files to transitive manifests.
391	additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
392	transitiveManifestPaths := append(android.Paths{manifestPath}, additionalManifests...)
393	transitiveManifestPaths = append(transitiveManifestPaths, staticManifestsDepSet.ToList()...)
394
395	if len(transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) {
396		manifestMergerParams := ManifestMergerParams{
397			staticLibManifests: transitiveManifestPaths[1:],
398			isLibrary:          a.isLibrary,
399			packageName:        a.manifestValues.applicationId,
400		}
401		a.mergedManifestFile = manifestMerger(ctx, transitiveManifestPaths[0], manifestMergerParams)
402		if !a.isLibrary {
403			// Only use the merged manifest for applications.  For libraries, the transitive closure of manifests
404			// will be propagated to the final application and merged there.  The merged manifest for libraries is
405			// only passed to Make, which can't handle transitive dependencies.
406			manifestPath = a.mergedManifestFile
407		}
408	} else {
409		a.mergedManifestFile = manifestPath
410	}
411
412	compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, opts.sdkContext, manifestPath)
413
414	linkFlags = append(linkFlags, libFlags...)
415	linkDeps = append(linkDeps, sharedExportPackages...)
416	linkDeps = append(linkDeps, staticDeps.resPackages()...)
417	linkFlags = append(linkFlags, opts.extraLinkFlags...)
418	if a.isLibrary {
419		linkFlags = append(linkFlags, "--static-lib")
420	}
421	if opts.forceNonFinalResourceIDs {
422		linkFlags = append(linkFlags, "--non-final-ids")
423	}
424
425	linkFlags = append(linkFlags, "--no-static-lib-packages")
426	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
427		// When building an android_library using ResourceProcessorBusyBox pass --merge-only to skip resource
428		// references validation until the final app link step when all static libraries are present.
429		linkFlags = append(linkFlags, "--merge-only")
430	}
431
432	packageRes := android.PathForModuleOut(ctx, "package-res.apk")
433	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
434	rTxt := android.PathForModuleOut(ctx, "R.txt")
435	// This file isn't used by Soong, but is generated for exporting
436	extraPackages := android.PathForModuleOut(ctx, "extra_packages")
437	var transitiveRJars android.Paths
438	var srcJar android.WritablePath
439
440	var compiledResDirs []android.Paths
441	for _, dir := range resDirs {
442		a.resourceFiles = append(a.resourceFiles, dir.files...)
443		compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths())
444	}
445
446	for i, zip := range resZips {
447		flata := android.PathForModuleOut(ctx, fmt.Sprintf("reszip.%d.flata", i))
448		aapt2CompileZip(ctx, flata, zip, "", compileFlags)
449		compiledResDirs = append(compiledResDirs, android.Paths{flata})
450	}
451
452	var compiledRes, compiledOverlay android.Paths
453
454	// AAPT2 overlays are in lowest to highest priority order, reverse the topological order
455	// of transitiveStaticLibs.
456	transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages())
457
458	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
459		// When building an android_library with ResourceProcessorBusyBox enabled treat static library dependencies
460		// as imports.  The resources from dependencies will not be merged into this module's package-res.apk, and
461		// instead modules depending on this module will reference package-res.apk from all transitive static
462		// dependencies.
463		for _, sharedDep := range sharedDeps {
464			if sharedDep.usedResourceProcessor {
465				transitiveRJars = append(transitiveRJars, sharedDep.rJar)
466			}
467		}
468		for _, staticDep := range staticDeps {
469			linkDeps = append(linkDeps, staticDep.resPackage)
470			linkFlags = append(linkFlags, "-I "+staticDep.resPackage.String())
471			if staticDep.usedResourceProcessor {
472				transitiveRJars = append(transitiveRJars, staticDep.rJar)
473			}
474		}
475	} else {
476		// When building an app or building a library without ResourceProcessorBusyBox enabled all static
477		// dependencies are compiled into this module's package-res.apk as overlays.
478		compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
479	}
480
481	if len(transitiveStaticLibs) > 0 {
482		// If we are using static android libraries, every source file becomes an overlay.
483		// This is to emulate old AAPT behavior which simulated library support.
484		for _, compiledResDir := range compiledResDirs {
485			compiledOverlay = append(compiledOverlay, compiledResDir...)
486		}
487	} else if a.isLibrary {
488		// Otherwise, for a static library we treat all the resources equally with no overlay.
489		for _, compiledResDir := range compiledResDirs {
490			compiledRes = append(compiledRes, compiledResDir...)
491		}
492	} else if len(compiledResDirs) > 0 {
493		// Without static libraries, the first directory is our directory, which can then be
494		// overlaid by the rest.
495		compiledRes = append(compiledRes, compiledResDirs[0]...)
496		for _, compiledResDir := range compiledResDirs[1:] {
497			compiledOverlay = append(compiledOverlay, compiledResDir...)
498		}
499	}
500
501	for _, dir := range overlayDirs {
502		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths()...)
503	}
504
505	var splitPackages android.WritablePaths
506	var splits []split
507
508	for _, s := range a.splitNames {
509		suffix := strings.Replace(s, ",", "_", -1)
510		path := android.PathForModuleOut(ctx, "package_"+suffix+".apk")
511		linkFlags = append(linkFlags, "--split", path.String()+":"+s)
512		splitPackages = append(splitPackages, path)
513		splits = append(splits, split{
514			name:   s,
515			suffix: suffix,
516			path:   path,
517		})
518	}
519
520	if !a.useResourceProcessorBusyBox(ctx) {
521		// the subdir "android" is required to be filtered by package names
522		srcJar = android.PathForModuleGen(ctx, "android", "R.srcjar")
523	}
524
525	// No need to specify assets from dependencies to aapt2Link for libraries, all transitive assets will be
526	// provided to the final app aapt2Link step.
527	var transitiveAssets android.Paths
528	if !a.isLibrary {
529		transitiveAssets = android.ReverseSliceInPlace(staticDeps.assets())
530	}
531	aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt,
532		linkFlags, linkDeps, compiledRes, compiledOverlay, transitiveAssets, splitPackages,
533		opts.aconfigTextFiles)
534	// Extract assets from the resource package output so that they can be used later in aapt2link
535	// for modules that depend on this one.
536	if android.PrefixInList(linkFlags, "-A ") {
537		assets := android.PathForModuleOut(ctx, "assets.zip")
538		ctx.Build(pctx, android.BuildParams{
539			Rule:        extractAssetsRule,
540			Input:       packageRes,
541			Output:      assets,
542			Description: "extract assets from built resource file",
543		})
544		a.assetPackage = android.OptionalPathForPath(assets)
545	}
546
547	if a.useResourceProcessorBusyBox(ctx) {
548		rJar := android.PathForModuleOut(ctx, "busybox/R.jar")
549		resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary, a.aaptProperties.Aaptflags,
550			opts.forceNonFinalResourceIDs)
551		aapt2ExtractExtraPackages(ctx, extraPackages, rJar)
552		transitiveRJars = append(transitiveRJars, rJar)
553		a.rJar = rJar
554	} else {
555		aapt2ExtractExtraPackages(ctx, extraPackages, srcJar)
556	}
557
558	transitiveAaptResourcePackages := staticDeps.resPackages().Strings()
559	transitiveAaptResourcePackages = slices.DeleteFunc(transitiveAaptResourcePackages, func(p string) bool {
560		return p == packageRes.String()
561	})
562	transitiveAaptResourcePackagesFile := android.PathForModuleOut(ctx, "transitive-res-packages")
563	android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n"))
564
565	// Reverse the list of R.jar files so that the current module comes first, and direct dependencies come before
566	// transitive dependencies.
567	transitiveRJars = android.ReversePaths(transitiveRJars)
568
569	a.aaptSrcJar = srcJar
570	a.transitiveAaptRJars = transitiveRJars
571	a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile
572	a.exportPackage = packageRes
573	a.manifestPath = manifestPath
574	a.proguardOptionsFile = proguardOptionsFile
575	a.extraAaptPackagesFile = extraPackages
576	a.rTxt = rTxt
577	a.splits = splits
578	a.resourcesNodesDepSet = android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL).
579		Direct(&resourcesNode{
580			resPackage:          a.exportPackage,
581			manifest:            a.manifestPath,
582			additionalManifests: additionalManifests,
583			rTxt:                a.rTxt,
584			rJar:                a.rJar,
585			assets:              a.assetPackage,
586
587			usedResourceProcessor: a.useResourceProcessorBusyBox(ctx),
588		}).
589		Transitive(staticResourcesNodesDepSet).Build()
590	a.rroDirsDepSet = android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL).
591		Direct(rroDirs...).
592		Transitive(staticRRODirsDepSet).Build()
593	a.manifestsDepSet = android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).
594		Direct(a.manifestPath).
595		DirectSlice(additionalManifests).
596		Transitive(staticManifestsDepSet).Build()
597}
598
599var resourceProcessorBusyBox = pctx.AndroidStaticRule("resourceProcessorBusyBox",
600	blueprint.RuleParams{
601		Command: "${config.JavaCmd} -cp ${config.ResourceProcessorBusyBox} " +
602			"com.google.devtools.build.android.ResourceProcessorBusyBox --tool=GENERATE_BINARY_R -- @${out}.args && " +
603			"if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out}; fi",
604		CommandDeps:    []string{"${config.ResourceProcessorBusyBox}"},
605		Rspfile:        "${out}.args",
606		RspfileContent: "--primaryRTxt ${rTxt} --primaryManifest ${manifest} --classJarOutput ${out}.tmp ${args}",
607		Restat:         true,
608	}, "rTxt", "manifest", "args")
609
610// resourceProcessorBusyBoxGenerateBinaryR converts the R.txt file produced by aapt2 into R.class files
611// using Bazel's ResourceProcessorBusyBox tool, which is faster than compiling the R.java files and
612// supports producing classes for static dependencies that only include resources from that dependency.
613func resourceProcessorBusyBoxGenerateBinaryR(ctx android.ModuleContext, rTxt, manifest android.Path,
614	rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool, aaptFlags []string,
615	forceNonFinalIds bool) {
616
617	var args []string
618	var deps android.Paths
619
620	if !isLibrary {
621		// When compiling an app, pass all R.txt and AndroidManifest.xml from transitive static library dependencies
622		// to ResourceProcessorBusyBox so that it can regenerate R.class files with the final resource IDs for each
623		// package.
624		args, deps = transitiveDeps.resourceProcessorDeps()
625		if forceNonFinalIds {
626			args = append(args, "--finalFields=false")
627		}
628	} else {
629		// When compiling a library don't pass any dependencies as it only needs to generate an R.class file for this
630		// library.  Pass --finalFields=false so that the R.class file contains non-final fields so they don't get
631		// inlined into the library before the final IDs are assigned during app compilation.
632		args = append(args, "--finalFields=false")
633	}
634
635	for i, arg := range aaptFlags {
636		const AAPT_CUSTOM_PACKAGE = "--custom-package"
637		if strings.HasPrefix(arg, AAPT_CUSTOM_PACKAGE) {
638			pkg := strings.TrimSpace(strings.TrimPrefix(arg, AAPT_CUSTOM_PACKAGE))
639			if pkg == "" && i+1 < len(aaptFlags) {
640				pkg = aaptFlags[i+1]
641			}
642			args = append(args, "--packageForR "+pkg)
643		}
644	}
645
646	deps = append(deps, rTxt, manifest)
647
648	ctx.Build(pctx, android.BuildParams{
649		Rule:        resourceProcessorBusyBox,
650		Output:      rJar,
651		Implicits:   deps,
652		Description: "ResourceProcessorBusyBox",
653		Args: map[string]string{
654			"rTxt":     rTxt.String(),
655			"manifest": manifest.String(),
656			"args":     strings.Join(args, " "),
657		},
658	})
659}
660
661type resourcesNode struct {
662	resPackage          android.Path
663	manifest            android.Path
664	additionalManifests android.Paths
665	rTxt                android.Path
666	rJar                android.Path
667	assets              android.OptionalPath
668
669	usedResourceProcessor bool
670}
671
672type transitiveAarDeps []*resourcesNode
673
674func (t transitiveAarDeps) resPackages() android.Paths {
675	paths := make(android.Paths, 0, len(t))
676	for _, dep := range t {
677		paths = append(paths, dep.resPackage)
678	}
679	return paths
680}
681
682func (t transitiveAarDeps) manifests() android.Paths {
683	paths := make(android.Paths, 0, len(t))
684	for _, dep := range t {
685		paths = append(paths, dep.manifest)
686		paths = append(paths, dep.additionalManifests...)
687	}
688	return paths
689}
690
691func (t transitiveAarDeps) resourceProcessorDeps() (args []string, deps android.Paths) {
692	for _, dep := range t {
693		args = append(args, "--library="+dep.rTxt.String()+","+dep.manifest.String())
694		deps = append(deps, dep.rTxt, dep.manifest)
695	}
696	return args, deps
697}
698
699func (t transitiveAarDeps) assets() android.Paths {
700	paths := make(android.Paths, 0, len(t))
701	for _, dep := range t {
702		if dep.assets.Valid() {
703			paths = append(paths, dep.assets.Path())
704		}
705	}
706	return paths
707}
708
709// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
710func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext,
711	classLoaderContexts dexpreopt.ClassLoaderContextMap, usesLibrary *usesLibrary) (
712	staticResourcesNodes, sharedResourcesNodes *android.DepSet[*resourcesNode], staticRRODirs *android.DepSet[rroDir],
713	staticManifests *android.DepSet[android.Path], sharedLibs android.Paths, flags []string) {
714
715	if classLoaderContexts == nil {
716		// Not all callers need to compute class loader context, those who don't just pass nil.
717		// Create a temporary class loader context here (it will be computed, but not used).
718		classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
719	}
720
721	sdkDep := decodeSdkDep(ctx, sdkContext)
722	if sdkDep.useFiles {
723		sharedLibs = append(sharedLibs, sdkDep.jars...)
724	}
725
726	var staticResourcesNodeDepSets []*android.DepSet[*resourcesNode]
727	var sharedResourcesNodeDepSets []*android.DepSet[*resourcesNode]
728	rroDirsDepSetBuilder := android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL)
729	manifestsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL)
730
731	ctx.VisitDirectDeps(func(module android.Module) {
732		depTag := ctx.OtherModuleDependencyTag(module)
733
734		var exportPackage android.Path
735		aarDep, _ := module.(AndroidLibraryDependency)
736		if aarDep != nil {
737			exportPackage = aarDep.ExportPackage()
738		}
739
740		switch depTag {
741		case instrumentationForTag:
742			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
743		case sdkLibTag, libTag:
744			if exportPackage != nil {
745				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
746				sharedLibs = append(sharedLibs, exportPackage)
747			}
748		case frameworkResTag:
749			if exportPackage != nil {
750				sharedLibs = append(sharedLibs, exportPackage)
751			}
752		case staticLibTag:
753			if exportPackage != nil {
754				staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
755				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet())
756				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet())
757			}
758		}
759
760		addCLCFromDep(ctx, module, classLoaderContexts)
761		if usesLibrary != nil {
762			addMissingOptionalUsesLibsFromDep(ctx, module, usesLibrary)
763		}
764	})
765
766	// AAPT2 overlays are in lowest to highest priority order, the topological order will be reversed later.
767	// Reverse the dependency order now going into the depset so that it comes out in order after the second
768	// reverse later.
769	// NOTE: this is legacy and probably incorrect behavior, for most other cases (e.g. conflicting classes in
770	// dependencies) the highest priority dependency is listed first, but for resources the highest priority
771	// dependency has to be listed last.  This is also inconsistent with the way manifests from the same
772	// transitive dependencies are merged.
773	staticResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil,
774		android.ReverseSliceInPlace(staticResourcesNodeDepSets))
775	sharedResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil,
776		android.ReverseSliceInPlace(sharedResourcesNodeDepSets))
777
778	staticRRODirs = rroDirsDepSetBuilder.Build()
779	staticManifests = manifestsDepSetBuilder.Build()
780
781	if len(staticResourcesNodes.ToList()) > 0 {
782		flags = append(flags, "--auto-add-overlay")
783	}
784
785	for _, sharedLib := range sharedLibs {
786		flags = append(flags, "-I "+sharedLib.String())
787	}
788
789	return staticResourcesNodes, sharedResourcesNodes, staticRRODirs, staticManifests, sharedLibs, flags
790}
791
792type AndroidLibrary struct {
793	Library
794	aapt
795
796	androidLibraryProperties androidLibraryProperties
797
798	aarFile android.WritablePath
799}
800
801var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
802
803// For OutputFileProducer interface
804func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) {
805	switch tag {
806	case ".aar":
807		return []android.Path{a.aarFile}, nil
808	default:
809		return a.Library.OutputFiles(tag)
810	}
811}
812
813var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
814
815func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
816	a.usesLibrary.deps(ctx, false)
817	a.Module.deps(ctx)
818	sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
819	if sdkDep.hasFrameworkLibs() {
820		a.aapt.deps(ctx, sdkDep)
821	}
822
823	for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
824		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
825	}
826}
827
828func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
829	a.aapt.isLibrary = true
830	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
831	if a.usesLibrary.shouldDisableDexpreopt {
832		a.dexpreopter.disableDexpreopt()
833	}
834	a.aapt.buildActions(ctx,
835		aaptBuildActionOptions{
836			sdkContext:                     android.SdkContext(a),
837			classLoaderContexts:            a.classLoaderContexts,
838			enforceDefaultTargetSdkVersion: false,
839			aconfigTextFiles:               getAconfigFilePaths(ctx),
840			usesLibrary:                    &a.usesLibrary,
841		},
842	)
843
844	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
845	a.hideApexVariantFromMake = !apexInfo.IsForPlatform()
846
847	a.stem = proptools.StringDefault(a.overridableProperties.Stem, ctx.ModuleName())
848
849	ctx.CheckbuildFile(a.aapt.proguardOptionsFile)
850	ctx.CheckbuildFile(a.aapt.exportPackage)
851	if a.useResourceProcessorBusyBox(ctx) {
852		ctx.CheckbuildFile(a.aapt.rJar)
853	} else {
854		ctx.CheckbuildFile(a.aapt.aaptSrcJar)
855	}
856
857	// apps manifests are handled by aapt, don't let Module see them
858	a.properties.Manifest = nil
859
860	a.linter.mergedManifest = a.aapt.mergedManifestFile
861	a.linter.manifest = a.aapt.manifestPath
862	a.linter.resources = a.aapt.resourceFiles
863
864	proguardSpecInfo := a.collectProguardSpecInfo(ctx)
865	android.SetProvider(ctx, ProguardSpecInfoProvider, proguardSpecInfo)
866	exportedProguardFlagsFiles := proguardSpecInfo.ProguardFlagsFiles.ToList()
867	a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, exportedProguardFlagsFiles...)
868	a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, a.proguardOptionsFile)
869
870	combinedExportedProguardFlagFile := android.PathForModuleOut(ctx, "export_proguard_flags")
871	writeCombinedProguardFlagsFile(ctx, combinedExportedProguardFlagFile, exportedProguardFlagsFiles)
872	a.combinedExportedProguardFlagsFile = combinedExportedProguardFlagFile
873
874	var extraSrcJars android.Paths
875	var extraCombinedJars android.Paths
876	var extraClasspathJars android.Paths
877	if a.useResourceProcessorBusyBox(ctx) {
878		// When building a library with ResourceProcessorBusyBox enabled ResourceProcessorBusyBox for this
879		// library and each of the transitive static android_library dependencies has already created an
880		// R.class file for the appropriate package.  Add all of those R.class files to the classpath.
881		extraClasspathJars = a.transitiveAaptRJars
882	} else {
883		// When building a library without ResourceProcessorBusyBox the aapt2 rule creates R.srcjar containing
884		// R.java files for the library's package and the packages from all transitive static android_library
885		// dependencies.  Compile the srcjar alongside the rest of the sources.
886		extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
887	}
888
889	a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars)
890
891	a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
892	var res android.Paths
893	if a.androidLibraryProperties.BuildAAR {
894		BuildAAR(ctx, a.aarFile, a.outputFile, a.manifestPath, a.rTxt, res)
895		ctx.CheckbuildFile(a.aarFile)
896	}
897
898	prebuiltJniPackages := android.Paths{}
899	ctx.VisitDirectDeps(func(module android.Module) {
900		if info, ok := android.OtherModuleProvider(ctx, module, JniPackageProvider); ok {
901			prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
902		}
903	})
904	if len(prebuiltJniPackages) > 0 {
905		android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
906			JniPackages: prebuiltJniPackages,
907		})
908	}
909}
910
911func (a *AndroidLibrary) IDEInfo(dpInfo *android.IdeInfo) {
912	a.Library.IDEInfo(dpInfo)
913	a.aapt.IDEInfo(dpInfo)
914}
915
916func (a *aapt) IDEInfo(dpInfo *android.IdeInfo) {
917	if a.rJar != nil {
918		dpInfo.Jars = append(dpInfo.Jars, a.rJar.String())
919	}
920}
921
922// android_library builds and links sources into a `.jar` file for the device along with Android resources.
923//
924// An android_library has a single variant that produces a `.jar` file containing `.class` files that were
925// compiled against the device bootclasspath, along with a `package-res.apk` file containing Android resources compiled
926// with aapt2.  This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
927// an android_app module.
928func AndroidLibraryFactory() android.Module {
929	module := &AndroidLibrary{}
930
931	module.Module.addHostAndDeviceProperties()
932	module.AddProperties(
933		&module.aaptProperties,
934		&module.androidLibraryProperties,
935		&module.sourceProperties)
936
937	module.androidLibraryProperties.BuildAAR = true
938	module.Module.linter.library = true
939
940	android.InitApexModule(module)
941	InitJavaModule(module, android.DeviceSupported)
942	return module
943}
944
945//
946// AAR (android library) prebuilts
947//
948
949// Properties for android_library_import
950type AARImportProperties struct {
951	// ARR (android library prebuilt) filepath. Exactly one ARR is required.
952	Aars []string `android:"path"`
953	// If not blank, set to the version of the sdk to compile against.
954	// Defaults to private.
955	// Values are of one of the following forms:
956	// 1) numerical API level, "current", "none", or "core_platform"
957	// 2) An SDK kind with an API level: "<sdk kind>_<API level>"
958	// See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds.
959	// If the SDK kind is empty, it will be set to public
960	Sdk_version *string
961	// If not blank, set the minimum version of the sdk that the compiled artifacts will run against.
962	// Defaults to sdk_version if not set. See sdk_version for possible values.
963	Min_sdk_version *string
964	// List of java static libraries that the included ARR (android library prebuilts) has dependencies to.
965	Static_libs []string
966	// List of java libraries that the included ARR (android library prebuilts) has dependencies to.
967	Libs []string
968	// If set to true, run Jetifier against .aar file. Defaults to false.
969	Jetifier *bool
970	// If true, extract JNI libs from AAR archive. These libs will be accessible to android_app modules and
971	// will be passed transitively through android_libraries to an android_app.
972	//TODO(b/241138093) evaluate whether we can have this flag default to true for Bazel conversion
973	Extract_jni *bool
974
975	// If set, overrides the manifest extracted from the AAR with the provided path.
976	Manifest *string `android:"path"`
977}
978
979type AARImport struct {
980	android.ModuleBase
981	android.DefaultableModuleBase
982	android.ApexModuleBase
983	prebuilt android.Prebuilt
984
985	// Functionality common to Module and Import.
986	embeddableInModuleAndImport
987
988	providesTransitiveHeaderJars
989
990	properties AARImportProperties
991
992	headerJarFile                      android.WritablePath
993	implementationJarFile              android.WritablePath
994	implementationAndResourcesJarFile  android.WritablePath
995	proguardFlags                      android.WritablePath
996	exportPackage                      android.WritablePath
997	transitiveAaptResourcePackagesFile android.Path
998	extraAaptPackagesFile              android.WritablePath
999	manifest                           android.Path
1000	assetsPackage                      android.WritablePath
1001	rTxt                               android.WritablePath
1002	rJar                               android.WritablePath
1003
1004	resourcesNodesDepSet *android.DepSet[*resourcesNode]
1005	manifestsDepSet      *android.DepSet[android.Path]
1006
1007	hideApexVariantFromMake bool
1008
1009	aarPath     android.Path
1010	jniPackages android.Paths
1011
1012	sdkVersion    android.SdkSpec
1013	minSdkVersion android.ApiLevel
1014
1015	usesLibrary
1016	classLoaderContexts dexpreopt.ClassLoaderContextMap
1017}
1018
1019var _ android.OutputFileProducer = (*AARImport)(nil)
1020
1021// For OutputFileProducer interface
1022func (a *AARImport) OutputFiles(tag string) (android.Paths, error) {
1023	switch tag {
1024	case ".aar":
1025		return []android.Path{a.aarPath}, nil
1026	case "":
1027		return []android.Path{a.implementationAndResourcesJarFile}, nil
1028	default:
1029		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
1030	}
1031}
1032
1033func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
1034	return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
1035}
1036
1037func (a *AARImport) SystemModules() string {
1038	return ""
1039}
1040
1041func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
1042	if a.properties.Min_sdk_version != nil {
1043		return android.ApiLevelFrom(ctx, *a.properties.Min_sdk_version)
1044	}
1045	return a.SdkVersion(ctx).ApiLevel
1046}
1047
1048func (a *AARImport) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
1049	return android.SdkSpecFrom(ctx, "").ApiLevel
1050}
1051
1052func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
1053	return a.SdkVersion(ctx).ApiLevel
1054}
1055
1056func (a *AARImport) javaVersion() string {
1057	return ""
1058}
1059
1060var _ AndroidLibraryDependency = (*AARImport)(nil)
1061
1062func (a *AARImport) ExportPackage() android.Path {
1063	return a.exportPackage
1064}
1065func (a *AARImport) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] {
1066	return a.resourcesNodesDepSet
1067}
1068
1069func (a *AARImport) RRODirsDepSet() *android.DepSet[rroDir] {
1070	return android.NewDepSet[rroDir](android.TOPOLOGICAL, nil, nil)
1071}
1072
1073func (a *AARImport) ManifestsDepSet() *android.DepSet[android.Path] {
1074	return a.manifestsDepSet
1075}
1076
1077// RRO enforcement is not available on aar_import since its RRO dirs are not
1078// exported.
1079func (a *AARImport) SetRROEnforcedForDependent(enforce bool) {
1080}
1081
1082// RRO enforcement is not available on aar_import since its RRO dirs are not
1083// exported.
1084func (a *AARImport) IsRROEnforced(ctx android.BaseModuleContext) bool {
1085	return false
1086}
1087
1088func (a *AARImport) Prebuilt() *android.Prebuilt {
1089	return &a.prebuilt
1090}
1091
1092func (a *AARImport) Name() string {
1093	return a.prebuilt.Name(a.ModuleBase.Name())
1094}
1095
1096func (a *AARImport) JacocoReportClassesFile() android.Path {
1097	return nil
1098}
1099
1100func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
1101	if !ctx.Config().AlwaysUsePrebuiltSdks() {
1102		sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
1103		if sdkDep.useModule && sdkDep.frameworkResModule != "" {
1104			ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
1105		}
1106	}
1107
1108	ctx.AddVariationDependencies(nil, libTag, a.properties.Libs...)
1109	ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs...)
1110
1111	a.usesLibrary.deps(ctx, false)
1112}
1113
1114type JniPackageInfo struct {
1115	// List of zip files containing JNI libraries
1116	// Zip files should have directory structure jni/<arch>/*.so
1117	JniPackages android.Paths
1118}
1119
1120var JniPackageProvider = blueprint.NewProvider[JniPackageInfo]()
1121
1122// Unzip an AAR and extract the JNI libs for $archString.
1123var extractJNI = pctx.AndroidStaticRule("extractJNI",
1124	blueprint.RuleParams{
1125		Command: `rm -rf $out $outDir && touch $out && ` +
1126			`unzip -qoDD -d $outDir $in "jni/${archString}/*" && ` +
1127			`jni_files=$$(find $outDir/jni -type f) && ` +
1128			// print error message if there are no JNI libs for this arch
1129			`[ -n "$$jni_files" ] || (echo "ERROR: no JNI libs found for arch ${archString}" && exit 1) && ` +
1130			`${config.SoongZipCmd} -o $out -L 0 -P 'lib/${archString}' ` +
1131			`-C $outDir/jni/${archString} $$(echo $$jni_files | xargs -n1 printf " -f %s")`,
1132		CommandDeps: []string{"${config.SoongZipCmd}"},
1133	},
1134	"outDir", "archString")
1135
1136// Unzip an AAR into its constituent files and directories.  Any files in Outputs that don't exist in the AAR will be
1137// touched to create an empty file. The res directory is not extracted, as it will be extracted in its own rule.
1138var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
1139	blueprint.RuleParams{
1140		Command: `rm -rf $outDir && mkdir -p $outDir && ` +
1141			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` +
1142			`${config.Zip2ZipCmd} -i $in -o $assetsPackage 'assets/**/*' && ` +
1143			`${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`,
1144		CommandDeps: []string{"${config.MergeZipsCmd}", "${config.Zip2ZipCmd}"},
1145	},
1146	"outDir", "combinedClassesJar", "assetsPackage")
1147
1148func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1149	if len(a.properties.Aars) != 1 {
1150		ctx.PropertyErrorf("aars", "exactly one aar is required")
1151		return
1152	}
1153
1154	a.sdkVersion = a.SdkVersion(ctx)
1155	a.minSdkVersion = a.MinSdkVersion(ctx)
1156
1157	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
1158	a.hideApexVariantFromMake = !apexInfo.IsForPlatform()
1159
1160	aarName := ctx.ModuleName() + ".aar"
1161	a.aarPath = android.PathForModuleSrc(ctx, a.properties.Aars[0])
1162
1163	if Bool(a.properties.Jetifier) {
1164		inputFile := a.aarPath
1165		a.aarPath = android.PathForModuleOut(ctx, "jetifier", aarName)
1166		TransformJetifier(ctx, a.aarPath.(android.WritablePath), inputFile)
1167	}
1168
1169	jarName := ctx.ModuleName() + ".jar"
1170	extractedAARDir := android.PathForModuleOut(ctx, "aar")
1171	classpathFile := extractedAARDir.Join(ctx, jarName)
1172
1173	extractedManifest := extractedAARDir.Join(ctx, "AndroidManifest.xml")
1174	providedManifest := android.OptionalPathForModuleSrc(ctx, a.properties.Manifest)
1175	if providedManifest.Valid() {
1176		a.manifest = providedManifest.Path()
1177	} else {
1178		a.manifest = extractedManifest
1179	}
1180
1181	a.rTxt = extractedAARDir.Join(ctx, "R.txt")
1182	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
1183	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
1184	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
1185	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
1186		ProguardFlagsFiles: android.NewDepSet[android.Path](
1187			android.POSTORDER,
1188			android.Paths{a.proguardFlags},
1189			transitiveProguardFlags,
1190		),
1191		UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path](
1192			android.POSTORDER,
1193			nil,
1194			transitiveUnconditionalExportedFlags,
1195		),
1196	})
1197
1198	ctx.Build(pctx, android.BuildParams{
1199		Rule:        unzipAAR,
1200		Input:       a.aarPath,
1201		Outputs:     android.WritablePaths{classpathFile, a.proguardFlags, extractedManifest, a.assetsPackage, a.rTxt},
1202		Description: "unzip AAR",
1203		Args: map[string]string{
1204			"outDir":             extractedAARDir.String(),
1205			"combinedClassesJar": classpathFile.String(),
1206			"assetsPackage":      a.assetsPackage.String(),
1207		},
1208	})
1209
1210	// Always set --pseudo-localize, it will be stripped out later for release
1211	// builds that don't want it.
1212	compileFlags := []string{"--pseudo-localize"}
1213	compiledResDir := android.PathForModuleOut(ctx, "flat-res")
1214	flata := compiledResDir.Join(ctx, "gen_res.flata")
1215	aapt2CompileZip(ctx, flata, a.aarPath, "res", compileFlags)
1216
1217	a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
1218	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
1219	aaptRTxt := android.PathForModuleOut(ctx, "R.txt")
1220	a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages")
1221
1222	var linkDeps android.Paths
1223
1224	linkFlags := []string{
1225		"--static-lib",
1226		"--merge-only",
1227		"--auto-add-overlay",
1228		"--no-static-lib-packages",
1229	}
1230
1231	linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
1232	linkDeps = append(linkDeps, a.manifest)
1233
1234	staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedLibs, libFlags :=
1235		aaptLibs(ctx, android.SdkContext(a), nil, nil)
1236
1237	_ = sharedResourcesNodesDepSet
1238	_ = staticRRODirsDepSet
1239
1240	staticDeps := transitiveAarDeps(staticResourcesNodesDepSet.ToList())
1241
1242	linkDeps = append(linkDeps, sharedLibs...)
1243	linkDeps = append(linkDeps, staticDeps.resPackages()...)
1244	linkFlags = append(linkFlags, libFlags...)
1245
1246	overlayRes := android.Paths{flata}
1247
1248	// Treat static library dependencies of static libraries as imports.
1249	transitiveStaticLibs := staticDeps.resPackages()
1250	linkDeps = append(linkDeps, transitiveStaticLibs...)
1251	for _, staticLib := range transitiveStaticLibs {
1252		linkFlags = append(linkFlags, "-I "+staticLib.String())
1253	}
1254
1255	transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets())
1256	aapt2Link(ctx, a.exportPackage, nil, proguardOptionsFile, aaptRTxt,
1257		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil)
1258
1259	a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar")
1260	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil, false)
1261
1262	aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar)
1263
1264	resourcesNodesDepSetBuilder := android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL)
1265	resourcesNodesDepSetBuilder.Direct(&resourcesNode{
1266		resPackage: a.exportPackage,
1267		manifest:   a.manifest,
1268		rTxt:       a.rTxt,
1269		rJar:       a.rJar,
1270		assets:     android.OptionalPathForPath(a.assetsPackage),
1271
1272		usedResourceProcessor: true,
1273	})
1274	resourcesNodesDepSetBuilder.Transitive(staticResourcesNodesDepSet)
1275	a.resourcesNodesDepSet = resourcesNodesDepSetBuilder.Build()
1276
1277	manifestDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(a.manifest)
1278	manifestDepSetBuilder.Transitive(staticManifestsDepSet)
1279	a.manifestsDepSet = manifestDepSetBuilder.Build()
1280
1281	transitiveAaptResourcePackages := staticDeps.resPackages().Strings()
1282	transitiveAaptResourcePackages = slices.DeleteFunc(transitiveAaptResourcePackages, func(p string) bool {
1283		return p == a.exportPackage.String()
1284	})
1285	transitiveAaptResourcePackagesFile := android.PathForModuleOut(ctx, "transitive-res-packages")
1286	android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n"))
1287	a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile
1288
1289	a.collectTransitiveHeaderJars(ctx)
1290
1291	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
1292
1293	var staticJars android.Paths
1294	var staticHeaderJars android.Paths
1295	var staticResourceJars android.Paths
1296	ctx.VisitDirectDeps(func(module android.Module) {
1297		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
1298			tag := ctx.OtherModuleDependencyTag(module)
1299			switch tag {
1300			case staticLibTag:
1301				staticJars = append(staticJars, dep.ImplementationJars...)
1302				staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...)
1303				staticResourceJars = append(staticResourceJars, dep.ResourceJars...)
1304			}
1305		}
1306		addCLCFromDep(ctx, module, a.classLoaderContexts)
1307		addMissingOptionalUsesLibsFromDep(ctx, module, &a.usesLibrary)
1308	})
1309
1310	var implementationJarFile android.OutputPath
1311	if len(staticJars) > 0 {
1312		combineJars := append(android.Paths{classpathFile}, staticJars...)
1313		implementationJarFile = android.PathForModuleOut(ctx, "combined", jarName).OutputPath
1314		TransformJarsToJar(ctx, implementationJarFile, "combine", combineJars, android.OptionalPath{}, false, nil, nil)
1315	} else {
1316		implementationJarFile = classpathFile
1317	}
1318
1319	var resourceJarFile android.Path
1320	if len(staticResourceJars) > 1 {
1321		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
1322		TransformJarsToJar(ctx, combinedJar, "for resources", staticResourceJars, android.OptionalPath{},
1323			false, nil, nil)
1324		resourceJarFile = combinedJar
1325	} else if len(staticResourceJars) == 1 {
1326		resourceJarFile = staticResourceJars[0]
1327	}
1328
1329	// merge implementation jar with resources if necessary
1330	implementationAndResourcesJar := implementationJarFile
1331	if resourceJarFile != nil {
1332		jars := android.Paths{resourceJarFile, implementationAndResourcesJar}
1333		combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
1334		TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{},
1335			false, nil, nil)
1336		implementationAndResourcesJar = combinedJar
1337	}
1338
1339	a.implementationJarFile = implementationJarFile
1340	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
1341	a.implementationAndResourcesJarFile = implementationAndResourcesJar.WithoutRel()
1342
1343	if len(staticHeaderJars) > 0 {
1344		combineJars := append(android.Paths{classpathFile}, staticHeaderJars...)
1345		a.headerJarFile = android.PathForModuleOut(ctx, "turbine-combined", jarName)
1346		TransformJarsToJar(ctx, a.headerJarFile, "combine header jars", combineJars, android.OptionalPath{}, false, nil, nil)
1347	} else {
1348		a.headerJarFile = classpathFile
1349	}
1350
1351	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
1352		HeaderJars:                     android.PathsIfNonNil(a.headerJarFile),
1353		ResourceJars:                   android.PathsIfNonNil(resourceJarFile),
1354		TransitiveLibsHeaderJars:       a.transitiveLibsHeaderJars,
1355		TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
1356		ImplementationAndResourcesJars: android.PathsIfNonNil(a.implementationAndResourcesJarFile),
1357		ImplementationJars:             android.PathsIfNonNil(a.implementationJarFile),
1358		StubsLinkType:                  Implementation,
1359		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
1360	})
1361
1362	if proptools.Bool(a.properties.Extract_jni) {
1363		for _, t := range ctx.MultiTargets() {
1364			arch := t.Arch.Abi[0]
1365			path := android.PathForModuleOut(ctx, arch+"_jni.zip")
1366			a.jniPackages = append(a.jniPackages, path)
1367
1368			outDir := android.PathForModuleOut(ctx, "aarForJni")
1369			aarPath := android.PathForModuleSrc(ctx, a.properties.Aars[0])
1370			ctx.Build(pctx, android.BuildParams{
1371				Rule:        extractJNI,
1372				Input:       aarPath,
1373				Outputs:     android.WritablePaths{path},
1374				Description: "extract JNI from AAR",
1375				Args: map[string]string{
1376					"outDir":     outDir.String(),
1377					"archString": arch,
1378				},
1379			})
1380		}
1381	}
1382
1383	android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
1384		JniPackages: a.jniPackages,
1385	})
1386}
1387
1388func (a *AARImport) HeaderJars() android.Paths {
1389	return android.Paths{a.headerJarFile}
1390}
1391
1392func (a *AARImport) ImplementationAndResourcesJars() android.Paths {
1393	return android.Paths{a.implementationAndResourcesJarFile}
1394}
1395
1396func (a *AARImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
1397	return OptionalDexJarPath{}
1398}
1399
1400func (a *AARImport) DexJarInstallPath() android.Path {
1401	return nil
1402}
1403
1404func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
1405	return a.classLoaderContexts
1406}
1407
1408var _ UsesLibraryDependency = (*AARImport)(nil)
1409
1410var _ android.ApexModule = (*AARImport)(nil)
1411
1412// Implements android.ApexModule
1413func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
1414	return a.depIsInSameApex(ctx, dep)
1415}
1416
1417// Implements android.ApexModule
1418func (a *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
1419	sdkVersion android.ApiLevel) error {
1420	return nil
1421}
1422
1423var _ android.PrebuiltInterface = (*AARImport)(nil)
1424
1425func (a *AARImport) UsesLibrary() *usesLibrary {
1426	return &a.usesLibrary
1427}
1428
1429var _ ModuleWithUsesLibrary = (*AARImport)(nil)
1430
1431// android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
1432//
1433// This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
1434// an android_app module.
1435func AARImportFactory() android.Module {
1436	module := &AARImport{}
1437
1438	module.AddProperties(
1439		&module.properties,
1440		&module.usesLibrary.usesLibraryProperties,
1441	)
1442
1443	android.InitPrebuiltModule(module, &module.properties.Aars)
1444	android.InitApexModule(module)
1445	InitJavaModuleMultiTargets(module, android.DeviceSupported)
1446	return module
1447}
1448