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/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)
+			}
 		}
 	})