Move monolithic stub flags generation to platform_bootclasspath

As part of that this change:
* Moves code that will be common to platform_bootclasspath and
  bootclasspath_fragment from hiddenapi_singleton.go into
  hiddenapi_modular.go.
* Fixes the tests in hiddenapi_singleton_test.go but intentionally
  does not rename them or move them into a more appropriate place so
  as to make it easier to see the differences. A TODO has been added
  and these will be cleaned up in a follow up change.

Bug: 179354495
Test: verified that the monolithic out/soong/hiddenapi/... files are
      unchanged by this change
Change-Id: I680e4dab2e6bdf4a655fa9f255c195175904667e
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 8daf197..8cc6f8f 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -21,6 +21,30 @@
 
 // Contains support for processing hiddenAPI in a modular fashion.
 
+type hiddenAPIStubsDependencyTag struct {
+	blueprint.BaseDependencyTag
+	sdkKind android.SdkKind
+}
+
+func (b hiddenAPIStubsDependencyTag) ExcludeFromApexContents() {
+}
+
+func (b hiddenAPIStubsDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
+// Avoid having to make stubs content explicitly visible to dependent modules.
+//
+// This is a temporary workaround to make it easier to migrate to bootclasspath_fragment modules
+// with proper dependencies.
+// TODO(b/177892522): Remove this and add needed visibility.
+func (b hiddenAPIStubsDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
+var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
+var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
+
 // hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden
 // API processing.
 var hiddenAPIRelevantSdkKinds = []android.SdkKind{
@@ -30,6 +54,125 @@
 	android.SdkCorePlatform,
 }
 
+// hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
+// needed to produce the hidden API monolithic stub flags file.
+func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[android.SdkKind][]string {
+	var publicStubModules []string
+	var systemStubModules []string
+	var testStubModules []string
+	var corePlatformStubModules []string
+
+	if config.AlwaysUsePrebuiltSdks() {
+		// Build configuration mandates using prebuilt stub modules
+		publicStubModules = append(publicStubModules, "sdk_public_current_android")
+		systemStubModules = append(systemStubModules, "sdk_system_current_android")
+		testStubModules = append(testStubModules, "sdk_test_current_android")
+	} else {
+		// Use stub modules built from source
+		publicStubModules = append(publicStubModules, "android_stubs_current")
+		systemStubModules = append(systemStubModules, "android_system_stubs_current")
+		testStubModules = append(testStubModules, "android_test_stubs_current")
+	}
+	// We do not have prebuilts of the core platform api yet
+	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
+
+	// Allow products to define their own stubs for custom product jars that apps can use.
+	publicStubModules = append(publicStubModules, config.ProductHiddenAPIStubs()...)
+	systemStubModules = append(systemStubModules, config.ProductHiddenAPIStubsSystem()...)
+	testStubModules = append(testStubModules, config.ProductHiddenAPIStubsTest()...)
+	if config.IsEnvTrue("EMMA_INSTRUMENT") {
+		publicStubModules = append(publicStubModules, "jacoco-stubs")
+	}
+
+	m := map[android.SdkKind][]string{}
+	m[android.SdkPublic] = publicStubModules
+	m[android.SdkSystem] = systemStubModules
+	m[android.SdkTest] = testStubModules
+	m[android.SdkCorePlatform] = corePlatformStubModules
+	return m
+}
+
+// hiddenAPIAddStubLibDependencies adds dependencies onto the modules specified in
+// sdkKindToStubLibModules. It adds them in a well known order and uses an SdkKind specific tag to
+// identify the source of the dependency.
+func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, sdkKindToStubLibModules map[android.SdkKind][]string) {
+	module := ctx.Module()
+	for _, sdkKind := range hiddenAPIRelevantSdkKinds {
+		modules := sdkKindToStubLibModules[sdkKind]
+		ctx.AddDependency(module, hiddenAPIStubsDependencyTag{sdkKind: sdkKind}, modules...)
+	}
+}
+
+// hiddenAPIGatherStubLibDexJarPaths gathers the paths to the dex jars from the dependencies added
+// in hiddenAPIAddStubLibDependencies.
+func hiddenAPIGatherStubLibDexJarPaths(ctx android.ModuleContext) map[android.SdkKind]android.Paths {
+	m := map[android.SdkKind]android.Paths{}
+	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+		tag := ctx.OtherModuleDependencyTag(module)
+		if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
+			kind := hiddenAPIStubsTag.sdkKind
+			dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module)
+			if dexJar != nil {
+				m[kind] = append(m[kind], dexJar)
+			}
+		}
+	})
+	return m
+}
+
+// hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if
+// available, or reports an error.
+func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module) android.Path {
+	if j, ok := module.(UsesLibraryDependency); ok {
+		dexJar := j.DexJarBuildPath()
+		if dexJar != nil {
+			return dexJar
+		}
+		ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module)
+	} else {
+		ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module))
+	}
+	return nil
+}
+
+var sdkKindToHiddenapiListOption = map[android.SdkKind]string{
+	android.SdkPublic:       "public-stub-classpath",
+	android.SdkSystem:       "system-stub-classpath",
+	android.SdkTest:         "test-stub-classpath",
+	android.SdkCorePlatform: "core-platform-stub-classpath",
+}
+
+// ruleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
+//
+// The rule is initialized but not built so that the caller can modify it and select an appropriate
+// name.
+func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.OutputPath, bootDexJars android.Paths, sdkKindToPathList map[android.SdkKind]android.Paths) *android.RuleBuilder {
+	// Singleton rule which applies hiddenapi on all boot class path dex files.
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	tempPath := tempPathForRestat(ctx, outputPath)
+
+	command := rule.Command().
+		Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
+		Text("list").
+		FlagForEachInput("--boot-dex=", bootDexJars)
+
+	// Iterate over the sdk kinds in a fixed order.
+	for _, sdkKind := range hiddenAPIRelevantSdkKinds {
+		paths := sdkKindToPathList[sdkKind]
+		if len(paths) > 0 {
+			option := sdkKindToHiddenapiListOption[sdkKind]
+			command.FlagWithInputList("--"+option+"=", paths, ":")
+		}
+	}
+
+	// Add the output path.
+	command.FlagWithOutput("--out-api-flags=", tempPath)
+
+	commitChangeForRestat(rule, tempPath, outputPath)
+	return rule
+}
+
 // HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
 // information obtained from annotations within the source code in order to create the complete set
 // of flags that should be applied to the dex implementation jars on the bootclasspath.
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 2e1f8db..3cc88e6 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -127,8 +127,6 @@
 		return
 	}
 
-	stubFlagsRule(ctx)
-
 	// If there is a prebuilt hiddenapi dir, generate rules to use the
 	// files within. Generally, we build the hiddenapi files from source
 	// during the build, ensuring consistency. It's possible, in a split
@@ -160,137 +158,6 @@
 	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
 }
 
-// hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
-// needed to produce the hidden API monolithic stub flags file.
-func hiddenAPIComputeMonolithicStubLibModules(ctx android.BuilderContext) map[android.SdkKind][]string {
-	var publicStubModules []string
-	var systemStubModules []string
-	var testStubModules []string
-	var corePlatformStubModules []string
-
-	if ctx.Config().AlwaysUsePrebuiltSdks() {
-		// Build configuration mandates using prebuilt stub modules
-		publicStubModules = append(publicStubModules, "sdk_public_current_android")
-		systemStubModules = append(systemStubModules, "sdk_system_current_android")
-		testStubModules = append(testStubModules, "sdk_test_current_android")
-	} else {
-		// Use stub modules built from source
-		publicStubModules = append(publicStubModules, "android_stubs_current")
-		systemStubModules = append(systemStubModules, "android_system_stubs_current")
-		testStubModules = append(testStubModules, "android_test_stubs_current")
-	}
-	// We do not have prebuilts of the core platform api yet
-	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
-
-	// Allow products to define their own stubs for custom product jars that apps can use.
-	publicStubModules = append(publicStubModules, ctx.Config().ProductHiddenAPIStubs()...)
-	systemStubModules = append(systemStubModules, ctx.Config().ProductHiddenAPIStubsSystem()...)
-	testStubModules = append(testStubModules, ctx.Config().ProductHiddenAPIStubsTest()...)
-	if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") {
-		publicStubModules = append(publicStubModules, "jacoco-stubs")
-	}
-
-	m := map[android.SdkKind][]string{}
-	m[android.SdkPublic] = publicStubModules
-	m[android.SdkSystem] = systemStubModules
-	m[android.SdkTest] = testStubModules
-	m[android.SdkCorePlatform] = corePlatformStubModules
-	return m
-}
-
-// stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
-// modules.
-func stubFlagsRule(ctx android.SingletonContext) {
-
-	sdkKindToModules := hiddenAPIComputeMonolithicStubLibModules(ctx)
-
-	// Create a set of path slices into which the DexJarBuildPath from the stub modules can be stored.
-	sdkKindToPathList := map[android.SdkKind]android.Paths{}
-	for sdkKind, modules := range sdkKindToModules {
-		sdkKindToPathList[sdkKind] = make(android.Paths, len(modules))
-	}
-
-	var bootDexJars android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		// Collect dex jar paths for the modules listed above.
-		if j, ok := module.(UsesLibraryDependency); ok {
-			name := ctx.ModuleName(module)
-			for _, sdkKind := range hiddenAPIRelevantSdkKinds {
-				if i := android.IndexList(name, sdkKindToModules[sdkKind]); i != -1 {
-					sdkKindToPathList[sdkKind][i] = j.DexJarBuildPath()
-				}
-			}
-		}
-
-		// Collect dex jar paths for modules that had hiddenapi encode called on them.
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if jar := h.bootDexJar(); jar != nil {
-				bootDexJars = append(bootDexJars, jar)
-			}
-		}
-	})
-
-	var missingDeps []string
-	// Ensure all modules were converted to paths
-	for _, sdkKind := range hiddenAPIRelevantSdkKinds {
-		pathList := sdkKindToPathList[sdkKind]
-		for i := range pathList {
-			if pathList[i] == nil {
-				moduleName := sdkKindToModules[sdkKind][i]
-				pathList[i] = android.PathForOutput(ctx, "missing/module", moduleName)
-				if ctx.Config().AllowMissingDependencies() {
-					missingDeps = append(missingDeps, moduleName)
-				} else {
-					ctx.Errorf("failed to find dex jar path for module %q", moduleName)
-				}
-			}
-		}
-	}
-
-	outputPath := hiddenAPISingletonPaths(ctx).stubFlags
-	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, outputPath, bootDexJars, sdkKindToPathList)
-	rule.MissingDeps(missingDeps)
-	rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
-}
-
-var sdkKindToHiddenapiListOption = map[android.SdkKind]string{
-	android.SdkPublic:       "public-stub-classpath",
-	android.SdkSystem:       "system-stub-classpath",
-	android.SdkTest:         "test-stub-classpath",
-	android.SdkCorePlatform: "core-platform-stub-classpath",
-}
-
-// ruleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
-//
-// The rule is initialized but not built so that the caller can modify it and select an appropriate
-// name.
-func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.SingletonContext, outputPath android.OutputPath, bootDexJars android.Paths, sdkKindToPathList map[android.SdkKind]android.Paths) *android.RuleBuilder {
-	// Singleton rule which applies hiddenapi on all boot class path dex files.
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	tempPath := tempPathForRestat(ctx, outputPath)
-
-	command := rule.Command().
-		Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
-		Text("list").
-		FlagForEachInput("--boot-dex=", bootDexJars)
-
-	// Iterate over the sdk
-	for _, sdkKind := range hiddenAPIRelevantSdkKinds {
-		paths := sdkKindToPathList[sdkKind]
-		if len(paths) > 0 {
-			option := sdkKindToHiddenapiListOption[sdkKind]
-			command.FlagWithInputList("--"+option+"=", paths, ":")
-		}
-	}
-
-	// Add the output path.
-	command.FlagWithOutput("--out-api-flags=", tempPath)
-
-	commitChangeForRestat(rule, tempPath, outputPath)
-	return rule
-}
-
 // Checks to see whether the supplied module variant is in the list of boot jars.
 //
 // This is similar to logic in getBootImageJar() so any changes needed here are likely to be needed
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 5ea9a5b..3ab2277 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -23,12 +23,20 @@
 	"github.com/google/blueprint/proptools"
 )
 
+// TODO(b/177892522): Move these tests into a more appropriate place.
+
 func fixtureSetPrebuiltHiddenApiDirProductVariable(prebuiltHiddenApiDir *string) android.FixturePreparer {
 	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
 	})
 }
 
+var prepareForTestWithDefaultPlatformBootclasspath = android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
+	platform_bootclasspath {
+		name: "platform-bootclasspath",
+	}
+`)
+
 var hiddenApiFixtureFactory = android.GroupFixturePreparers(
 	prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
 
@@ -36,6 +44,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -44,8 +53,8 @@
 		}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
@@ -59,6 +68,7 @@
 	android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
 		RunTestWithBp(t, `
 		java_library {
@@ -79,6 +89,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_import {
 			name: "foo",
@@ -87,8 +98,8 @@
 	}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
@@ -97,6 +108,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -112,8 +124,8 @@
 		}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
 
@@ -125,6 +137,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -140,8 +153,8 @@
 		}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
 
@@ -184,13 +197,14 @@
 			result := android.GroupFixturePreparers(
 				hiddenApiFixtureFactory,
 				tc.preparer,
+				prepareForTestWithDefaultPlatformBootclasspath,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
 				}),
 			).RunTest(t)
 
-			hiddenAPI := result.SingletonForTests("hiddenapi")
-			hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+			hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+			hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
 			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
 
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index ba758dd..568f5e4 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -140,6 +140,8 @@
 }
 
 func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	b.hiddenAPIDepsMutator(ctx)
+
 	if SkipDexpreoptBootJars(ctx) {
 		return
 	}
@@ -149,6 +151,16 @@
 	dexpreopt.RegisterToolDeps(ctx)
 }
 
+func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
+	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+		return
+	}
+
+	// Add dependencies onto the stub lib modules.
+	sdkKindToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
+	hiddenAPIAddStubLibDependencies(ctx, sdkKindToStubLibModules)
+}
+
 func platformBootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	m := ctx.Module()
 	if p, ok := m.(*platformBootclasspathModule); ok {
@@ -353,10 +365,24 @@
 	baseFlagsPath := hiddenAPISingletonPaths(ctx).stubFlags
 	ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, flagFileInfo)
 
+	b.generateHiddenAPIStubFlagsRules(ctx, hiddenAPISupportingModules)
 	b.generateHiddenAPIIndexRules(ctx, hiddenAPISupportingModules)
 	b.generatedHiddenAPIMetadataRules(ctx, hiddenAPISupportingModules)
 }
 
+func (b *platformBootclasspathModule) generateHiddenAPIStubFlagsRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+	bootDexJars := android.Paths{}
+	for _, module := range modules {
+		bootDexJars = append(bootDexJars, module.bootDexJar())
+	}
+
+	sdkKindToStubPaths := hiddenAPIGatherStubLibDexJarPaths(ctx)
+
+	outputPath := hiddenAPISingletonPaths(ctx).stubFlags
+	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, outputPath, bootDexJars, sdkKindToStubPaths)
+	rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
+}
+
 func (b *platformBootclasspathModule) generateHiddenAPIIndexRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
 	indexes := android.Paths{}
 	for _, module := range modules {