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
diff --git a/android/sdk.go b/android/sdk.go
index 5c58612..e700031 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -375,6 +375,8 @@
// 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
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index f97404a..8554c19 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -22,6 +22,7 @@
"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 @@
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.
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 23e4e8d..8ea9f82 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -124,6 +124,15 @@
// 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 @@
// 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 @@
// 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
}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 96534ad..b322c6f 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -37,13 +37,65 @@
// 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 @@
// 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) 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 @@
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 @@
// 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 @@
}
}
+// 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 @@
// 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 @@
FlagFilesByCategory: FlagFilesByCategory{},
StubDexJarsByScope: StubDexJarsByScope{},
DependencyStubDexJarsByScope: StubDexJarsByScope{},
+ AdditionalStubDexJarsByScope: StubDexJarsByScope{},
}
return input
@@ -601,7 +681,14 @@
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)
+ }
}
})
diff --git a/sdk/update.go b/sdk/update.go
index 3f61339..b146b62 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -156,6 +156,11 @@
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())