Add support for /product-services partition

This is an adaptation of Icc4f8c16bc389fe20db680849f311d02df1299c3, to
support modules that are installed on the /product-services partition.

Bug: 80741439
Test: m -j both with and without enabling the new partition
Change-Id: I72b335ad38baff5848cd3da7489343f8cf98ff16
diff --git a/android/androidmk.go b/android/androidmk.go
index a24d7bc..9f711bf 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -242,6 +242,9 @@
 		if Bool(amod.commonProperties.Product_specific) {
 			fmt.Fprintln(&data.preamble, "LOCAL_PRODUCT_MODULE := true")
 		}
+		if Bool(amod.commonProperties.ProductServices_specific) {
+			fmt.Fprintln(&data.preamble, "LOCAL_PRODUCT_SERVICES_MODULE := true")
+		}
 		if amod.commonProperties.Owner != nil {
 			fmt.Fprintln(&data.preamble, "LOCAL_MODULE_OWNER :=", *amod.commonProperties.Owner)
 		}
diff --git a/android/config.go b/android/config.go
index 7ba05c1..0015c02 100644
--- a/android/config.go
+++ b/android/config.go
@@ -763,6 +763,13 @@
 	return "product"
 }
 
+func (c *deviceConfig) ProductServicesPath() string {
+	if c.config.productVariables.ProductServicesPath != nil {
+		return *c.config.productVariables.ProductServicesPath
+	}
+	return "product-services"
+}
+
 func (c *deviceConfig) BtConfigIncludeDir() string {
 	return String(c.config.productVariables.BtConfigIncludeDir)
 }
diff --git a/android/module.go b/android/module.go
index b6220dc..b58dd4b 100644
--- a/android/module.go
+++ b/android/module.go
@@ -69,6 +69,7 @@
 	DeviceSpecific() bool
 	SocSpecific() bool
 	ProductSpecific() bool
+	ProductServicesSpecific() bool
 	AConfig() Config
 	DeviceConfig() DeviceConfig
 }
@@ -241,6 +242,11 @@
 	// /system/product if product partition does not exist).
 	Product_specific *bool
 
+	// whether this module provides services owned by the OS provider to the core platform. When set
+	// to true, it is installed into  /product-services (or /system/product-services if
+	// product-services partition does not exist).
+	ProductServices_specific *bool
+
 	// Whether this module is installed to recovery partition
 	Recovery *bool
 
@@ -303,6 +309,7 @@
 	deviceSpecificModule
 	socSpecificModule
 	productSpecificModule
+	productServicesSpecificModule
 )
 
 func (k moduleKind) String() string {
@@ -315,6 +322,8 @@
 		return "soc-specific"
 	case productSpecificModule:
 		return "product-specific"
+	case productServicesSpecificModule:
+		return "productservices-specific"
 	default:
 		panic(fmt.Errorf("unknown module kind %d", k))
 	}
@@ -507,7 +516,7 @@
 }
 
 func (a *ModuleBase) Platform() bool {
-	return !a.DeviceSpecific() && !a.SocSpecific() && !a.ProductSpecific()
+	return !a.DeviceSpecific() && !a.SocSpecific() && !a.ProductSpecific() && !a.ProductServicesSpecific()
 }
 
 func (a *ModuleBase) DeviceSpecific() bool {
@@ -522,6 +531,10 @@
 	return Bool(a.commonProperties.Product_specific)
 }
 
+func (a *ModuleBase) ProductServicesSpecific() bool {
+	return Bool(a.commonProperties.ProductServices_specific)
+}
+
 func (a *ModuleBase) Enabled() bool {
 	if a.commonProperties.Enabled == nil {
 		return !a.Os().DefaultDisabled
@@ -632,17 +645,11 @@
 	var socSpecific = Bool(a.commonProperties.Vendor) || Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Soc_specific)
 	var deviceSpecific = Bool(a.commonProperties.Device_specific)
 	var productSpecific = Bool(a.commonProperties.Product_specific)
+	var productServicesSpecific = Bool(a.commonProperties.ProductServices_specific)
 
-	if ((socSpecific || deviceSpecific) && productSpecific) || (socSpecific && deviceSpecific) {
-		msg := "conflicting value set here"
-		if productSpecific {
-			ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
-			if deviceSpecific {
-				ctx.PropertyErrorf("device_specific", msg)
-			}
-		} else {
-			ctx.PropertyErrorf("device_specific", "a module cannot be specific to SoC and device at the same time.")
-		}
+	msg := "conflicting value set here"
+	if socSpecific && deviceSpecific {
+		ctx.PropertyErrorf("device_specific", "a module cannot be specific to SoC and device at the same time.")
 		if Bool(a.commonProperties.Vendor) {
 			ctx.PropertyErrorf("vendor", msg)
 		}
@@ -654,8 +661,36 @@
 		}
 	}
 
+	if productSpecific && productServicesSpecific {
+		ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and product_services at the same time.")
+		ctx.PropertyErrorf("product_services_specific", msg)
+	}
+
+	if (socSpecific || deviceSpecific) && (productSpecific || productServicesSpecific) {
+		if productSpecific {
+			ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
+		} else {
+			ctx.PropertyErrorf("product_services_specific", "a module cannot be specific to SoC or device and product_services at the same time.")
+		}
+		if deviceSpecific {
+			ctx.PropertyErrorf("device_specific", msg)
+		} else {
+			if Bool(a.commonProperties.Vendor) {
+				ctx.PropertyErrorf("vendor", msg)
+			}
+			if Bool(a.commonProperties.Proprietary) {
+				ctx.PropertyErrorf("proprietary", msg)
+			}
+			if Bool(a.commonProperties.Soc_specific) {
+				ctx.PropertyErrorf("soc_specific", msg)
+			}
+		}
+	}
+
 	if productSpecific {
 		return productSpecificModule
+	} else if productServicesSpecific {
+		return productServicesSpecificModule
 	} else if deviceSpecific {
 		return deviceSpecificModule
 	} else if socSpecific {
@@ -1012,6 +1047,10 @@
 	return a.kind == productSpecificModule
 }
 
+func (a *androidBaseContextImpl) ProductServicesSpecific() bool {
+	return a.kind == productServicesSpecificModule
+}
+
 func (a *androidModuleContext) InstallInData() bool {
 	return a.module.InstallInData()
 }
diff --git a/android/paths.go b/android/paths.go
index 31c5977..c9e7150 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -961,6 +961,8 @@
 			partition = ctx.DeviceConfig().OdmPath()
 		} else if ctx.ProductSpecific() {
 			partition = ctx.DeviceConfig().ProductPath()
+		} else if ctx.ProductServicesSpecific() {
+			partition = ctx.DeviceConfig().ProductServicesPath()
 		} else {
 			partition = "system"
 		}
diff --git a/android/paths_test.go b/android/paths_test.go
index b3dc9de..ff0eeb3 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -292,6 +292,17 @@
 			in:  []string{"bin", "my_test"},
 			out: "target/product/test_device/product/bin/my_test",
 		},
+		{
+			name: "product-services binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productServicesSpecificModule,
+				},
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/product-services/bin/my_test",
+		},
 
 		{
 			name: "system native test binary",
@@ -342,6 +353,19 @@
 		},
 
 		{
+			name: "product-services native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productServicesSpecificModule,
+				},
+				inData: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/nativetest/my_test",
+		},
+
+		{
 			name: "sanitized system binary",
 			ctx: &moduleInstallPathContextImpl{
 				androidBaseContextImpl: androidBaseContextImpl{
@@ -390,6 +414,19 @@
 		},
 
 		{
+			name: "sanitized product-services binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productServicesSpecificModule,
+				},
+				inSanitizerDir: true,
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/data/asan/product-services/bin/my_test",
+		},
+
+		{
 			name: "sanitized system native test binary",
 			ctx: &moduleInstallPathContextImpl{
 				androidBaseContextImpl: androidBaseContextImpl{
@@ -440,6 +477,19 @@
 			in:  []string{"nativetest", "my_test"},
 			out: "target/product/test_device/data/asan/data/nativetest/my_test",
 		},
+		{
+			name: "sanitized product-services native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productServicesSpecificModule,
+				},
+				inData:         true,
+				inSanitizerDir: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/android/variable.go b/android/variable.go
index 5edcdbc..d97fc0b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -182,9 +182,10 @@
 	CFIExcludePaths *[]string `json:",omitempty"`
 	CFIIncludePaths *[]string `json:",omitempty"`
 
-	VendorPath  *string `json:",omitempty"`
-	OdmPath     *string `json:",omitempty"`
-	ProductPath *string `json:",omitempty"`
+	VendorPath          *string `json:",omitempty"`
+	OdmPath             *string `json:",omitempty"`
+	ProductPath         *string `json:",omitempty"`
+	ProductServicesPath *string `json:",omitempty"`
 
 	UseClangLld *bool `json:",omitempty"`
 
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 29c7365..1d50980 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -174,6 +174,7 @@
 			"LOCAL_VENDOR_MODULE":            "vendor",
 			"LOCAL_ODM_MODULE":               "device_specific",
 			"LOCAL_PRODUCT_MODULE":           "product_specific",
+			"LOCAL_PRODUCT_SERVICES_MODULE":  "product_services_specific",
 			"LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
 			"LOCAL_PRIVILEGED_MODULE":        "privileged",