Create a singleton all_apex_contributions module type

This will be a container for the the apex_contributions
selected using build flags. This module will be used to query the state of
selected apex contributions instead of a global that can be mutated by
anyone.

It will set a provider containing metadata for source vs prebuilts
selection. To reduce the overhead of a new mutator, this will be done in
the existing `prebuilt_select` mutator.

It will validate that there are no dups (`foo` and `prebuilt_foo` cannot
be both selected)

Bug: 308174923

Test: go test ./android

Change-Id: Ie42999a71f35d70e0e977f5ab07ce451608d9f35
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index a28ac31..6eaca8b 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -24,6 +25,7 @@
 
 func RegisterApexContributionsBuildComponents(ctx RegistrationContext) {
 	ctx.RegisterModuleType("apex_contributions", apexContributionsFactory)
+	ctx.RegisterSingletonModuleType("all_apex_contributions", allApexContributionsFactory)
 }
 
 type apexContributions struct {
@@ -65,3 +67,109 @@
 // prebuilts selection.
 func (m *apexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
 }
+
+// A container for apex_contributions.
+// Based on product_config, it will create a dependency on the selected
+// apex_contributions per mainline module
+type allApexContributions struct {
+	SingletonModuleBase
+}
+
+func allApexContributionsFactory() SingletonModule {
+	module := &allApexContributions{}
+	InitAndroidModule(module)
+	return module
+}
+
+type apexContributionsDepTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var (
+	acDepTag = apexContributionsDepTag{}
+)
+
+// Creates a dep to each selected apex_contributions
+func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), acDepTag, ctx.Config().AllApexContributions()...)
+}
+
+// Set PrebuiltSelectionInfoProvider in post deps phase
+func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
+	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
+		for _, content := range m.Contents() {
+			if !ctx.OtherModuleExists(content) {
+				ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name())
+			}
+			pi := &PrebuiltSelectionInfo{
+				baseModuleName:     RemoveOptionalPrebuiltPrefix(content),
+				selectedModuleName: content,
+				metadataModuleName: m.Name(),
+				apiDomain:          m.ApiDomain(),
+			}
+			p.Add(ctx, pi)
+		}
+	}
+
+	if ctx.Config().Bp2buildMode() { // Skip bp2build
+		return
+	}
+	p := PrebuiltSelectionInfoMap{}
+	ctx.VisitDirectDepsWithTag(acDepTag, func(child Module) {
+		if m, ok := child.(*apexContributions); ok {
+			addContentsToProvider(&p, m)
+		} else {
+			ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
+		}
+	})
+	ctx.SetProvider(PrebuiltSelectionInfoProvider, p)
+}
+
+// A provider containing metadata about whether source or prebuilt should be used
+// This provider will be used in prebuilt_select mutator to redirect deps
+var PrebuiltSelectionInfoProvider = blueprint.NewMutatorProvider(PrebuiltSelectionInfoMap{}, "prebuilt_select")
+
+// Map of baseModuleName to the selected source or prebuilt
+type PrebuiltSelectionInfoMap map[string]PrebuiltSelectionInfo
+
+// Add a new entry to the map with some validations
+func (pm *PrebuiltSelectionInfoMap) Add(ctx BaseModuleContext, p *PrebuiltSelectionInfo) {
+	if p == nil {
+		return
+	}
+	// Do not allow dups. If the base module (without the prebuilt_) has been added before, raise an exception.
+	if old, exists := (*pm)[p.baseModuleName]; exists {
+		ctx.ModuleErrorf("Cannot use Soong module: %s from apex_contributions: %s because it has been added previously as: %s from apex_contributions: %s\n",
+			p.selectedModuleName, p.metadataModuleName, old.selectedModuleName, old.metadataModuleName,
+		)
+	}
+	(*pm)[p.baseModuleName] = *p
+}
+
+type PrebuiltSelectionInfo struct {
+	// e.g. libc
+	baseModuleName string
+	// e.g. (libc|prebuilt_libc)
+	selectedModuleName string
+	// Name of the apex_contributions module
+	metadataModuleName string
+	// e.g. com.android.runtime
+	apiDomain string
+}
+
+// Returns true if `name` is explicitly requested using one of the selected
+// apex_contributions metadata modules.
+func (p *PrebuiltSelectionInfoMap) IsSelected(baseModuleName, name string) bool {
+	if i, exists := (*p)[baseModuleName]; exists {
+		return i.selectedModuleName == name
+	} else {
+		return false
+	}
+}
+
+// This module type does not have any build actions.
+func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (a *allApexContributions) GenerateSingletonBuildActions(ctx SingletonContext) {
+}