platform_bootclasspath: aggregate hidden API flag files from fragments

Aggregates hidden API flag files from the bootclasspath_fragments which
will allow the hidden API flag files in frameworks/base/boot/hiddenapi
to be modularized and moved to the appropriate repo.

Bug: 177892522
Test: verified that the out/soong/hiddenapi/... files are unchanged
      by this change
      also verified that changes to the fragment provided files do
      affect the monolithic files.
Change-Id: Ifce14c9ef24c58c7ab1085475d85b61cfbfefecd
diff --git a/java/boot_image.go b/java/boot_image.go
index 0c47976..78215f0 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -84,6 +84,8 @@
 	//
 	// The order of this list matters as it is the order that is used in the bootclasspath.
 	Contents []string
+
+	Hidden_api HiddenAPIFlagFileProperties
 }
 
 type BootImageModule struct {
@@ -213,6 +215,9 @@
 }
 
 func (b *BootImageModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Perform hidden API processing.
+	b.generateHiddenAPIBuildActions(ctx)
+
 	// Nothing to do if skipping the dexpreopt of boot image jars.
 	if SkipDexpreoptBootJars(ctx) {
 		return
@@ -253,6 +258,15 @@
 	return imageConfig
 }
 
+// generateHiddenAPIBuildActions generates all the hidden API related build rules.
+func (b *BootImageModule) generateHiddenAPIBuildActions(ctx android.ModuleContext) {
+	// Resolve the properties to paths.
+	flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
+
+	// Store the information for use by platform_bootclasspath.
+	ctx.SetProvider(hiddenAPIFlagFileInfoProvider, flagFileInfo)
+}
+
 type bootImageMemberType struct {
 	android.SdkMemberTypeBase
 }
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 7cf082b..e5dba33 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"github.com/google/blueprint"
 )
 
 // Contains support for processing hiddenAPI in a modular fashion.
@@ -172,6 +173,14 @@
 	categoryToPaths map[*hiddenAPIFlagFileCategory]android.Paths
 }
 
+func (i *hiddenAPIFlagFileInfo) append(other hiddenAPIFlagFileInfo) {
+	for _, category := range hiddenAPIFlagFileCategories {
+		i.categoryToPaths[category] = append(i.categoryToPaths[category], other.categoryToPaths[category]...)
+	}
+}
+
+var hiddenAPIFlagFileInfoProvider = blueprint.NewProvider(hiddenAPIFlagFileInfo{})
+
 // ruleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from the
 // flags from all the modules, the stub flags, augmented with some additional configuration files.
 //
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index cb8ad68..ba758dd 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -258,7 +258,7 @@
 		}
 	})
 
-	b.generateHiddenAPIBuildActions(ctx, b.configuredModules)
+	b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 
 	// Nothing to do if skipping the dexpreopt of boot image jars.
 	if SkipDexpreoptBootJars(ctx) {
@@ -286,7 +286,7 @@
 }
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module) {
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) {
 
 	// Save the paths to the monolithic files for retrieval via OutputFiles().
 	b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
@@ -338,11 +338,20 @@
 		moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV())
 	}
 
-	augmentationInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
+	flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
+	for _, fragment := range fragments {
+		if ctx.OtherModuleHasProvider(fragment, hiddenAPIFlagFileInfoProvider) {
+			info := ctx.OtherModuleProvider(fragment, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
+			flagFileInfo.append(info)
+		}
+	}
+
+	// Store the information for testing.
+	ctx.SetProvider(hiddenAPIFlagFileInfoProvider, flagFileInfo)
 
 	outputPath := hiddenAPISingletonPaths(ctx).flags
 	baseFlagsPath := hiddenAPISingletonPaths(ctx).stubFlags
-	ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, augmentationInfo)
+	ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, flagFileInfo)
 
 	b.generateHiddenAPIIndexRules(ctx, hiddenAPISupportingModules)
 	b.generatedHiddenAPIMetadataRules(ctx, hiddenAPISupportingModules)
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index e51b049..955e387 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -15,6 +15,8 @@
 package java
 
 import (
+	"fmt"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -132,6 +134,96 @@
 	})
 }
 
+func TestPlatformBootclasspath_Fragments(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+				fragments: [
+					{module:"bar-fragment"},
+				],
+				hidden_api: {
+					unsupported: [
+							"unsupported.txt",
+					],
+					removed: [
+							"removed.txt",
+					],
+					max_target_r_low_priority: [
+							"max-target-r-low-priority.txt",
+					],
+					max_target_q: [
+							"max-target-q.txt",
+					],
+					max_target_p: [
+							"max-target-p.txt",
+					],
+					max_target_o_low_priority: [
+							"max-target-o-low-priority.txt",
+					],
+					blocked: [
+							"blocked.txt",
+					],
+					unsupported_packages: [
+							"unsupported-packages.txt",
+					],
+				},
+			}
+
+			bootclasspath_fragment {
+				name: "bar-fragment",
+				contents: ["bar"],
+				hidden_api: {
+					unsupported: [
+							"bar-unsupported.txt",
+					],
+					removed: [
+							"bar-removed.txt",
+					],
+					max_target_r_low_priority: [
+							"bar-max-target-r-low-priority.txt",
+					],
+					max_target_q: [
+							"bar-max-target-q.txt",
+					],
+					max_target_p: [
+							"bar-max-target-p.txt",
+					],
+					max_target_o_low_priority: [
+							"bar-max-target-o-low-priority.txt",
+					],
+					blocked: [
+							"bar-blocked.txt",
+					],
+					unsupported_packages: [
+							"bar-unsupported-packages.txt",
+					],
+				},
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["a.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+			}
+		`),
+	).RunTest(t)
+
+	pbcp := result.Module("platform-bootclasspath", "android_common")
+	info := result.ModuleProvider(pbcp, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
+
+	for _, category := range hiddenAPIFlagFileCategories {
+		name := category.propertyName
+		message := fmt.Sprintf("category %s", name)
+		filename := strings.ReplaceAll(name, "_", "-")
+		expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)}
+		android.AssertPathsRelativeToTopEquals(t, message, expected, info.categoryToPaths[category])
+	}
+}
+
 func TestPlatformBootclasspathVariant(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,