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
This commit is contained in:
Spandan Das 2023-12-20 20:13:34 +00:00
parent ad579a8c71
commit 64c9e0ce6e
5 changed files with 80 additions and 16 deletions

View file

@ -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)
}
}

View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -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.