Generate info about the selected apex variant in platform builds

out/soong/prebuilt_info.json will contain information about whether
source or prebuilt of an apex was used. If prebuilt is used, it will
print the path to its prebuilt_info file. This file will be
used to pick a matching set of MTS test prebuilts.

Each apex will have an entry in this file with the following properties
- Name, mandatory
- Is_prebuilt, mandatory
- Prebuilt_info_file_path, optional

Implementation details
- Introduce a new `prebuilt_info` prop to `apex_set` and `prebuilt_apex`
- All apex module types will set a prebuiltInfoProvider in
  GenerateAndroidBuildActions, including the source apex module types
- Create a `apex_prebuiltinfo_singleton` that visits all apex modules.
  It uses `IsHideFromMake` to filter out the unselected variants of a
  specific apex. This new singleton will create prebuilt_info.json
- Dist prebuilt_info.json for droidcore

Test: m nothing --no-skip-soong-tests
Test: m out/soong/prebuilt_info.json
Test: ls -l out/soong/prebuilt_info.json --human-readable
-rw------- 1 spandandas primarygroup 25K Mar 11 23:46 out/soong/prebuilt_info.json

Test: #modified trunk_staging.locally to select prebuilts of some
mainline modules. Spot-checked that `is_prebuilt` and
`prebuilt_info_file_path` get populated appropriately

Bug: 327480225

Change-Id: I65c73010142b034ad1d2b3d05ef493be034e8d74
diff --git a/apex/apex.go b/apex/apex.go
index 9d7af18..c688438 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2424,6 +2424,33 @@
 
 	// Set a provider for dexpreopt of bootjars
 	a.provideApexExportsInfo(ctx)
+
+	a.providePrebuiltInfo(ctx)
+}
+
+var prebuiltInfoProvider = blueprint.NewProvider[prebuiltInfo]()
+
+// contents of prebuilt_info.json
+type prebuiltInfo struct {
+	// Name of the apex, without the prebuilt_ prefix
+	Name string
+
+	Is_prebuilt bool
+
+	// This is relative to root of the workspace.
+	// In case of mainline modules, this file contains the build_id that was used
+	// to generate the mainline module prebuilt.
+	Prebuilt_info_file_path string `json:",omitempty"`
+}
+
+// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
+// with information about whether source or prebuilt of an apex was used during the build.
+func (a *apexBundle) providePrebuiltInfo(ctx android.ModuleContext) {
+	info := prebuiltInfo{
+		Name:        a.Name(),
+		Is_prebuilt: false,
+	}
+	android.SetProvider(ctx, prebuiltInfoProvider, info)
 }
 
 // Set a provider containing information about the jars and .prof provided by the apex
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 25c0cc4..8aaddbe 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -17,6 +17,8 @@
 package apex
 
 import (
+	"encoding/json"
+
 	"github.com/google/blueprint"
 
 	"android/soong/android"
@@ -129,3 +131,43 @@
 	// Export check result to Make. The path is added to droidcore.
 	ctx.Strict("APEX_ALLOWED_DEPS_CHECK", s.allowedApexDepsInfoCheckResult.String())
 }
+
+func init() {
+	registerApexPrebuiltInfoComponents(android.InitRegistrationContext)
+}
+
+func registerApexPrebuiltInfoComponents(ctx android.RegistrationContext) {
+	ctx.RegisterParallelSingletonType("apex_prebuiltinfo_singleton", apexPrebuiltInfoFactory)
+}
+
+func apexPrebuiltInfoFactory() android.Singleton {
+	return &apexPrebuiltInfo{}
+}
+
+type apexPrebuiltInfo struct {
+	out android.WritablePath
+}
+
+func (a *apexPrebuiltInfo) GenerateBuildActions(ctx android.SingletonContext) {
+	prebuiltInfos := []prebuiltInfo{}
+
+	ctx.VisitAllModules(func(m android.Module) {
+		prebuiltInfo, exists := android.SingletonModuleProvider(ctx, m, prebuiltInfoProvider)
+		// Use prebuiltInfoProvider to filter out non apex soong modules.
+		// Use HideFromMake to filter out the unselected variants of a specific apex.
+		if exists && !m.IsHideFromMake() {
+			prebuiltInfos = append(prebuiltInfos, prebuiltInfo)
+		}
+	})
+
+	j, err := json.Marshal(prebuiltInfos)
+	if err != nil {
+		ctx.Errorf("Could not convert prebuilt info of apexes to json due to error: %v", err)
+	}
+	a.out = android.PathForOutput(ctx, "prebuilt_info.json")
+	android.WriteFileRule(ctx, a.out, string(j))
+}
+
+func (a *apexPrebuiltInfo) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("droidcore", a.out)
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index cebbae9..34dfc46 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -121,6 +121,11 @@
 	// List of systemserverclasspath fragments inside this prebuilt APEX bundle and for which this
 	// APEX bundle will create an APEX variant.
 	Exported_systemserverclasspath_fragments []string
+
+	// Path to the .prebuilt_info file of the prebuilt apex.
+	// In case of mainline modules, the .prebuilt_info file contains the build_id that was used to
+	// generate the prebuilt.
+	Prebuilt_info *string `android:"path"`
 }
 
 // initPrebuiltCommon initializes the prebuiltCommon structure and performs initialization of the
@@ -819,6 +824,20 @@
 	}
 }
 
+// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
+// with information about whether source or prebuilt of an apex was used during the build.
+func (p *prebuiltCommon) providePrebuiltInfo(ctx android.ModuleContext) {
+	info := prebuiltInfo{
+		Name:        p.BaseModuleName(), // BaseModuleName ensures that this will not contain the prebuilt_ prefix.
+		Is_prebuilt: true,
+	}
+	// If Prebuilt_info information is available in the soong module definition, add it to prebuilt_info.json.
+	if p.prebuiltCommonProperties.Prebuilt_info != nil {
+		info.Prebuilt_info_file_path = android.PathForModuleSrc(ctx, *p.prebuiltCommonProperties.Prebuilt_info).String()
+	}
+	android.SetProvider(ctx, prebuiltInfoProvider, info)
+}
+
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
@@ -846,6 +865,8 @@
 	// provide info used for generating the boot image
 	p.provideApexExportsInfo(ctx)
 
+	p.providePrebuiltInfo(ctx)
+
 	// Save the files that need to be made available to Make.
 	p.initApexFilesForAndroidMk(ctx)
 
@@ -1068,6 +1089,8 @@
 	// provide info used for generating the boot image
 	a.provideApexExportsInfo(ctx)
 
+	a.providePrebuiltInfo(ctx)
+
 	// Save the files that need to be made available to Make.
 	a.initApexFilesForAndroidMk(ctx)