From 67c1e575515c9c0fa51e52bfda4eb7bc220c878e Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Mon, 17 May 2021 07:38:47 +0100 Subject: [PATCH] Support hidden API processing for fragments with dependencies Previously, a bootclasspath_fragment that depended on classes provided by another bootclasspath_fragment did not support hidden API processing as it would not supply information about those dependencies. This change adds support for that as follows. Each fragment: 1. Exports the transitive sets of stub dex jars for each of the public, system, test and core_platform APIs (where relevant). 2. Adds dependencies onto its dependent fragments. 3. Retrieves the API stubs dex jars from its dependent fragments and passes them to the "hiddenapi list" tool which will use them to resolve dependencies but will not output them to the generated flags. Once the flags are generated the existing encoding functionality encodes the flags into the dex files of the bootclasspath_fragment's content modules which are then packaged into the apex. Bug: 179354495 Test: m com.android.sdkext - verify that this does not change the contents of the apex files Merged-In: I3e82a6dbb437f1e417e5d7e25aeb212e378603d0 Change-Id: I3e82a6dbb437f1e417e5d7e25aeb212e378603d0 (cherry picked from commit f1b358cb5764039ff7502e9f73ad0abfdc91c7aa) --- apex/bootclasspath_fragment_test.go | 122 ++++++++++++++++++++++++++++ java/bootclasspath_fragment.go | 30 +++++-- java/bootclasspath_fragment_test.go | 8 +- java/hiddenapi_modular.go | 59 +++++++++++++- 4 files changed, 207 insertions(+), 12 deletions(-) diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index b3f70cdac..9965f83bd 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -141,6 +141,128 @@ test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.vde `) } +func TestBootclasspathFragments_FragmentDependency(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithBootclasspathFragment, + // Configure some libraries in the art bootclasspath_fragment and platform_bootclasspath. + java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"), + prepareForTestWithArtApex, + + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("foo", "baz"), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: { + enabled: true, + }, + system: { + enabled: true, + }, + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + } + + apex { + name: "com.android.art", + key: "com.android.art.key", + bootclasspath_fragments: ["art-bootclasspath-fragment"], + updatable: false, + } + + apex_key { + name: "com.android.art.key", + public_key: "com.android.art.avbpubkey", + private_key: "com.android.art.pem", + } + + java_sdk_library { + name: "baz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + shared_library: false, + public: { + enabled: true, + }, + system: { + enabled: true, + }, + test: { + enabled: true, + }, + } + + java_library { + name: "quuz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + bootclasspath_fragment { + name: "art-bootclasspath-fragment", + image_name: "art", + // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. + contents: ["baz", "quuz"], + apex_available: [ + "com.android.art", + ], + } + + bootclasspath_fragment { + name: "other-bootclasspath-fragment", + contents: ["foo", "bar"], + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + } +`, + ) + + checkSdkKindStubs := func(message string, info java.HiddenAPIInfo, kind android.SdkKind, expectedPaths ...string) { + t.Helper() + android.AssertPathsRelativeToTopEquals(t, fmt.Sprintf("%s %s", message, kind), expectedPaths, info.TransitiveStubDexJarsByKind[kind]) + } + + // Check stub dex paths exported by art. + artFragment := result.Module("art-bootclasspath-fragment", "android_common") + artInfo := result.ModuleProvider(artFragment, java.HiddenAPIInfoProvider).(java.HiddenAPIInfo) + + bazPublicStubs := "out/soong/.intermediates/baz.stubs/android_common/dex/baz.stubs.jar" + bazSystemStubs := "out/soong/.intermediates/baz.stubs.system/android_common/dex/baz.stubs.system.jar" + bazTestStubs := "out/soong/.intermediates/baz.stubs.test/android_common/dex/baz.stubs.test.jar" + + checkSdkKindStubs("art", artInfo, android.SdkPublic, bazPublicStubs) + checkSdkKindStubs("art", artInfo, android.SdkSystem, bazSystemStubs) + checkSdkKindStubs("art", artInfo, android.SdkTest, bazTestStubs) + checkSdkKindStubs("art", artInfo, android.SdkCorePlatform) + + // Check stub dex paths exported by other. + otherFragment := result.Module("other-bootclasspath-fragment", "android_common") + otherInfo := result.ModuleProvider(otherFragment, java.HiddenAPIInfoProvider).(java.HiddenAPIInfo) + + fooPublicStubs := "out/soong/.intermediates/foo.stubs/android_common/dex/foo.stubs.jar" + fooSystemStubs := "out/soong/.intermediates/foo.stubs.system/android_common/dex/foo.stubs.system.jar" + + checkSdkKindStubs("other", otherInfo, android.SdkPublic, bazPublicStubs, fooPublicStubs) + checkSdkKindStubs("other", otherInfo, android.SdkSystem, bazSystemStubs, fooSystemStubs) + checkSdkKindStubs("other", otherInfo, android.SdkTest, bazTestStubs, fooSystemStubs) + checkSdkKindStubs("other", otherInfo, android.SdkCorePlatform) +} + func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName string, expectedConfiguredModules string, expectedBootclasspathFragmentFiles string) { t.Helper() diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 0d61d82c9..db49df8df 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -367,6 +367,11 @@ func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorCon dexpreopt.RegisterToolDeps(ctx) } +func (b *BootclasspathFragmentModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) { + // Add dependencies on all the fragments. + b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx) +} + func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Only perform a consistency check if this module is the active module. That will prevent an // unused prebuilt that was created without instrumentation from breaking an instrumentation @@ -387,8 +392,10 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo } }) + fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag) + // Perform hidden API processing. - hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents) + hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments) // Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a // prebuilt which will not use the image config. @@ -502,10 +509,10 @@ func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleCont } // generateHiddenAPIBuildActions generates all the hidden API related build rules. -func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module) *HiddenAPIFlagOutput { +func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIFlagOutput { // Create hidden API input structure. - input := b.createHiddenAPIFlagInput(ctx, contents) + input := b.createHiddenAPIFlagInput(ctx, contents, fragments) var output *HiddenAPIFlagOutput @@ -531,8 +538,10 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // perform its own flag generation. FlagFilesByCategory: input.FlagFilesByCategory, - // Make these available for tests. - StubDexJarsByKind: input.StubDexJarsByKind, + // Other bootclasspath_fragments that depend on this need the transitive set of stub dex jars + // from this to resolve any references from their code to classes provided by this fragment + // and the fragments this depends upon. + TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(), } if output != nil { @@ -549,7 +558,13 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived // from the properties on this module and its dependencies. -func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module) HiddenAPIFlagInput { +func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput { + + // Merge the HiddenAPIInfo from all the fragment dependencies. + dependencyHiddenApiInfo := newHiddenAPIInfo() + dependencyHiddenApiInfo.mergeFromFragmentDeps(ctx, fragments) + + // Create hidden API flag input structure. input := newHiddenAPIFlagInput() // Update the input structure with information obtained from the stub libraries. @@ -558,6 +573,9 @@ func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.Modul // Populate with flag file paths from the properties. input.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api) + // Store the stub dex jars from this module's fragment dependencies. + input.DependencyStubDexJarsByKind = dependencyHiddenApiInfo.TransitiveStubDexJarsByKind + return input } diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go index f1c9077c7..581625d8c 100644 --- a/java/bootclasspath_fragment_test.go +++ b/java/bootclasspath_fragment_test.go @@ -264,17 +264,17 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs/android_common/dex/myothersdklibrary.stubs.jar" // Check that SdkPublic uses public stubs for all sdk libraries. - android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.StubDexJarsByKind[android.SdkPublic]) + android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkPublic]) // Check that SdkSystem uses system stubs for mysdklibrary and public stubs for myothersdklibrary // as it does not provide system stubs. - android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.StubDexJarsByKind[android.SdkSystem]) + android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkSystem]) // Check that SdkTest also uses system stubs for mysdklibrary as it does not provide test stubs // and public stubs for myothersdklibrary as it does not provide test stubs either. - android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.StubDexJarsByKind[android.SdkTest]) + android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkTest]) // Check that SdkCorePlatform uses public stubs from the mycoreplatform library. corePlatformStubsJar := "out/soong/.intermediates/mycoreplatform.stubs/android_common/dex/mycoreplatform.stubs.jar" - android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.StubDexJarsByKind[android.SdkCorePlatform]) + android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.TransitiveStubDexJarsByKind[android.SdkCorePlatform]) } diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 94fe2caad..370794229 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -67,6 +67,12 @@ var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{} // hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden // API processing. +// +// These are in order from narrowest API surface to widest. Widest means the API stubs with the +// biggest API surface, e.g. test is wider than system is wider than public. Core platform is +// considered wider than test even though it has no relationship with test because the libraries +// that provide core platform API don't provide test. While the core platform API is being converted +// to a system API the system API is still a subset of core platform. var hiddenAPIRelevantSdkKinds = []android.SdkKind{ android.SdkPublic, android.SdkSystem, @@ -164,14 +170,30 @@ func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath tempPath := tempPathForRestat(ctx, outputPath) + // Find the widest API stubs provided by the fragments on which this depends, if any. + var dependencyStubDexJars android.Paths + for i := len(hiddenAPIRelevantSdkKinds) - 1; i >= 0; i-- { + kind := hiddenAPIRelevantSdkKinds[i] + stubsForKind := input.DependencyStubDexJarsByKind[kind] + if len(stubsForKind) != 0 { + dependencyStubDexJars = stubsForKind + break + } + } + command := rule.Command(). Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")). Text("list"). + FlagForEachInput("--dependency-stub-dex=", dependencyStubDexJars). FlagForEachInput("--boot-dex=", bootDexJars) // Iterate over the sdk kinds in a fixed order. for _, sdkKind := range hiddenAPIRelevantSdkKinds { - paths := input.StubDexJarsByKind[sdkKind] + // Merge in the stub dex jar paths for this kind from the fragments on which it depends. They + // will be needed to resolve dependencies from this fragment's stubs to classes in the other + // fragment's APIs. + dependencyPaths := input.DependencyStubDexJarsByKind[sdkKind] + paths := append(dependencyPaths, input.StubDexJarsByKind[sdkKind]...) if len(paths) > 0 { option := sdkKindToHiddenapiListOption[sdkKind] command.FlagWithInputList("--"+option+"=", paths, ":") @@ -349,12 +371,34 @@ type HiddenAPIInfo struct { FlagFilesByCategory FlagFilesByCategory // The paths to the stub dex jars for each of the android.SdkKind in hiddenAPIRelevantSdkKinds. - StubDexJarsByKind StubDexJarsByKind + TransitiveStubDexJarsByKind StubDexJarsByKind // The output from the hidden API processing needs to be made available to other modules. HiddenAPIFlagOutput } +func newHiddenAPIInfo() *HiddenAPIInfo { + info := HiddenAPIInfo{ + FlagFilesByCategory: FlagFilesByCategory{}, + TransitiveStubDexJarsByKind: StubDexJarsByKind{}, + } + return &info +} + +func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragments []android.Module) { + // Merge all the information from the fragments. The fragments form a DAG so it is possible that + // this will introduce duplicates so they will be resolved after processing all the fragments. + for _, fragment := range fragments { + if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { + info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) + i.TransitiveStubDexJarsByKind.append(info.TransitiveStubDexJarsByKind) + } + } + + // Dedup and sort paths. + i.TransitiveStubDexJarsByKind.dedupAndSort() +} + var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{}) // StubDexJarsByKind maps an android.SdkKind to the paths to stub dex jars appropriate for that @@ -387,6 +431,11 @@ type HiddenAPIFlagInput struct { // StubDexJarsByKind contains the stub dex jars for different android.SdkKind and which determine // the initial flags for each dex member. StubDexJarsByKind StubDexJarsByKind + + // DependencyStubDexJarsByKind contains the stub dex jars provided by the fragments on which this + // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByKind from each + // fragment on which this depends. + DependencyStubDexJarsByKind StubDexJarsByKind } // newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct. @@ -480,6 +529,12 @@ func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleCo } } +func (i *HiddenAPIFlagInput) transitiveStubDexJarsByKind() StubDexJarsByKind { + transitive := i.DependencyStubDexJarsByKind + transitive.append(i.StubDexJarsByKind) + return transitive +} + // HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a // bootclasspath_fragment module. type HiddenAPIFlagOutput struct {