Merge CSV files generated by UnsupportedAppUsageProcessor.

Flow:
1. Annotation processor generates a CSV file per class as a CLASS_OUTPUT resource.
2. hiddenapi.go extracts individual .csv files and merges them into an index.csv file per module.
3. hiddenapi_singleton.go merges individual index.csv files into a combined .csv file.

In a follow up hiddenapi-index.csv would replace unsupportedappusage_index.csv

Bug: 145132366
Change-Id: I87d92f9c8d4b1cc1df526fc576ee3c2101116b58
Test: diff unsupportedappusage_index.csv hiddenapi-index.csv
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 6020aba..d48c767 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -28,9 +28,10 @@
 }, "outFlag", "stubAPIFlags")
 
 type hiddenAPI struct {
-	flagsCSVPath    android.Path
-	metadataCSVPath android.Path
 	bootDexJarPath  android.Path
+	flagsCSVPath    android.Path
+	indexCSVPath    android.Path
+	metadataCSVPath android.Path
 }
 
 func (h *hiddenAPI) flagsCSV() android.Path {
@@ -45,17 +46,21 @@
 	return h.bootDexJarPath
 }
 
+func (h *hiddenAPI) indexCSV() android.Path {
+	return h.indexCSVPath
+}
+
 type hiddenAPIIntf interface {
-	flagsCSV() android.Path
-	metadataCSV() android.Path
 	bootDexJar() android.Path
+	flagsCSV() android.Path
+	indexCSV() android.Path
+	metadataCSV() android.Path
 }
 
 var _ hiddenAPIIntf = (*hiddenAPI)(nil)
 
-func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, dexJar android.ModuleOutPath, implementationJar android.Path,
-	uncompressDex bool) android.ModuleOutPath {
-
+func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, dexJar android.ModuleOutPath,
+	implementationJar android.Path, uncompressDex bool) android.ModuleOutPath {
 	if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
 		name := ctx.ModuleName()
 
@@ -77,9 +82,8 @@
 			// Derive the greylist from classes jar.
 			flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
 			metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv")
-			hiddenAPIGenerateCSV(ctx, flagsCSV, metadataCSV, implementationJar)
-			h.flagsCSVPath = flagsCSV
-			h.metadataCSVPath = metadataCSV
+			indexCSV := android.PathForModuleOut(ctx, "hiddenapi", "index.csv")
+			h.hiddenAPIGenerateCSV(ctx, flagsCSV, metadataCSV, indexCSV, implementationJar)
 
 			// If this module is actually on the boot jars list and not providing
 			// hiddenapi information for a module on the boot jars list then encode
@@ -96,9 +100,7 @@
 	return dexJar
 }
 
-func hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, metadataCSV android.WritablePath,
-	classesJar android.Path) {
-
+func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, metadataCSV, indexCSV android.WritablePath, classesJar android.Path) {
 	stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags
 
 	ctx.Build(pctx, android.BuildParams{
@@ -112,6 +114,7 @@
 			"stubAPIFlags": stubFlagsCSV.String(),
 		},
 	})
+	h.flagsCSVPath = flagsCSV
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        hiddenAPIGenerateCSVRule,
@@ -124,18 +127,26 @@
 			"stubAPIFlags": stubFlagsCSV.String(),
 		},
 	})
+	h.metadataCSVPath = metadataCSV
 
+	rule := android.NewRuleBuilder()
+	rule.Command().
+		BuiltTool(ctx, "merge_csv").
+		FlagWithInput("--zip_input=", classesJar).
+		FlagWithOutput("--output=", indexCSV)
+	rule.Build(pctx, ctx, "merged-hiddenapi-index", "Merged Hidden API index")
+	h.indexCSVPath = indexCSV
 }
 
 var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
-	Command: `rm -rf $tmpDir && mkdir -p $tmpDir && mkdir $tmpDir/dex-input && mkdir $tmpDir/dex-output && ` +
-		`unzip -o -q $in 'classes*.dex' -d $tmpDir/dex-input && ` +
-		`for INPUT_DEX in $$(find $tmpDir/dex-input -maxdepth 1 -name 'classes*.dex' | sort); do ` +
-		`  echo "--input-dex=$${INPUT_DEX}"; ` +
-		`  echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})"; ` +
-		`done | xargs ${config.HiddenAPI} encode --api-flags=$flagsCsv $hiddenapiFlags && ` +
-		`${config.SoongZipCmd} $soongZipFlags -o $tmpDir/dex.jar -C $tmpDir/dex-output -f "$tmpDir/dex-output/classes*.dex" && ` +
-		`${config.MergeZipsCmd} -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" $out $tmpDir/dex.jar $in`,
+	Command: `rm -rf $tmpDir && mkdir -p $tmpDir && mkdir $tmpDir/dex-input && mkdir $tmpDir/dex-output &&
+		unzip -o -q $in 'classes*.dex' -d $tmpDir/dex-input &&
+		for INPUT_DEX in $$(find $tmpDir/dex-input -maxdepth 1 -name 'classes*.dex' | sort); do
+		  echo "--input-dex=$${INPUT_DEX}";
+		  echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})";
+		done | xargs ${config.HiddenAPI} encode --api-flags=$flagsCsv $hiddenapiFlags &&
+		${config.SoongZipCmd} $soongZipFlags -o $tmpDir/dex.jar -C $tmpDir/dex-output -f "$tmpDir/dex-output/classes*.dex" &&
+		${config.MergeZipsCmd} -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" -stripFile "**/*.uau" $out $tmpDir/dex.jar $in`,
 	CommandDeps: []string{
 		"${config.HiddenAPI}",
 		"${config.SoongZipCmd}",