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	"strconv"
21	"strings"
22
23	"android/soong/android"
24	"android/soong/dexpreopt"
25
26	"github.com/google/blueprint"
27	"github.com/google/blueprint/proptools"
28)
29
30type AndroidLibraryDependency interface {
31	ExportPackage() android.Path
32	ExportedProguardFlagFiles() android.Paths
33	ExportedRRODirs() []rroDir
34	ExportedStaticPackages() android.Paths
35	ExportedManifests() android.Paths
36	ExportedAssets() android.OptionalPath
37	SetRROEnforcedForDependent(enforce bool)
38	IsRROEnforced(ctx android.BaseModuleContext) bool
39}
40
41func init() {
42	RegisterAARBuildComponents(android.InitRegistrationContext)
43}
44
45func RegisterAARBuildComponents(ctx android.RegistrationContext) {
46	ctx.RegisterModuleType("android_library_import", AARImportFactory)
47	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
48	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
49		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
50	})
51}
52
53//
54// AAR (android library)
55//
56
57type androidLibraryProperties struct {
58	BuildAAR bool `blueprint:"mutated"`
59}
60
61type aaptProperties struct {
62	// flags passed to aapt when creating the apk
63	Aaptflags []string
64
65	// include all resource configurations, not just the product-configured
66	// ones.
67	Aapt_include_all_resources *bool
68
69	// list of directories relative to the Blueprints file containing assets.
70	// Defaults to ["assets"] if a directory called assets exists.  Set to []
71	// to disable the default.
72	Asset_dirs []string
73
74	// list of directories relative to the Blueprints file containing
75	// Android resources.  Defaults to ["res"] if a directory called res exists.
76	// Set to [] to disable the default.
77	Resource_dirs []string
78
79	// list of zip files containing Android resources.
80	Resource_zips []string `android:"path"`
81
82	// path to AndroidManifest.xml.  If unset, defaults to "AndroidManifest.xml".
83	Manifest *string `android:"path"`
84
85	// paths to additional manifest files to merge with main manifest.
86	Additional_manifests []string `android:"path"`
87
88	// do not include AndroidManifest from dependent libraries
89	Dont_merge_manifests *bool
90
91	// true if RRO is enforced for any of the dependent modules
92	RROEnforcedForDependent bool `blueprint:"mutated"`
93}
94
95type aapt struct {
96	aaptSrcJar              android.Path
97	exportPackage           android.Path
98	manifestPath            android.Path
99	transitiveManifestPaths android.Paths
100	proguardOptionsFile     android.Path
101	rroDirs                 []rroDir
102	rTxt                    android.Path
103	extraAaptPackagesFile   android.Path
104	mergedManifestFile      android.Path
105	noticeFile              android.OptionalPath
106	assetPackage            android.OptionalPath
107	isLibrary               bool
108	useEmbeddedNativeLibs   bool
109	useEmbeddedDex          bool
110	usesNonSdkApis          bool
111	hasNoCode               bool
112	LoggingParent           string
113	resourceFiles           android.Paths
114
115	splitNames []string
116	splits     []split
117
118	aaptProperties aaptProperties
119}
120
121type split struct {
122	name   string
123	suffix string
124	path   android.Path
125}
126
127// Propagate RRO enforcement flag to static lib dependencies transitively.
128func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) {
129	m := ctx.Module()
130	if d, ok := m.(AndroidLibraryDependency); ok && d.IsRROEnforced(ctx) {
131		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
132			if a, ok := d.(AndroidLibraryDependency); ok {
133				a.SetRROEnforcedForDependent(true)
134			}
135		})
136	}
137}
138
139func (a *aapt) ExportPackage() android.Path {
140	return a.exportPackage
141}
142
143func (a *aapt) ExportedRRODirs() []rroDir {
144	return a.rroDirs
145}
146
147func (a *aapt) ExportedManifests() android.Paths {
148	return a.transitiveManifestPaths
149}
150
151func (a *aapt) ExportedAssets() android.OptionalPath {
152	return a.assetPackage
153}
154
155func (a *aapt) SetRROEnforcedForDependent(enforce bool) {
156	a.aaptProperties.RROEnforcedForDependent = enforce
157}
158
159func (a *aapt) IsRROEnforced(ctx android.BaseModuleContext) bool {
160	// True if RRO is enforced for this module or...
161	return ctx.Config().EnforceRROForModule(ctx.ModuleName()) ||
162		// if RRO is enforced for any of its dependents.
163		a.aaptProperties.RROEnforcedForDependent
164}
165
166func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkContext,
167	manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths,
168	resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
169
170	hasVersionCode := android.PrefixInList(a.aaptProperties.Aaptflags, "--version-code")
171	hasVersionName := android.PrefixInList(a.aaptProperties.Aaptflags, "--version-name")
172
173	// Flags specified in Android.bp
174	linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...)
175
176	linkFlags = append(linkFlags, "--no-static-lib-packages")
177
178	// Find implicit or explicit asset and resource dirs
179	assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
180	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
181	resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
182
183	// Glob directories into lists of paths
184	for _, dir := range resourceDirs {
185		resDirs = append(resDirs, globbedResourceDir{
186			dir:   dir,
187			files: androidResourceGlob(ctx, dir),
188		})
189		resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, a, dir)
190		overlayDirs = append(overlayDirs, resOverlayDirs...)
191		rroDirs = append(rroDirs, resRRODirs...)
192	}
193
194	var assetDeps android.Paths
195	for i, dir := range assetDirs {
196		// Add a dependency on every file in the asset directory.  This ensures the aapt2
197		// rule will be rerun if one of the files in the asset directory is modified.
198		assetDeps = append(assetDeps, androidResourceGlob(ctx, dir)...)
199
200		// Add a dependency on a file that contains a list of all the files in the asset directory.
201		// This ensures the aapt2 rule will be run if a file is removed from the asset directory,
202		// or a file is added whose timestamp is older than the output of aapt2.
203		assetFileListFile := android.PathForModuleOut(ctx, "asset_dir_globs", strconv.Itoa(i)+".glob")
204		androidResourceGlobList(ctx, dir, assetFileListFile)
205		assetDeps = append(assetDeps, assetFileListFile)
206	}
207
208	assetDirStrings := assetDirs.Strings()
209	if a.noticeFile.Valid() {
210		assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
211		assetDeps = append(assetDeps, a.noticeFile.Path())
212	}
213
214	linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
215	linkDeps = append(linkDeps, manifestPath)
216
217	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
218	linkDeps = append(linkDeps, assetDeps...)
219
220	// SDK version flags
221	minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
222	if err != nil {
223		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
224	}
225
226	linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
227	linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
228
229	// Version code
230	if !hasVersionCode {
231		linkFlags = append(linkFlags, "--version-code", ctx.Config().PlatformSdkVersion().String())
232	}
233
234	if !hasVersionName {
235		var versionName string
236		if ctx.ModuleName() == "framework-res" {
237			// Some builds set AppsDefaultVersionName() to include the build number ("O-123456").  aapt2 copies the
238			// version name of framework-res into app manifests as compileSdkVersionCodename, which confuses things
239			// if it contains the build number.  Use the PlatformVersionName instead.
240			versionName = ctx.Config().PlatformVersionName()
241		} else {
242			versionName = ctx.Config().AppsDefaultVersionName()
243		}
244		versionName = proptools.NinjaEscape(versionName)
245		linkFlags = append(linkFlags, "--version-name ", versionName)
246	}
247
248	linkFlags, compileFlags = android.FilterList(linkFlags, []string{"--legacy"})
249
250	// Always set --pseudo-localize, it will be stripped out later for release
251	// builds that don't want it.
252	compileFlags = append(compileFlags, "--pseudo-localize")
253
254	return compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
255}
256
257func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkDep sdkDep) {
258	if sdkDep.frameworkResModule != "" {
259		ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
260	}
261}
262
263var extractAssetsRule = pctx.AndroidStaticRule("extractAssets",
264	blueprint.RuleParams{
265		Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} "assets/**/*"`,
266		CommandDeps: []string{"${config.Zip2ZipCmd}"},
267	})
268
269func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext,
270	classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
271
272	transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
273		aaptLibs(ctx, sdkContext, classLoaderContexts)
274
275	// App manifest file
276	manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
277	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
278
279	manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts,
280		a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode,
281		a.LoggingParent)
282
283	// Add additional manifest files to transitive manifests.
284	additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
285	a.transitiveManifestPaths = append(android.Paths{manifestPath}, additionalManifests...)
286	a.transitiveManifestPaths = append(a.transitiveManifestPaths, transitiveStaticLibManifests...)
287
288	if len(a.transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) {
289		a.mergedManifestFile = manifestMerger(ctx, a.transitiveManifestPaths[0], a.transitiveManifestPaths[1:], a.isLibrary)
290		if !a.isLibrary {
291			// Only use the merged manifest for applications.  For libraries, the transitive closure of manifests
292			// will be propagated to the final application and merged there.  The merged manifest for libraries is
293			// only passed to Make, which can't handle transitive dependencies.
294			manifestPath = a.mergedManifestFile
295		}
296	} else {
297		a.mergedManifestFile = manifestPath
298	}
299
300	compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath)
301
302	rroDirs = append(rroDirs, staticRRODirs...)
303	linkFlags = append(linkFlags, libFlags...)
304	linkDeps = append(linkDeps, libDeps...)
305	linkFlags = append(linkFlags, extraLinkFlags...)
306	if a.isLibrary {
307		linkFlags = append(linkFlags, "--static-lib")
308	}
309
310	packageRes := android.PathForModuleOut(ctx, "package-res.apk")
311	// the subdir "android" is required to be filtered by package names
312	srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar")
313	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
314	rTxt := android.PathForModuleOut(ctx, "R.txt")
315	// This file isn't used by Soong, but is generated for exporting
316	extraPackages := android.PathForModuleOut(ctx, "extra_packages")
317
318	var compiledResDirs []android.Paths
319	for _, dir := range resDirs {
320		a.resourceFiles = append(a.resourceFiles, dir.files...)
321		compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths())
322	}
323
324	for i, zip := range resZips {
325		flata := android.PathForModuleOut(ctx, fmt.Sprintf("reszip.%d.flata", i))
326		aapt2CompileZip(ctx, flata, zip, "", compileFlags)
327		compiledResDirs = append(compiledResDirs, android.Paths{flata})
328	}
329
330	var compiledRes, compiledOverlay android.Paths
331
332	compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
333
334	if len(transitiveStaticLibs) > 0 {
335		// If we are using static android libraries, every source file becomes an overlay.
336		// This is to emulate old AAPT behavior which simulated library support.
337		for _, compiledResDir := range compiledResDirs {
338			compiledOverlay = append(compiledOverlay, compiledResDir...)
339		}
340	} else if a.isLibrary {
341		// Otherwise, for a static library we treat all the resources equally with no overlay.
342		for _, compiledResDir := range compiledResDirs {
343			compiledRes = append(compiledRes, compiledResDir...)
344		}
345	} else if len(compiledResDirs) > 0 {
346		// Without static libraries, the first directory is our directory, which can then be
347		// overlaid by the rest.
348		compiledRes = append(compiledRes, compiledResDirs[0]...)
349		for _, compiledResDir := range compiledResDirs[1:] {
350			compiledOverlay = append(compiledOverlay, compiledResDir...)
351		}
352	}
353
354	for _, dir := range overlayDirs {
355		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths()...)
356	}
357
358	var splitPackages android.WritablePaths
359	var splits []split
360
361	for _, s := range a.splitNames {
362		suffix := strings.Replace(s, ",", "_", -1)
363		path := android.PathForModuleOut(ctx, "package_"+suffix+".apk")
364		linkFlags = append(linkFlags, "--split", path.String()+":"+s)
365		splitPackages = append(splitPackages, path)
366		splits = append(splits, split{
367			name:   s,
368			suffix: suffix,
369			path:   path,
370		})
371	}
372
373	aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, extraPackages,
374		linkFlags, linkDeps, compiledRes, compiledOverlay, assetPackages, splitPackages)
375
376	// Extract assets from the resource package output so that they can be used later in aapt2link
377	// for modules that depend on this one.
378	if android.PrefixInList(linkFlags, "-A ") || len(assetPackages) > 0 {
379		assets := android.PathForModuleOut(ctx, "assets.zip")
380		ctx.Build(pctx, android.BuildParams{
381			Rule:        extractAssetsRule,
382			Input:       packageRes,
383			Output:      assets,
384			Description: "extract assets from built resource file",
385		})
386		a.assetPackage = android.OptionalPathForPath(assets)
387	}
388
389	a.aaptSrcJar = srcJar
390	a.exportPackage = packageRes
391	a.manifestPath = manifestPath
392	a.proguardOptionsFile = proguardOptionsFile
393	a.rroDirs = rroDirs
394	a.extraAaptPackagesFile = extraPackages
395	a.rTxt = rTxt
396	a.splits = splits
397}
398
399// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
400func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
401	transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, assets, deps android.Paths, flags []string) {
402
403	var sharedLibs android.Paths
404
405	if classLoaderContexts == nil {
406		// Not all callers need to compute class loader context, those who don't just pass nil.
407		// Create a temporary class loader context here (it will be computed, but not used).
408		classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
409	}
410
411	sdkDep := decodeSdkDep(ctx, sdkContext)
412	if sdkDep.useFiles {
413		sharedLibs = append(sharedLibs, sdkDep.jars...)
414	}
415
416	ctx.VisitDirectDeps(func(module android.Module) {
417		depTag := ctx.OtherModuleDependencyTag(module)
418
419		var exportPackage android.Path
420		aarDep, _ := module.(AndroidLibraryDependency)
421		if aarDep != nil {
422			exportPackage = aarDep.ExportPackage()
423		}
424
425		switch depTag {
426		case instrumentationForTag:
427			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
428		case libTag:
429			if exportPackage != nil {
430				sharedLibs = append(sharedLibs, exportPackage)
431			}
432		case frameworkResTag:
433			if exportPackage != nil {
434				sharedLibs = append(sharedLibs, exportPackage)
435			}
436		case staticLibTag:
437			if exportPackage != nil {
438				transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
439				transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
440				transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...)
441				if aarDep.ExportedAssets().Valid() {
442					assets = append(assets, aarDep.ExportedAssets().Path())
443				}
444
445			outer:
446				for _, d := range aarDep.ExportedRRODirs() {
447					for _, e := range staticRRODirs {
448						if d.path == e.path {
449							continue outer
450						}
451					}
452					staticRRODirs = append(staticRRODirs, d)
453				}
454			}
455		}
456
457		addCLCFromDep(ctx, module, classLoaderContexts)
458	})
459
460	deps = append(deps, sharedLibs...)
461	deps = append(deps, transitiveStaticLibs...)
462
463	if len(transitiveStaticLibs) > 0 {
464		flags = append(flags, "--auto-add-overlay")
465	}
466
467	for _, sharedLib := range sharedLibs {
468		flags = append(flags, "-I "+sharedLib.String())
469	}
470
471	transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs)
472	transitiveStaticLibManifests = android.FirstUniquePaths(transitiveStaticLibManifests)
473
474	return transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assets, deps, flags
475}
476
477type AndroidLibrary struct {
478	Library
479	aapt
480
481	androidLibraryProperties androidLibraryProperties
482
483	aarFile android.WritablePath
484
485	exportedProguardFlagFiles android.Paths
486	exportedStaticPackages    android.Paths
487}
488
489var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
490
491// For OutputFileProducer interface
492func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) {
493	switch tag {
494	case ".aar":
495		return []android.Path{a.aarFile}, nil
496	default:
497		return a.Library.OutputFiles(tag)
498	}
499}
500
501func (a *AndroidLibrary) ExportedProguardFlagFiles() android.Paths {
502	return a.exportedProguardFlagFiles
503}
504
505func (a *AndroidLibrary) ExportedStaticPackages() android.Paths {
506	return a.exportedStaticPackages
507}
508
509var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
510
511func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
512	a.Module.deps(ctx)
513	sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
514	if sdkDep.hasFrameworkLibs() {
515		a.aapt.deps(ctx, sdkDep)
516	}
517}
518
519func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
520	a.aapt.isLibrary = true
521	a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
522	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
523
524	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
525
526	ctx.CheckbuildFile(a.proguardOptionsFile)
527	ctx.CheckbuildFile(a.exportPackage)
528	ctx.CheckbuildFile(a.aaptSrcJar)
529
530	// apps manifests are handled by aapt, don't let Module see them
531	a.properties.Manifest = nil
532
533	a.linter.mergedManifest = a.aapt.mergedManifestFile
534	a.linter.manifest = a.aapt.manifestPath
535	a.linter.resources = a.aapt.resourceFiles
536
537	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles,
538		a.proguardOptionsFile)
539
540	a.Module.compile(ctx, a.aaptSrcJar)
541
542	a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
543	var res android.Paths
544	if a.androidLibraryProperties.BuildAAR {
545		BuildAAR(ctx, a.aarFile, a.outputFile, a.manifestPath, a.rTxt, res)
546		ctx.CheckbuildFile(a.aarFile)
547	}
548
549	a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles,
550		android.PathsForModuleSrc(ctx, a.dexProperties.Optimize.Proguard_flags_files)...)
551	ctx.VisitDirectDeps(func(m android.Module) {
552		if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
553			a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
554			a.exportedStaticPackages = append(a.exportedStaticPackages, lib.ExportPackage())
555			a.exportedStaticPackages = append(a.exportedStaticPackages, lib.ExportedStaticPackages()...)
556		}
557	})
558
559	a.exportedProguardFlagFiles = android.FirstUniquePaths(a.exportedProguardFlagFiles)
560	a.exportedStaticPackages = android.FirstUniquePaths(a.exportedStaticPackages)
561}
562
563// android_library builds and links sources into a `.jar` file for the device along with Android resources.
564//
565// An android_library has a single variant that produces a `.jar` file containing `.class` files that were
566// compiled against the device bootclasspath, along with a `package-res.apk` file containing  Android resources compiled
567// with aapt2.  This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
568// an android_app module.
569func AndroidLibraryFactory() android.Module {
570	module := &AndroidLibrary{}
571
572	module.Module.addHostAndDeviceProperties()
573	module.AddProperties(
574		&module.aaptProperties,
575		&module.androidLibraryProperties)
576
577	module.androidLibraryProperties.BuildAAR = true
578	module.Module.linter.library = true
579
580	android.InitApexModule(module)
581	InitJavaModule(module, android.DeviceSupported)
582	return module
583}
584
585//
586// AAR (android library) prebuilts
587//
588
589type AARImportProperties struct {
590	Aars []string `android:"path"`
591
592	Sdk_version     *string
593	Min_sdk_version *string
594
595	Static_libs []string
596	Libs        []string
597
598	// if set to true, run Jetifier against .aar file. Defaults to false.
599	Jetifier *bool
600}
601
602type AARImport struct {
603	android.ModuleBase
604	android.DefaultableModuleBase
605	android.ApexModuleBase
606	prebuilt android.Prebuilt
607
608	// Functionality common to Module and Import.
609	embeddableInModuleAndImport
610
611	properties AARImportProperties
612
613	classpathFile         android.WritablePath
614	proguardFlags         android.WritablePath
615	exportPackage         android.WritablePath
616	extraAaptPackagesFile android.WritablePath
617	manifest              android.WritablePath
618
619	exportedStaticPackages android.Paths
620
621	hideApexVariantFromMake bool
622
623	aarPath android.Path
624
625	sdkVersion    android.SdkSpec
626	minSdkVersion android.SdkSpec
627}
628
629var _ android.OutputFileProducer = (*AARImport)(nil)
630
631// For OutputFileProducer interface
632func (a *AARImport) OutputFiles(tag string) (android.Paths, error) {
633	switch tag {
634	case ".aar":
635		return []android.Path{a.aarPath}, nil
636	case "":
637		return []android.Path{a.classpathFile}, nil
638	default:
639		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
640	}
641}
642
643func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
644	return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
645}
646
647func (a *AARImport) SystemModules() string {
648	return ""
649}
650
651func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
652	if a.properties.Min_sdk_version != nil {
653		return android.SdkSpecFrom(ctx, *a.properties.Min_sdk_version)
654	}
655	return a.SdkVersion(ctx)
656}
657
658func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
659	return a.SdkVersion(ctx)
660}
661
662func (a *AARImport) javaVersion() string {
663	return ""
664}
665
666var _ AndroidLibraryDependency = (*AARImport)(nil)
667
668func (a *AARImport) ExportPackage() android.Path {
669	return a.exportPackage
670}
671
672func (a *AARImport) ExportedProguardFlagFiles() android.Paths {
673	return android.Paths{a.proguardFlags}
674}
675
676func (a *AARImport) ExportedRRODirs() []rroDir {
677	return nil
678}
679
680func (a *AARImport) ExportedStaticPackages() android.Paths {
681	return a.exportedStaticPackages
682}
683
684func (a *AARImport) ExportedManifests() android.Paths {
685	return android.Paths{a.manifest}
686}
687
688// TODO(jungjw): Decide whether we want to implement this.
689func (a *AARImport) ExportedAssets() android.OptionalPath {
690	return android.OptionalPath{}
691}
692
693// RRO enforcement is not available on aar_import since its RRO dirs are not
694// exported.
695func (a *AARImport) SetRROEnforcedForDependent(enforce bool) {
696}
697
698// RRO enforcement is not available on aar_import since its RRO dirs are not
699// exported.
700func (a *AARImport) IsRROEnforced(ctx android.BaseModuleContext) bool {
701	return false
702}
703
704func (a *AARImport) Prebuilt() *android.Prebuilt {
705	return &a.prebuilt
706}
707
708func (a *AARImport) Name() string {
709	return a.prebuilt.Name(a.ModuleBase.Name())
710}
711
712func (a *AARImport) JacocoReportClassesFile() android.Path {
713	return nil
714}
715
716func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
717	if !ctx.Config().AlwaysUsePrebuiltSdks() {
718		sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
719		if sdkDep.useModule && sdkDep.frameworkResModule != "" {
720			ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
721		}
722	}
723
724	ctx.AddVariationDependencies(nil, libTag, a.properties.Libs...)
725	ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs...)
726}
727
728// Unzip an AAR into its constituent files and directories.  Any files in Outputs that don't exist in the AAR will be
729// touched to create an empty file. The res directory is not extracted, as it will be extracted in its own rule.
730var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
731	blueprint.RuleParams{
732		Command: `rm -rf $outDir && mkdir -p $outDir && ` +
733			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` +
734			`${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`,
735		CommandDeps: []string{"${config.MergeZipsCmd}"},
736	},
737	"outDir", "combinedClassesJar")
738
739func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
740	if len(a.properties.Aars) != 1 {
741		ctx.PropertyErrorf("aars", "exactly one aar is required")
742		return
743	}
744
745	a.sdkVersion = a.SdkVersion(ctx)
746	a.minSdkVersion = a.MinSdkVersion(ctx)
747
748	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
749
750	aarName := ctx.ModuleName() + ".aar"
751	a.aarPath = android.PathForModuleSrc(ctx, a.properties.Aars[0])
752
753	if Bool(a.properties.Jetifier) {
754		inputFile := a.aarPath
755		a.aarPath = android.PathForModuleOut(ctx, "jetifier", aarName)
756		TransformJetifier(ctx, a.aarPath.(android.WritablePath), inputFile)
757	}
758
759	extractedAARDir := android.PathForModuleOut(ctx, "aar")
760	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
761	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
762	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
763
764	ctx.Build(pctx, android.BuildParams{
765		Rule:        unzipAAR,
766		Input:       a.aarPath,
767		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
768		Description: "unzip AAR",
769		Args: map[string]string{
770			"outDir":             extractedAARDir.String(),
771			"combinedClassesJar": a.classpathFile.String(),
772		},
773	})
774
775	// Always set --pseudo-localize, it will be stripped out later for release
776	// builds that don't want it.
777	compileFlags := []string{"--pseudo-localize"}
778	compiledResDir := android.PathForModuleOut(ctx, "flat-res")
779	flata := compiledResDir.Join(ctx, "gen_res.flata")
780	aapt2CompileZip(ctx, flata, a.aarPath, "res", compileFlags)
781
782	a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
783	// the subdir "android" is required to be filtered by package names
784	srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar")
785	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
786	rTxt := android.PathForModuleOut(ctx, "R.txt")
787	a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages")
788
789	var linkDeps android.Paths
790
791	linkFlags := []string{
792		"--static-lib",
793		"--no-static-lib-packages",
794		"--auto-add-overlay",
795	}
796
797	linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
798	linkDeps = append(linkDeps, a.manifest)
799
800	transitiveStaticLibs, staticLibManifests, staticRRODirs, transitiveAssets, libDeps, libFlags :=
801		aaptLibs(ctx, android.SdkContext(a), nil)
802
803	_ = staticLibManifests
804	_ = staticRRODirs
805
806	linkDeps = append(linkDeps, libDeps...)
807	linkFlags = append(linkFlags, libFlags...)
808
809	overlayRes := append(android.Paths{flata}, transitiveStaticLibs...)
810
811	aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
812		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil)
813
814	ctx.SetProvider(JavaInfoProvider, JavaInfo{
815		HeaderJars:                     android.PathsIfNonNil(a.classpathFile),
816		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
817		ImplementationJars:             android.PathsIfNonNil(a.classpathFile),
818	})
819}
820
821func (a *AARImport) HeaderJars() android.Paths {
822	return android.Paths{a.classpathFile}
823}
824
825func (a *AARImport) ImplementationAndResourcesJars() android.Paths {
826	return android.Paths{a.classpathFile}
827}
828
829func (a *AARImport) DexJarBuildPath() android.Path {
830	return nil
831}
832
833func (a *AARImport) DexJarInstallPath() android.Path {
834	return nil
835}
836
837func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
838	return nil
839}
840
841var _ android.ApexModule = (*AARImport)(nil)
842
843// Implements android.ApexModule
844func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
845	return a.depIsInSameApex(ctx, dep)
846}
847
848// Implements android.ApexModule
849func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
850	sdkVersion android.ApiLevel) error {
851	return nil
852}
853
854var _ android.PrebuiltInterface = (*Import)(nil)
855
856// android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
857//
858// This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
859// an android_app module.
860func AARImportFactory() android.Module {
861	module := &AARImport{}
862
863	module.AddProperties(&module.properties)
864
865	android.InitPrebuiltModule(module, &module.properties.Aars)
866	android.InitApexModule(module)
867	InitJavaModule(module, android.DeviceSupported)
868	return module
869}
870