Add apex_available to control the availablity of a module to APEXes

apex_available property controls the availability of a module to APEXes.
For example, `apex_available: ["myapex", "otherapex"]` makes the module
available only to the two APEXes: myapex and otherapex, and nothing
else, even to the platform.

If the module is intended to be available to any APEX, then a pseudo
name "//apex_available:anyapex" can be used.

If the module is intended to be available to the platform, then another
pseudo name "//apex_available:platform" is used.

For now, if unspecified, this property defaults to ["//apex_available:platform",
"//apex_available:anyapex"], which means the module is available to everybody.
This will be reduced to ["//apex_available:platform"], when marking for
apex_available for existing modules are finished.

Bug: 139870423
Bug: 128708192
Test: m
Change-Id: Id4b233c3056c7858f984cbf9427cfac4118b2682
diff --git a/android/apex.go b/android/apex.go
index 99b13ab..557febf 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -78,12 +78,23 @@
 
 	// Return the no_apex property
 	NoApex() bool
+
+	// Tests if this module is available for the specified APEX or ":platform"
+	AvailableFor(what string) bool
 }
 
 type ApexProperties struct {
 	// Whether this module should not be part of any APEX. Default is false.
+	// TODO(b/128708192): remove this as this is equal to apex_available: [":platform"]
 	No_apex *bool
 
+	// Availability of this module in APEXes. Only the listed APEXes can include this module.
+	// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
+	// "//apex_available:platform" refers to non-APEX partitions like "system.img".
+	// Default is ["//apex_available:platform", "//apex_available:anyapex"].
+	// TODO(b/128708192) change the default to ["//apex_available:platform"]
+	Apex_available []string
+
 	// Name of the apex variant that this module is mutated into
 	ApexName string `blueprint:"mutated"`
 }
@@ -136,15 +147,46 @@
 	return proptools.Bool(m.ApexProperties.No_apex)
 }
 
+const (
+	availableToPlatform = "//apex_available:platform"
+	availableToAnyApex  = "//apex_available:anyapex"
+)
+
+func (m *ApexModuleBase) AvailableFor(what string) bool {
+	if len(m.ApexProperties.Apex_available) == 0 {
+		// apex_available defaults to ["//apex_available:platform", "//apex_available:anyapex"],
+		// which means 'available to everybody'.
+		return true
+	}
+	return InList(what, m.ApexProperties.Apex_available) ||
+		(what != availableToPlatform && InList(availableToAnyApex, m.ApexProperties.Apex_available))
+}
+
+func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
+	for _, n := range m.ApexProperties.Apex_available {
+		if n == availableToPlatform || n == availableToAnyApex {
+			continue
+		}
+		if !mctx.OtherModuleExists(n) {
+			mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n)
+		}
+	}
+}
+
 func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module {
 	if len(m.apexVariations) > 0 {
+		m.checkApexAvailableProperty(mctx)
 		sort.Strings(m.apexVariations)
-		variations := []string{""} // Original variation for platform
+		variations := []string{}
+		availableForPlatform := m.AvailableFor(availableToPlatform)
+		if availableForPlatform {
+			variations = append(variations, "") // Original variation for platform
+		}
 		variations = append(variations, m.apexVariations...)
 
 		modules := mctx.CreateVariations(variations...)
 		for i, m := range modules {
-			if i == 0 {
+			if availableForPlatform && i == 0 {
 				continue
 			}
 			m.(ApexModule).setApexName(variations[i])