xref: /aosp_15_r20/build/soong/java/app_import.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2020 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
17// This file contains the module implementations for android_app_import and android_test_import.
18
19import (
20	"reflect"
21	"strings"
22
23	"github.com/google/blueprint"
24
25	"github.com/google/blueprint/proptools"
26
27	"android/soong/android"
28	"android/soong/provenance"
29)
30
31func init() {
32	RegisterAppImportBuildComponents(android.InitRegistrationContext)
33
34	initAndroidAppImportVariantGroupTypes()
35}
36
37var (
38	uncompressEmbeddedJniLibsRule = pctx.AndroidStaticRule("uncompress-embedded-jni-libs", blueprint.RuleParams{
39		Command: `if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
40			`${config.Zip2ZipCmd} -i $in -o $out -0 'lib/**/*.so'` +
41			`; else cp -f $in $out; fi`,
42		CommandDeps: []string{"${config.Zip2ZipCmd}"},
43		Description: "Uncompress embedded JNI libs",
44	})
45
46	uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
47		Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
48			`${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
49			`; else cp -f $in $out; fi`,
50		CommandDeps: []string{"${config.Zip2ZipCmd}"},
51		Description: "Uncompress dex files",
52	})
53
54	checkPresignedApkRule = pctx.AndroidStaticRule("check-presigned-apk", blueprint.RuleParams{
55		Command:     "build/soong/scripts/check_prebuilt_presigned_apk.py --aapt2 ${config.Aapt2Cmd} --zipalign ${config.ZipAlign} $extraArgs $in $out",
56		CommandDeps: []string{"build/soong/scripts/check_prebuilt_presigned_apk.py", "${config.Aapt2Cmd}", "${config.ZipAlign}"},
57		Description: "Check presigned apk",
58	}, "extraArgs")
59)
60
61func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
62	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
63	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
64	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
65		ctx.BottomUp("disable_prebuilts_without_apk", disablePrebuiltsWithoutApkMutator)
66	})
67}
68
69type AndroidAppImport struct {
70	android.ModuleBase
71	android.DefaultableModuleBase
72	android.ApexModuleBase
73	prebuilt android.Prebuilt
74
75	properties       AndroidAppImportProperties
76	dpiVariants      interface{}
77	archVariants     interface{}
78	arch_dpiVariants interface{}
79
80	outputFile  android.Path
81	certificate Certificate
82
83	dexpreopter
84
85	usesLibrary usesLibrary
86
87	installPath android.InstallPath
88
89	hideApexVariantFromMake bool
90
91	provenanceMetaDataFile android.Path
92}
93
94type AndroidAppImportProperties struct {
95	// A prebuilt apk to import
96	Apk proptools.Configurable[string] `android:"path,replace_instead_of_append"`
97
98	// The name of a certificate in the default certificate directory or an android_app_certificate
99	// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
100	Certificate proptools.Configurable[string] `android:"replace_instead_of_append"`
101
102	// Names of extra android_app_certificate modules to sign the apk with in the form ":module".
103	Additional_certificates []string
104
105	// Set this flag to true if the prebuilt apk is already signed. The certificate property must not
106	// be set for presigned modules.
107	Presigned *bool
108
109	// Name of the signing certificate lineage file or filegroup module.
110	Lineage *string `android:"path"`
111
112	// For overriding the --rotation-min-sdk-version property of apksig
113	RotationMinSdkVersion *string
114
115	// Sign with the default system dev certificate. Must be used judiciously. Most imported apps
116	// need to either specify a specific certificate or be presigned.
117	Default_dev_cert *bool
118
119	// Specifies that this app should be installed to the priv-app directory,
120	// where the system will grant it additional privileges not available to
121	// normal apps.
122	Privileged *bool
123
124	// Names of modules to be overridden. Listed modules can only be other binaries
125	// (in Make or Soong).
126	// This does not completely prevent installation of the overridden binaries, but if both
127	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
128	// from PRODUCT_PACKAGES.
129	Overrides []string
130
131	// Optional name for the installed app. If unspecified, it is derived from the module name.
132	Filename *string
133
134	// If set, create package-export.apk, which other packages can
135	// use to get PRODUCT-agnostic resource data like IDs and type definitions.
136	Export_package_resources *bool
137
138	// Optional. Install to a subdirectory of the default install path for the module
139	Relative_install_path *string
140
141	// Whether the prebuilt apk can be installed without additional processing. Default is false.
142	Preprocessed *bool
143
144	// Whether or not to skip checking the preprocessed apk for proper alignment and uncompressed
145	// JNI libs and dex files. Default is false
146	Skip_preprocessed_apk_checks *bool
147
148	// Name of the source soong module that gets shadowed by this prebuilt
149	// If unspecified, follows the naming convention that the source module of
150	// the prebuilt is Name() without "prebuilt_" prefix
151	Source_module_name *string
152
153	// Path to the .prebuilt_info file of the prebuilt app.
154	// In case of mainline modules, the .prebuilt_info file contains the build_id that was used
155	// to generate the prebuilt.
156	Prebuilt_info *string `android:"path"`
157}
158
159func (a *AndroidAppImport) IsInstallable() bool {
160	return true
161}
162
163// Updates properties with variant-specific values.
164// This happens as a DefaultableHook instead of a LoadHook because we want to run it after
165// soong config variables are applied.
166func (a *AndroidAppImport) processVariants(ctx android.DefaultableHookContext) {
167	config := ctx.Config()
168	dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName(DpiGroupName)
169
170	// Try DPI variant matches in the reverse-priority order so that the highest priority match
171	// overwrites everything else.
172	// TODO(jungjw): Can we optimize this by making it priority order?
173	for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
174		MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
175	}
176	if config.ProductAAPTPreferredConfig() != "" {
177		MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
178	}
179	archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName(ArchGroupName)
180	archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType
181	MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
182
183	// Process "arch" includes "dpi_variants"
184	archStructPtr := reflect.ValueOf(a.arch_dpiVariants).Elem().FieldByName(ArchGroupName)
185	if archStruct := archStructPtr.Elem(); archStruct.IsValid() {
186		archPartPropsPtr := archStruct.FieldByName(proptools.FieldNameForProperty(archType.Name))
187		if archPartProps := archPartPropsPtr.Elem(); archPartProps.IsValid() {
188			archDpiPropsPtr := archPartProps.FieldByName(DpiGroupName)
189			if archDpiProps := archDpiPropsPtr.Elem(); archDpiProps.IsValid() {
190				for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
191					MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPrebuiltDPI()[i])
192				}
193				if config.ProductAAPTPreferredConfig() != "" {
194					MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPreferredConfig())
195				}
196			}
197		}
198	}
199}
200
201func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
202	dst interface{}, variantGroup reflect.Value, variant string) {
203	src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
204	if !src.IsValid() {
205		return
206	}
207
208	err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
209	if err != nil {
210		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
211			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
212		} else {
213			panic(err)
214		}
215	}
216}
217
218// disablePrebuiltsWithoutApkMutator is a pre-arch mutator that disables AndroidAppImport or
219// AndroidTestImport modules that don't have an apk set. We need this separate mutator instead
220// of doing it in processVariants because processVariants is a defaultable hook, and configurable
221// properties can only be evaluated after the defaults (and eventually, base configurabtion)
222// mutators.
223func disablePrebuiltsWithoutApkMutator(ctx android.BottomUpMutatorContext) {
224	switch a := ctx.Module().(type) {
225	case *AndroidAppImport:
226		if a.properties.Apk.GetOrDefault(ctx, "") == "" {
227			// Disable this module since the apk property is still empty after processing all
228			// matching variants. This likely means there is no matching variant, and the default
229			// variant doesn't have an apk property value either.
230			a.Disable()
231		}
232	case *AndroidTestImport:
233		if a.properties.Apk.GetOrDefault(ctx, "") == "" {
234			// Disable this module since the apk property is still empty after processing all
235			// matching variants. This likely means there is no matching variant, and the default
236			// variant doesn't have an apk property value either.
237			a.Disable()
238		}
239	}
240}
241
242func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
243	cert := android.SrcIsModule(a.properties.Certificate.GetOrDefault(ctx, ""))
244	if cert != "" {
245		ctx.AddDependency(ctx.Module(), certificateTag, cert)
246	}
247
248	for _, cert := range a.properties.Additional_certificates {
249		cert = android.SrcIsModule(cert)
250		if cert != "" {
251			ctx.AddDependency(ctx.Module(), certificateTag, cert)
252		} else {
253			ctx.PropertyErrorf("additional_certificates",
254				`must be names of android_app_certificate modules in the form ":module"`)
255		}
256	}
257
258	a.usesLibrary.deps(ctx, true)
259}
260
261func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
262	ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) {
263	// Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
264	// with them may invalidate pre-existing signature data.
265	if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || Bool(a.properties.Preprocessed)) {
266		ctx.Build(pctx, android.BuildParams{
267			Rule:   android.Cp,
268			Output: outputPath,
269			Input:  inputPath,
270		})
271		return
272	}
273
274	ctx.Build(pctx, android.BuildParams{
275		Rule:   uncompressEmbeddedJniLibsRule,
276		Input:  inputPath,
277		Output: outputPath,
278	})
279}
280
281// Returns whether this module should have the dex file stored uncompressed in the APK.
282func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
283	if ctx.Config().UnbundledBuild() || proptools.Bool(a.properties.Preprocessed) {
284		return false
285	}
286
287	// Uncompress dex in APKs of priv-apps if and only if DONT_UNCOMPRESS_PRIV_APPS_DEXS is false.
288	if a.Privileged() {
289		return ctx.Config().UncompressPrivAppDex()
290	}
291
292	return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter)
293}
294
295func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
296	a.generateAndroidBuildActions(ctx)
297}
298
299func (a *AndroidAppImport) InstallApkName() string {
300	return a.BaseModuleName()
301}
302
303func (a *AndroidAppImport) BaseModuleName() string {
304	return proptools.StringDefault(a.properties.Source_module_name, a.ModuleBase.Name())
305}
306
307func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
308	if a.Name() == "prebuilt_framework-res" {
309		ctx.ModuleErrorf("prebuilt_framework-res found. This used to have special handling in soong, but was removed due to prebuilt_framework-res no longer existing. This check is to ensure it doesn't come back without readding the special handling.")
310	}
311
312	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
313	if !apexInfo.IsForPlatform() {
314		a.hideApexVariantFromMake = true
315	}
316
317	if Bool(a.properties.Preprocessed) {
318		if a.properties.Presigned != nil && !*a.properties.Presigned {
319			ctx.ModuleErrorf("Setting preprocessed: true implies presigned: true, so you cannot set presigned to false")
320		}
321		t := true
322		a.properties.Presigned = &t
323	}
324
325	numCertPropsSet := 0
326	if a.properties.Certificate.GetOrDefault(ctx, "") != "" {
327		numCertPropsSet++
328	}
329	if Bool(a.properties.Presigned) {
330		numCertPropsSet++
331	}
332	if Bool(a.properties.Default_dev_cert) {
333		numCertPropsSet++
334	}
335	if numCertPropsSet != 1 {
336		ctx.ModuleErrorf("One and only one of certficate, presigned (implied by preprocessed), and default_dev_cert properties must be set")
337	}
338
339	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
340	// TODO: LOCAL_PACKAGE_SPLITS
341
342	srcApk := a.prebuilt.SingleSourcePath(ctx)
343
344	// TODO: Install or embed JNI libraries
345
346	// Uncompress JNI libraries in the apk
347	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
348	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed)
349
350	var pathFragments []string
351	relInstallPath := String(a.properties.Relative_install_path)
352
353	if Bool(a.properties.Privileged) {
354		pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()}
355	} else if ctx.InstallInTestcases() {
356		pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()}
357	} else {
358		pathFragments = []string{"app", relInstallPath, a.BaseModuleName()}
359	}
360
361	installDir := android.PathForModuleInstall(ctx, pathFragments...)
362	a.dexpreopter.isApp = true
363	a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
364	a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
365	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
366
367	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries(ctx)
368	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
369	if a.usesLibrary.shouldDisableDexpreopt {
370		a.dexpreopter.disableDexpreopt()
371	}
372
373	if a.usesLibrary.enforceUsesLibraries(ctx) {
374		a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk, &a.dexpreopter.classLoaderContexts)
375	}
376
377	a.dexpreopter.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), jnisUncompressed)
378	if a.dexpreopter.uncompressedDex {
379		dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
380		ctx.Build(pctx, android.BuildParams{
381			Rule:   uncompressDexRule,
382			Input:  jnisUncompressed,
383			Output: dexUncompressed,
384		})
385		jnisUncompressed = dexUncompressed
386	}
387
388	apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
389
390	// TODO: Handle EXTERNAL
391
392	// Sign or align the package if package has not been preprocessed
393
394	if proptools.Bool(a.properties.Preprocessed) {
395		validationStamp := a.validatePresignedApk(ctx, srcApk)
396		output := android.PathForModuleOut(ctx, apkFilename)
397		ctx.Build(pctx, android.BuildParams{
398			Rule:       android.Cp,
399			Input:      srcApk,
400			Output:     output,
401			Validation: validationStamp,
402		})
403		a.outputFile = output
404		a.certificate = PresignedCertificate
405	} else if !Bool(a.properties.Presigned) {
406		// If the certificate property is empty at this point, default_dev_cert must be set to true.
407		// Which makes processMainCert's behavior for the empty cert string WAI.
408		_, _, certificates := collectAppDeps(ctx, a, false, false)
409		a.certificate, certificates = processMainCert(a.ModuleBase, a.properties.Certificate.GetOrDefault(ctx, ""), certificates, ctx)
410		signed := android.PathForModuleOut(ctx, "signed", apkFilename)
411		var lineageFile android.Path
412		if lineage := String(a.properties.Lineage); lineage != "" {
413			lineageFile = android.PathForModuleSrc(ctx, lineage)
414		}
415
416		rotationMinSdkVersion := String(a.properties.RotationMinSdkVersion)
417
418		SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion)
419		a.outputFile = signed
420	} else {
421		validationStamp := a.validatePresignedApk(ctx, srcApk)
422		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
423		TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{validationStamp})
424		a.outputFile = alignedApk
425		a.certificate = PresignedCertificate
426	}
427
428	// TODO: Optionally compress the output apk.
429
430	if apexInfo.IsForPlatform() {
431		a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
432		artifactPath := android.PathForModuleSrc(ctx, a.properties.Apk.GetOrDefault(ctx, ""))
433		a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
434	}
435
436	providePrebuiltInfo(ctx,
437		prebuiltInfoProps{
438			baseModuleName: a.BaseModuleName(),
439			isPrebuilt:     true,
440			prebuiltInfo:   a.properties.Prebuilt_info,
441		},
442	)
443
444	ctx.SetOutputFiles([]android.Path{a.outputFile}, "")
445
446	// TODO: androidmk converter jni libs
447}
448
449func (a *AndroidAppImport) validatePresignedApk(ctx android.ModuleContext, srcApk android.Path) android.Path {
450	stamp := android.PathForModuleOut(ctx, "validated-prebuilt", "check.stamp")
451	var extraArgs []string
452	if a.Privileged() {
453		extraArgs = append(extraArgs, "--privileged")
454		if ctx.Config().UncompressPrivAppDex() {
455			extraArgs = append(extraArgs, "--uncompress-priv-app-dex")
456		}
457	}
458	if proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
459		extraArgs = append(extraArgs, "--skip-preprocessed-apk-checks")
460	}
461	if proptools.Bool(a.properties.Preprocessed) {
462		extraArgs = append(extraArgs, "--preprocessed")
463	}
464
465	ctx.Build(pctx, android.BuildParams{
466		Rule:   checkPresignedApkRule,
467		Input:  srcApk,
468		Output: stamp,
469		Args: map[string]string{
470			"extraArgs": strings.Join(extraArgs, " "),
471		},
472	})
473	return stamp
474}
475
476func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
477	return &a.prebuilt
478}
479
480func (a *AndroidAppImport) Name() string {
481	return a.prebuilt.Name(a.ModuleBase.Name())
482}
483
484func (a *AndroidAppImport) OutputFile() android.Path {
485	return a.outputFile
486}
487
488func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
489	return nil
490}
491
492func (a *AndroidAppImport) Certificate() Certificate {
493	return a.certificate
494}
495
496func (a *AndroidAppImport) ProvenanceMetaDataFile() android.Path {
497	return a.provenanceMetaDataFile
498}
499
500func (a *AndroidAppImport) PrivAppAllowlist() android.OptionalPath {
501	return android.OptionalPath{}
502}
503
504const (
505	ArchGroupName = "Arch"
506	DpiGroupName  = "Dpi_variants"
507)
508
509var dpiVariantGroupType reflect.Type
510var archVariantGroupType reflect.Type
511var archdpiVariantGroupType reflect.Type
512var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
513
514func initAndroidAppImportVariantGroupTypes() {
515	dpiVariantGroupType = createVariantGroupType(supportedDpis, DpiGroupName)
516
517	archNames := make([]string, len(android.ArchTypeList()))
518	for i, archType := range android.ArchTypeList() {
519		archNames[i] = archType.Name
520	}
521	archVariantGroupType = createVariantGroupType(archNames, ArchGroupName)
522	archdpiVariantGroupType = createArchDpiVariantGroupType(archNames, supportedDpis)
523}
524
525// Populates all variant struct properties at creation time.
526func (a *AndroidAppImport) populateAllVariantStructs() {
527	a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
528	a.AddProperties(a.dpiVariants)
529
530	a.archVariants = reflect.New(archVariantGroupType).Interface()
531	a.AddProperties(a.archVariants)
532
533	a.arch_dpiVariants = reflect.New(archdpiVariantGroupType).Interface()
534	a.AddProperties(a.arch_dpiVariants)
535}
536
537func (a *AndroidAppImport) Privileged() bool {
538	return Bool(a.properties.Privileged)
539}
540
541func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
542	// android_app_import might have extra dependencies via uses_libs property.
543	// Don't track the dependency as we don't automatically add those libraries
544	// to the classpath. It should be explicitly added to java_libs property of APEX
545	return false
546}
547
548func (a *AndroidAppImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
549	return android.SdkSpecPrivate
550}
551
552func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
553	return android.SdkSpecPrivate.ApiLevel
554}
555
556var _ android.ApexModule = (*AndroidAppImport)(nil)
557
558// Implements android.ApexModule
559func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
560	sdkVersion android.ApiLevel) error {
561	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
562	return nil
563}
564
565func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
566	props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
567
568	variantFields := make([]reflect.StructField, len(variants))
569	for i, variant := range variants {
570		variantFields[i] = reflect.StructField{
571			Name: proptools.FieldNameForProperty(variant),
572			Type: props,
573		}
574	}
575
576	variantGroupStruct := reflect.StructOf(variantFields)
577	return reflect.StructOf([]reflect.StructField{
578		{
579			Name: variantGroupName,
580			Type: variantGroupStruct,
581		},
582	})
583}
584
585func createArchDpiVariantGroupType(archNames []string, dpiNames []string) reflect.Type {
586	props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
587
588	dpiVariantFields := make([]reflect.StructField, len(dpiNames))
589	for i, variant_dpi := range dpiNames {
590		dpiVariantFields[i] = reflect.StructField{
591			Name: proptools.FieldNameForProperty(variant_dpi),
592			Type: props,
593		}
594	}
595	dpiVariantGroupStruct := reflect.StructOf(dpiVariantFields)
596	dpi_struct := reflect.StructOf([]reflect.StructField{
597		{
598			Name: DpiGroupName,
599			Type: reflect.PointerTo(dpiVariantGroupStruct),
600		},
601	})
602
603	archVariantFields := make([]reflect.StructField, len(archNames))
604	for i, variant_arch := range archNames {
605		archVariantFields[i] = reflect.StructField{
606			Name: proptools.FieldNameForProperty(variant_arch),
607			Type: reflect.PointerTo(dpi_struct),
608		}
609	}
610	archVariantGroupStruct := reflect.StructOf(archVariantFields)
611
612	return_struct := reflect.StructOf([]reflect.StructField{
613		{
614			Name: ArchGroupName,
615			Type: reflect.PointerTo(archVariantGroupStruct),
616		},
617	})
618	return return_struct
619}
620
621func (a *AndroidAppImport) UsesLibrary() *usesLibrary {
622	return &a.usesLibrary
623}
624
625var _ ModuleWithUsesLibrary = (*AndroidAppImport)(nil)
626
627// android_app_import imports a prebuilt apk with additional processing specified in the module.
628// DPI-specific apk source files can be specified using dpi_variants. Example:
629//
630//	android_app_import {
631//	    name: "example_import",
632//	    apk: "prebuilts/example.apk",
633//	    dpi_variants: {
634//	        mdpi: {
635//	            apk: "prebuilts/example_mdpi.apk",
636//	        },
637//	        xhdpi: {
638//	            apk: "prebuilts/example_xhdpi.apk",
639//	        },
640//	    },
641//	    presigned: true,
642//	}
643func AndroidAppImportFactory() android.Module {
644	module := &AndroidAppImport{}
645	module.AddProperties(&module.properties)
646	module.AddProperties(&module.dexpreoptProperties)
647	module.AddProperties(&module.usesLibrary.usesLibraryProperties)
648	module.populateAllVariantStructs()
649	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
650		module.processVariants(ctx)
651	})
652
653	android.InitApexModule(module)
654	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
655	android.InitDefaultableModule(module)
656	android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk")
657
658	module.usesLibrary.enforce = true
659
660	return module
661}
662
663type AndroidTestImport struct {
664	AndroidAppImport
665
666	testProperties struct {
667		// list of compatibility suites (for example "cts", "vts") that the module should be
668		// installed into.
669		Test_suites []string `android:"arch_variant"`
670
671		// list of files or filegroup modules that provide data that should be installed alongside
672		// the test
673		Data []string `android:"path"`
674
675		// Install the test into a folder named for the module in all test suites.
676		Per_testcase_directory *bool
677	}
678
679	data android.Paths
680}
681
682func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
683	a.generateAndroidBuildActions(ctx)
684
685	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
686}
687
688func (a *AndroidTestImport) InstallInTestcases() bool {
689	return true
690}
691
692// android_test_import imports a prebuilt test apk with additional processing specified in the
693// module. DPI or arch variant configurations can be made as with android_app_import.
694func AndroidTestImportFactory() android.Module {
695	module := &AndroidTestImport{}
696	module.AddProperties(&module.properties)
697	module.AddProperties(&module.dexpreoptProperties)
698	module.AddProperties(&module.testProperties)
699	module.populateAllVariantStructs()
700	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
701		module.processVariants(ctx)
702	})
703
704	module.dexpreopter.isTest = true
705
706	android.InitApexModule(module)
707	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
708	android.InitDefaultableModule(module)
709	android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk")
710
711	return module
712}
713