From 64c9e0ce6e4aa183b2fae40a18e3e5435c49229d Mon Sep 17 00:00:00 2001 From: Spandan Das Date: Wed, 20 Dec 2023 20:13:34 +0000 Subject: [PATCH] Use the correct bootjars for hiddneapi when multiple prebuilts exist hiddenapi processing require boot jars from apexes to determine the full set of methods available at runtime. When building with prebuilts, this comes via java_import/java_sdk_library_import, which acts as a hook for prebuilt_apex/apex_set. If we have multiple apexes in the tree, this hook becomes 1:many. This CL prepares platform_bootclasspath to select the right deapexerd .jar files when mutliple prebuilts exist. Implementation details - Create a dependency edge from platform_bootclasspath to all_apex_contributions (DepsMutator) - For every boot jar, query all_apex_contributions to get the path to dexjar file (GenerateAndroidBuildActions) Some other important details - This CL does not drop the old mechanism to get the dex file (i.e. by creating a dep on java_library). Once all mainline modules have been flagged using apex_contributions, the old mechanism will be dropped - This CL has a functional change when building with source apexes. At ToT, the unecoded hiddenapi dex jar is used for package check and generating the monolithic stub file. After this change, the hiddenapi encoded file will be used for these operations. This should be fine since the package and dex signature do not change across the encoded and unencoded dex file. In fact, we already have a split today. When building with prebuilts, we use the encoded dex file. When building with source, we use the unecoded dex file. Test: Added a unit test Test: Manual testing in internal described below - lunch cf_x86_64_phone-next-userdebug - flagged com.google.android.adservices using apex_contributions - aninja -t commands out/soong/hiddenapi/hiddenapi-stubs-flags.txt # no diff before and after Bug: 308790777 Change-Id: I72c70f0ae1b587679203ea254c9c12a48e7aa782 --- apex/apex_test.go | 16 +++++++++++ apex/platform_bootclasspath_test.go | 6 ++++ java/dexpreopt_bootjars.go | 44 ++++++++++++++++++----------- java/hiddenapi_modular.go | 19 +++++++++++++ java/platform_bootclasspath.go | 11 ++++++++ 5 files changed, 80 insertions(+), 16 deletions(-) diff --git a/apex/apex_test.go b/apex/apex_test.go index 616421af5..1b9fa19f0 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -11436,6 +11436,20 @@ func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) { } } + // Check that the boot jars of the selected apex are run through boot_jars_package_check + // This validates that the jars on the bootclasspath do not contain packages outside an allowlist + checkBootJarsPackageCheck := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) { + platformBcp := ctx.ModuleForTests("platform-bootclasspath", "android_common") + bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check") + android.AssertStringMatches(t, "Could not find the correct boot dex jar in package check rule", bootJarsCheckRule.RuleParams.Command, "build/soong/scripts/check_boot_jars/package_allowed_list.txt.*"+expectedBootJar) + } + + // Check that the boot jars used to generate the monolithic hiddenapi flags come from the selected apex + checkBootJarsForMonolithicHiddenapi := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) { + monolithicHiddenapiFlagsCmd := ctx.ModuleForTests("platform-bootclasspath", "android_common").Output("out/soong/hiddenapi/hiddenapi-stub-flags.txt").RuleParams.Command + android.AssertStringMatches(t, "Could not find the correct boot dex jar in monolithic hiddenapi flags generation command", monolithicHiddenapiFlagsCmd, "--boot-dex="+expectedBootJar) + } + bp := ` // Source APEX. @@ -11575,5 +11589,7 @@ func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) { ) ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) checkBootDexJarPath(t, ctx, "framework-foo", tc.expectedBootJar) + checkBootJarsPackageCheck(t, ctx, tc.expectedBootJar) + checkBootJarsForMonolithicHiddenapi(t, ctx, tc.expectedBootJar) } } diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index b741963c8..01b616bb6 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -382,6 +382,9 @@ func TestPlatformBootclasspathDependencies(t *testing.T) { // Make sure that the myplatform-bootclasspath has the correct dependencies. CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ + // source vs prebuilt selection metadata module + `platform:all_apex_contributions`, + // The following are stubs. `platform:android_stubs_current`, `platform:android_system_stubs_current`, @@ -534,6 +537,9 @@ func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) { // Make sure that the myplatform-bootclasspath has the correct dependencies. CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ + // source vs prebuilt selection metadata module + `platform:all_apex_contributions`, + // The following are stubs. "platform:prebuilt_sdk_public_current_android", "platform:prebuilt_sdk_system_current_android", diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 73609bfb1..82cece346 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -699,28 +699,40 @@ func getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) // extractEncodedDexJarsFromModulesOrBootclasspathFragments gets the hidden API encoded dex jars for // the given modules. func extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx android.ModuleContext, apexJarModulePairs []apexJarModulePair) bootDexJarByModule { - apexNameToBcpInfoMap := getApexNameToBcpInfoMap(ctx) + apexNameToApexExportInfoMap := getApexNameToApexExportsInfoMap(ctx) encodedDexJarsByModuleName := bootDexJarByModule{} for _, pair := range apexJarModulePairs { - dexJarPath := getDexJarForApex(ctx, pair, apexNameToBcpInfoMap) + dexJarPath := getDexJarForApex(ctx, pair, apexNameToApexExportInfoMap) encodedDexJarsByModuleName.addPath(pair.jarModule, dexJarPath) } return encodedDexJarsByModuleName } +type apexNameToApexExportsInfoMap map[string]android.ApexExportsInfo + +// javaLibraryPathOnHost returns the path to the java library which is exported by the apex for hiddenapi and dexpreopt and a boolean indicating whether the java library exists +// For prebuilt apexes, this is created by deapexing the prebuilt apex +func (m *apexNameToApexExportsInfoMap) javaLibraryDexPathOnHost(ctx android.ModuleContext, apex string, javalib string) (android.Path, bool) { + if info, exists := (*m)[apex]; exists { + if dex, exists := info.LibraryNameToDexJarPathOnHost[javalib]; exists { + return dex, true + } else { + ctx.ModuleErrorf("Apex %s does not provide a dex boot jar for library %s\n", apex, javalib) + } + } + // An apex entry could not be found. Return false. + // TODO: b/308174306 - When all the mainline modules have been flagged, make this a hard error + return nil, false +} + // Returns the java libraries exported by the apex for hiddenapi and dexpreopt // This information can come from two mechanisms // 1. New: Direct deps to _selected_ apexes. The apexes return a ApexExportsInfo // 2. Legacy: An edge to java_library or java_import (java_sdk_library) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes // TODO: b/308174306 - Once all mainline modules have been flagged, drop (2) -func getDexJarForApex(ctx android.ModuleContext, pair apexJarModulePair, apexNameToBcpInfoMap map[string]android.ApexExportsInfo) android.Path { - if info, exists := apexNameToBcpInfoMap[pair.apex]; exists { - libraryName := android.RemoveOptionalPrebuiltPrefix(pair.jarModule.Name()) - if dex, exists := info.LibraryNameToDexJarPathOnHost[libraryName]; exists { - return dex - } else { - ctx.ModuleErrorf("Apex %s does not provide a dex boot jar for library %s\n", pair.apex, libraryName) - } +func getDexJarForApex(ctx android.ModuleContext, pair apexJarModulePair, apexNameToApexExportsInfoMap apexNameToApexExportsInfoMap) android.Path { + if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, pair.apex, android.RemoveOptionalPrebuiltPrefix(pair.jarModule.Name())); found { + return dex } // TODO: b/308174306 - Remove the legacy mechanism if android.IsConfiguredJarForPlatform(pair.apex) || android.IsModulePrebuilt(pair.jarModule) { @@ -900,14 +912,14 @@ func getProfilePathForApex(ctx android.ModuleContext, apexName string, apexNameT return fragment.(commonBootclasspathFragment).getProfilePath() } -func getApexNameToBcpInfoMap(ctx android.ModuleContext) map[string]android.ApexExportsInfo { - apexNameToBcpInfoMap := map[string]android.ApexExportsInfo{} +func getApexNameToApexExportsInfoMap(ctx android.ModuleContext) apexNameToApexExportsInfoMap { + apexNameToApexExportsInfoMap := apexNameToApexExportsInfoMap{} ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(am android.Module) { if info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider); exists { - apexNameToBcpInfoMap[info.ApexName] = info + apexNameToApexExportsInfoMap[info.ApexName] = info } }) - return apexNameToBcpInfoMap + return apexNameToApexExportsInfoMap } // Generate boot image build rules for a specific target. @@ -952,7 +964,7 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p invocationPath := outputPath.ReplaceExtension(ctx, "invocation") - apexNameToBcpInfoMap := getApexNameToBcpInfoMap(ctx) + apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx) cmd.Tool(globalSoong.Dex2oat). Flag("--avoid-storing-invocation"). @@ -966,7 +978,7 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p } for _, apex := range image.profileImports { - importedProfile := getProfilePathForApex(ctx, apex, apexNameToBcpInfoMap) + importedProfile := getProfilePathForApex(ctx, apex, apexNameToApexExportsInfoMap) if importedProfile == nil { ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+ "doesn't provide a profile", diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index bf9975784..c3caa084f 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -19,6 +19,7 @@ import ( "strings" "android/soong/android" + "android/soong/dexpreopt" "github.com/google/blueprint" ) @@ -1250,9 +1251,27 @@ func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, suffix s } // extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules. +// This information can come from two mechanisms +// 1. New: Direct deps to _selected_ apexes. The apexes contain a ApexExportsInfo +// 2. Legacy: An edge to java_sdk_library(_import) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes +// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2) func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { bootDexJars := bootDexJarByModule{} + + apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx) + // For ART and mainline module jars, query apexNameToApexExportsInfoMap to get the dex file + apexJars := dexpreopt.GetGlobalConfig(ctx).ArtApexJars.AppendList(&dexpreopt.GetGlobalConfig(ctx).ApexBootJars) + for i := 0; i < apexJars.Len(); i++ { + if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, apexJars.Apex(i), apexJars.Jar(i)); found { + bootDexJars[apexJars.Jar(i)] = dex + } + } + + // TODO - b/308174306: Drop the legacy mechanism for _, module := range contents { + if _, exists := bootDexJars[android.RemoveOptionalPrebuiltPrefix(module.Name())]; exists { + continue + } hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module) if hiddenAPIModule == nil { continue diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 88d1ae8c0..4db426e0d 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -106,6 +106,9 @@ func (b *platformBootclasspathModule) OutputFiles(tag string) (android.Paths, er } func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) { + // Create a dependency on all_apex_contributions to determine the selected mainline module + ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions") + b.hiddenAPIDepsMutator(ctx) if !dexpreopt.IsDex2oatNeeded(ctx) { @@ -130,6 +133,8 @@ func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpM func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) { // Add dependencies on all the ART jars. global := dexpreopt.GetGlobalConfig(ctx) + addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art") + // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag) // Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable @@ -138,6 +143,12 @@ func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.Botto // Add dependencies on all the updatable jars, except the ART jars. apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars + apexes := []string{} + for i := 0; i < apexJars.Len(); i++ { + apexes = append(apexes, apexJars.Apex(i)) + } + addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...) + // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag) // Add dependencies on all the fragments.