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