Break up app.go.
Test: m nothing + TreeHugger
Change-Id: I64c6d7f10530c424bc282d8111dfaf9159426f00
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 4a6aecf..4aebc99 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -258,6 +258,9 @@
java.RegisterJavaBuildComponents(ctx)
java.RegisterSystemModulesBuildComponents(ctx)
java.RegisterAppBuildComponents(ctx)
+ java.RegisterAppImportBuildComponents(ctx)
+ java.RegisterAppSetBuildComponents(ctx)
+ java.RegisterRuntimeResourceOverlayBuildComponents(ctx)
java.RegisterSdkLibraryBuildComponents(ctx)
ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
ctx.RegisterModuleType("bpf", bpf.BpfFactory)
diff --git a/java/Android.bp b/java/Android.bp
index 39502b3..9c28968 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -22,6 +22,8 @@
"androidmk.go",
"app_builder.go",
"app.go",
+ "app_import.go",
+ "app_set.go",
"boot_jars.go",
"builder.go",
"device_host_converter.go",
@@ -46,6 +48,7 @@
"prebuilt_apis.go",
"proto.go",
"robolectric.go",
+ "rro.go",
"sdk.go",
"sdk_library.go",
"sdk_library_external.go",
@@ -57,6 +60,8 @@
],
testSrcs: [
"androidmk_test.go",
+ "app_import_test.go",
+ "app_set_test.go",
"app_test.go",
"device_host_converter_test.go",
"dexpreopt_test.go",
@@ -66,6 +71,7 @@
"jdeps_test.go",
"kotlin_test.go",
"plugin_test.go",
+ "rro_test.go",
"sdk_test.go",
],
pluginFor: ["soong_build"],
diff --git a/java/app.go b/java/app.go
index bcb610c..4369865 100755
--- a/java/app.go
+++ b/java/app.go
@@ -14,13 +14,12 @@
package java
-// This file contains the module types for compiling Android apps.
+// This file contains the module implementations for android_app, android_test, and some more
+// related module types, including their override variants.
import (
"path/filepath"
- "reflect"
"sort"
- "strconv"
"strings"
"github.com/google/blueprint"
@@ -32,12 +31,8 @@
"android/soong/tradefed"
)
-var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
-
func init() {
RegisterAppBuildComponents(android.InitRegistrationContext)
-
- initAndroidAppImportVariantGroupTypes()
}
func RegisterAppBuildComponents(ctx android.RegistrationContext) {
@@ -47,139 +42,6 @@
ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
- ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
- ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
- ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
- ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
- ctx.RegisterModuleType("android_app_set", AndroidApkSetFactory)
-}
-
-type AndroidAppSetProperties struct {
- // APK Set path
- Set *string
-
- // Specifies that this app should be installed to the priv-app directory,
- // where the system will grant it additional privileges not available to
- // normal apps.
- Privileged *bool
-
- // APKs in this set use prerelease SDK version
- Prerelease *bool
-
- // Names of modules to be overridden. Listed modules can only be other apps
- // (in Make or Soong).
- Overrides []string
-}
-
-type AndroidAppSet struct {
- android.ModuleBase
- android.DefaultableModuleBase
- prebuilt android.Prebuilt
-
- properties AndroidAppSetProperties
- packedOutput android.WritablePath
- installFile string
- apkcertsFile android.ModuleOutPath
-}
-
-func (as *AndroidAppSet) Name() string {
- return as.prebuilt.Name(as.ModuleBase.Name())
-}
-
-func (as *AndroidAppSet) IsInstallable() bool {
- return true
-}
-
-func (as *AndroidAppSet) Prebuilt() *android.Prebuilt {
- return &as.prebuilt
-}
-
-func (as *AndroidAppSet) Privileged() bool {
- return Bool(as.properties.Privileged)
-}
-
-func (as *AndroidAppSet) OutputFile() android.Path {
- return as.packedOutput
-}
-
-func (as *AndroidAppSet) InstallFile() string {
- return as.installFile
-}
-
-func (as *AndroidAppSet) APKCertsFile() android.Path {
- return as.apkcertsFile
-}
-
-var TargetCpuAbi = map[string]string{
- "arm": "ARMEABI_V7A",
- "arm64": "ARM64_V8A",
- "x86": "X86",
- "x86_64": "X86_64",
-}
-
-func SupportedAbis(ctx android.ModuleContext) []string {
- abiName := func(targetIdx int, deviceArch string) string {
- if abi, found := TargetCpuAbi[deviceArch]; found {
- return abi
- }
- ctx.ModuleErrorf("Target %d has invalid Arch: %s", targetIdx, deviceArch)
- return "BAD_ABI"
- }
-
- var result []string
- for i, target := range ctx.Config().Targets[android.Android] {
- result = append(result, abiName(i, target.Arch.ArchType.String()))
- }
- return result
-}
-
-func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
- as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
- // We are assuming here that the install file in the APK
- // set has `.apk` suffix. If it doesn't the build will fail.
- // APK sets containing APEX files are handled elsewhere.
- as.installFile = as.BaseModuleName() + ".apk"
- screenDensities := "all"
- if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
- screenDensities = strings.ToUpper(strings.Join(dpis, ","))
- }
- // TODO(asmundak): handle locales.
- // TODO(asmundak): do we support device features
- ctx.Build(pctx,
- android.BuildParams{
- Rule: extractMatchingApks,
- Description: "Extract APKs from APK set",
- Output: as.packedOutput,
- ImplicitOutput: as.apkcertsFile,
- Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
- Args: map[string]string{
- "abis": strings.Join(SupportedAbis(ctx), ","),
- "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
- "screen-densities": screenDensities,
- "sdk-version": ctx.Config().PlatformSdkVersion().String(),
- "stem": as.BaseModuleName(),
- "apkcerts": as.apkcertsFile.String(),
- "partition": as.PartitionTag(ctx.DeviceConfig()),
- },
- })
-}
-
-// android_app_set extracts a set of APKs based on the target device
-// configuration and installs this set as "split APKs".
-// The extracted set always contains an APK whose name is
-// _module_name_.apk and every split APK matching target device.
-// The extraction of the density-specific splits depends on
-// PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
-// be a list density names: LDPI, MDPI, HDPI, etc.), only listed
-// splits will be extracted. Otherwise all density-specific splits
-// will be extracted.
-func AndroidApkSetFactory() android.Module {
- module := &AndroidAppSet{}
- module.AddProperties(&module.properties)
- InitJavaModule(module, android.DeviceSupported)
- android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
- return module
}
// AndroidManifest.xml merging
@@ -273,15 +135,6 @@
Rename_resources_package *bool
}
-// runtime_resource_overlay properties that can be overridden by override_runtime_resource_overlay
-type OverridableRuntimeResourceOverlayProperties struct {
- // the package name of this app. The package name in the manifest file is used if one was not given.
- Package_name *string
-
- // the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
- Target_package_name *string
-}
-
type AndroidApp struct {
Library
aapt
@@ -1298,629 +1151,6 @@
return m
}
-type OverrideRuntimeResourceOverlay struct {
- android.ModuleBase
- android.OverrideModuleBase
-}
-
-func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(_ android.ModuleContext) {
- // All the overrides happen in the base module.
- // TODO(jungjw): Check the base module type.
-}
-
-// override_runtime_resource_overlay is used to create a module based on another
-// runtime_resource_overlay module by overriding some of its properties.
-func OverrideRuntimeResourceOverlayModuleFactory() android.Module {
- m := &OverrideRuntimeResourceOverlay{}
- m.AddProperties(&OverridableRuntimeResourceOverlayProperties{})
-
- android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
- android.InitOverrideModule(m)
- return m
-}
-
-type AndroidAppImport struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.ApexModuleBase
- prebuilt android.Prebuilt
-
- properties AndroidAppImportProperties
- dpiVariants interface{}
- archVariants interface{}
-
- outputFile android.Path
- certificate Certificate
-
- dexpreopter
-
- usesLibrary usesLibrary
-
- preprocessed bool
-
- installPath android.InstallPath
-
- hideApexVariantFromMake bool
-}
-
-type AndroidAppImportProperties struct {
- // A prebuilt apk to import
- Apk *string
-
- // The name of a certificate in the default certificate directory or an android_app_certificate
- // module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
- Certificate *string
-
- // Set this flag to true if the prebuilt apk is already signed. The certificate property must not
- // be set for presigned modules.
- Presigned *bool
-
- // Name of the signing certificate lineage file.
- Lineage *string
-
- // Sign with the default system dev certificate. Must be used judiciously. Most imported apps
- // need to either specify a specific certificate or be presigned.
- Default_dev_cert *bool
-
- // Specifies that this app should be installed to the priv-app directory,
- // where the system will grant it additional privileges not available to
- // normal apps.
- Privileged *bool
-
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-
- // Optional name for the installed app. If unspecified, it is derived from the module name.
- Filename *string
-}
-
-func (a *AndroidAppImport) IsInstallable() bool {
- return true
-}
-
-// Updates properties with variant-specific values.
-func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
- config := ctx.Config()
-
- dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
- // Try DPI variant matches in the reverse-priority order so that the highest priority match
- // overwrites everything else.
- // TODO(jungjw): Can we optimize this by making it priority order?
- for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
- MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
- }
- if config.ProductAAPTPreferredConfig() != "" {
- MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
- }
-
- archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
- archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType
- MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
-
- if String(a.properties.Apk) == "" {
- // Disable this module since the apk property is still empty after processing all matching
- // variants. This likely means there is no matching variant, and the default variant doesn't
- // have an apk property value either.
- a.Disable()
- }
-}
-
-func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
- dst interface{}, variantGroup reflect.Value, variant string) {
- src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
- if !src.IsValid() {
- return
- }
-
- err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
- if err != nil {
- if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
- } else {
- panic(err)
- }
- }
-}
-
-func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
- cert := android.SrcIsModule(String(a.properties.Certificate))
- if cert != "" {
- ctx.AddDependency(ctx.Module(), certificateTag, cert)
- }
-
- a.usesLibrary.deps(ctx, true)
-}
-
-func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
- ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
- // with them may invalidate pre-existing signature data.
- if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) {
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Output: outputPath,
- Input: inputPath,
- })
- return
- }
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'lib/**/*.so'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
-}
-
-// Returns whether this module should have the dex file stored uncompressed in the APK.
-func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
- if ctx.Config().UnbundledBuild() || a.preprocessed {
- return false
- }
-
- // Uncompress dex in APKs of privileged apps
- if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
- return true
- }
-
- return shouldUncompressDex(ctx, &a.dexpreopter)
-}
-
-func (a *AndroidAppImport) uncompressDex(
- ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'classes*.dex'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-dex", "Uncompress dex files")
-}
-
-func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- a.generateAndroidBuildActions(ctx)
-}
-
-func (a *AndroidAppImport) InstallApkName() string {
- return a.BaseModuleName()
-}
-
-func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
- apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if !apexInfo.IsForPlatform() {
- a.hideApexVariantFromMake = true
- }
-
- numCertPropsSet := 0
- if String(a.properties.Certificate) != "" {
- numCertPropsSet++
- }
- if Bool(a.properties.Presigned) {
- numCertPropsSet++
- }
- if Bool(a.properties.Default_dev_cert) {
- numCertPropsSet++
- }
- if numCertPropsSet != 1 {
- ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
- }
-
- _, certificates := collectAppDeps(ctx, a, false, false)
-
- // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
- // TODO: LOCAL_PACKAGE_SPLITS
-
- srcApk := a.prebuilt.SingleSourcePath(ctx)
-
- if a.usesLibrary.enforceUsesLibraries() {
- srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
- }
-
- // TODO: Install or embed JNI libraries
-
- // Uncompress JNI libraries in the apk
- jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
- a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
-
- var installDir android.InstallPath
- if Bool(a.properties.Privileged) {
- installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
- } else if ctx.InstallInTestcases() {
- installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
- } else {
- installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
- }
-
- a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
- a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
- a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
-
- a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
- a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
-
- a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
- if a.dexpreopter.uncompressedDex {
- dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
- a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
- jnisUncompressed = dexUncompressed
- }
-
- apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
-
- // TODO: Handle EXTERNAL
-
- // Sign or align the package if package has not been preprocessed
- if a.preprocessed {
- a.outputFile = srcApk
- a.certificate = PresignedCertificate
- } else if !Bool(a.properties.Presigned) {
- // If the certificate property is empty at this point, default_dev_cert must be set to true.
- // Which makes processMainCert's behavior for the empty cert string WAI.
- certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
- if len(certificates) != 1 {
- ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
- }
- a.certificate = certificates[0]
- signed := android.PathForModuleOut(ctx, "signed", apkFilename)
- var lineageFile android.Path
- if lineage := String(a.properties.Lineage); lineage != "" {
- lineageFile = android.PathForModuleSrc(ctx, lineage)
- }
- SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile)
- a.outputFile = signed
- } else {
- alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
- TransformZipAlign(ctx, alignedApk, jnisUncompressed)
- a.outputFile = alignedApk
- a.certificate = PresignedCertificate
- }
-
- // TODO: Optionally compress the output apk.
-
- if apexInfo.IsForPlatform() {
- a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
- }
-
- // TODO: androidmk converter jni libs
-}
-
-func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
- return &a.prebuilt
-}
-
-func (a *AndroidAppImport) Name() string {
- return a.prebuilt.Name(a.ModuleBase.Name())
-}
-
-func (a *AndroidAppImport) OutputFile() android.Path {
- return a.outputFile
-}
-
-func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
- return nil
-}
-
-func (a *AndroidAppImport) Certificate() Certificate {
- return a.certificate
-}
-
-var dpiVariantGroupType reflect.Type
-var archVariantGroupType reflect.Type
-
-func initAndroidAppImportVariantGroupTypes() {
- dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
-
- archNames := make([]string, len(android.ArchTypeList()))
- for i, archType := range android.ArchTypeList() {
- archNames[i] = archType.Name
- }
- archVariantGroupType = createVariantGroupType(archNames, "Arch")
-}
-
-// Populates all variant struct properties at creation time.
-func (a *AndroidAppImport) populateAllVariantStructs() {
- a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
- a.AddProperties(a.dpiVariants)
-
- a.archVariants = reflect.New(archVariantGroupType).Interface()
- a.AddProperties(a.archVariants)
-}
-
-func (a *AndroidAppImport) Privileged() bool {
- return Bool(a.properties.Privileged)
-}
-
-func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
- // android_app_import might have extra dependencies via uses_libs property.
- // Don't track the dependency as we don't automatically add those libraries
- // to the classpath. It should be explicitly added to java_libs property of APEX
- return false
-}
-
-func (a *AndroidAppImport) sdkVersion() sdkSpec {
- return sdkSpecFrom("")
-}
-
-func (a *AndroidAppImport) minSdkVersion() sdkSpec {
- return sdkSpecFrom("")
-}
-
-var _ android.ApexModule = (*AndroidAppImport)(nil)
-
-// Implements android.ApexModule
-func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
- sdkVersion android.ApiLevel) error {
- // Do not check for prebuilts against the min_sdk_version of enclosing APEX
- return nil
-}
-
-func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
- props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
-
- variantFields := make([]reflect.StructField, len(variants))
- for i, variant := range variants {
- variantFields[i] = reflect.StructField{
- Name: proptools.FieldNameForProperty(variant),
- Type: props,
- }
- }
-
- variantGroupStruct := reflect.StructOf(variantFields)
- return reflect.StructOf([]reflect.StructField{
- {
- Name: variantGroupName,
- Type: variantGroupStruct,
- },
- })
-}
-
-// android_app_import imports a prebuilt apk with additional processing specified in the module.
-// DPI-specific apk source files can be specified using dpi_variants. Example:
-//
-// android_app_import {
-// name: "example_import",
-// apk: "prebuilts/example.apk",
-// dpi_variants: {
-// mdpi: {
-// apk: "prebuilts/example_mdpi.apk",
-// },
-// xhdpi: {
-// apk: "prebuilts/example_xhdpi.apk",
-// },
-// },
-// certificate: "PRESIGNED",
-// }
-func AndroidAppImportFactory() android.Module {
- module := &AndroidAppImport{}
- module.AddProperties(&module.properties)
- module.AddProperties(&module.dexpreoptProperties)
- module.AddProperties(&module.usesLibrary.usesLibraryProperties)
- module.populateAllVariantStructs()
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- module.processVariants(ctx)
- })
-
- android.InitApexModule(module)
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
-
- return module
-}
-
-type androidTestImportProperties struct {
- // Whether the prebuilt apk can be installed without additional processing. Default is false.
- Preprocessed *bool
-}
-
-type AndroidTestImport struct {
- AndroidAppImport
-
- testProperties testProperties
-
- testImportProperties androidTestImportProperties
-
- data android.Paths
-}
-
-func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- a.preprocessed = Bool(a.testImportProperties.Preprocessed)
-
- a.generateAndroidBuildActions(ctx)
-
- a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
-}
-
-func (a *AndroidTestImport) InstallInTestcases() bool {
- return true
-}
-
-// android_test_import imports a prebuilt test apk with additional processing specified in the
-// module. DPI or arch variant configurations can be made as with android_app_import.
-func AndroidTestImportFactory() android.Module {
- module := &AndroidTestImport{}
- module.AddProperties(&module.properties)
- module.AddProperties(&module.dexpreoptProperties)
- module.AddProperties(&module.testProperties)
- module.AddProperties(&module.testImportProperties)
- module.populateAllVariantStructs()
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- module.processVariants(ctx)
- })
-
- module.dexpreopter.isTest = true
-
- android.InitApexModule(module)
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
-
- return module
-}
-
-type RuntimeResourceOverlay struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.OverridableModuleBase
- aapt
-
- properties RuntimeResourceOverlayProperties
- overridableProperties OverridableRuntimeResourceOverlayProperties
-
- certificate Certificate
-
- outputFile android.Path
- installDir android.InstallPath
-}
-
-type RuntimeResourceOverlayProperties struct {
- // the name of a certificate in the default certificate directory or an android_app_certificate
- // module name in the form ":module".
- Certificate *string
-
- // Name of the signing certificate lineage file.
- Lineage *string
-
- // optional theme name. If specified, the overlay package will be applied
- // only when the ro.boot.vendor.overlay.theme system property is set to the same value.
- Theme *string
-
- // if not blank, set to the version of the sdk to compile against.
- // Defaults to compiling against the current platform.
- Sdk_version *string
-
- // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
- // Defaults to sdk_version if not set.
- Min_sdk_version *string
-
- // list of android_library modules whose resources are extracted and linked against statically
- Static_libs []string
-
- // list of android_app modules whose resources are extracted and linked against
- Resource_libs []string
-
- // Names of modules to be overridden. Listed modules can only be other overlays
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden overlays, but if both
- // overlays would be installed by default (in PRODUCT_PACKAGES) the other overlay will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-}
-
-// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
-// a RuntimeResourceOverlay module.
-type RuntimeResourceOverlayModule interface {
- android.Module
- OutputFile() android.Path
- Certificate() Certificate
- Theme() string
-}
-
-func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
- sdkDep := decodeSdkDep(ctx, sdkContext(r))
- if sdkDep.hasFrameworkLibs() {
- r.aapt.deps(ctx, sdkDep)
- }
-
- cert := android.SrcIsModule(String(r.properties.Certificate))
- if cert != "" {
- ctx.AddDependency(ctx.Module(), certificateTag, cert)
- }
-
- ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
- ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
-}
-
-func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // Compile and link resources
- r.aapt.hasNoCode = true
- // Do not remove resources without default values nor dedupe resource configurations with the same value
- aaptLinkFlags := []string{"--no-resource-deduping", "--no-resource-removal"}
- // Allow the override of "package name" and "overlay target package name"
- manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
- if overridden || r.overridableProperties.Package_name != nil {
- // The product override variable has a priority over the package_name property.
- if !overridden {
- manifestPackageName = *r.overridableProperties.Package_name
- }
- aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, false)...)
- }
- if r.overridableProperties.Target_package_name != nil {
- aaptLinkFlags = append(aaptLinkFlags,
- "--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
- }
- r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
-
- // Sign the built package
- _, certificates := collectAppDeps(ctx, r, false, false)
- certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
- signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
- var lineageFile android.Path
- if lineage := String(r.properties.Lineage); lineage != "" {
- lineageFile = android.PathForModuleSrc(ctx, lineage)
- }
- SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile)
- r.certificate = certificates[0]
-
- r.outputFile = signed
- r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
- ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
-}
-
-func (r *RuntimeResourceOverlay) sdkVersion() sdkSpec {
- return sdkSpecFrom(String(r.properties.Sdk_version))
-}
-
-func (r *RuntimeResourceOverlay) systemModules() string {
- return ""
-}
-
-func (r *RuntimeResourceOverlay) minSdkVersion() sdkSpec {
- if r.properties.Min_sdk_version != nil {
- return sdkSpecFrom(*r.properties.Min_sdk_version)
- }
- return r.sdkVersion()
-}
-
-func (r *RuntimeResourceOverlay) targetSdkVersion() sdkSpec {
- return r.sdkVersion()
-}
-
-func (r *RuntimeResourceOverlay) Certificate() Certificate {
- return r.certificate
-}
-
-func (r *RuntimeResourceOverlay) OutputFile() android.Path {
- return r.outputFile
-}
-
-func (r *RuntimeResourceOverlay) Theme() string {
- return String(r.properties.Theme)
-}
-
-// runtime_resource_overlay generates a resource-only apk file that can overlay application and
-// system resources at run time.
-func RuntimeResourceOverlayFactory() android.Module {
- module := &RuntimeResourceOverlay{}
- module.AddProperties(
- &module.properties,
- &module.aaptProperties,
- &module.overridableProperties)
-
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitOverridableModule(module, &module.properties.Overrides)
- return module
-}
-
type UsesLibraryProperties struct {
// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file.
Uses_libs []string
diff --git a/java/app_import.go b/java/app_import.go
new file mode 100644
index 0000000..2054785
--- /dev/null
+++ b/java/app_import.go
@@ -0,0 +1,484 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+// This file contains the module implementations for android_app_import and android_test_import.
+
+import (
+ "reflect"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ RegisterAppImportBuildComponents(android.InitRegistrationContext)
+
+ initAndroidAppImportVariantGroupTypes()
+}
+
+func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
+ ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
+}
+
+type AndroidAppImport struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ android.ApexModuleBase
+ prebuilt android.Prebuilt
+
+ properties AndroidAppImportProperties
+ dpiVariants interface{}
+ archVariants interface{}
+
+ outputFile android.Path
+ certificate Certificate
+
+ dexpreopter
+
+ usesLibrary usesLibrary
+
+ preprocessed bool
+
+ installPath android.InstallPath
+
+ hideApexVariantFromMake bool
+}
+
+type AndroidAppImportProperties struct {
+ // A prebuilt apk to import
+ Apk *string
+
+ // The name of a certificate in the default certificate directory or an android_app_certificate
+ // module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
+ Certificate *string
+
+ // Set this flag to true if the prebuilt apk is already signed. The certificate property must not
+ // be set for presigned modules.
+ Presigned *bool
+
+ // Name of the signing certificate lineage file.
+ Lineage *string
+
+ // Sign with the default system dev certificate. Must be used judiciously. Most imported apps
+ // need to either specify a specific certificate or be presigned.
+ Default_dev_cert *bool
+
+ // Specifies that this app should be installed to the priv-app directory,
+ // where the system will grant it additional privileges not available to
+ // normal apps.
+ Privileged *bool
+
+ // Names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // Optional name for the installed app. If unspecified, it is derived from the module name.
+ Filename *string
+}
+
+func (a *AndroidAppImport) IsInstallable() bool {
+ return true
+}
+
+// Updates properties with variant-specific values.
+func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
+ config := ctx.Config()
+
+ dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
+ // Try DPI variant matches in the reverse-priority order so that the highest priority match
+ // overwrites everything else.
+ // TODO(jungjw): Can we optimize this by making it priority order?
+ for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
+ MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
+ }
+ if config.ProductAAPTPreferredConfig() != "" {
+ MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
+ }
+
+ archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
+ archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType
+ MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
+
+ if String(a.properties.Apk) == "" {
+ // Disable this module since the apk property is still empty after processing all matching
+ // variants. This likely means there is no matching variant, and the default variant doesn't
+ // have an apk property value either.
+ a.Disable()
+ }
+}
+
+func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
+ dst interface{}, variantGroup reflect.Value, variant string) {
+ src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
+ if !src.IsValid() {
+ return
+ }
+
+ err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
+}
+
+func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
+ cert := android.SrcIsModule(String(a.properties.Certificate))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+
+ a.usesLibrary.deps(ctx, true)
+}
+
+func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
+ ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+ // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
+ // with them may invalidate pre-existing signature data.
+ if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: outputPath,
+ Input: inputPath,
+ })
+ return
+ }
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
+ BuiltTool("zip2zip").
+ FlagWithInput("-i ", inputPath).
+ FlagWithOutput("-o ", outputPath).
+ FlagWithArg("-0 ", "'lib/**/*.so'").
+ Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
+ rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+}
+
+// Returns whether this module should have the dex file stored uncompressed in the APK.
+func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
+ if ctx.Config().UnbundledBuild() || a.preprocessed {
+ return false
+ }
+
+ // Uncompress dex in APKs of privileged apps
+ if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
+ return true
+ }
+
+ return shouldUncompressDex(ctx, &a.dexpreopter)
+}
+
+func (a *AndroidAppImport) uncompressDex(
+ ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
+ BuiltTool("zip2zip").
+ FlagWithInput("-i ", inputPath).
+ FlagWithOutput("-o ", outputPath).
+ FlagWithArg("-0 ", "'classes*.dex'").
+ Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
+ rule.Build("uncompress-dex", "Uncompress dex files")
+}
+
+func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.generateAndroidBuildActions(ctx)
+}
+
+func (a *AndroidAppImport) InstallApkName() string {
+ return a.BaseModuleName()
+}
+
+func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if !apexInfo.IsForPlatform() {
+ a.hideApexVariantFromMake = true
+ }
+
+ numCertPropsSet := 0
+ if String(a.properties.Certificate) != "" {
+ numCertPropsSet++
+ }
+ if Bool(a.properties.Presigned) {
+ numCertPropsSet++
+ }
+ if Bool(a.properties.Default_dev_cert) {
+ numCertPropsSet++
+ }
+ if numCertPropsSet != 1 {
+ ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
+ }
+
+ _, certificates := collectAppDeps(ctx, a, false, false)
+
+ // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
+ // TODO: LOCAL_PACKAGE_SPLITS
+
+ srcApk := a.prebuilt.SingleSourcePath(ctx)
+
+ if a.usesLibrary.enforceUsesLibraries() {
+ srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+ }
+
+ // TODO: Install or embed JNI libraries
+
+ // Uncompress JNI libraries in the apk
+ jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
+ a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
+
+ var installDir android.InstallPath
+ if Bool(a.properties.Privileged) {
+ installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
+ } else if ctx.InstallInTestcases() {
+ installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
+ } else {
+ installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
+ }
+
+ a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
+ a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
+ a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
+
+ a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
+ a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+
+ a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
+ if a.dexpreopter.uncompressedDex {
+ dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
+ a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
+ jnisUncompressed = dexUncompressed
+ }
+
+ apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
+
+ // TODO: Handle EXTERNAL
+
+ // Sign or align the package if package has not been preprocessed
+ if a.preprocessed {
+ a.outputFile = srcApk
+ a.certificate = PresignedCertificate
+ } else if !Bool(a.properties.Presigned) {
+ // If the certificate property is empty at this point, default_dev_cert must be set to true.
+ // Which makes processMainCert's behavior for the empty cert string WAI.
+ certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
+ if len(certificates) != 1 {
+ ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
+ }
+ a.certificate = certificates[0]
+ signed := android.PathForModuleOut(ctx, "signed", apkFilename)
+ var lineageFile android.Path
+ if lineage := String(a.properties.Lineage); lineage != "" {
+ lineageFile = android.PathForModuleSrc(ctx, lineage)
+ }
+ SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile)
+ a.outputFile = signed
+ } else {
+ alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
+ TransformZipAlign(ctx, alignedApk, jnisUncompressed)
+ a.outputFile = alignedApk
+ a.certificate = PresignedCertificate
+ }
+
+ // TODO: Optionally compress the output apk.
+
+ if apexInfo.IsForPlatform() {
+ a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
+ }
+
+ // TODO: androidmk converter jni libs
+}
+
+func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
+ return &a.prebuilt
+}
+
+func (a *AndroidAppImport) Name() string {
+ return a.prebuilt.Name(a.ModuleBase.Name())
+}
+
+func (a *AndroidAppImport) OutputFile() android.Path {
+ return a.outputFile
+}
+
+func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
+ return nil
+}
+
+func (a *AndroidAppImport) Certificate() Certificate {
+ return a.certificate
+}
+
+var dpiVariantGroupType reflect.Type
+var archVariantGroupType reflect.Type
+var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
+
+func initAndroidAppImportVariantGroupTypes() {
+ dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
+
+ archNames := make([]string, len(android.ArchTypeList()))
+ for i, archType := range android.ArchTypeList() {
+ archNames[i] = archType.Name
+ }
+ archVariantGroupType = createVariantGroupType(archNames, "Arch")
+}
+
+// Populates all variant struct properties at creation time.
+func (a *AndroidAppImport) populateAllVariantStructs() {
+ a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
+ a.AddProperties(a.dpiVariants)
+
+ a.archVariants = reflect.New(archVariantGroupType).Interface()
+ a.AddProperties(a.archVariants)
+}
+
+func (a *AndroidAppImport) Privileged() bool {
+ return Bool(a.properties.Privileged)
+}
+
+func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
+ // android_app_import might have extra dependencies via uses_libs property.
+ // Don't track the dependency as we don't automatically add those libraries
+ // to the classpath. It should be explicitly added to java_libs property of APEX
+ return false
+}
+
+func (a *AndroidAppImport) sdkVersion() sdkSpec {
+ return sdkSpecFrom("")
+}
+
+func (a *AndroidAppImport) minSdkVersion() sdkSpec {
+ return sdkSpecFrom("")
+}
+
+var _ android.ApexModule = (*AndroidAppImport)(nil)
+
+// Implements android.ApexModule
+func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+ sdkVersion android.ApiLevel) error {
+ // Do not check for prebuilts against the min_sdk_version of enclosing APEX
+ return nil
+}
+
+func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
+ props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
+
+ variantFields := make([]reflect.StructField, len(variants))
+ for i, variant := range variants {
+ variantFields[i] = reflect.StructField{
+ Name: proptools.FieldNameForProperty(variant),
+ Type: props,
+ }
+ }
+
+ variantGroupStruct := reflect.StructOf(variantFields)
+ return reflect.StructOf([]reflect.StructField{
+ {
+ Name: variantGroupName,
+ Type: variantGroupStruct,
+ },
+ })
+}
+
+// android_app_import imports a prebuilt apk with additional processing specified in the module.
+// DPI-specific apk source files can be specified using dpi_variants. Example:
+//
+// android_app_import {
+// name: "example_import",
+// apk: "prebuilts/example.apk",
+// dpi_variants: {
+// mdpi: {
+// apk: "prebuilts/example_mdpi.apk",
+// },
+// xhdpi: {
+// apk: "prebuilts/example_xhdpi.apk",
+// },
+// },
+// certificate: "PRESIGNED",
+// }
+func AndroidAppImportFactory() android.Module {
+ module := &AndroidAppImport{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.dexpreoptProperties)
+ module.AddProperties(&module.usesLibrary.usesLibraryProperties)
+ module.populateAllVariantStructs()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ module.processVariants(ctx)
+ })
+
+ android.InitApexModule(module)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+
+ return module
+}
+
+type androidTestImportProperties struct {
+ // Whether the prebuilt apk can be installed without additional processing. Default is false.
+ Preprocessed *bool
+}
+
+type AndroidTestImport struct {
+ AndroidAppImport
+
+ testProperties testProperties
+
+ testImportProperties androidTestImportProperties
+
+ data android.Paths
+}
+
+func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.preprocessed = Bool(a.testImportProperties.Preprocessed)
+
+ a.generateAndroidBuildActions(ctx)
+
+ a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
+}
+
+func (a *AndroidTestImport) InstallInTestcases() bool {
+ return true
+}
+
+// android_test_import imports a prebuilt test apk with additional processing specified in the
+// module. DPI or arch variant configurations can be made as with android_app_import.
+func AndroidTestImportFactory() android.Module {
+ module := &AndroidTestImport{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.dexpreoptProperties)
+ module.AddProperties(&module.testProperties)
+ module.AddProperties(&module.testImportProperties)
+ module.populateAllVariantStructs()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ module.processVariants(ctx)
+ })
+
+ module.dexpreopter.isTest = true
+
+ android.InitApexModule(module)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+
+ return module
+}
diff --git a/java/app_import_test.go b/java/app_import_test.go
new file mode 100644
index 0000000..3b55c81
--- /dev/null
+++ b/java/app_import_test.go
@@ -0,0 +1,495 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "regexp"
+ "strings"
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func TestAndroidAppImport(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+
+ // Check cert signing flag.
+ signedApk := variant.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs. They shouldn't exist.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
+ t.Errorf("dexpreopt shouldn't have run.")
+ }
+}
+
+func TestAndroidAppImport_Presigned(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+ // Make sure signing was skipped and aligning was done.
+ if variant.MaybeOutput("signed/foo.apk").Rule != nil {
+ t.Errorf("signing rule shouldn't be included.")
+ }
+ if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
+ t.Errorf("can't find aligning rule")
+ }
+}
+
+func TestAndroidAppImport_SigningLineage(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ lineage: "lineage.bin",
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check cert signing lineage flag.
+ signedApk := variant.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["flags"]
+ expected := "--lineage lineage.bin"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ default_dev_cert: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+
+ // Check cert signing flag.
+ signedApk := variant.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_DpiVariants(t *testing.T) {
+ bp := `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ dpi_variants: {
+ xhdpi: {
+ apk: "prebuilts/apk/app_xhdpi.apk",
+ },
+ xxhdpi: {
+ apk: "prebuilts/apk/app_xxhdpi.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `
+ testCases := []struct {
+ name string
+ aaptPreferredConfig *string
+ aaptPrebuiltDPI []string
+ expected string
+ }{
+ {
+ name: "no preferred",
+ aaptPreferredConfig: nil,
+ aaptPrebuiltDPI: []string{},
+ expected: "prebuilts/apk/app.apk",
+ },
+ {
+ name: "AAPTPreferredConfig matches",
+ aaptPreferredConfig: proptools.StringPtr("xhdpi"),
+ aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
+ expected: "prebuilts/apk/app_xhdpi.apk",
+ },
+ {
+ name: "AAPTPrebuiltDPI matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
+ expected: "prebuilts/apk/app_xxhdpi.apk",
+ },
+ {
+ name: "non-first AAPTPrebuiltDPI matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
+ expected: "prebuilts/apk/app_xhdpi.apk",
+ },
+ {
+ name: "no matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
+ expected: "prebuilts/apk/app.apk",
+ },
+ }
+
+ jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
+ for _, test := range testCases {
+ config := testAppConfig(nil, bp, nil)
+ config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
+ config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ ctx := testContext(config)
+
+ run(t, ctx, config)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+ jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+ matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
+ if len(matches) != 2 {
+ t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
+ }
+ if test.expected != matches[1] {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ }
+ }
+}
+
+func TestAndroidAppImport_Filename(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ }
+
+ android_app_import {
+ name: "bar",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ filename: "bar_sample.apk"
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ expected string
+ }{
+ {
+ name: "foo",
+ expected: "foo.apk",
+ },
+ {
+ name: "bar",
+ expected: "bar_sample.apk",
+ },
+ }
+
+ for _, test := range testCases {
+ variant := ctx.ModuleForTests(test.name, "android_common")
+ if variant.MaybeOutput(test.expected).Rule == nil {
+ t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
+ }
+
+ a := variant.Module().(*AndroidAppImport)
+ expectedValues := []string{test.expected}
+ actualValues := android.AndroidMkEntriesForTest(
+ t, config, "", a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+ if !reflect.DeepEqual(actualValues, expectedValues) {
+ t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
+ actualValues, expectedValues)
+ }
+ }
+}
+
+func TestAndroidAppImport_ArchVariants(t *testing.T) {
+ // The test config's target arch is ARM64.
+ testCases := []struct {
+ name string
+ bp string
+ expected string
+ }{
+ {
+ name: "matching arch",
+ bp: `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ arch: {
+ arm64: {
+ apk: "prebuilts/apk/app_arm64.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "prebuilts/apk/app_arm64.apk",
+ },
+ {
+ name: "no matching arch",
+ bp: `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ arch: {
+ arm: {
+ apk: "prebuilts/apk/app_arm.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "prebuilts/apk/app.apk",
+ },
+ {
+ name: "no matching arch without default",
+ bp: `
+ android_app_import {
+ name: "foo",
+ arch: {
+ arm: {
+ apk: "prebuilts/apk/app_arm.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "",
+ },
+ }
+
+ jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
+ for _, test := range testCases {
+ ctx, _ := testJava(t, test.bp)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+ if test.expected == "" {
+ if variant.Module().Enabled() {
+ t.Error("module should have been disabled, but wasn't")
+ }
+ continue
+ }
+ jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+ matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
+ if len(matches) != 2 {
+ t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
+ }
+ if test.expected != matches[1] {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ }
+ }
+}
+
+func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ enabled: false,
+ }
+
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ prefer: true,
+ }
+ `)
+
+ variant := ctx.ModuleForTests("prebuilt_foo", "android_common")
+ a := variant.Module().(*AndroidAppImport)
+ // The prebuilt module should still be enabled and active even if the source-based counterpart
+ // is disabled.
+ if !a.prebuilt.UsePrebuilt() {
+ t.Errorf("prebuilt foo module is not active")
+ }
+ if !a.Enabled() {
+ t.Errorf("prebuilt foo module is disabled")
+ }
+}
+
+func TestAndroidTestImport(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_test_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ data: [
+ "testdata/data",
+ ],
+ }
+ `)
+
+ test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
+
+ // Check android mks.
+ entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
+ expected := []string{"tests"}
+ actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Unexpected module tags - expected: %q, actual: %q", expected, actual)
+ }
+ expected = []string{"testdata/data:testdata/data"}
+ actual = entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
+ }
+}
+
+func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_test_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "cert/new_cert",
+ data: [
+ "testdata/data",
+ ],
+ }
+
+ android_test_import {
+ name: "foo_presigned",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ data: [
+ "testdata/data",
+ ],
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+ jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+ if !strings.HasPrefix(jniRule, "if (zipinfo") {
+ t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
+ }
+
+ variant = ctx.ModuleForTests("foo_presigned", "android_common")
+ jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
+ if jniRule != android.Cp.String() {
+ t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+ }
+ if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
+ t.Errorf("Presigned test apk should be aligned")
+ }
+}
+
+func TestAndroidTestImport_Preprocessed(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_test_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ preprocessed: true,
+ }
+
+ android_test_import {
+ name: "foo_cert",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "cert/new_cert",
+ preprocessed: true,
+ }
+ `)
+
+ testModules := []string{"foo", "foo_cert"}
+ for _, m := range testModules {
+ apkName := m + ".apk"
+ variant := ctx.ModuleForTests(m, "android_common")
+ jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
+ if jniRule != android.Cp.String() {
+ t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+ }
+
+ // Make sure signing and aligning were skipped.
+ if variant.MaybeOutput("signed/"+apkName).Rule != nil {
+ t.Errorf("signing rule shouldn't be included for preprocessed.")
+ }
+ if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
+ t.Errorf("aligning rule shouldn't be for preprocessed")
+ }
+ }
+}
diff --git a/java/app_set.go b/java/app_set.go
new file mode 100644
index 0000000..6b25638
--- /dev/null
+++ b/java/app_set.go
@@ -0,0 +1,162 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+// This file contains the module implementation for android_app_set.
+
+import (
+ "strconv"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ RegisterAppSetBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterAppSetBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_app_set", AndroidAppSetFactory)
+}
+
+type AndroidAppSetProperties struct {
+ // APK Set path
+ Set *string
+
+ // Specifies that this app should be installed to the priv-app directory,
+ // where the system will grant it additional privileges not available to
+ // normal apps.
+ Privileged *bool
+
+ // APKs in this set use prerelease SDK version
+ Prerelease *bool
+
+ // Names of modules to be overridden. Listed modules can only be other apps
+ // (in Make or Soong).
+ Overrides []string
+}
+
+type AndroidAppSet struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ prebuilt android.Prebuilt
+
+ properties AndroidAppSetProperties
+ packedOutput android.WritablePath
+ installFile string
+ apkcertsFile android.ModuleOutPath
+}
+
+func (as *AndroidAppSet) Name() string {
+ return as.prebuilt.Name(as.ModuleBase.Name())
+}
+
+func (as *AndroidAppSet) IsInstallable() bool {
+ return true
+}
+
+func (as *AndroidAppSet) Prebuilt() *android.Prebuilt {
+ return &as.prebuilt
+}
+
+func (as *AndroidAppSet) Privileged() bool {
+ return Bool(as.properties.Privileged)
+}
+
+func (as *AndroidAppSet) OutputFile() android.Path {
+ return as.packedOutput
+}
+
+func (as *AndroidAppSet) InstallFile() string {
+ return as.installFile
+}
+
+func (as *AndroidAppSet) APKCertsFile() android.Path {
+ return as.apkcertsFile
+}
+
+var TargetCpuAbi = map[string]string{
+ "arm": "ARMEABI_V7A",
+ "arm64": "ARM64_V8A",
+ "x86": "X86",
+ "x86_64": "X86_64",
+}
+
+func SupportedAbis(ctx android.ModuleContext) []string {
+ abiName := func(targetIdx int, deviceArch string) string {
+ if abi, found := TargetCpuAbi[deviceArch]; found {
+ return abi
+ }
+ ctx.ModuleErrorf("Target %d has invalid Arch: %s", targetIdx, deviceArch)
+ return "BAD_ABI"
+ }
+
+ var result []string
+ for i, target := range ctx.Config().Targets[android.Android] {
+ result = append(result, abiName(i, target.Arch.ArchType.String()))
+ }
+ return result
+}
+
+func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+ as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
+ // We are assuming here that the install file in the APK
+ // set has `.apk` suffix. If it doesn't the build will fail.
+ // APK sets containing APEX files are handled elsewhere.
+ as.installFile = as.BaseModuleName() + ".apk"
+ screenDensities := "all"
+ if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
+ screenDensities = strings.ToUpper(strings.Join(dpis, ","))
+ }
+ // TODO(asmundak): handle locales.
+ // TODO(asmundak): do we support device features
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: extractMatchingApks,
+ Description: "Extract APKs from APK set",
+ Output: as.packedOutput,
+ ImplicitOutput: as.apkcertsFile,
+ Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
+ Args: map[string]string{
+ "abis": strings.Join(SupportedAbis(ctx), ","),
+ "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
+ "screen-densities": screenDensities,
+ "sdk-version": ctx.Config().PlatformSdkVersion().String(),
+ "stem": as.BaseModuleName(),
+ "apkcerts": as.apkcertsFile.String(),
+ "partition": as.PartitionTag(ctx.DeviceConfig()),
+ },
+ })
+}
+
+// android_app_set extracts a set of APKs based on the target device
+// configuration and installs this set as "split APKs".
+// The extracted set always contains an APK whose name is
+// _module_name_.apk and every split APK matching target device.
+// The extraction of the density-specific splits depends on
+// PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
+// be a list density names: LDPI, MDPI, HDPI, etc.), only listed
+// splits will be extracted. Otherwise all density-specific splits
+// will be extracted.
+func AndroidAppSetFactory() android.Module {
+ module := &AndroidAppSet{}
+ module.AddProperties(&module.properties)
+ InitJavaModule(module, android.DeviceSupported)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
+ return module
+}
diff --git a/java/app_set_test.go b/java/app_set_test.go
new file mode 100644
index 0000000..d31900d
--- /dev/null
+++ b/java/app_set_test.go
@@ -0,0 +1,115 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestAndroidAppSet(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_app_set {
+ name: "foo",
+ set: "prebuilts/apks/app.apks",
+ prerelease: true,
+ }`)
+ module := ctx.ModuleForTests("foo", "android_common")
+ const packedSplitApks = "foo.zip"
+ params := module.Output(packedSplitApks)
+ if params.Rule == nil {
+ t.Errorf("expected output %s is missing", packedSplitApks)
+ }
+ if s := params.Args["allow-prereleased"]; s != "true" {
+ t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
+ }
+ if s := params.Args["partition"]; s != "system" {
+ t.Errorf("wrong partition value: '%s', expected 'system'", s)
+ }
+ mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+ actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
+ expectedInstallFile := []string{"foo.apk"}
+ if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
+ t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
+ actualInstallFile, expectedInstallFile)
+ }
+}
+
+func TestAndroidAppSet_Variants(t *testing.T) {
+ bp := `
+ android_app_set {
+ name: "foo",
+ set: "prebuilts/apks/app.apks",
+ }`
+ testCases := []struct {
+ name string
+ targets []android.Target
+ aaptPrebuiltDPI []string
+ sdkVersion int
+ expected map[string]string
+ }{
+ {
+ name: "One",
+ targets: []android.Target{
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
+ },
+ aaptPrebuiltDPI: []string{"ldpi", "xxhdpi"},
+ sdkVersion: 29,
+ expected: map[string]string{
+ "abis": "X86",
+ "allow-prereleased": "false",
+ "screen-densities": "LDPI,XXHDPI",
+ "sdk-version": "29",
+ "stem": "foo",
+ },
+ },
+ {
+ name: "Two",
+ targets: []android.Target{
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64}},
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
+ },
+ aaptPrebuiltDPI: nil,
+ sdkVersion: 30,
+ expected: map[string]string{
+ "abis": "X86_64,X86",
+ "allow-prereleased": "false",
+ "screen-densities": "all",
+ "sdk-version": "30",
+ "stem": "foo",
+ },
+ },
+ }
+
+ for _, test := range testCases {
+ config := testAppConfig(nil, bp, nil)
+ config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
+ config.Targets[android.Android] = test.targets
+ ctx := testContext(config)
+ run(t, ctx, config)
+ module := ctx.ModuleForTests("foo", "android_common")
+ const packedSplitApks = "foo.zip"
+ params := module.Output(packedSplitApks)
+ for k, v := range test.expected {
+ if actual := params.Args[k]; actual != v {
+ t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
+ test.name, k, actual, v)
+ }
+ }
+ }
+}
diff --git a/java/app_test.go b/java/app_test.go
index e13c6b9..8e13154 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -18,7 +18,6 @@
"fmt"
"path/filepath"
"reflect"
- "regexp"
"sort"
"strings"
"testing"
@@ -141,99 +140,6 @@
}
}
-func TestAndroidAppSet(t *testing.T) {
- ctx, config := testJava(t, `
- android_app_set {
- name: "foo",
- set: "prebuilts/apks/app.apks",
- prerelease: true,
- }`)
- module := ctx.ModuleForTests("foo", "android_common")
- const packedSplitApks = "foo.zip"
- params := module.Output(packedSplitApks)
- if params.Rule == nil {
- t.Errorf("expected output %s is missing", packedSplitApks)
- }
- if s := params.Args["allow-prereleased"]; s != "true" {
- t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
- }
- if s := params.Args["partition"]; s != "system" {
- t.Errorf("wrong partition value: '%s', expected 'system'", s)
- }
- mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
- actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
- expectedInstallFile := []string{"foo.apk"}
- if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
- t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
- actualInstallFile, expectedInstallFile)
- }
-}
-
-func TestAndroidAppSet_Variants(t *testing.T) {
- bp := `
- android_app_set {
- name: "foo",
- set: "prebuilts/apks/app.apks",
- }`
- testCases := []struct {
- name string
- targets []android.Target
- aaptPrebuiltDPI []string
- sdkVersion int
- expected map[string]string
- }{
- {
- name: "One",
- targets: []android.Target{
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
- },
- aaptPrebuiltDPI: []string{"ldpi", "xxhdpi"},
- sdkVersion: 29,
- expected: map[string]string{
- "abis": "X86",
- "allow-prereleased": "false",
- "screen-densities": "LDPI,XXHDPI",
- "sdk-version": "29",
- "stem": "foo",
- },
- },
- {
- name: "Two",
- targets: []android.Target{
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64}},
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
- },
- aaptPrebuiltDPI: nil,
- sdkVersion: 30,
- expected: map[string]string{
- "abis": "X86_64,X86",
- "allow-prereleased": "false",
- "screen-densities": "all",
- "sdk-version": "30",
- "stem": "foo",
- },
- },
- }
-
- for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
- config.Targets[android.Android] = test.targets
- ctx := testContext(config)
- run(t, ctx, config)
- module := ctx.ModuleForTests("foo", "android_common")
- const packedSplitApks = "foo.zip"
- params := module.Output(packedSplitApks)
- for k, v := range test.expected {
- if actual := params.Args[k]; actual != v {
- t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
- test.name, k, actual, v)
- }
- }
- }
-}
-
func TestPlatformAPIs(t *testing.T) {
testJava(t, `
android_app {
@@ -2226,475 +2132,6 @@
}
}
-func TestAndroidAppImport(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "platform",
- dex_preopt: {
- enabled: true,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
- t.Errorf("can't find dexpreopt outputs")
- }
-
- // Check cert signing flag.
- signedApk := variant.Output("signed/foo.apk")
- signingFlag := signedApk.Args["certificates"]
- expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
-}
-
-func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "platform",
- dex_preopt: {
- enabled: false,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs. They shouldn't exist.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
- t.Errorf("dexpreopt shouldn't have run.")
- }
-}
-
-func TestAndroidAppImport_Presigned(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
- t.Errorf("can't find dexpreopt outputs")
- }
- // Make sure signing was skipped and aligning was done.
- if variant.MaybeOutput("signed/foo.apk").Rule != nil {
- t.Errorf("signing rule shouldn't be included.")
- }
- if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
- t.Errorf("can't find aligning rule")
- }
-}
-
-func TestAndroidAppImport_SigningLineage(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "platform",
- lineage: "lineage.bin",
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check cert signing lineage flag.
- signedApk := variant.Output("signed/foo.apk")
- signingFlag := signedApk.Args["flags"]
- expected := "--lineage lineage.bin"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
-}
-
-func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- default_dev_cert: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
- t.Errorf("can't find dexpreopt outputs")
- }
-
- // Check cert signing flag.
- signedApk := variant.Output("signed/foo.apk")
- signingFlag := signedApk.Args["certificates"]
- expected := "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
-}
-
-func TestAndroidAppImport_DpiVariants(t *testing.T) {
- bp := `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- dpi_variants: {
- xhdpi: {
- apk: "prebuilts/apk/app_xhdpi.apk",
- },
- xxhdpi: {
- apk: "prebuilts/apk/app_xxhdpi.apk",
- },
- },
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `
- testCases := []struct {
- name string
- aaptPreferredConfig *string
- aaptPrebuiltDPI []string
- expected string
- }{
- {
- name: "no preferred",
- aaptPreferredConfig: nil,
- aaptPrebuiltDPI: []string{},
- expected: "prebuilts/apk/app.apk",
- },
- {
- name: "AAPTPreferredConfig matches",
- aaptPreferredConfig: proptools.StringPtr("xhdpi"),
- aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
- expected: "prebuilts/apk/app_xhdpi.apk",
- },
- {
- name: "AAPTPrebuiltDPI matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
- expected: "prebuilts/apk/app_xxhdpi.apk",
- },
- {
- name: "non-first AAPTPrebuiltDPI matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
- expected: "prebuilts/apk/app_xhdpi.apk",
- },
- {
- name: "no matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
- expected: "prebuilts/apk/app.apk",
- },
- }
-
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
- for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- ctx := testContext(config)
-
- run(t, ctx, config)
-
- variant := ctx.ModuleForTests("foo", "android_common")
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if test.expected != matches[1] {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
- }
- }
-}
-
-func TestAndroidAppImport_Filename(t *testing.T) {
- ctx, config := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- }
-
- android_app_import {
- name: "bar",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- filename: "bar_sample.apk"
- }
- `)
-
- testCases := []struct {
- name string
- expected string
- }{
- {
- name: "foo",
- expected: "foo.apk",
- },
- {
- name: "bar",
- expected: "bar_sample.apk",
- },
- }
-
- for _, test := range testCases {
- variant := ctx.ModuleForTests(test.name, "android_common")
- if variant.MaybeOutput(test.expected).Rule == nil {
- t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
- }
-
- a := variant.Module().(*AndroidAppImport)
- expectedValues := []string{test.expected}
- actualValues := android.AndroidMkEntriesForTest(
- t, config, "", a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
- if !reflect.DeepEqual(actualValues, expectedValues) {
- t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
- actualValues, expectedValues)
- }
- }
-}
-
-func TestAndroidAppImport_ArchVariants(t *testing.T) {
- // The test config's target arch is ARM64.
- testCases := []struct {
- name string
- bp string
- expected string
- }{
- {
- name: "matching arch",
- bp: `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- arch: {
- arm64: {
- apk: "prebuilts/apk/app_arm64.apk",
- },
- },
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `,
- expected: "prebuilts/apk/app_arm64.apk",
- },
- {
- name: "no matching arch",
- bp: `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- arch: {
- arm: {
- apk: "prebuilts/apk/app_arm.apk",
- },
- },
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `,
- expected: "prebuilts/apk/app.apk",
- },
- {
- name: "no matching arch without default",
- bp: `
- android_app_import {
- name: "foo",
- arch: {
- arm: {
- apk: "prebuilts/apk/app_arm.apk",
- },
- },
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `,
- expected: "",
- },
- }
-
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
- for _, test := range testCases {
- ctx, _ := testJava(t, test.bp)
-
- variant := ctx.ModuleForTests("foo", "android_common")
- if test.expected == "" {
- if variant.Module().Enabled() {
- t.Error("module should have been disabled, but wasn't")
- }
- continue
- }
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if test.expected != matches[1] {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
- }
- }
-}
-
-func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app {
- name: "foo",
- srcs: ["a.java"],
- enabled: false,
- }
-
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "platform",
- prefer: true,
- }
- `)
-
- variant := ctx.ModuleForTests("prebuilt_foo", "android_common")
- a := variant.Module().(*AndroidAppImport)
- // The prebuilt module should still be enabled and active even if the source-based counterpart
- // is disabled.
- if !a.prebuilt.UsePrebuilt() {
- t.Errorf("prebuilt foo module is not active")
- }
- if !a.Enabled() {
- t.Errorf("prebuilt foo module is disabled")
- }
-}
-
-func TestAndroidTestImport(t *testing.T) {
- ctx, config := testJava(t, `
- android_test_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- data: [
- "testdata/data",
- ],
- }
- `)
-
- test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
-
- // Check android mks.
- entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
- expected := []string{"tests"}
- actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
- if !reflect.DeepEqual(expected, actual) {
- t.Errorf("Unexpected module tags - expected: %q, actual: %q", expected, actual)
- }
- expected = []string{"testdata/data:testdata/data"}
- actual = entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
- if !reflect.DeepEqual(expected, actual) {
- t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
- }
-}
-
-func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
- ctx, _ := testJava(t, `
- android_test_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "cert/new_cert",
- data: [
- "testdata/data",
- ],
- }
-
- android_test_import {
- name: "foo_presigned",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- data: [
- "testdata/data",
- ],
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
- jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- if !strings.HasPrefix(jniRule, "if (zipinfo") {
- t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
- }
-
- variant = ctx.ModuleForTests("foo_presigned", "android_common")
- jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
- if jniRule != android.Cp.String() {
- t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
- }
- if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
- t.Errorf("Presigned test apk should be aligned")
- }
-}
-
-func TestAndroidTestImport_Preprocessed(t *testing.T) {
- ctx, _ := testJava(t, `
- android_test_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- preprocessed: true,
- }
-
- android_test_import {
- name: "foo_cert",
- apk: "prebuilts/apk/app.apk",
- certificate: "cert/new_cert",
- preprocessed: true,
- }
- `)
-
- testModules := []string{"foo", "foo_cert"}
- for _, m := range testModules {
- apkName := m + ".apk"
- variant := ctx.ModuleForTests(m, "android_common")
- jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
- if jniRule != android.Cp.String() {
- t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
- }
-
- // Make sure signing and aligning were skipped.
- if variant.MaybeOutput("signed/"+apkName).Rule != nil {
- t.Errorf("signing rule shouldn't be included for preprocessed.")
- }
- if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
- t.Errorf("aligning rule shouldn't be for preprocessed")
- }
- }
-}
-
func TestStl(t *testing.T) {
ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
@@ -3237,356 +2674,6 @@
}
}
-func TestRuntimeResourceOverlay(t *testing.T) {
- fs := map[string][]byte{
- "baz/res/res/values/strings.xml": nil,
- "bar/res/res/values/strings.xml": nil,
- }
- bp := `
- runtime_resource_overlay {
- name: "foo",
- certificate: "platform",
- lineage: "lineage.bin",
- product_specific: true,
- static_libs: ["bar"],
- resource_libs: ["baz"],
- aaptflags: ["--keep-raw-values"],
- }
-
- runtime_resource_overlay {
- name: "foo_themed",
- certificate: "platform",
- product_specific: true,
- theme: "faza",
- overrides: ["foo"],
- }
-
- android_library {
- name: "bar",
- resource_dirs: ["bar/res"],
- }
-
- android_app {
- name: "baz",
- sdk_version: "current",
- resource_dirs: ["baz/res"],
- }
- `
- config := testAppConfig(nil, bp, fs)
- ctx := testContext(config)
- run(t, ctx, config)
-
- m := ctx.ModuleForTests("foo", "android_common")
-
- // Check AAPT2 link flags.
- aapt2Flags := m.Output("package-res.apk").Args["flags"]
- expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
- absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
- if len(absentFlags) > 0 {
- t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
- }
-
- // Check overlay.list output for static_libs dependency.
- overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
- staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
- if !inList(staticLibPackage, overlayList) {
- t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
- }
-
- // Check AAPT2 link flags for resource_libs dependency.
- resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
- if !strings.Contains(aapt2Flags, resourceLibFlag) {
- t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
- }
-
- // Check cert signing flag.
- signedApk := m.Output("signed/foo.apk")
- lineageFlag := signedApk.Args["flags"]
- expectedLineageFlag := "--lineage lineage.bin"
- if expectedLineageFlag != lineageFlag {
- t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag)
- }
- signingFlag := signedApk.Args["certificates"]
- expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
- androidMkEntries := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
- path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
- expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_CERTIFICATE value: %v, expected: %v", path, expectedPath)
- }
-
- // Check device location.
- path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/product/overlay"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
-
- // A themed module has a different device location
- m = ctx.ModuleForTests("foo_themed", "android_common")
- androidMkEntries = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
- path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
-
- overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
- expectedOverrides := []string{"foo"}
- if !reflect.DeepEqual(overrides, expectedOverrides) {
- t.Errorf("Unexpected LOCAL_OVERRIDES_PACKAGES value: %v, expected: %v", overrides, expectedOverrides)
- }
-}
-
-func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
- ctx, config := testJava(t, `
- java_defaults {
- name: "rro_defaults",
- theme: "default_theme",
- product_specific: true,
- aaptflags: ["--keep-raw-values"],
- }
-
- runtime_resource_overlay {
- name: "foo_with_defaults",
- defaults: ["rro_defaults"],
- }
-
- runtime_resource_overlay {
- name: "foo_barebones",
- }
- `)
-
- //
- // RRO module with defaults
- //
- m := ctx.ModuleForTests("foo_with_defaults", "android_common")
-
- // Check AAPT2 link flags.
- aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
- expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
- absentFlags := android.RemoveListFromList(expectedFlags, aapt2Flags)
- if len(absentFlags) > 0 {
- t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
- }
-
- // Check device location.
- path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
- }
-
- //
- // RRO module without defaults
- //
- m = ctx.ModuleForTests("foo_barebones", "android_common")
-
- // Check AAPT2 link flags.
- aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
- unexpectedFlags := "--keep-raw-values"
- if inList(unexpectedFlags, aapt2Flags) {
- t.Errorf("unexpected value, %q is present in aapt2 link flags, %q", unexpectedFlags, aapt2Flags)
- }
-
- // Check device location.
- path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/system/overlay"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
-}
-
-func TestOverrideRuntimeResourceOverlay(t *testing.T) {
- ctx, _ := testJava(t, `
- runtime_resource_overlay {
- name: "foo_overlay",
- certificate: "platform",
- product_specific: true,
- sdk_version: "current",
- }
-
- override_runtime_resource_overlay {
- name: "bar_overlay",
- base: "foo_overlay",
- package_name: "com.android.bar.overlay",
- target_package_name: "com.android.bar",
- }
- `)
-
- expectedVariants := []struct {
- moduleName string
- variantName string
- apkPath string
- overrides []string
- targetVariant string
- packageFlag string
- targetPackageFlag string
- }{
- {
- variantName: "android_common",
- apkPath: "/target/product/test_device/product/overlay/foo_overlay.apk",
- overrides: nil,
- targetVariant: "android_common",
- packageFlag: "",
- targetPackageFlag: "",
- },
- {
- variantName: "android_common_bar_overlay",
- apkPath: "/target/product/test_device/product/overlay/bar_overlay.apk",
- overrides: []string{"foo_overlay"},
- targetVariant: "android_common_bar",
- packageFlag: "com.android.bar.overlay",
- targetPackageFlag: "com.android.bar",
- },
- }
- for _, expected := range expectedVariants {
- variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
-
- // Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
-
- // Check if the overrides field values are correctly aggregated.
- mod := variant.Module().(*RuntimeResourceOverlay)
- if !reflect.DeepEqual(expected.overrides, mod.properties.Overrides) {
- t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
- expected.overrides, mod.properties.Overrides)
- }
-
- // Check aapt2 flags.
- res := variant.Output("package-res.apk")
- aapt2Flags := res.Args["flags"]
- checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
- checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
- checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
- }
-}
-
-func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
- testCases := []struct {
- name string
- enforceRROTargets []string
- enforceRROExemptTargets []string
- rroDirs map[string][]string
- }{
- {
- name: "no RRO",
- enforceRROTargets: nil,
- enforceRROExemptTargets: nil,
- rroDirs: map[string][]string{
- "foo": nil,
- "bar": nil,
- },
- },
- {
- name: "enforce RRO on all",
- enforceRROTargets: []string{"*"},
- enforceRROExemptTargets: nil,
- rroDirs: map[string][]string{
- "foo": {"product/vendor/blah/overlay/lib2/res"},
- "bar": {"product/vendor/blah/overlay/lib2/res"},
- },
- },
- {
- name: "enforce RRO on foo",
- enforceRROTargets: []string{"foo"},
- enforceRROExemptTargets: nil,
- rroDirs: map[string][]string{
- "foo": {"product/vendor/blah/overlay/lib2/res"},
- "bar": {"product/vendor/blah/overlay/lib2/res"},
- },
- },
- {
- name: "enforce RRO on foo, bar exempted",
- enforceRROTargets: []string{"foo"},
- enforceRROExemptTargets: []string{"bar"},
- rroDirs: map[string][]string{
- "foo": {"product/vendor/blah/overlay/lib2/res"},
- "bar": nil,
- },
- },
- }
-
- productResourceOverlays := []string{
- "product/vendor/blah/overlay",
- }
-
- fs := map[string][]byte{
- "lib2/res/values/strings.xml": nil,
- "product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
- }
-
- bp := `
- android_app {
- name: "foo",
- sdk_version: "current",
- resource_dirs: [],
- static_libs: ["lib"],
- }
-
- android_app {
- name: "bar",
- sdk_version: "current",
- resource_dirs: [],
- static_libs: ["lib"],
- }
-
- android_library {
- name: "lib",
- sdk_version: "current",
- resource_dirs: [],
- static_libs: ["lib2"],
- }
-
- android_library {
- name: "lib2",
- sdk_version: "current",
- resource_dirs: ["lib2/res"],
- }
- `
-
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- config := testAppConfig(nil, bp, fs)
- config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
- if testCase.enforceRROTargets != nil {
- config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
- }
- if testCase.enforceRROExemptTargets != nil {
- config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets
- }
-
- ctx := testContext(config)
- run(t, ctx, config)
-
- modules := []string{"foo", "bar"}
- for _, moduleName := range modules {
- module := ctx.ModuleForTests(moduleName, "android_common")
- mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
- actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
- if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
- t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
- moduleName, testCase.rroDirs[moduleName], actualRRODirs)
- }
- }
- })
- }
-}
-
func TestExportedProguardFlagFiles(t *testing.T) {
ctx, _ := testJava(t, `
android_app {
diff --git a/java/java_test.go b/java/java_test.go
index f7cf03f..de5c55c 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -75,8 +75,11 @@
ctx := android.NewTestArchContext(config)
RegisterJavaBuildComponents(ctx)
RegisterAppBuildComponents(ctx)
+ RegisterAppImportBuildComponents(ctx)
+ RegisterAppSetBuildComponents(ctx)
RegisterAARBuildComponents(ctx)
RegisterGenRuleBuildComponents(ctx)
+ RegisterRuntimeResourceOverlayBuildComponents(ctx)
RegisterSystemModulesBuildComponents(ctx)
ctx.RegisterModuleType("java_plugin", PluginFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
diff --git a/java/rro.go b/java/rro.go
new file mode 100644
index 0000000..98cd379
--- /dev/null
+++ b/java/rro.go
@@ -0,0 +1,214 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+// This file contains the module implementations for runtime_resource_overlay and
+// override_runtime_resource_overlay.
+
+import "android/soong/android"
+
+func init() {
+ RegisterRuntimeResourceOverlayBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterRuntimeResourceOverlayBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
+ ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
+}
+
+type RuntimeResourceOverlay struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ android.OverridableModuleBase
+ aapt
+
+ properties RuntimeResourceOverlayProperties
+ overridableProperties OverridableRuntimeResourceOverlayProperties
+
+ certificate Certificate
+
+ outputFile android.Path
+ installDir android.InstallPath
+}
+
+type RuntimeResourceOverlayProperties struct {
+ // the name of a certificate in the default certificate directory or an android_app_certificate
+ // module name in the form ":module".
+ Certificate *string
+
+ // Name of the signing certificate lineage file.
+ Lineage *string
+
+ // optional theme name. If specified, the overlay package will be applied
+ // only when the ro.boot.vendor.overlay.theme system property is set to the same value.
+ Theme *string
+
+ // if not blank, set to the version of the sdk to compile against.
+ // Defaults to compiling against the current platform.
+ Sdk_version *string
+
+ // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+ // Defaults to sdk_version if not set.
+ Min_sdk_version *string
+
+ // list of android_library modules whose resources are extracted and linked against statically
+ Static_libs []string
+
+ // list of android_app modules whose resources are extracted and linked against
+ Resource_libs []string
+
+ // Names of modules to be overridden. Listed modules can only be other overlays
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden overlays, but if both
+ // overlays would be installed by default (in PRODUCT_PACKAGES) the other overlay will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+}
+
+// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
+// a RuntimeResourceOverlay module.
+type RuntimeResourceOverlayModule interface {
+ android.Module
+ OutputFile() android.Path
+ Certificate() Certificate
+ Theme() string
+}
+
+func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
+ sdkDep := decodeSdkDep(ctx, sdkContext(r))
+ if sdkDep.hasFrameworkLibs() {
+ r.aapt.deps(ctx, sdkDep)
+ }
+
+ cert := android.SrcIsModule(String(r.properties.Certificate))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+
+ ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
+ ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
+}
+
+func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Compile and link resources
+ r.aapt.hasNoCode = true
+ // Do not remove resources without default values nor dedupe resource configurations with the same value
+ aaptLinkFlags := []string{"--no-resource-deduping", "--no-resource-removal"}
+ // Allow the override of "package name" and "overlay target package name"
+ manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
+ if overridden || r.overridableProperties.Package_name != nil {
+ // The product override variable has a priority over the package_name property.
+ if !overridden {
+ manifestPackageName = *r.overridableProperties.Package_name
+ }
+ aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, false)...)
+ }
+ if r.overridableProperties.Target_package_name != nil {
+ aaptLinkFlags = append(aaptLinkFlags,
+ "--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
+ }
+ r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
+
+ // Sign the built package
+ _, certificates := collectAppDeps(ctx, r, false, false)
+ certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
+ signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
+ var lineageFile android.Path
+ if lineage := String(r.properties.Lineage); lineage != "" {
+ lineageFile = android.PathForModuleSrc(ctx, lineage)
+ }
+ SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile)
+ r.certificate = certificates[0]
+
+ r.outputFile = signed
+ r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
+ ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
+}
+
+func (r *RuntimeResourceOverlay) sdkVersion() sdkSpec {
+ return sdkSpecFrom(String(r.properties.Sdk_version))
+}
+
+func (r *RuntimeResourceOverlay) systemModules() string {
+ return ""
+}
+
+func (r *RuntimeResourceOverlay) minSdkVersion() sdkSpec {
+ if r.properties.Min_sdk_version != nil {
+ return sdkSpecFrom(*r.properties.Min_sdk_version)
+ }
+ return r.sdkVersion()
+}
+
+func (r *RuntimeResourceOverlay) targetSdkVersion() sdkSpec {
+ return r.sdkVersion()
+}
+
+func (r *RuntimeResourceOverlay) Certificate() Certificate {
+ return r.certificate
+}
+
+func (r *RuntimeResourceOverlay) OutputFile() android.Path {
+ return r.outputFile
+}
+
+func (r *RuntimeResourceOverlay) Theme() string {
+ return String(r.properties.Theme)
+}
+
+// runtime_resource_overlay generates a resource-only apk file that can overlay application and
+// system resources at run time.
+func RuntimeResourceOverlayFactory() android.Module {
+ module := &RuntimeResourceOverlay{}
+ module.AddProperties(
+ &module.properties,
+ &module.aaptProperties,
+ &module.overridableProperties)
+
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitOverridableModule(module, &module.properties.Overrides)
+ return module
+}
+
+// runtime_resource_overlay properties that can be overridden by override_runtime_resource_overlay
+type OverridableRuntimeResourceOverlayProperties struct {
+ // the package name of this app. The package name in the manifest file is used if one was not given.
+ Package_name *string
+
+ // the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
+ Target_package_name *string
+}
+
+type OverrideRuntimeResourceOverlay struct {
+ android.ModuleBase
+ android.OverrideModuleBase
+}
+
+func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(_ android.ModuleContext) {
+ // All the overrides happen in the base module.
+ // TODO(jungjw): Check the base module type.
+}
+
+// override_runtime_resource_overlay is used to create a module based on another
+// runtime_resource_overlay module by overriding some of its properties.
+func OverrideRuntimeResourceOverlayModuleFactory() android.Module {
+ m := &OverrideRuntimeResourceOverlay{}
+ m.AddProperties(&OverridableRuntimeResourceOverlayProperties{})
+
+ android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ android.InitOverrideModule(m)
+ return m
+}
diff --git a/java/rro_test.go b/java/rro_test.go
new file mode 100644
index 0000000..345f2ee
--- /dev/null
+++ b/java/rro_test.go
@@ -0,0 +1,373 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestRuntimeResourceOverlay(t *testing.T) {
+ fs := map[string][]byte{
+ "baz/res/res/values/strings.xml": nil,
+ "bar/res/res/values/strings.xml": nil,
+ }
+ bp := `
+ runtime_resource_overlay {
+ name: "foo",
+ certificate: "platform",
+ lineage: "lineage.bin",
+ product_specific: true,
+ static_libs: ["bar"],
+ resource_libs: ["baz"],
+ aaptflags: ["--keep-raw-values"],
+ }
+
+ runtime_resource_overlay {
+ name: "foo_themed",
+ certificate: "platform",
+ product_specific: true,
+ theme: "faza",
+ overrides: ["foo"],
+ }
+
+ android_library {
+ name: "bar",
+ resource_dirs: ["bar/res"],
+ }
+
+ android_app {
+ name: "baz",
+ sdk_version: "current",
+ resource_dirs: ["baz/res"],
+ }
+ `
+ config := testAppConfig(nil, bp, fs)
+ ctx := testContext(config)
+ run(t, ctx, config)
+
+ m := ctx.ModuleForTests("foo", "android_common")
+
+ // Check AAPT2 link flags.
+ aapt2Flags := m.Output("package-res.apk").Args["flags"]
+ expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
+ absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
+ if len(absentFlags) > 0 {
+ t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
+ }
+
+ // Check overlay.list output for static_libs dependency.
+ overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
+ staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
+ if !inList(staticLibPackage, overlayList) {
+ t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
+ }
+
+ // Check AAPT2 link flags for resource_libs dependency.
+ resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
+ if !strings.Contains(aapt2Flags, resourceLibFlag) {
+ t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
+ }
+
+ // Check cert signing flag.
+ signedApk := m.Output("signed/foo.apk")
+ lineageFlag := signedApk.Args["flags"]
+ expectedLineageFlag := "--lineage lineage.bin"
+ if expectedLineageFlag != lineageFlag {
+ t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag)
+ }
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+ androidMkEntries := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+ path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
+ expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_CERTIFICATE value: %v, expected: %v", path, expectedPath)
+ }
+
+ // Check device location.
+ path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{"/tmp/target/product/test_device/product/overlay"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+
+ // A themed module has a different device location
+ m = ctx.ModuleForTests("foo_themed", "android_common")
+ androidMkEntries = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+ path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+
+ overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
+ expectedOverrides := []string{"foo"}
+ if !reflect.DeepEqual(overrides, expectedOverrides) {
+ t.Errorf("Unexpected LOCAL_OVERRIDES_PACKAGES value: %v, expected: %v", overrides, expectedOverrides)
+ }
+}
+
+func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
+ ctx, config := testJava(t, `
+ java_defaults {
+ name: "rro_defaults",
+ theme: "default_theme",
+ product_specific: true,
+ aaptflags: ["--keep-raw-values"],
+ }
+
+ runtime_resource_overlay {
+ name: "foo_with_defaults",
+ defaults: ["rro_defaults"],
+ }
+
+ runtime_resource_overlay {
+ name: "foo_barebones",
+ }
+ `)
+
+ //
+ // RRO module with defaults
+ //
+ m := ctx.ModuleForTests("foo_with_defaults", "android_common")
+
+ // Check AAPT2 link flags.
+ aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
+ expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
+ absentFlags := android.RemoveListFromList(expectedFlags, aapt2Flags)
+ if len(absentFlags) > 0 {
+ t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
+ }
+
+ // Check device location.
+ path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
+ }
+
+ //
+ // RRO module without defaults
+ //
+ m = ctx.ModuleForTests("foo_barebones", "android_common")
+
+ // Check AAPT2 link flags.
+ aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
+ unexpectedFlags := "--keep-raw-values"
+ if inList(unexpectedFlags, aapt2Flags) {
+ t.Errorf("unexpected value, %q is present in aapt2 link flags, %q", unexpectedFlags, aapt2Flags)
+ }
+
+ // Check device location.
+ path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{"/tmp/target/product/test_device/system/overlay"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+}
+
+func TestOverrideRuntimeResourceOverlay(t *testing.T) {
+ ctx, _ := testJava(t, `
+ runtime_resource_overlay {
+ name: "foo_overlay",
+ certificate: "platform",
+ product_specific: true,
+ sdk_version: "current",
+ }
+
+ override_runtime_resource_overlay {
+ name: "bar_overlay",
+ base: "foo_overlay",
+ package_name: "com.android.bar.overlay",
+ target_package_name: "com.android.bar",
+ }
+ `)
+
+ expectedVariants := []struct {
+ moduleName string
+ variantName string
+ apkPath string
+ overrides []string
+ targetVariant string
+ packageFlag string
+ targetPackageFlag string
+ }{
+ {
+ variantName: "android_common",
+ apkPath: "/target/product/test_device/product/overlay/foo_overlay.apk",
+ overrides: nil,
+ targetVariant: "android_common",
+ packageFlag: "",
+ targetPackageFlag: "",
+ },
+ {
+ variantName: "android_common_bar_overlay",
+ apkPath: "/target/product/test_device/product/overlay/bar_overlay.apk",
+ overrides: []string{"foo_overlay"},
+ targetVariant: "android_common_bar",
+ packageFlag: "com.android.bar.overlay",
+ targetPackageFlag: "com.android.bar",
+ },
+ }
+ for _, expected := range expectedVariants {
+ variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
+
+ // Check the final apk name
+ outputs := variant.AllOutputs()
+ expectedApkPath := buildDir + expected.apkPath
+ found := false
+ for _, o := range outputs {
+ if o == expectedApkPath {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
+ }
+
+ // Check if the overrides field values are correctly aggregated.
+ mod := variant.Module().(*RuntimeResourceOverlay)
+ if !reflect.DeepEqual(expected.overrides, mod.properties.Overrides) {
+ t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
+ expected.overrides, mod.properties.Overrides)
+ }
+
+ // Check aapt2 flags.
+ res := variant.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
+ checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
+ checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
+ }
+}
+
+func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
+ testCases := []struct {
+ name string
+ enforceRROTargets []string
+ enforceRROExemptTargets []string
+ rroDirs map[string][]string
+ }{
+ {
+ name: "no RRO",
+ enforceRROTargets: nil,
+ enforceRROExemptTargets: nil,
+ rroDirs: map[string][]string{
+ "foo": nil,
+ "bar": nil,
+ },
+ },
+ {
+ name: "enforce RRO on all",
+ enforceRROTargets: []string{"*"},
+ enforceRROExemptTargets: nil,
+ rroDirs: map[string][]string{
+ "foo": {"product/vendor/blah/overlay/lib2/res"},
+ "bar": {"product/vendor/blah/overlay/lib2/res"},
+ },
+ },
+ {
+ name: "enforce RRO on foo",
+ enforceRROTargets: []string{"foo"},
+ enforceRROExemptTargets: nil,
+ rroDirs: map[string][]string{
+ "foo": {"product/vendor/blah/overlay/lib2/res"},
+ "bar": {"product/vendor/blah/overlay/lib2/res"},
+ },
+ },
+ {
+ name: "enforce RRO on foo, bar exempted",
+ enforceRROTargets: []string{"foo"},
+ enforceRROExemptTargets: []string{"bar"},
+ rroDirs: map[string][]string{
+ "foo": {"product/vendor/blah/overlay/lib2/res"},
+ "bar": nil,
+ },
+ },
+ }
+
+ productResourceOverlays := []string{
+ "product/vendor/blah/overlay",
+ }
+
+ fs := map[string][]byte{
+ "lib2/res/values/strings.xml": nil,
+ "product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
+ }
+
+ bp := `
+ android_app {
+ name: "foo",
+ sdk_version: "current",
+ resource_dirs: [],
+ static_libs: ["lib"],
+ }
+
+ android_app {
+ name: "bar",
+ sdk_version: "current",
+ resource_dirs: [],
+ static_libs: ["lib"],
+ }
+
+ android_library {
+ name: "lib",
+ sdk_version: "current",
+ resource_dirs: [],
+ static_libs: ["lib2"],
+ }
+
+ android_library {
+ name: "lib2",
+ sdk_version: "current",
+ resource_dirs: ["lib2/res"],
+ }
+ `
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ config := testAppConfig(nil, bp, fs)
+ config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
+ if testCase.enforceRROTargets != nil {
+ config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
+ }
+ if testCase.enforceRROExemptTargets != nil {
+ config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets
+ }
+
+ ctx := testContext(config)
+ run(t, ctx, config)
+
+ modules := []string{"foo", "bar"}
+ for _, moduleName := range modules {
+ module := ctx.ModuleForTests(moduleName, "android_common")
+ mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+ actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
+ if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
+ t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
+ moduleName, testCase.rroDirs[moduleName], actualRRODirs)
+ }
+ }
+ })
+ }
+}