diff options
Diffstat (limited to 'java')
-rw-r--r-- | java/aar.go | 79 | ||||
-rw-r--r-- | java/android_manifest.go | 60 | ||||
-rw-r--r-- | java/androidmk.go | 36 | ||||
-rw-r--r-- | java/app.go | 389 | ||||
-rw-r--r-- | java/app_builder.go | 9 | ||||
-rw-r--r-- | java/app_test.go | 330 | ||||
-rw-r--r-- | java/config/config.go | 2 | ||||
-rw-r--r-- | java/dexpreopt.go | 13 | ||||
-rw-r--r-- | java/dexpreopt_bootjars.go | 4 | ||||
-rw-r--r-- | java/dexpreopt_bootjars_test.go | 2 | ||||
-rw-r--r-- | java/droiddoc.go | 30 | ||||
-rw-r--r-- | java/hiddenapi.go | 9 | ||||
-rw-r--r-- | java/java.go | 24 | ||||
-rw-r--r-- | java/java_test.go | 11 | ||||
-rw-r--r-- | java/sdk.go | 1 | ||||
-rw-r--r-- | java/testing.go | 5 |
16 files changed, 868 insertions, 136 deletions
diff --git a/java/aar.go b/java/aar.go index 6273a9b5..53d946f6 100644 --- a/java/aar.go +++ b/java/aar.go @@ -30,7 +30,7 @@ type AndroidLibraryDependency interface { ExportedProguardFlagFiles() android.Paths ExportedRRODirs() []rroDir ExportedStaticPackages() android.Paths - ExportedManifest() android.Path + ExportedManifests() android.Paths } func init() { @@ -69,21 +69,26 @@ type aaptProperties struct { // path to AndroidManifest.xml. If unset, defaults to "AndroidManifest.xml". Manifest *string `android:"path"` + + // paths to additional manifest files to merge with main manifest. + Additional_manifests []string `android:"path"` } type aapt struct { - aaptSrcJar android.Path - exportPackage android.Path - manifestPath android.Path - proguardOptionsFile android.Path - rroDirs []rroDir - rTxt android.Path - extraAaptPackagesFile android.Path - noticeFile android.OptionalPath - isLibrary bool - uncompressedJNI bool - useEmbeddedDex bool - usesNonSdkApis bool + aaptSrcJar android.Path + exportPackage android.Path + manifestPath android.Path + transitiveManifestPaths android.Paths + proguardOptionsFile android.Path + rroDirs []rroDir + rTxt android.Path + extraAaptPackagesFile android.Path + mergedManifestFile android.Path + noticeFile android.OptionalPath + isLibrary bool + uncompressedJNI bool + useEmbeddedDex bool + usesNonSdkApis bool splitNames []string splits []split @@ -105,8 +110,8 @@ func (a *aapt) ExportedRRODirs() []rroDir { return a.rroDirs } -func (a *aapt) ExportedManifest() android.Path { - return a.manifestPath +func (a *aapt) ExportedManifests() android.Paths { + return a.transitiveManifestPaths } func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (flags []string, @@ -177,7 +182,7 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, mani if !hasVersionName { var versionName string - if ctx.ModuleName() == "framework-res" { + if ctx.ModuleName() == "framework-res" || ctx.ModuleName() == "org.lineageos.platform-res" { // Some builds set AppsDefaultVersionName() to include the build number ("O-123456"). aapt2 copies the // version name of framework-res into app manifests as compileSdkVersionCodename, which confuses things // if it contains the build number. Use the PlatformVersionName instead. @@ -197,17 +202,37 @@ func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkContext sdkContext) { if sdkDep.frameworkResModule != "" { ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule) } + if sdkDep.lineageResModule != "" { + ctx.AddDependency(ctx.Module(), lineageResTag, sdkDep.lineageResModule) + } } func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, extraLinkFlags ...string) { - transitiveStaticLibs, staticLibManifests, staticRRODirs, libDeps, libFlags := aaptLibs(ctx, sdkContext) + transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, libDeps, libFlags := aaptLibs(ctx, sdkContext) // App manifest file manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) - manifestPath := manifestMerger(ctx, manifestSrcPath, sdkContext, staticLibManifests, a.isLibrary, - a.uncompressedJNI, a.useEmbeddedDex, a.usesNonSdkApis) + manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, + a.isLibrary, a.uncompressedJNI, a.usesNonSdkApis, a.useEmbeddedDex) + + // Add additional manifest files to transitive manifests. + additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests) + a.transitiveManifestPaths = append(android.Paths{manifestPath}, additionalManifests...) + a.transitiveManifestPaths = append(a.transitiveManifestPaths, transitiveStaticLibManifests...) + + if len(a.transitiveManifestPaths) > 1 { + a.mergedManifestFile = manifestMerger(ctx, a.transitiveManifestPaths[0], a.transitiveManifestPaths[1:], a.isLibrary) + if !a.isLibrary { + // Only use the merged manifest for applications. For libraries, the transitive closure of manifests + // will be propagated to the final application and merged there. The merged manifest for libraries is + // only passed to Make, which can't handle transitive dependencies. + manifestPath = a.mergedManifestFile + } + } else { + a.mergedManifestFile = manifestPath + } linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath) @@ -294,7 +319,7 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, ex } // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths -func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, staticLibManifests android.Paths, +func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, deps android.Paths, flags []string) { var sharedLibs android.Paths @@ -314,7 +339,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati switch ctx.OtherModuleDependencyTag(module) { case instrumentationForTag: // Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2. - case libTag, frameworkResTag: + case libTag, frameworkResTag, lineageResTag: if exportPackage != nil { sharedLibs = append(sharedLibs, exportPackage) } @@ -322,7 +347,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati if exportPackage != nil { transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...) transitiveStaticLibs = append(transitiveStaticLibs, exportPackage) - staticLibManifests = append(staticLibManifests, aarDep.ExportedManifest()) + transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...) outer: for _, d := range aarDep.ExportedRRODirs() { @@ -349,8 +374,9 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati } transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs) + transitiveStaticLibManifests = android.FirstUniquePaths(transitiveStaticLibManifests) - return transitiveStaticLibs, staticLibManifests, staticRRODirs, deps, flags + return transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, deps, flags } type AndroidLibrary struct { @@ -506,8 +532,8 @@ func (a *AARImport) ExportedStaticPackages() android.Paths { return a.exportedStaticPackages } -func (a *AARImport) ExportedManifest() android.Path { - return a.manifest +func (a *AARImport) ExportedManifests() android.Paths { + return android.Paths{a.manifest} } func (a *AARImport) Prebuilt() *android.Prebuilt { @@ -524,6 +550,9 @@ func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) { if sdkDep.useModule && sdkDep.frameworkResModule != "" { ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule) } + if sdkDep.useModule && sdkDep.lineageResModule != "" { + ctx.AddDependency(ctx.Module(), lineageResTag, sdkDep.lineageResModule) + } } ctx.AddVariationDependencies(nil, libTag, a.properties.Libs...) diff --git a/java/android_manifest.go b/java/android_manifest.go index 8dc3b475..ea7c2dd4 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -36,13 +36,14 @@ var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer", var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", blueprint.RuleParams{ - Command: `${config.ManifestMergerCmd} --main $in $libs --out $out`, + Command: `${config.ManifestMergerCmd} $args --main $in $libs --out $out`, CommandDeps: []string{"${config.ManifestMergerCmd}"}, }, - "libs") + "args", "libs") -func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, - staticLibManifests android.Paths, isLibrary, uncompressedJNI, useEmbeddedDex, usesNonSdkApis bool) android.Path { +// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml +func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, + isLibrary, uncompressedJNI, usesNonSdkApis, useEmbeddedDex bool) android.Path { var args []string if isLibrary { @@ -79,35 +80,44 @@ func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext deps = append(deps, apiFingerprint) } - // Inject minSdkVersion into the manifest fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml") ctx.Build(pctx, android.BuildParams{ - Rule: manifestFixerRule, - Input: manifest, - Implicits: deps, - Output: fixedManifest, + Rule: manifestFixerRule, + Description: "fix manifest", + Input: manifest, + Implicits: deps, + Output: fixedManifest, Args: map[string]string{ "minSdkVersion": sdkVersionOrDefault(ctx, sdkContext.minSdkVersion()), "targetSdkVersion": targetSdkVersion, "args": strings.Join(args, " "), }, }) - manifest = fixedManifest - - // Merge static aar dependency manifests if necessary - if len(staticLibManifests) > 0 { - mergedManifest := android.PathForModuleOut(ctx, "manifest_merger", "AndroidManifest.xml") - ctx.Build(pctx, android.BuildParams{ - Rule: manifestMergerRule, - Input: manifest, - Implicits: staticLibManifests, - Output: mergedManifest, - Args: map[string]string{ - "libs": android.JoinWithPrefix(staticLibManifests.Strings(), "--libs "), - }, - }) - manifest = mergedManifest + + return fixedManifest +} + +func manifestMerger(ctx android.ModuleContext, manifest android.Path, staticLibManifests android.Paths, + isLibrary bool) android.Path { + + var args string + if !isLibrary { + // Follow Gradle's behavior, only pass --remove-tools-declarations when merging app manifests. + args = "--remove-tools-declarations" } - return manifest + mergedManifest := android.PathForModuleOut(ctx, "manifest_merger", "AndroidManifest.xml") + ctx.Build(pctx, android.BuildParams{ + Rule: manifestMergerRule, + Description: "merge manifest", + Input: manifest, + Implicits: staticLibManifests, + Output: mergedManifest, + Args: map[string]string{ + "libs": android.JoinWithPrefix(staticLibManifests.Strings(), "--libs "), + "args": args, + }, + }) + + return mergedManifest } diff --git a/java/androidmk.go b/java/androidmk.go index 43b9f488..9d238c5e 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -39,8 +39,8 @@ func (library *Library) AndroidMkHostDex(w io.Writer, name string, data android. fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String()) fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String()) fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(data.Required, " ")) - if r := library.deviceProperties.Target.Hostdex.Required; len(r) > 0 { - fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(r, " ")) + if len(data.Required) > 0 { + fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(data.Required, " ")) } fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk") } @@ -271,7 +271,7 @@ func (app *AndroidApp) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", app.proguardDictionary.String()) } - if app.Name() == "framework-res" { + if app.Name() == "framework-res" || app.Name() == "org.lineageos.platform-res" { fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)") // Make base_rules.mk not put framework-res in a subdirectory called // framework_res. @@ -320,7 +320,8 @@ func (app *AndroidApp) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", app.dexpreopter.builtInstalled) } for _, split := range app.aapt.splits { - install := "$(LOCAL_MODULE_PATH)/" + strings.TrimSuffix(app.installApkName, ".apk") + split.suffix + ".apk" + install := app.onDeviceDir + "/" + + strings.TrimSuffix(app.installApkName, ".apk") + "_" + split.suffix + ".apk" fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED +=", split.path.String()+":"+install) } }, @@ -372,7 +373,7 @@ func (a *AndroidLibrary) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", a.proguardDictionary.String()) } - if a.Name() == "framework-res" { + if a.Name() == "framework-res" || a.Name() == "org.lineageos.platform-res" { fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)") // Make base_rules.mk not put framework-res in a subdirectory called // framework_res. @@ -381,7 +382,7 @@ func (a *AndroidLibrary) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", a.exportPackage.String()) fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", a.extraAaptPackagesFile.String()) - fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", a.manifestPath.String()) + fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", a.mergedManifestFile.String()) fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", strings.Join(a.exportedProguardFlagFiles.Strings(), " ")) fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") @@ -584,6 +585,29 @@ func (dstubs *Droidstubs) AndroidMk() android.AndroidMkData { } } +func (a *AndroidAppImport) AndroidMkEntries() android.AndroidMkEntries { + return android.AndroidMkEntries{ + Class: "APPS", + OutputFile: android.OptionalPathForPath(a.outputFile), + Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk", + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(entries *android.AndroidMkEntries) { + entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", Bool(a.properties.Privileged)) + if a.certificate != nil { + entries.SetString("LOCAL_CERTIFICATE", a.certificate.Pem.String()) + } else { + entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED") + } + entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...) + if len(a.dexpreopter.builtInstalled) > 0 { + entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled) + } + entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel()) + }, + }, + } +} + func androidMkWriteTestData(data android.Paths, ret *android.AndroidMkData) { var testFiles []string for _, d := range data { diff --git a/java/app.go b/java/app.go index ad672ead..fdea4350 100644 --- a/java/app.go +++ b/java/app.go @@ -17,24 +17,29 @@ package java // This file contains the module types for compiling Android apps. import ( + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" "path/filepath" + "reflect" "sort" "strings" - "github.com/google/blueprint" - "github.com/google/blueprint/proptools" - "android/soong/android" "android/soong/cc" "android/soong/tradefed" ) +var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"} + func init() { android.RegisterModuleType("android_app", AndroidAppFactory) android.RegisterModuleType("android_test", AndroidTestFactory) android.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory) android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory) android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory) + android.RegisterModuleType("android_app_import", AndroidAppImportFactory) + + initAndroidAppImportVariantGroupTypes() } // AndroidManifest.xml merging @@ -114,6 +119,10 @@ type AndroidApp struct { // the install APK name is normally the same as the module name, but can be overridden with PRODUCT_PACKAGE_NAME_OVERRIDES. installApkName string + installDir android.OutputPath + + onDeviceDir string + additionalAaptFlags []string } @@ -188,11 +197,9 @@ func (a *AndroidApp) shouldUncompressDex(ctx android.ModuleContext) bool { return true } - // Uncompress dex in APKs of privileged apps, and modules used by privileged apps - // (even for unbundled builds, they may be preinstalled as prebuilts). - if ctx.Config().UncompressPrivAppDex() && - (Bool(a.appProperties.Privileged) || - inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules())) { + // Uncompress dex in APKs of privileged apps (even for unbundled builds, they may + // be preinstalled as prebuilts). + if ctx.Config().UncompressPrivAppDex() && Bool(a.appProperties.Privileged) { return true } @@ -200,12 +207,7 @@ func (a *AndroidApp) shouldUncompressDex(ctx android.ModuleContext) bool { return false } - // Uncompress if the dex files is preopted on /system. - if !a.dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, a.dexpreopter.installPath)) { - return true - } - - return false + return shouldUncompressDex(ctx, &a.dexpreopter) } func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { @@ -286,7 +288,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) a.deviceProperties.UncompressDex = a.dexpreopter.uncompressedDex - if ctx.ModuleName() != "framework-res" { + if ctx.ModuleName() != "framework-res" && ctx.ModuleName() != "org.lineageos.platform-res" { a.Module.compile(ctx, a.aaptSrcJar) } @@ -308,40 +310,41 @@ func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext return jniJarFile } -func (a *AndroidApp) certificateBuildActions(certificateDeps []Certificate, ctx android.ModuleContext) []Certificate { - cert := a.getCertString(ctx) - certModule := android.SrcIsModule(cert) - if certModule != "" { - a.certificate = certificateDeps[0] - certificateDeps = certificateDeps[1:] - } else if cert != "" { - defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) - a.certificate = Certificate{ - defaultDir.Join(ctx, cert+".x509.pem"), - defaultDir.Join(ctx, cert+".pk8"), +// Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it +// isn't a cert module reference. Also checks and enforces system cert restriction if applicable. +func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate { + if android.SrcIsModule(certPropValue) == "" { + var mainCert Certificate + if certPropValue != "" { + defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) + mainCert = Certificate{ + defaultDir.Join(ctx, certPropValue+".x509.pem"), + defaultDir.Join(ctx, certPropValue+".pk8"), + } + } else { + pem, key := ctx.Config().DefaultAppCertificate(ctx) + mainCert = Certificate{pem, key} } - } else { - pem, key := ctx.Config().DefaultAppCertificate(ctx) - a.certificate = Certificate{pem, key} + certificates = append([]Certificate{mainCert}, certificates...) } - if !a.Module.Platform() { - certPath := a.certificate.Pem.String() + if !m.Platform() { + certPath := certificates[0].Pem.String() systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String() if strings.HasPrefix(certPath, systemCertPath) { enforceSystemCert := ctx.Config().EnforceSystemCertificate() whitelist := ctx.Config().EnforceSystemCertificateWhitelist() - if enforceSystemCert && !inList(a.Module.Name(), whitelist) { + if enforceSystemCert && !inList(m.Name(), whitelist) { ctx.PropertyErrorf("certificate", "The module in product partition cannot be signed with certificate in system.") } } } - return append([]Certificate{a.certificate}, certificateDeps...) + return certificates } -func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) android.OptionalPath { +func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) android.OptionalPath { if !Bool(a.appProperties.Embed_notices) && !ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { return android.OptionalPath{} } @@ -388,7 +391,7 @@ func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir an sort.Slice(noticePaths, func(i, j int) bool { return noticePaths[i].String() < noticePaths[j].String() }) - noticeFile := android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths) + noticeFile := android.BuildNoticeOutput(ctx, a.installDir, a.installApkName+".apk", noticePaths) return android.OptionalPathForPath(noticeFile) } @@ -397,17 +400,22 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { // Check if the install APK name needs to be overridden. a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name()) - var installDir android.OutputPath if ctx.ModuleName() == "framework-res" { // framework-res.apk is installed as system/framework/framework-res.apk - installDir = android.PathForModuleInstall(ctx, "framework") + a.installDir = android.PathForModuleInstall(ctx, "framework") + } else if ctx.ModuleName() == "org.lineageos.platform-res" { + // org.lineageos.platform-res.apk needs to be in system/framework + a.installDir = android.PathForModuleInstall(ctx, "framework") } else if Bool(a.appProperties.Privileged) { - installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName) + a.installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName) + } else if ctx.InstallInTestcases() { + a.installDir = android.PathForModuleInstall(ctx, a.installApkName) } else { - installDir = android.PathForModuleInstall(ctx, "app", a.installApkName) + a.installDir = android.PathForModuleInstall(ctx, "app", a.installApkName) } + a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir) - a.aapt.noticeFile = a.noticeBuildActions(ctx, installDir) + a.aapt.noticeFile = a.noticeBuildActions(ctx) // Process all building blocks, from AAPT to certificates. a.aaptBuildActions(ctx) @@ -416,25 +424,26 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { dexJarFile := a.dexBuildActions(ctx) - jniLibs, certificateDeps := a.collectAppDeps(ctx) + jniLibs, certificateDeps := collectAppDeps(ctx) jniJarFile := a.jniBuildActions(jniLibs, ctx) if ctx.Failed() { return } - certificates := a.certificateBuildActions(certificateDeps, ctx) + certificates := processMainCert(a.ModuleBase, a.getCertString(ctx), certificateDeps, ctx) + a.certificate = certificates[0] // Build a final signed app package. // TODO(jungjw): Consider changing this to installApkName. packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk") - CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates) + CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates) a.outputFile = packageFile for _, split := range a.aapt.splits { // Sign the split APKs packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"_"+split.suffix+".apk") - CreateAppPackage(ctx, packageFile, split.path, nil, nil, certificates) + CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates) a.extraOutputFiles = append(a.extraOutputFiles, packageFile) } @@ -444,13 +453,13 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.bundleFile = bundleFile // Install the app package. - ctx.InstallFile(installDir, a.installApkName+".apk", a.outputFile) + ctx.InstallFile(a.installDir, a.installApkName+".apk", a.outputFile) for _, split := range a.aapt.splits { - ctx.InstallFile(installDir, a.installApkName+"_"+split.suffix+".apk", split.path) + ctx.InstallFile(a.installDir, a.installApkName+"_"+split.suffix+".apk", split.path) } } -func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Certificate) { +func collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Certificate) { var jniLibs []jniLib var certificates []Certificate @@ -472,7 +481,6 @@ func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Cert } } else { ctx.ModuleErrorf("jni_libs dependency %q must be a cc library", otherName) - } } else if tag == certificateTag { if dep, ok := module.(*AndroidAppCertificate); ok { @@ -539,6 +547,10 @@ type AndroidTest struct { data android.Paths } +func (a *AndroidTest) InstallInTestcases() bool { + return true +} + func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Check if the instrumentation target package is overridden before generating build actions. if a.appTestProperties.Instrumentation_for != nil { @@ -680,3 +692,284 @@ func OverrideAndroidAppModuleFactory() android.Module { android.InitOverrideModule(m) return m } + +type AndroidAppImport struct { + android.ModuleBase + android.DefaultableModuleBase + prebuilt android.Prebuilt + + properties AndroidAppImportProperties + dpiVariants interface{} + archVariants interface{} + + outputFile android.Path + certificate *Certificate + + dexpreopter + + installPath android.OutputPath +} + +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 + + // 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 +} + +// 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().Targets[android.Android][0].Arch.ArchType + MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name) +} + +func MergePropertiesFromVariant(ctx android.BaseModuleContext, + 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) + } +} + +func (a *AndroidAppImport) uncompressEmbeddedJniLibs( + ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { + rule := android.NewRuleBuilder() + rule.Command(). + Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath). + Tool(ctx.Config().HostToolPath(ctx, "zip2zip")). + FlagWithInput("-i ", inputPath). + FlagWithOutput("-o ", outputPath). + FlagWithArg("-0 ", "'lib/**/*.so'"). + Textf(`; else cp -f %s %s; fi`, inputPath, outputPath) + rule.Build(pctx, ctx, "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() { + return false + } + + // Uncompress dex in APKs of privileged apps + if ctx.Config().UncompressPrivAppDex() && Bool(a.properties.Privileged) { + return true + } + + return shouldUncompressDex(ctx, &a.dexpreopter) +} + +func (a *AndroidAppImport) uncompressDex( + ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { + rule := android.NewRuleBuilder() + rule.Command(). + Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath). + Tool(ctx.Config().HostToolPath(ctx, "zip2zip")). + FlagWithInput("-i ", inputPath). + FlagWithOutput("-o ", outputPath). + FlagWithArg("-0 ", "'classes*.dex'"). + Textf(`; else cp -f %s %s; fi`, inputPath, outputPath) + rule.Build(pctx, ctx, "uncompress-dex", "Uncompress dex files") +} + +func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { + 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) + + // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK + // TODO: LOCAL_PACKAGE_SPLITS + + srcApk := a.prebuilt.SingleSourcePath(ctx) + + // 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) + + installDir := android.PathForModuleInstall(ctx, "app", a.BaseModuleName()) + a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk") + a.dexpreopter.isInstallable = true + a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) + a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) + dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed) + if a.dexpreopter.uncompressedDex { + dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk") + a.uncompressDex(ctx, dexOutput, dexUncompressed.OutputPath) + dexOutput = dexUncompressed + } + + // Sign or align the package + // TODO: Handle EXTERNAL + 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", ctx.ModuleName()+".apk") + SignAppPackage(ctx, signed, dexOutput, certificates) + a.outputFile = signed + } else { + alignedApk := android.PathForModuleOut(ctx, "zip-aligned", ctx.ModuleName()+".apk") + TransformZipAlign(ctx, alignedApk, dexOutput) + a.outputFile = alignedApk + } + + // TODO: Optionally compress the output apk. + + a.installPath = ctx.InstallFile(installDir, + proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk"), 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()) +} + +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 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.populateAllVariantStructs() + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + module.processVariants(ctx) + }) + + InitJavaModule(module, android.DeviceSupported) + android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk") + + return module +} diff --git a/java/app_builder.go b/java/app_builder.go index 5bacb677..82a390f6 100644 --- a/java/app_builder.go +++ b/java/app_builder.go @@ -62,7 +62,7 @@ var combineApk = pctx.AndroidStaticRule("combineApk", CommandDeps: []string{"${config.MergeZipsCmd}"}, }) -func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath, +func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath, packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate) { unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk" @@ -83,6 +83,11 @@ func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath Output: unsignedApk, }) + SignAppPackage(ctx, outputFile, unsignedApk, certificates) +} + +func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate) { + var certificateArgs []string var deps android.Paths for _, c := range certificates { @@ -93,7 +98,7 @@ func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath ctx.Build(pctx, android.BuildParams{ Rule: Signapk, Description: "signapk", - Output: outputFile, + Output: signedApk, Input: unsignedApk, Implicits: deps, Args: map[string]string{ diff --git a/java/app_test.go b/java/app_test.go index 2bd44ad4..a0beaca8 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -15,17 +15,22 @@ package java import ( - "android/soong/android" - "android/soong/cc" - "fmt" "path/filepath" "reflect" +/* + "regexp" +*/ "sort" "strings" "testing" +/* "github.com/google/blueprint/proptools" +*/ + + "android/soong/android" + "android/soong/cc" ) var ( @@ -82,8 +87,11 @@ func TestApp(t *testing.T) { expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String()) frameworkRes := ctx.ModuleForTests("framework-res", "android_common") + lineageRes := ctx.ModuleForTests("org.lineageos.platform-res", "android_common") expectedLinkImplicits = append(expectedLinkImplicits, frameworkRes.Output("package-res.apk").Output.String()) + expectedLinkImplicits = append(expectedLinkImplicits, + lineageRes.Output("package-res.apk").Output.String()) // Test the mapping from input files to compiled output file names compile := foo.Output(compiledResourceFiles[0]) @@ -714,7 +722,7 @@ func TestCertificates(t *testing.T) { } `, certificateOverride: "", - expected: "build/target/product/security/testkey.x509.pem build/target/product/security/testkey.pk8", + expected: "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8", }, { name: "module certificate property", @@ -743,7 +751,7 @@ func TestCertificates(t *testing.T) { } `, certificateOverride: "", - expected: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8", + expected: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", }, { name: "certificate overrides", @@ -912,7 +920,7 @@ func TestOverrideAndroidApp(t *testing.T) { { variantName: "android_common", apkPath: "/target/product/test_device/system/app/foo/foo.apk", - signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8", + signFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", overrides: []string{"baz"}, aaptFlag: "", }, @@ -926,7 +934,7 @@ func TestOverrideAndroidApp(t *testing.T) { { variantName: "baz_android_common", apkPath: "/target/product/test_device/system/app/baz/baz.apk", - signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8", + signFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", overrides: []string{"baz", "foo"}, aaptFlag: "--rename-manifest-package org.dandroid.bp", }, @@ -1066,6 +1074,7 @@ func TestEmbedNotice(t *testing.T) { } } +/* func TestUncompressDex(t *testing.T) { testCases := []struct { name string @@ -1148,3 +1157,310 @@ func TestUncompressDex(t *testing.T) { }) } } +*/ + +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 stripping wasn't done. + stripRule := variant.Output("dexpreopt/foo.apk") + if !strings.HasPrefix(stripRule.RuleParams.Command, "cp -f") { + t.Errorf("unexpected, non-skipping strip command: %q", stripRule.RuleParams.Command) + } + + // Make sure signing was skipped and aligning was done instead. + 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_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 := testConfig(nil) + config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig + config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI + ctx := testAppContext(config, bp, nil) + + 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) { + config := testConfig(nil) + ctx := 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).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", + }, + } + + jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)") + for _, test := range testCases { + ctx := testJava(t, test.bp) + + 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]) + } + } +} +*/ diff --git a/java/config/config.go b/java/config/config.go index 7f968bcc..7263205b 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -53,7 +53,7 @@ func init() { pctx.StaticVariable("JavacHeapSize", "2048M") pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}") - pctx.StaticVariable("DexFlags", "-JXX:+TieredCompilation -JXX:TieredStopAtLevel=1") + pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads") pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{ `-Xmaxerrs 9999999`, diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 9141f9ef..088c12df 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -22,11 +22,12 @@ import ( type dexpreopter struct { dexpreoptProperties DexpreoptProperties - installPath android.OutputPath - uncompressedDex bool - isSDKLibrary bool - isTest bool - isInstallable bool + installPath android.OutputPath + uncompressedDex bool + isSDKLibrary bool + isTest bool + isInstallable bool + isPresignedPrebuilt bool builtInstalled string } @@ -177,6 +178,8 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true), ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false), + PresignedPrebuilt: d.isPresignedPrebuilt, + NoStripping: Bool(d.dexpreoptProperties.Dex_preopt.No_stripping), StripInputPath: dexJarFile, StripOutputPath: strippedDexJarFile.OutputPath, diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 4d87b2f7..87d8428d 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -151,6 +151,10 @@ func buildBootImage(ctx android.SingletonContext, config bootImageConfig) *bootI bootDexJars := make(android.Paths, len(image.modules)) ctx.VisitAllModules(func(module android.Module) { + if m, ok := module.(interface{ BootJarProvider() bool }); !ok || + !m.BootJarProvider() { + return + } // Collect dex jar paths for the modules listed above. if j, ok := module.(interface{ DexJar() android.Path }); ok { name := ctx.ModuleName(module) diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go index cbb52f15..d4f84914 100644 --- a/java/dexpreopt_bootjars_test.go +++ b/java/dexpreopt_bootjars_test.go @@ -53,6 +53,8 @@ func TestDexpreoptBootJars(t *testing.T) { ctx := testContext(config, bp, nil) + ctx.PreArchMutators(android.RegisterBootJarMutators) + ctx.RegisterSingletonType("dex_bootjars", android.SingletonFactoryAdaptor(dexpreoptBootJarsFactory)) run(t, ctx, config) diff --git a/java/droiddoc.go b/java/droiddoc.go index f56cae82..003811ac 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -429,8 +429,10 @@ func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDevi android.InitDefaultableModule(module) } -func apiCheckEnabled(apiToCheck ApiToCheck, apiVersionTag string) bool { - if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" { +func apiCheckEnabled(ctx android.ModuleContext, apiToCheck ApiToCheck, apiVersionTag string) bool { + if ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") { + return false + } else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" { return true } else if String(apiToCheck.Api_file) != "" { panic("for " + apiVersionTag + " removed_api_file has to be non-empty!") @@ -993,8 +995,8 @@ func (d *Droiddoc) collectDoclavaDocsFlags(ctx android.ModuleContext, implicits func (d *Droiddoc) collectStubsFlags(ctx android.ModuleContext, implicitOutputs *android.WritablePaths) string { var doclavaFlags string - if apiCheckEnabled(d.properties.Check_api.Current, "current") || - apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") || + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || + apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || String(d.properties.Api_filename) != "" { d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt") doclavaFlags += " -api " + d.apiFile.String() @@ -1002,8 +1004,8 @@ func (d *Droiddoc) collectStubsFlags(ctx android.ModuleContext, d.apiFilePath = d.apiFile } - if apiCheckEnabled(d.properties.Check_api.Current, "current") || - apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") || + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || + apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || String(d.properties.Removed_api_filename) != "" { d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt") doclavaFlags += " -removedApi " + d.removedApiFile.String() @@ -1183,7 +1185,7 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { flags.postDoclavaCmds) } - if apiCheckEnabled(d.properties.Check_api.Current, "current") && + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") && !ctx.Config().IsPdkBuild() { apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file), "check_api.current.api_file") @@ -1209,7 +1211,7 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { d.updateCurrentApiTimestamp) } - if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") && + if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") && !ctx.Config().IsPdkBuild() { apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file), "check_api.last_released.api_file") @@ -1336,8 +1338,8 @@ func (d *Droidstubs) initBuilderFlags(ctx android.ModuleContext, implicits *andr func (d *Droidstubs) collectStubsFlags(ctx android.ModuleContext, implicitOutputs *android.WritablePaths) string { var metalavaFlags string - if apiCheckEnabled(d.properties.Check_api.Current, "current") || - apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") || + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || + apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || String(d.properties.Api_filename) != "" { d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt") metalavaFlags = metalavaFlags + " --api " + d.apiFile.String() @@ -1345,8 +1347,8 @@ func (d *Droidstubs) collectStubsFlags(ctx android.ModuleContext, d.apiFilePath = d.apiFile } - if apiCheckEnabled(d.properties.Check_api.Current, "current") || - apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") || + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || + apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || String(d.properties.Removed_api_filename) != "" { d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt") metalavaFlags = metalavaFlags + " --removed-api " + d.removedApiFile.String() @@ -1683,7 +1685,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { flags.metalavaStubsFlags+flags.metalavaAnnotationsFlags+flags.metalavaInclusionAnnotationsFlags+ flags.metalavaApiLevelsAnnotationsFlags+flags.metalavaApiToXmlFlags+" "+d.Javadoc.args) - if apiCheckEnabled(d.properties.Check_api.Current, "current") && + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") && !ctx.Config().IsPdkBuild() { apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file), "check_api.current.api_file") @@ -1720,7 +1722,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { d.updateCurrentApiTimestamp) } - if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") && + if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") && !ctx.Config().IsPdkBuild() { apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file), "check_api.last_released.api_file") diff --git a/java/hiddenapi.go b/java/hiddenapi.go index 6020aba6..28724f2a 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -73,7 +73,14 @@ func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, dexJar android.ModuleOu // to the hidden API for the bootclassloader. If information is gathered for modules // not on the list then that will cause failures in the CtsHiddenApiBlacklist... // tests. - if inList(bootJarName, ctx.Config().BootJars()) { + isBootJarProvider := false + ctx.VisitAllModuleVariants(func(module android.Module) { + if m, ok := module.(interface{ BootJarProvider() bool }); ok && + m.BootJarProvider() { + isBootJarProvider = true + } + }) + if isBootJarProvider && inList(bootJarName, ctx.Config().BootJars()) { // Derive the greylist from classes jar. flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv") metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv") diff --git a/java/java.go b/java/java.go index 9ac38c92..7fedf6d6 100644 --- a/java/java.go +++ b/java/java.go @@ -406,6 +406,7 @@ var ( bootClasspathTag = dependencyTag{name: "bootclasspath"} systemModulesTag = dependencyTag{name: "system modules"} frameworkResTag = dependencyTag{name: "framework-res"} + lineageResTag = dependencyTag{name: "org.lineageos.platform-res"} frameworkApkTag = dependencyTag{name: "framework-apk"} kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"} kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"} @@ -421,6 +422,7 @@ type sdkDep struct { systemModules string frameworkResModule string + lineageResModule string jars android.Paths aidl android.OptionalPath @@ -486,12 +488,21 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } if (ctx.ModuleName() == "framework") || (ctx.ModuleName() == "framework-annotation-proc") { ctx.AddVariationDependencies(nil, frameworkResTag, "framework-res") + ctx.AddDependency(ctx.Module(), lineageResTag, "org.lineageos.platform-res") } if ctx.ModuleName() == "android_stubs_current" || ctx.ModuleName() == "android_system_stubs_current" || ctx.ModuleName() == "android_test_stubs_current" { ctx.AddVariationDependencies(nil, frameworkApkTag, "framework-res") } + if ctx.ModuleName() == "org.lineageos.platform-res" { + ctx.AddDependency(ctx.Module(), frameworkResTag, "framework-res") + } + if ctx.ModuleName() == "org.lineageos.platform" || + ctx.ModuleName() == "org.lineageos.platform.internal" || + ctx.ModuleName() == "org.lineageos.platform.sdk" { + ctx.AddDependency(ctx.Module(), lineageResTag, "org.lineageos.platform-res") + } } ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...) @@ -760,6 +771,19 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { // generated by framework-res.apk deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar) } + case lineageResTag: + if ctx.ModuleName() == "org.lineageos.platform" || + ctx.ModuleName() == "org.lineageos.platform.internal" || + ctx.ModuleName() == "org.lineageos.platform.sdk" { + // org.lineageos.platform.jar has a one-off dependency on the R.java and Manifest.java files + // generated by org.lineageos.platform-res.apk + deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar) + } + if ctx.ModuleName() == "framework" { + // framework.jar has a one-off dependency on the R.java and Manifest.java files + // generated by org.lineageos.platform-res.apk + deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar) + } case frameworkApkTag: if ctx.ModuleName() == "android_stubs_current" || ctx.ModuleName() == "android_system_stubs_current" || diff --git a/java/java_test.go b/java/java_test.go index 31b23e76..bafb466c 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -63,6 +63,7 @@ func testContext(config android.Config, bp string, ctx := android.NewTestArchContext() ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory)) ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(AndroidAppCertificateFactory)) + ctx.RegisterModuleType("android_app_import", android.ModuleFactoryAdaptor(AndroidAppImportFactory)) ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory)) ctx.RegisterModuleType("android_test", android.ModuleFactoryAdaptor(AndroidTestFactory)) ctx.RegisterModuleType("android_test_helper_app", android.ModuleFactoryAdaptor(AndroidTestHelperAppFactory)) @@ -167,9 +168,15 @@ func testContext(config android.Config, bp string, "prebuilts/sdk/tools/core-lambda-stubs.jar": nil, "prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`), + "prebuilts/apk/app.apk": nil, + "prebuilts/apk/app_arm.apk": nil, + "prebuilts/apk/app_arm64.apk": nil, + "prebuilts/apk/app_xhdpi.apk": nil, + "prebuilts/apk/app_xxhdpi.apk": nil, + // For framework-res, which is an implicit dependency for framework - "AndroidManifest.xml": nil, - "build/target/product/security/testkey": nil, + "AndroidManifest.xml": nil, + "build/make/target/product/security/testkey": nil, "build/soong/scripts/jar-wrapper.sh": nil, diff --git a/java/sdk.go b/java/sdk.go index e93f8fb6..dab04e71 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -182,6 +182,7 @@ func decodeSdkDep(ctx android.BaseContext, sdkContext sdkContext) sdkDep { return sdkDep{ useDefaultLibs: true, frameworkResModule: "framework-res", + lineageResModule: "org.lineageos.platform-res", } case "current": return toModule("android_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx)) diff --git a/java/testing.go b/java/testing.go index 6b35bd04..c805e0ef 100644 --- a/java/testing.go +++ b/java/testing.go @@ -76,6 +76,11 @@ func GatherRequiredDepsForTest() string { name: "framework-res", no_framework_libs: true, } + + android_app { + name: "org.lineageos.platform-res", + no_framework_libs: true, + } ` systemModules := []string{ |