diff --git a/android/apex.go b/android/apex.go index 31c62e994..4f1d41bdf 100644 --- a/android/apex.go +++ b/android/apex.go @@ -111,6 +111,19 @@ func (i ApexInfo) InApex(apex string) bool { return false } +// InApexByBaseName tells whether this apex variant of the module is part of the given APEX or not, +// where the APEX is specified by its canonical base name, i.e. typically beginning with +// "com.android.". In particular this function doesn't differentiate between source and prebuilt +// APEXes, where the latter may have "prebuilt_" prefixes. +func (i ApexInfo) InApexByBaseName(apex string) bool { + for _, a := range i.InApexes { + if RemoveOptionalPrebuiltPrefix(a) == apex { + return true + } + } + return false +} + // ApexTestForInfo stores the contents of APEXes for which this module is a test - although this // module is not part of the APEX - and thus has access to APEX internals. type ApexTestForInfo struct { diff --git a/apex/apex_test.go b/apex/apex_test.go index ae0cbe969..39eeeb218 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -4330,6 +4330,215 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { }) } +func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { + transform := func(config *dexpreopt.GlobalConfig) { + config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"}) + } + + checkBootDexJarPath := func(ctx *android.TestContext, bootDexJarPath string) { + s := ctx.SingletonForTests("dex_bootjars") + foundLibfooJar := false + for _, output := range s.AllOutputs() { + if strings.HasSuffix(output, "/libfoo.jar") { + foundLibfooJar = true + buildRule := s.Output(output) + actual := android.NormalizePathForTesting(buildRule.Input) + if actual != bootDexJarPath { + t.Errorf("Incorrect boot dex jar path '%s', expected '%s'", actual, bootDexJarPath) + } + } + } + if !foundLibfooJar { + t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs") + } + } + + t.Run("prebuilt only", func(t *testing.T) { + bp := ` + prebuilt_apex { + name: "myapex", + arch: { + arm64: { + src: "myapex-arm64.apex", + }, + arm: { + src: "myapex-arm.apex", + }, + }, + exported_java_libs: ["libfoo"], + } + + java_import { + name: "libfoo", + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + }) + + t.Run("prebuilt with source library preferred", func(t *testing.T) { + bp := ` + prebuilt_apex { + name: "myapex", + arch: { + arm64: { + src: "myapex-arm64.apex", + }, + arm: { + src: "myapex-arm.apex", + }, + }, + exported_java_libs: ["libfoo"], + } + + java_import { + name: "libfoo", + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + apex_available: ["myapex"], + } + ` + + // In this test the source (java_library) libfoo is active since the + // prebuilt (java_import) defaults to prefer:false. However the + // prebuilt_apex module always depends on the prebuilt, and so it doesn't + // find the dex boot jar in it. We either need to disable the source libfoo + // or make the prebuilt libfoo preferred. + testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", transform) + }) + + t.Run("prebuilt library preferred with source", func(t *testing.T) { + bp := ` + prebuilt_apex { + name: "myapex", + arch: { + arm64: { + src: "myapex-arm64.apex", + }, + arm: { + src: "myapex-arm.apex", + }, + }, + exported_java_libs: ["libfoo"], + } + + java_import { + name: "libfoo", + prefer: true, + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + }) + + t.Run("prebuilt with source apex preferred", func(t *testing.T) { + bp := ` + apex { + name: "myapex", + key: "myapex.key", + java_libs: ["libfoo"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + prebuilt_apex { + name: "myapex", + arch: { + arm64: { + src: "myapex-arm64.apex", + }, + arm: { + src: "myapex-arm.apex", + }, + }, + exported_java_libs: ["libfoo"], + } + + java_import { + name: "libfoo", + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/libfoo/android_common_apex10000/aligned/libfoo.jar") + }) + + t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) { + bp := ` + apex { + name: "myapex", + enabled: false, + key: "myapex.key", + java_libs: ["libfoo"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + prebuilt_apex { + name: "myapex", + arch: { + arm64: { + src: "myapex-arm64.apex", + }, + arm: { + src: "myapex-arm.apex", + }, + }, + exported_java_libs: ["libfoo"], + } + + java_import { + name: "libfoo", + prefer: true, + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + }) +} + func TestApexWithTests(t *testing.T) { ctx, config := testApex(t, ` apex_test { @@ -5934,10 +6143,11 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt "build/make/target/product/security": nil, "apex_manifest.json": nil, "AndroidManifest.xml": nil, + "system/sepolicy/apex/myapex-file_contexts": nil, "system/sepolicy/apex/some-updatable-apex-file_contexts": nil, "system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil, "system/sepolicy/apex/com.android.art.debug-file_contexts": nil, - "framework/aidl/a.aidl": nil, + "framework/aidl/a.aidl": nil, } cc.GatherRequiredFilesForTest(fs) diff --git a/java/boot_jars.go b/java/boot_jars.go index 823275b1d..ac8107b7c 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -49,14 +49,36 @@ func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex return true } +// isActiveModule returns true if the given module should be considered for boot +// jars, i.e. if it's enabled and the preferred one in case of source and +// prebuilt alternatives. +func isActiveModule(module android.Module) bool { + if !module.Enabled() { + return false + } + if module.IsReplacedByPrebuilt() { + // A source module that has been replaced by a prebuilt counterpart. + return false + } + if prebuilt, ok := module.(android.PrebuiltInterface); ok { + if p := prebuilt.Prebuilt(); p != nil { + return p.UsePrebuilt() + } + } + return true +} + func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { config := ctx.Config() if config.SkipBootJarsCheck() { return } - // Populate a map from module name to APEX from the boot jars. If there is a problem - // such as duplicate modules then fail and return immediately. + // Populate a map from module name to APEX from the boot jars. If there is a + // problem such as duplicate modules then fail and return immediately. Note + // that both module and APEX names are tracked by base names here, so we need + // to be careful to remove "prebuilt_" prefixes when comparing them with + // actual modules and APEX bundles. moduleToApex := make(map[string]string) if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") || !populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") { @@ -69,10 +91,14 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { // Scan all the modules looking for the module/apex variants corresponding to the // boot jars. ctx.VisitAllModules(func(module android.Module) { - name := ctx.ModuleName(module) + if !isActiveModule(module) { + return + } + + name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module)) if apex, ok := moduleToApex[name]; ok { apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) - if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApex(apex) { + if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexByBaseName(apex) { // The module name/apex variant should be unique in the system but double check // just in case something has gone wrong. if existing, ok := nameToApexVariant[name]; ok { diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 004cbbb5a..2dcc9e234 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -479,7 +479,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul // A platform variant is required but this is for an apex so ignore it. return -1, nil } - } else if !android.InList(requiredApex, apexInfo.InApexes) { + } else if !apexInfo.InApexByBaseName(requiredApex) { // An apex variant for a specific apex is required but this is the wrong apex. return -1, nil } @@ -489,7 +489,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul switch image.name { case artBootImageName: - if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") { + if apexInfo.InApexByBaseName("com.android.art") || apexInfo.InApexByBaseName("com.android.art.debug") || apexInfo.InApexByBaseName("com.android.art,testing") { // ok: found the jar in the ART apex } else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { // exception (skip and continue): Jacoco platform variant for a coverage build @@ -516,21 +516,17 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul return index, jar.DexJarBuildPath() } -func allHavePrefix(list []string, prefix string) bool { - for _, s := range list { - if s != prefix && !strings.HasPrefix(s, prefix+".") { - return false - } - } - return true -} - // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image. func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig { // Collect dex jar paths for the boot image modules. // This logic is tested in the apex package to avoid import cycle apex <-> java. bootDexJars := make(android.Paths, image.modules.Len()) + ctx.VisitAllModules(func(module android.Module) { + if !isActiveModule(module) { + return + } + if i, j := getBootImageJar(ctx, image, module); i != -1 { if existing := bootDexJars[i]; existing != nil { ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s", @@ -860,6 +856,9 @@ func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConf // Collect `permitted_packages` for updatable boot jars. var updatablePackages []string ctx.VisitAllModules(func(module android.Module) { + if !isActiveModule(module) { + return + } if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok { name := ctx.ModuleName(module) if i := android.IndexList(name, updatableModules); i != -1 {