'vendor_available: *' will not create product variant

With this patch, `vendor_available: true` will no longer creates
product variant. Instead, modules need to set `product_available:
true` if they have to be available to product vanriant.
If both properties are defined for VNDKs, they must have the same
values.

Bug: 150902910
Test: m nothing
Change-Id: I28fb6886e6114583227a31151136627c8516ac9c
diff --git a/cc/image.go b/cc/image.go
index 380c1db..b55e1f4 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -17,6 +17,8 @@
 // functions to determine where a module is installed, etc.
 
 import (
+	"fmt"
+	"reflect"
 	"strings"
 
 	"android/soong/android"
@@ -97,17 +99,23 @@
 
 // Returns true when this module is configured to have core and vendor variants.
 func (c *Module) HasVendorVariant() bool {
+	// In case of a VNDK, 'vendor_available: false' still creates a vendor variant.
 	return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
 }
 
 // Returns true when this module is configured to have core and product variants.
 func (c *Module) HasProductVariant() bool {
+	if c.VendorProperties.Product_available == nil {
+		// Without 'product_available', product variant will not be created even for VNDKs.
+		return false
+	}
+	// However, 'product_available: false' in a VNDK still creates a product variant.
 	return c.IsVndk() || Bool(c.VendorProperties.Product_available)
 }
 
 // Returns true when this module is configured to have core and either product or vendor variants.
 func (c *Module) HasNonSystemVariants() bool {
-	return c.IsVndk() || Bool(c.VendorProperties.Vendor_available) || Bool(c.VendorProperties.Product_available)
+	return c.HasVendorVariant() || c.HasProductVariant()
 }
 
 // Returns true if the module is "product" variant. Usually these modules are installed in /product
@@ -144,6 +152,52 @@
 	return c.ModuleBase.InstallInRecovery()
 }
 
+func visitPropsAndCompareVendorAndProductProps(v reflect.Value) bool {
+	if v.Kind() != reflect.Struct {
+		return true
+	}
+	for i := 0; i < v.NumField(); i++ {
+		prop := v.Field(i)
+		if prop.Kind() == reflect.Struct && v.Type().Field(i).Name == "Target" {
+			vendor_prop := prop.FieldByName("Vendor")
+			product_prop := prop.FieldByName("Product")
+			if vendor_prop.Kind() != reflect.Struct && product_prop.Kind() != reflect.Struct {
+				// Neither Target.Vendor nor Target.Product is defined
+				continue
+			}
+			if vendor_prop.Kind() != reflect.Struct || product_prop.Kind() != reflect.Struct ||
+				!reflect.DeepEqual(vendor_prop.Interface(), product_prop.Interface()) {
+				// If only one of either Target.Vendor or Target.Product is
+				// defined or they have different values, it fails the build
+				// since VNDK must have the same properties for both vendor
+				// and product variants.
+				return false
+			}
+		} else if !visitPropsAndCompareVendorAndProductProps(prop) {
+			// Visit the substructures to find Target.Vendor and Target.Product
+			return false
+		}
+	}
+	return true
+}
+
+// In the case of VNDK, vendor and product variants must have the same properties.
+// VNDK installs only one file and shares it for both vendor and product modules on
+// runtime. We may not define different versions of a VNDK lib for each partition.
+// This function is used only for the VNDK modules that is available to both vendor
+// and product partitions.
+func (c *Module) compareVendorAndProductProps() bool {
+	if !c.IsVndk() && c.VendorProperties.Product_available != nil {
+		panic(fmt.Errorf("This is only for product available VNDK libs. %q is not a VNDK library or not product available", c.Name()))
+	}
+	for _, properties := range c.GetProperties() {
+		if !visitPropsAndCompareVendorAndProductProps(reflect.ValueOf(properties).Elem()) {
+			return false
+		}
+	}
+	return true
+}
+
 func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
 	// Validation check
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
@@ -154,14 +208,6 @@
 			mctx.PropertyErrorf("vendor_available",
 				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
 		}
-		// If defined, make sure vendor_available and product_available has the
-		// same value since `false` for these properties means the module is
-		// for system only but provides the variant.
-		if m.VendorProperties.Product_available != nil {
-			if Bool(m.VendorProperties.Vendor_available) != Bool(m.VendorProperties.Product_available) {
-				mctx.PropertyErrorf("product_available", "may not have different value than `vendor_available`")
-			}
-		}
 	}
 
 	if m.VendorProperties.Product_available != nil {
@@ -198,6 +244,18 @@
 					mctx.PropertyErrorf("vndk",
 						"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
 				}
+				if m.VendorProperties.Product_available != nil {
+					// If product_available is defined for a VNDK, make sure vendor_available and
+					// product_available has the same value since `false` for these properties
+					// means the module is VNDK-private.
+					if Bool(m.VendorProperties.Vendor_available) != Bool(m.VendorProperties.Product_available) {
+						mctx.PropertyErrorf("product_available", "may not have different value than `vendor_available` for a VNDK")
+					}
+					// Also, both variants must have the same properties since they share a single VNDK library on runtime.
+					if !m.compareVendorAndProductProps() {
+						mctx.ModuleErrorf("product properties must have the same values with the vendor properties for VNDK modules")
+					}
+				}
 			}
 		} else {
 			if vndkdep.isVndkSp() {
@@ -281,13 +339,13 @@
 			}
 		}
 
-		// vendor_available modules are also available to /product.
-		// TODO(b/150902910): product variant will be created only if
-		// m.HasProductVariant() is true.
-		productVariants = append(productVariants, platformVndkVersion)
-		// VNDK is always PLATFORM_VNDK_VERSION
-		if !m.IsVndk() {
-			productVariants = append(productVariants, productVndkVersion)
+		// product_available modules are available to /product.
+		if m.HasProductVariant() {
+			productVariants = append(productVariants, platformVndkVersion)
+			// VNDK is always PLATFORM_VNDK_VERSION
+			if !m.IsVndk() {
+				productVariants = append(productVariants, productVndkVersion)
+			}
 		}
 	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
 		// This will be available in /vendor (or /odm) only
@@ -471,7 +529,6 @@
 	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
 		m.Properties.ImageVariationPrefix = ProductVariationPrefix
 		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
-		// TODO (b/150902910): This will be replaced with squashProductSrcs(m).
-		squashVendorSrcs(m)
+		squashProductSrcs(m)
 	}
 }