Separate monolithic hidden API processing from hiddenAPIFlagFileInfo

The hiddenAPIFlagFileInfo was being used for both the input and output
of bootclasspath_fragment and platform_bootclasspath and also to pass
information around to various hidden API rule methods. Supporting
multiple different uses in this way made it hard to reason about.

This change creates a separate structure for use by the
platform_bootclasspath. Follow up changes will split out other
functionality into separate types.

Bug: 179354495
Test: m com.android.art com.android.ipsec com.android.os.statsd com.android.conscrypt
      - verify that this does not change the contents of the apex files
Change-Id: Ia5c5f65ae5645486c42819c669a8601588217f88
diff --git a/java/Android.bp b/java/Android.bp
index 623a6c5..680f3a1 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -45,6 +45,7 @@
         "genrule.go",
         "hiddenapi.go",
         "hiddenapi_modular.go",
+        "hiddenapi_monolithic.go",
         "hiddenapi_singleton.go",
         "jacoco.go",
         "java.go",
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index e151d63..9310b12 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -651,7 +651,7 @@
 	Core_platform_stub_libs []string
 
 	// Flag files by *hiddenAPIFlagFileCategory
-	Flag_files_by_category map[*hiddenAPIFlagFileCategory]android.Paths
+	Flag_files_by_category FlagFilesByCategory
 
 	// The path to the generated stub-flags.csv file.
 	Stub_flags_path android.OptionalPath
@@ -689,7 +689,7 @@
 	// Get the flag file information from the module.
 	mctx := ctx.SdkModuleContext()
 	flagFileInfo := mctx.OtherModuleProvider(module, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
-	b.Flag_files_by_category = flagFileInfo.categoryToPaths
+	b.Flag_files_by_category = flagFileInfo.FlagFilesByCategory
 
 	// Copy all the generated file paths.
 	b.Stub_flags_path = pathsToOptionalPath(flagFileInfo.StubFlagsPaths)
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index f5afe5d..8372132 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -261,10 +261,10 @@
 }
 
 func (p *HiddenAPIFlagFileProperties) hiddenAPIFlagFileInfo(ctx android.ModuleContext) hiddenAPIFlagFileInfo {
-	info := hiddenAPIFlagFileInfo{categoryToPaths: map[*hiddenAPIFlagFileCategory]android.Paths{}}
+	info := hiddenAPIFlagFileInfo{FlagFilesByCategory: FlagFilesByCategory{}}
 	for _, category := range hiddenAPIFlagFileCategories {
 		paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
-		info.categoryToPaths[category] = paths
+		info.FlagFilesByCategory[category] = paths
 	}
 	return info
 }
@@ -365,6 +365,24 @@
 	},
 }
 
+// FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category.
+type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths
+
+// append appends the supplied flags files to the corresponding category in this map.
+func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
+	for _, category := range hiddenAPIFlagFileCategories {
+		s[category] = append(s[category], other[category]...)
+	}
+}
+
+// dedup removes duplicates in the flag files, while maintaining the order in which they were
+// appended.
+func (s FlagFilesByCategory) dedup() {
+	for category, paths := range s {
+		s[category] = android.FirstUniquePaths(paths)
+	}
+}
+
 // hiddenAPIFlagFileInfo contains paths resolved from HiddenAPIFlagFileProperties and also generated
 // by hidden API processing.
 //
@@ -372,9 +390,9 @@
 // for a module to collate the files from the fragments it depends upon. That is why the fields are
 // all Paths even though they are initialized with a single path.
 type hiddenAPIFlagFileInfo struct {
-	// categoryToPaths maps from the flag file category to the paths containing information for that
-	// category.
-	categoryToPaths map[*hiddenAPIFlagFileCategory]android.Paths
+	// FlagFilesByCategory maps from the flag file category to the paths containing information for
+	// that category.
+	FlagFilesByCategory FlagFilesByCategory
 
 	// The paths to the generated stub-flags.csv files.
 	StubFlagsPaths android.Paths
@@ -392,17 +410,6 @@
 	AllFlagsPaths android.Paths
 }
 
-func (i *hiddenAPIFlagFileInfo) append(other hiddenAPIFlagFileInfo) {
-	for _, category := range hiddenAPIFlagFileCategories {
-		i.categoryToPaths[category] = append(i.categoryToPaths[category], other.categoryToPaths[category]...)
-	}
-	i.StubFlagsPaths = append(i.StubFlagsPaths, other.StubFlagsPaths...)
-	i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPaths...)
-	i.MetadataPaths = append(i.MetadataPaths, other.MetadataPaths...)
-	i.IndexPaths = append(i.IndexPaths, other.IndexPaths...)
-	i.AllFlagsPaths = append(i.AllFlagsPaths, other.AllFlagsPaths...)
-}
-
 var hiddenAPIFlagFileInfoProvider = blueprint.NewProvider(hiddenAPIFlagFileInfo{})
 
 // pathForValidation creates a path of the same type as the supplied type but with a name of
@@ -428,14 +435,14 @@
 //
 // flagFileInfo is a struct containing paths to files that augment the information provided by
 // the annotationFlags.
-func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, flagFileInfo *hiddenAPIFlagFileInfo) {
+func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths) {
 
 	// The file which is used to record that the flags file is valid.
 	var validFile android.WritablePath
 
 	// If there are flag files that have been generated by fragments on which this depends then use
 	// them to validate the flag file generated by the rules created by this method.
-	if allFlagsPaths := flagFileInfo.AllFlagsPaths; len(allFlagsPaths) > 0 {
+	if len(allFlagsPaths) > 0 {
 		// The flags file generated by the rule created by this method needs to be validated to ensure
 		// that it is consistent with the flag files generated by the individual fragments.
 
@@ -463,7 +470,7 @@
 
 	// Add the options for the different categories of flag files.
 	for _, category := range hiddenAPIFlagFileCategories {
-		paths := flagFileInfo.categoryToPaths[category]
+		paths := flagFilesByCategory[category]
 		for _, path := range paths {
 			category.commandMutator(command, path)
 		}
@@ -530,7 +537,7 @@
 	// Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
 	// files.
 	outputPath := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
-	buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, flagFileInfo)
+	buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, flagFileInfo.FlagFilesByCategory, nil)
 
 	// Store the paths in the info for use by other modules and sdk snapshot generation.
 	flagFileInfo.StubFlagsPaths = android.Paths{stubFlagsCSV}
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
new file mode 100644
index 0000000..9ca3cfb
--- /dev/null
+++ b/java/hiddenapi_monolithic.go
@@ -0,0 +1,91 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+// MonolithicHiddenAPIInfo contains information needed/provided by the hidden API generation of the
+// monolithic hidden API files.
+//
+// Each list of paths includes all the equivalent paths from each of the bootclasspath_fragment
+// modules that contribute to the platform-bootclasspath.
+type MonolithicHiddenAPIInfo struct {
+	// FlagsFilesByCategory maps from the flag file category to the paths containing information for
+	// that category.
+	FlagsFilesByCategory FlagFilesByCategory
+
+	// The paths to the generated stub-flags.csv files.
+	StubFlagsPaths android.Paths
+
+	// The paths to the generated annotation-flags.csv files.
+	AnnotationFlagsPaths android.Paths
+
+	// The paths to the generated metadata.csv files.
+	MetadataPaths android.Paths
+
+	// The paths to the generated index.csv files.
+	IndexPaths android.Paths
+
+	// The paths to the generated all-flags.csv files.
+	AllFlagsPaths android.Paths
+}
+
+// newMonolithicHiddenAPIInfo creates a new MonolithicHiddenAPIInfo from the flagFilesByCategory
+// plus information provided by each of the fragments.
+func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, fragments []android.Module) MonolithicHiddenAPIInfo {
+	monolithicInfo := MonolithicHiddenAPIInfo{}
+
+	monolithicInfo.FlagsFilesByCategory = flagFilesByCategory
+
+	// Merge all the information from the fragments. The fragments form a DAG so it is possible that
+	// this will introduce duplicates so they will be resolved after processing all the fragments.
+	for _, fragment := range fragments {
+		if ctx.OtherModuleHasProvider(fragment, hiddenAPIFlagFileInfoProvider) {
+			info := ctx.OtherModuleProvider(fragment, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
+			monolithicInfo.append(&info)
+		}
+	}
+
+	// Dedup paths.
+	monolithicInfo.dedup()
+
+	return monolithicInfo
+}
+
+// append appends all the files from the supplied info to the corresponding files in this struct.
+func (i *MonolithicHiddenAPIInfo) append(other *hiddenAPIFlagFileInfo) {
+	i.FlagsFilesByCategory.append(other.FlagFilesByCategory)
+	i.StubFlagsPaths = append(i.StubFlagsPaths, other.StubFlagsPaths...)
+	i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPaths...)
+	i.MetadataPaths = append(i.MetadataPaths, other.MetadataPaths...)
+	i.IndexPaths = append(i.IndexPaths, other.IndexPaths...)
+	i.AllFlagsPaths = append(i.AllFlagsPaths, other.AllFlagsPaths...)
+}
+
+// dedup removes duplicates in all the paths, while maintaining the order in which they were
+// appended.
+func (i *MonolithicHiddenAPIInfo) dedup() {
+	i.FlagsFilesByCategory.dedup()
+	i.StubFlagsPaths = android.FirstUniquePaths(i.StubFlagsPaths)
+	i.AnnotationFlagsPaths = android.FirstUniquePaths(i.AnnotationFlagsPaths)
+	i.MetadataPaths = android.FirstUniquePaths(i.MetadataPaths)
+	i.IndexPaths = android.FirstUniquePaths(i.IndexPaths)
+	i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths)
+}
+
+var monolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 5db2efe..df0ba2a 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -279,21 +279,12 @@
 		return
 	}
 
-	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)
-
-	hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules)
+	monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments)
 
 	sdkKindToStubPaths := hiddenAPIGatherStubLibDexJarPaths(ctx, nil)
 
+	hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules)
+
 	// Generate the monolithic stub-flags.csv file.
 	bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
 	stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
@@ -309,7 +300,7 @@
 
 	// Generate the monotlithic hiddenapi-flags.csv file.
 	allFlags := hiddenAPISingletonPaths(ctx).flags
-	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, &flagFileInfo)
+	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths)
 
 	// Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations
 	// in the source code.
@@ -328,6 +319,17 @@
 	buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV)
 }
 
+// createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for
+// testing.
+func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, fragments []android.Module) MonolithicHiddenAPIInfo {
+	flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
+	monolithicInfo := newMonolithicHiddenAPIInfo(ctx, flagFileInfo.FlagFilesByCategory, fragments)
+
+	// Store the information for testing.
+	ctx.SetProvider(monolithicHiddenAPIInfoProvider, monolithicInfo)
+	return monolithicInfo
+}
+
 func (b *platformBootclasspathModule) buildRuleMergeCSV(ctx android.ModuleContext, desc string, inputPaths android.Paths, outputPath android.WritablePath) {
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index efcbc80..d332f63 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -245,14 +245,14 @@
 	).RunTest(t)
 
 	pbcp := result.Module("platform-bootclasspath", "android_common")
-	info := result.ModuleProvider(pbcp, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
+	info := result.ModuleProvider(pbcp, monolithicHiddenAPIInfoProvider).(MonolithicHiddenAPIInfo)
 
 	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])
+		android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
 	}
 
 	android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)