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