diff --git a/android/module.go b/android/module.go index 26451812c..02b2c8926 100644 --- a/android/module.go +++ b/android/module.go @@ -227,6 +227,7 @@ type Module interface { InstallBypassMake() bool InstallForceOS() *OsType SkipInstall() + IsSkipInstall() bool ExportedToMake() bool InitRc() Paths VintfFragments() Paths @@ -950,6 +951,10 @@ func (m *ModuleBase) SkipInstall() { m.commonProperties.SkipInstall = true } +func (m *ModuleBase) IsSkipInstall() bool { + return m.commonProperties.SkipInstall == true +} + func (m *ModuleBase) ExportedToMake() bool { return m.commonProperties.NamespaceExportedToMake } diff --git a/apex/apex_test.go b/apex/apex_test.go index 0e8c9af6f..62c2c4277 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -27,6 +27,7 @@ import ( "android/soong/android" "android/soong/cc" + "android/soong/dexpreopt" "android/soong/java" ) @@ -4198,6 +4199,175 @@ func TestAppBundle(t *testing.T) { ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo/AppFoo.apk"}]}`) } +func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) { + t.Helper() + + bp = bp + ` + filegroup { + name: "some-updatable-apex-file_contexts", + srcs: [ + "system/sepolicy/apex/some-updatable-apex-file_contexts", + ], + } + ` + bp += cc.GatherRequiredDepsForTest(android.Android) + bp += java.GatherRequiredDepsForTest() + bp += dexpreopt.BpToolModulesForTest() + + fs := map[string][]byte{ + "a.java": nil, + "a.jar": nil, + "build/make/target/product/security": nil, + "apex_manifest.json": nil, + "AndroidManifest.xml": nil, + "system/sepolicy/apex/some-updatable-apex-file_contexts": nil, + "system/sepolicy/apex/com.android.art.something-file_contexts": nil, + "framework/aidl/a.aidl": nil, + } + cc.GatherRequiredFilesForTest(fs) + + ctx := android.NewTestArchContext() + ctx.RegisterModuleType("apex", BundleFactory) + ctx.RegisterModuleType("apex_key", ApexKeyFactory) + ctx.RegisterModuleType("filegroup", android.FileGroupFactory) + cc.RegisterRequiredBuildComponentsForTest(ctx) + java.RegisterJavaBuildComponents(ctx) + java.RegisterSystemModulesBuildComponents(ctx) + java.RegisterAppBuildComponents(ctx) + java.RegisterDexpreoptBootJarsComponents(ctx) + ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) + ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) + ctx.PreDepsMutators(RegisterPreDepsMutators) + ctx.PostDepsMutators(RegisterPostDepsMutators) + + config := android.TestArchConfig(buildDir, nil, bp, fs) + ctx.Register(config) + + _ = dexpreopt.GlobalSoongConfigForTests(config) + dexpreopt.RegisterToolModulesForTest(ctx) + pathCtx := android.PathContextForTesting(config) + dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx) + transformDexpreoptConfig(dexpreoptConfig) + dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig) + + _, errs := ctx.ParseBlueprintsFiles("Android.bp") + android.FailIfErrored(t, errs) + + _, errs = ctx.PrepareBuildActions(config) + if errmsg == "" { + android.FailIfErrored(t, errs) + } else if len(errs) > 0 { + android.FailIfNoMatchingErrors(t, errmsg, errs) + return + } else { + t.Fatalf("missing expected error %q (0 errors are returned)", errmsg) + } +} + +func TestNoUpdatableJarsInBootImage(t *testing.T) { + bp := ` + java_library { + name: "some-updatable-apex-lib", + srcs: ["a.java"], + apex_available: [ + "some-updatable-apex", + ], + } + + java_library { + name: "some-platform-lib", + srcs: ["a.java"], + installable: true, + } + + java_library { + name: "some-art-lib", + srcs: ["a.java"], + apex_available: [ + "com.android.art.something", + ], + hostdex: true, + } + + apex { + name: "some-updatable-apex", + key: "some-updatable-apex.key", + java_libs: ["some-updatable-apex-lib"], + } + + apex_key { + name: "some-updatable-apex.key", + } + + apex { + name: "com.android.art.something", + key: "com.android.art.something.key", + java_libs: ["some-art-lib"], + } + + apex_key { + name: "com.android.art.something.key", + } + ` + + var error string + var transform func(*dexpreopt.GlobalConfig) + + // updatable jar from ART apex in the ART boot image => ok + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"some-art-lib"} + } + testNoUpdatableJarsInBootImage(t, "", bp, transform) + + // updatable jar from ART apex in the framework boot image => error + error = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"some-art-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // updatable jar from some other apex in the ART boot image => error + error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"some-updatable-apex-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // updatable jar from some other apex in the framework boot image => error + error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"some-updatable-apex-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // nonexistent jar in the ART boot image => error + error = "failed to find a dex jar path for module 'nonexistent'" + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"nonexistent"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // nonexistent jar in the framework boot image => error + error = "failed to find a dex jar path for module 'nonexistent'" + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"nonexistent"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // platform jar in the ART boot image => error + error = "module 'some-platform-lib' is part of the platform and not allowed in the ART boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"some-platform-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // platform jar in the framework boot image => ok + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"some-platform-lib"} + } + testNoUpdatableJarsInBootImage(t, "", bp, transform) +} + func TestMain(m *testing.M) { run := func() int { setUp() diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index d00864d23..d53022f0a 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -26,7 +26,7 @@ import ( ) func init() { - android.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory) + RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext) } // Target-independent description of pre-compiled boot image. @@ -174,6 +174,10 @@ func dexpreoptBootJarsFactory() android.Singleton { return &dexpreoptBootJars{} } +func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) { + ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory) +} + func skipDexpreoptBootJars(ctx android.PathContext) bool { if dexpreopt.GetGlobalConfig(ctx).DisablePreopt { return true @@ -242,16 +246,63 @@ func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) { dumpOatRules(ctx, d.defaultBootImage) } +// Inspect this module to see if it contains a bootclasspath dex jar. +// Note that the same jar may occur in multiple modules. +// This logic is tested in the apex package to avoid import cycle apex <-> java. +func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) { + // All apex Java libraries have non-installable platform variants, skip them. + if module.IsSkipInstall() { + return -1, nil + } + + jar, hasJar := module.(interface{ DexJar() android.Path }) + if !hasJar { + return -1, nil + } + + name := ctx.ModuleName(module) + index := android.IndexList(name, image.modules) + if index == -1 { + return -1, nil + } + + // Check that this module satisfies constraints for a particular boot image. + apex, isApexModule := module.(android.ApexModule) + if image.name == artBootImageName { + if isApexModule && strings.HasPrefix(apex.ApexName(), "com.android.art.") { + // ok, found the jar in the ART apex + } else if isApexModule && !apex.IsForPlatform() { + // this jar is part of an updatable apex other than ART, fail immediately + ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the ART boot image", name, apex.ApexName()) + } else if isApexModule && apex.IsForPlatform() && Bool(module.(*Library).deviceProperties.Hostdex) { + // this is a special "hostdex" variant, skip it and resume search + return -1, nil + } else { + // this (installable) jar is part of the platform, fail immediately + ctx.Errorf("module '%s' is part of the platform and not allowed in the ART boot image", name) + } + } else if image.name == frameworkBootImageName { + if !isApexModule || apex.IsForPlatform() { + // ok, this jar is part of the platform + } else { + // this jar is part of an updatable apex, fail immediately + ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the framework boot image", name, apex.ApexName()) + } + } else { + panic("unknown boot image: " + image.name) + } + + return index, jar.DexJar() +} + // 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, len(image.modules)) ctx.VisitAllModules(func(module android.Module) { - // Collect dex jar paths for the modules listed above. - if j, ok := module.(interface{ DexJar() android.Path }); ok { - name := ctx.ModuleName(module) - if i := android.IndexList(name, image.modules); i != -1 { - bootDexJars[i] = j.DexJar() - } + if i, j := getBootImageJar(ctx, image, module); i != -1 { + bootDexJars[i] = j } }) @@ -263,7 +314,8 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI missingDeps = append(missingDeps, image.modules[i]) bootDexJars[i] = android.PathForOutput(ctx, "missing") } else { - ctx.Errorf("failed to find dex jar path for module %q", + ctx.Errorf("failed to find a dex jar path for module '%s'"+ + ", note that some jars may be filtered out by module constraints", image.modules[i]) } } diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go index 94ca8bb97..127c20159 100644 --- a/java/dexpreopt_bootjars_test.go +++ b/java/dexpreopt_bootjars_test.go @@ -53,7 +53,7 @@ func TestDexpreoptBootJars(t *testing.T) { ctx := testContext() - ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory) + RegisterDexpreoptBootJarsComponents(ctx) run(t, ctx, config)