Support hidden API processing for modules that use platform APIs

Previously, hidden API processing could only be done by those
bootclasspath_fragment modules that either did not depend on any other
fragments (e.g. art-bootclasspath-fragment) or only depended on APIs
provided by other fragments (e.g. i18n-bootclasspath-fragment). That
meant that modules like com.android.os.statsd-bootclasspath-fragment
that depended on APIs provided by parts of the platform which are not
yet part of another bootclasspath_fragment could not perform hidden
API processing.

This change adds support for a bootclasspath_fragment to specify the
additional stubs needed to perform hidden API processing. It adds a new
additional_stubs property that can be used to specify the additional
stub libraries.

Most bootclasspath_fragments that need to use the property will need
access to the APIs provided by the android-non-updatable.* libraries.
Rather than have each fragment explicitly specify the correct module
for each scope it treats "android-non-updatable" as if it was a
java_sdk_library that can provide different jars for each scope.
Soong will handle mapping that to the correct android-non-updatable.*
module.

Bug: 179354495
Test: m out/soong/hiddenapi/hiddenapi-flags.csv \
        out/soong/hiddenapi/hiddenapi-index.csv \
        out/soong/hiddenapi/hiddenapi-stub-flags.txt \
        out/soong/hiddenapi/hiddenapi-unsupported.csv
      - make sure that this change does not change the contents.
      m TARGET_BUILD_APPS=Calendar nothing
Change-Id: Ia8b79830ed0e6d42100de03d76b0c51b7f6c8ade
This commit is contained in:
Paul Duffin 2021-05-26 10:16:01 +01:00
parent 136fd5554d
commit 5cca7c44e5
5 changed files with 451 additions and 12 deletions

View file

@ -375,6 +375,8 @@ type SdkMemberTypeDependencyTag interface {
// SdkMemberType returns the SdkMemberType that will be used to automatically add the child module
// to the sdk.
//
// Returning nil will prevent the module being added to the sdk.
SdkMemberType(child Module) SdkMemberType
// ExportMember determines whether a module added to the sdk through this tag will be exported

View file

@ -22,6 +22,7 @@ import (
"android/soong/android"
"android/soong/java"
"github.com/google/blueprint/proptools"
)
// Contains tests for bootclasspath_fragment logic from java/bootclasspath_fragment.go as the ART
@ -868,4 +869,330 @@ func TestBootclasspathFragment_HiddenAPIList(t *testing.T) {
android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+quuzTestStubs+":"+fooStubs)
}
// TestBootclasspathFragment_AndroidNonUpdatable checks to make sure that setting
// additional_stubs: ["android-non-updatable"] causes the source android-non-updatable modules to be
// added to the hiddenapi list tool.
func TestBootclasspathFragment_AndroidNonUpdatable(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithBootclasspathFragment,
prepareForTestWithArtApex,
prepareForTestWithMyapex,
// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
// is disabled.
android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("foo", "android-non-updatable"),
).RunTestWithBp(t, `
java_sdk_library {
name: "android-non-updatable",
srcs: ["b.java"],
compile_dex: true,
public: {
enabled: true,
},
system: {
enabled: true,
},
test: {
enabled: true,
},
module_lib: {
enabled: true,
},
}
apex {
name: "com.android.art",
key: "com.android.art.key",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
java_libs: [
"baz",
"quuz",
],
updatable: false,
}
apex_key {
name: "com.android.art.key",
public_key: "com.android.art.avbpubkey",
private_key: "com.android.art.pem",
}
java_library {
name: "baz",
apex_available: [
"com.android.art",
],
srcs: ["b.java"],
compile_dex: 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",
],
}
apex {
name: "myapex",
key: "myapex.key",
bootclasspath_fragments: [
"mybootclasspathfragment",
],
updatable: false,
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
java_sdk_library {
name: "foo",
srcs: ["b.java"],
shared_library: false,
public: {enabled: true},
apex_available: [
"myapex",
],
}
java_library {
name: "bar",
srcs: ["b.java"],
installable: true,
apex_available: [
"myapex",
],
}
bootclasspath_fragment {
name: "mybootclasspathfragment",
contents: [
"foo",
"bar",
],
apex_available: [
"myapex",
],
additional_stubs: ["android-non-updatable"],
fragments: [
{
apex: "com.android.art",
module: "art-bootclasspath-fragment",
},
],
}
`)
java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
"android-non-updatable.stubs",
"android-non-updatable.stubs.module_lib",
"android-non-updatable.stubs.system",
"android-non-updatable.stubs.test",
"art-bootclasspath-fragment",
"bar",
"dex2oatd",
"foo",
})
nonUpdatablePublicStubs := getDexJarPath(result, "android-non-updatable.stubs")
nonUpdatableSystemStubs := getDexJarPath(result, "android-non-updatable.stubs.system")
nonUpdatableTestStubs := getDexJarPath(result, "android-non-updatable.stubs.test")
nonUpdatableModuleLibStubs := getDexJarPath(result, "android-non-updatable.stubs.module_lib")
// Make sure that the fragment uses the android-non-updatable modules when generating the hidden
// API flags.
fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
command := rule.RuleParams.Command
android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list")
// Make sure that the module_lib non-updatable stubs are available for resolving references from
// the implementation boot dex jars provided by this module.
android.AssertStringDoesContain(t, "android-non-updatable widest", command, "--dependency-stub-dex="+nonUpdatableModuleLibStubs)
// Make sure that the appropriate non-updatable stubs are available for resolving references from
// the different API stubs provided by this module.
android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+nonUpdatablePublicStubs)
android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+nonUpdatableSystemStubs)
android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs)
}
// TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks checks to make sure that
// setting additional_stubs: ["android-non-updatable"] causes the prebuilt android-non-updatable
// modules to be added to the hiddenapi list tool.
func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithBootclasspathFragment,
java.PrepareForTestWithJavaDefaultModules,
prepareForTestWithArtApex,
prepareForTestWithMyapex,
// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
// is disabled.
android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
}),
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithPrebuiltApis(map[string][]string{
"current": {"android-non-updatable"},
"30": {"foo"},
}),
).RunTestWithBp(t, `
apex {
name: "com.android.art",
key: "com.android.art.key",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
java_libs: [
"baz",
"quuz",
],
updatable: false,
}
apex_key {
name: "com.android.art.key",
public_key: "com.android.art.avbpubkey",
private_key: "com.android.art.pem",
}
java_library {
name: "baz",
apex_available: [
"com.android.art",
],
srcs: ["b.java"],
compile_dex: 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",
],
}
apex {
name: "myapex",
key: "myapex.key",
bootclasspath_fragments: [
"mybootclasspathfragment",
],
updatable: false,
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
java_sdk_library {
name: "foo",
srcs: ["b.java"],
shared_library: false,
public: {enabled: true},
apex_available: [
"myapex",
],
}
java_library {
name: "bar",
srcs: ["b.java"],
installable: true,
apex_available: [
"myapex",
],
}
bootclasspath_fragment {
name: "mybootclasspathfragment",
contents: [
"foo",
"bar",
],
apex_available: [
"myapex",
],
additional_stubs: ["android-non-updatable"],
fragments: [
{
apex: "com.android.art",
module: "art-bootclasspath-fragment",
},
],
}
`)
java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
"art-bootclasspath-fragment",
"bar",
"dex2oatd",
"foo",
"prebuilt_sdk_module-lib_current_android-non-updatable",
"prebuilt_sdk_public_current_android-non-updatable",
"prebuilt_sdk_system_current_android-non-updatable",
"prebuilt_sdk_test_current_android-non-updatable",
})
nonUpdatablePublicStubs := getDexJarPath(result, "sdk_public_current_android-non-updatable")
nonUpdatableSystemStubs := getDexJarPath(result, "sdk_system_current_android-non-updatable")
nonUpdatableTestStubs := getDexJarPath(result, "sdk_test_current_android-non-updatable")
nonUpdatableModuleLibStubs := getDexJarPath(result, "sdk_module-lib_current_android-non-updatable")
// Make sure that the fragment uses the android-non-updatable modules when generating the hidden
// API flags.
fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
command := rule.RuleParams.Command
android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list")
// Make sure that the module_lib non-updatable stubs are available for resolving references from
// the implementation boot dex jars provided by this module.
android.AssertStringDoesContain(t, "android-non-updatable widest", command, "--dependency-stub-dex="+nonUpdatableModuleLibStubs)
// Make sure that the appropriate non-updatable stubs are available for resolving references from
// the different API stubs provided by this module.
android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+nonUpdatablePublicStubs)
android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+nonUpdatableSystemStubs)
android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs)
}
// TODO(b/177892522) - add test for host apex.

View file

@ -124,6 +124,15 @@ type bootclasspathFragmentProperties struct {
// Hidden API related properties.
Hidden_api HiddenAPIFlagFileProperties
// The list of additional stub libraries which this fragment's contents use but which are not
// provided by another bootclasspath_fragment.
//
// Note, "android-non-updatable" is treated specially. While no such module exists it is treated
// as if it was a java_sdk_library. So, when public API stubs are needed then it will be replaced
// with "android-non-updatable.stubs", with "androidn-non-updatable.system.stubs" when the system
// stubs are needed and so on.
Additional_stubs []string
// Properties that allow a fragment to depend on other fragments. This is needed for hidden API
// processing as it needs access to all the classes used by a fragment including those provided
// by other fragments.
@ -381,6 +390,15 @@ func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorCon
// bootclasspath fragment.
hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs())
for _, additionalStubModule := range b.properties.Additional_stubs {
for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
// Add a dependency onto a possibly scope specific stub library.
scopeSpecificDependency := apiScope.scopeSpecificStubModule(ctx, additionalStubModule)
tag := hiddenAPIStubsDependencyTag{apiScope: apiScope, fromAdditionalDependency: true}
ctx.AddVariationDependencies(nil, tag, scopeSpecificDependency)
}
}
if SkipDexpreoptBootJars(ctx) {
return
}
@ -640,8 +658,8 @@ 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.DependencyStubDexJarsByScope = dependencyHiddenApiInfo.TransitiveStubDexJarsByScope
// Add the stub dex jars from this module's fragment dependencies.
input.DependencyStubDexJarsByScope.append(dependencyHiddenApiInfo.TransitiveStubDexJarsByScope)
return input
}

View file

@ -37,13 +37,65 @@ type HiddenAPIScope struct {
// The option needed to passed to "hiddenapi list".
hiddenAPIListOption string
// The name sof the source stub library modules that contain the API provided by the platform,
// i.e. by modules that are not in an APEX.
nonUpdatableSourceModule string
// The names of the prebuilt stub library modules that contain the API provided by the platform,
// i.e. by modules that are not in an APEX.
nonUpdatablePrebuiltModule string
}
// initHiddenAPIScope initializes the scope.
func initHiddenAPIScope(apiScope *HiddenAPIScope) *HiddenAPIScope {
sdkKind := apiScope.sdkKind
// The platform does not provide a core platform API.
if sdkKind != android.SdkCorePlatform {
kindAsString := sdkKind.String()
var insert string
if sdkKind == android.SdkPublic {
insert = ""
} else {
insert = "." + strings.ReplaceAll(kindAsString, "-", "_")
}
nonUpdatableModule := "android-non-updatable"
// Construct the name of the android-non-updatable source module for this scope.
apiScope.nonUpdatableSourceModule = fmt.Sprintf("%s.stubs%s", nonUpdatableModule, insert)
prebuiltModuleName := func(name string, kind string) string {
return fmt.Sprintf("sdk_%s_current_%s", kind, name)
}
// Construct the name of the android-non-updatable prebuilt module for this scope.
apiScope.nonUpdatablePrebuiltModule = prebuiltModuleName(nonUpdatableModule, kindAsString)
}
return apiScope
}
// android-non-updatable takes the name of a module and returns a possibly scope specific name of
// the module.
func (l *HiddenAPIScope) scopeSpecificStubModule(ctx android.BaseModuleContext, name string) string {
// The android-non-updatable is not a java_sdk_library but there are separate stub libraries for
// each scope.
// TODO(b/192067200): Remove special handling of android-non-updatable.
if name == "android-non-updatable" {
if ctx.Config().AlwaysUsePrebuiltSdks() {
return l.nonUpdatablePrebuiltModule
} else {
return l.nonUpdatableSourceModule
}
} else {
// Assume that the module is either a java_sdk_library (or equivalent) and so will provide
// separate stub jars for each scope or is a java_library (or equivalent) in which case it will
// have the same stub jar for each scope.
return name
}
}
func (l *HiddenAPIScope) String() string {
return fmt.Sprintf("HiddenAPIScope{%s}", l.name)
}
@ -122,6 +174,11 @@ type hiddenAPIStubsDependencyTag struct {
// The api scope for which this dependency was added.
apiScope *HiddenAPIScope
// Indicates that the dependency is not for an API provided by the current bootclasspath fragment
// but is an additional API provided by a module that is not part of the current bootclasspath
// fragment.
fromAdditionalDependency bool
}
func (b hiddenAPIStubsDependencyTag) ExcludeFromApexContents() {
@ -132,6 +189,11 @@ func (b hiddenAPIStubsDependencyTag) ReplaceSourceWithPrebuilt() bool {
}
func (b hiddenAPIStubsDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType {
// Do not add additional dependencies to the sdk.
if b.fromAdditionalDependency {
return nil
}
// If the module is a java_sdk_library then treat it as if it was specific in the java_sdk_libs
// property, otherwise treat if it was specified in the java_header_libs property.
if javaSdkLibrarySdkMemberType.IsInstance(child) {
@ -243,15 +305,10 @@ 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(hiddenAPIScopes) - 1; i >= 0; i-- {
apiScope := hiddenAPIScopes[i]
stubsForAPIScope := input.DependencyStubDexJarsByScope[apiScope]
if len(stubsForAPIScope) != 0 {
dependencyStubDexJars = stubsForAPIScope
break
}
}
dependencyStubDexJars := input.DependencyStubDexJarsByScope.stubDexJarsForWidestAPIScope()
// Add widest API stubs from the additional dependencies of this, if any.
dependencyStubDexJars = append(dependencyStubDexJars, input.AdditionalStubDexJarsByScope.stubDexJarsForWidestAPIScope()...)
command := rule.Command().
Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
@ -266,6 +323,7 @@ func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath
// other fragment's APIs.
var paths android.Paths
paths = append(paths, input.DependencyStubDexJarsByScope[apiScope]...)
paths = append(paths, input.AdditionalStubDexJarsByScope[apiScope]...)
paths = append(paths, input.StubDexJarsByScope[apiScope]...)
if len(paths) > 0 {
option := apiScope.hiddenAPIListOption
@ -501,6 +559,20 @@ func (s StubDexJarsByScope) dedupAndSort() {
}
}
// stubDexJarsForWidestAPIScope returns the stub dex jars for the widest API scope provided by this
// map. The relative width of APIs is determined by their order in hiddenAPIScopes.
func (s StubDexJarsByScope) stubDexJarsForWidestAPIScope() android.Paths {
for i := len(hiddenAPIScopes) - 1; i >= 0; i-- {
apiScope := hiddenAPIScopes[i]
stubsForAPIScope := s[apiScope]
if len(stubsForAPIScope) != 0 {
return stubsForAPIScope
}
}
return nil
}
// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
// needed for hidden API flag generation.
type HiddenAPIFlagInput struct {
@ -517,6 +589,13 @@ type HiddenAPIFlagInput struct {
// fragment on which this depends.
DependencyStubDexJarsByScope StubDexJarsByScope
// AdditionalStubDexJarsByScope contains stub dex jars provided by other modules in addition to
// the ones that are obtained from fragments on which this depends.
//
// These are kept separate from stub dex jars in HiddenAPIFlagInput.DependencyStubDexJarsByScope
// as there are not propagated transitively to other fragments that depend on this.
AdditionalStubDexJarsByScope StubDexJarsByScope
// RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are
// specified in the bootclasspath_fragment's stub_libs and contents properties.
RemovedTxtFiles android.Paths
@ -528,6 +607,7 @@ func newHiddenAPIFlagInput() HiddenAPIFlagInput {
FlagFilesByCategory: FlagFilesByCategory{},
StubDexJarsByScope: StubDexJarsByScope{},
DependencyStubDexJarsByScope: StubDexJarsByScope{},
AdditionalStubDexJarsByScope: StubDexJarsByScope{},
}
return input
@ -601,7 +681,14 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten
tag := ctx.OtherModuleDependencyTag(module)
if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
apiScope := hiddenAPIStubsTag.apiScope
addFromModule(ctx, module, apiScope)
if hiddenAPIStubsTag.fromAdditionalDependency {
dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, apiScope.sdkKind)
if dexJar != nil {
i.AdditionalStubDexJarsByScope[apiScope] = append(i.AdditionalStubDexJarsByScope[apiScope], dexJar)
}
} else {
addFromModule(ctx, module, apiScope)
}
}
})

View file

@ -156,6 +156,11 @@ func (s *sdk) collectMembers(ctx android.ModuleContext) {
if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
memberType := memberTag.SdkMemberType(child)
// If a nil SdkMemberType was returned then this module should not be added to the sdk.
if memberType == nil {
return false
}
// Make sure that the resolved module is allowed in the member list property.
if !memberType.IsInstance(child) {
ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(child), memberType.SdkPropertyName())