Add [soc|device|product]_specific

Added three properties (soc_specific, device_specific, and
product_specific) that shows what a module is specific to.

`soc_specific: true` means that the module is specific to an SoC
(System-On-a-Chip) and thus need to be installed to vendor partition.
This has the same meaning as the old `vendor: true` setting.

`device_specific: true` means that the module is specific to the entire
hardware configuration of a device includeing the SoC and off-chip
peripherals. These modules are installed to odm partition (or /vendor/odm
when odm partition does not exist).

`product_specific: true` means that the module is specific to the
software configuration of a product such as country, network operator,
etc. These modules are installed to oem partition (or /system/oem when
oem partition does not exist). These modules are assumed to be agnostic
to hardware, so this property can't be true when either soc_specific or
device_specific is set to true.

Bug: 68187740
Test: Build. path_tests amended.
Change-Id: I44ff055d87d53b0d2676758c506060de54cbffa0
diff --git a/android/androidmk.go b/android/androidmk.go
index fb3934f..704b560 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -230,9 +230,15 @@
 		if Bool(amod.commonProperties.Proprietary) {
 			fmt.Fprintln(&data.preamble, "LOCAL_PROPRIETARY_MODULE := true")
 		}
-		if Bool(amod.commonProperties.Vendor) {
+		if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
 			fmt.Fprintln(&data.preamble, "LOCAL_VENDOR_MODULE := true")
 		}
+		if Bool(amod.commonProperties.Device_specific) {
+			fmt.Fprintln(&data.preamble, "LOCAL_ODM_MODULE := true")
+		}
+		if Bool(amod.commonProperties.Product_specific) {
+			fmt.Fprintln(&data.preamble, "LOCAL_OEM_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 dd0301e..887291d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -661,6 +661,20 @@
 	return c.config.ProductVariables.ExtraVndkVersions
 }
 
+func (c *deviceConfig) OdmPath() string {
+	if c.config.ProductVariables.OdmPath != nil {
+		return *c.config.ProductVariables.OdmPath
+	}
+	return "odm"
+}
+
+func (c *deviceConfig) OemPath() string {
+	if c.config.ProductVariables.OemPath != nil {
+		return *c.config.ProductVariables.OemPath
+	}
+	return "oem"
+}
+
 func (c *deviceConfig) BtConfigIncludeDir() string {
 	return String(c.config.ProductVariables.BtConfigIncludeDir)
 }
diff --git a/android/module.go b/android/module.go
index 3d8f683..cb068ab 100644
--- a/android/module.go
+++ b/android/module.go
@@ -65,7 +65,10 @@
 	Windows() bool
 	Debug() bool
 	PrimaryArch() bool
-	InstallOnVendorPartition() bool
+	Platform() bool
+	DeviceSpecific() bool
+	SocSpecific() bool
+	ProductSpecific() bool
 	AConfig() Config
 	DeviceConfig() DeviceConfig
 }
@@ -212,9 +215,26 @@
 	// vendor who owns this module
 	Owner *string
 
-	// whether this module is device specific and should be installed into /vendor
+	// whether this module is specific to an SoC (System-On-a-Chip). When set to true,
+	// it is installed into /vendor (or /system/vendor if vendor partition does not exist).
+	// Use `soc_specific` instead for better meaning.
 	Vendor *bool
 
+	// whether this module is specific to an SoC (System-On-a-Chip). When set to true,
+	// it is installed into /vendor (or /system/vendor if vendor partition does not exist).
+	Soc_specific *bool
+
+	// whether this module is specific to a device, not only for SoC, but also for off-chip
+	// peripherals. When set to true, it is installed into /odm (or /vendor/odm if odm partition
+	// does not exist, or /system/vendor/odm if both odm and vendor partitions do not exist).
+	// This implies `soc_specific:true`.
+	Device_specific *bool
+
+	// whether this module is specific to a software configuration of a product (e.g. country,
+	// network operator, etc). When set to true, it is installed into /oem (or /system/oem if
+	// oem partition does not exist).
+	Product_specific *bool
+
 	// init.rc files to be installed if this module is installed
 	Init_rc []string
 
@@ -264,6 +284,30 @@
 	NeitherHostNorDeviceSupported
 )
 
+type moduleKind int
+
+const (
+	platformModule moduleKind = iota
+	deviceSpecificModule
+	socSpecificModule
+	productSpecificModule
+)
+
+func (k moduleKind) String() string {
+	switch k {
+	case platformModule:
+		return "platform"
+	case deviceSpecificModule:
+		return "device-specific"
+	case socSpecificModule:
+		return "soc-specific"
+	case productSpecificModule:
+		return "product-specific"
+	default:
+		panic(fmt.Errorf("unknown module kind %d", k))
+	}
+}
+
 func InitAndroidModule(m Module) {
 	base := m.base()
 	base.module = m
@@ -546,11 +590,48 @@
 	}
 }
 
+func determineModuleKind(a *ModuleBase, ctx blueprint.BaseModuleContext) moduleKind {
+	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)
+
+	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.")
+		}
+		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 deviceSpecific {
+		return deviceSpecificModule
+	} else if socSpecific {
+		return socSpecificModule
+	} else {
+		return platformModule
+	}
+}
+
 func (a *ModuleBase) androidBaseContextFactory(ctx blueprint.BaseModuleContext) androidBaseContextImpl {
 	return androidBaseContextImpl{
 		target:        a.commonProperties.CompileTarget,
 		targetPrimary: a.commonProperties.CompilePrimary,
-		vendor:        Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Vendor),
+		kind:          determineModuleKind(a, ctx),
 		config:        ctx.Config().(Config),
 	}
 }
@@ -606,7 +687,7 @@
 	target        Target
 	targetPrimary bool
 	debug         bool
-	vendor        bool
+	kind          moduleKind
 	config        Config
 }
 
@@ -867,8 +948,20 @@
 	return DeviceConfig{a.config.deviceConfig}
 }
 
-func (a *androidBaseContextImpl) InstallOnVendorPartition() bool {
-	return a.vendor
+func (a *androidBaseContextImpl) Platform() bool {
+	return a.kind == platformModule
+}
+
+func (a *androidBaseContextImpl) DeviceSpecific() bool {
+	return a.kind == deviceSpecificModule
+}
+
+func (a *androidBaseContextImpl) SocSpecific() bool {
+	return a.kind == socSpecificModule
+}
+
+func (a *androidBaseContextImpl) ProductSpecific() bool {
+	return a.kind == productSpecificModule
 }
 
 func (a *androidModuleContext) InstallInData() bool {
diff --git a/android/paths.go b/android/paths.go
index e47b9e4..4d9c858 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -845,8 +845,12 @@
 		var partition string
 		if ctx.InstallInData() {
 			partition = "data"
-		} else if ctx.InstallOnVendorPartition() {
+		} else if ctx.SocSpecific() {
 			partition = ctx.DeviceConfig().VendorPath()
+		} else if ctx.DeviceSpecific() {
+			partition = ctx.DeviceConfig().OdmPath()
+		} else if ctx.ProductSpecific() {
+			partition = ctx.DeviceConfig().OemPath()
 		} else {
 			partition = "system"
 		}
diff --git a/android/paths_test.go b/android/paths_test.go
index 1e4ba53..110974f 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -246,12 +246,34 @@
 			ctx: &moduleInstallPathContextImpl{
 				androidBaseContextImpl: androidBaseContextImpl{
 					target: deviceTarget,
-					vendor: true,
+					kind:   socSpecificModule,
 				},
 			},
 			in:  []string{"bin", "my_test"},
 			out: "target/product/test_device/vendor/bin/my_test",
 		},
+		{
+			name: "odm binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   deviceSpecificModule,
+				},
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/odm/bin/my_test",
+		},
+		{
+			name: "oem binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productSpecificModule,
+				},
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/oem/bin/my_test",
+		},
 
 		{
 			name: "system native test binary",
@@ -269,7 +291,31 @@
 			ctx: &moduleInstallPathContextImpl{
 				androidBaseContextImpl: androidBaseContextImpl{
 					target: deviceTarget,
-					vendor: true,
+					kind:   socSpecificModule,
+				},
+				inData: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/nativetest/my_test",
+		},
+		{
+			name: "odm native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   deviceSpecificModule,
+				},
+				inData: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/nativetest/my_test",
+		},
+		{
+			name: "oem native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productSpecificModule,
 				},
 				inData: true,
 			},
@@ -293,13 +339,37 @@
 			ctx: &moduleInstallPathContextImpl{
 				androidBaseContextImpl: androidBaseContextImpl{
 					target: deviceTarget,
-					vendor: true,
+					kind:   socSpecificModule,
 				},
 				inSanitizerDir: true,
 			},
 			in:  []string{"bin", "my_test"},
 			out: "target/product/test_device/data/asan/vendor/bin/my_test",
 		},
+		{
+			name: "sanitized odm binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   deviceSpecificModule,
+				},
+				inSanitizerDir: true,
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/data/asan/odm/bin/my_test",
+		},
+		{
+			name: "sanitized oem binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productSpecificModule,
+				},
+				inSanitizerDir: true,
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/data/asan/oem/bin/my_test",
+		},
 
 		{
 			name: "sanitized system native test binary",
@@ -318,7 +388,33 @@
 			ctx: &moduleInstallPathContextImpl{
 				androidBaseContextImpl: androidBaseContextImpl{
 					target: deviceTarget,
-					vendor: true,
+					kind:   socSpecificModule,
+				},
+				inData:         true,
+				inSanitizerDir: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+		},
+		{
+			name: "sanitized odm native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   deviceSpecificModule,
+				},
+				inData:         true,
+				inSanitizerDir: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+		},
+		{
+			name: "sanitized oem native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					kind:   productSpecificModule,
 				},
 				inData:         true,
 				inSanitizerDir: true,
diff --git a/android/variable.go b/android/variable.go
index 155eee5..6962b0f 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -174,6 +174,8 @@
 	CFIIncludePaths *[]string `json:",omitempty"`
 
 	VendorPath *string `json:",omitempty"`
+	OdmPath    *string `json:",omitempty"`
+	OemPath    *string `json:",omitempty"`
 
 	ClangTidy  *bool   `json:",omitempty"`
 	TidyChecks *string `json:",omitempty"`
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index c6e90f0..82b5eb9 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -152,6 +152,8 @@
 			"LOCAL_TIDY":                     "tidy",
 			"LOCAL_PROPRIETARY_MODULE":       "proprietary",
 			"LOCAL_VENDOR_MODULE":            "vendor",
+			"LOCAL_ODM_MODULE":               "device_specific",
+			"LOCAL_OEM_MODULE":               "product_specific",
 			"LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
 			"LOCAL_PRIVILEGED_MODULE":        "privileged",
 
diff --git a/cc/cc.go b/cc/cc.go
index 04aa6a6..13d0e3b 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -425,8 +425,9 @@
 	moduleContextImpl
 }
 
-func (ctx *moduleContext) InstallOnVendorPartition() bool {
-	return ctx.ModuleContext.InstallOnVendorPartition() || (ctx.mod.useVndk() && !ctx.mod.isVndk())
+func (ctx *moduleContext) SocSpecific() bool {
+	return ctx.ModuleContext.SocSpecific() ||
+		(ctx.mod.hasVendorVariant() && ctx.mod.useVndk() && !ctx.mod.isVndk())
 }
 
 type moduleContextImpl struct {
@@ -1402,7 +1403,7 @@
 				mctx.CreateVariations(coreMode)
 			} else if Bool(props.Vendor_available) {
 				mctx.CreateVariations(coreMode, vendorMode)
-			} else if mctx.InstallOnVendorPartition() {
+			} else if mctx.SocSpecific() || mctx.DeviceSpecific() {
 				mctx.CreateVariations(vendorMode)
 			} else {
 				mctx.CreateVariations(coreMode)
@@ -1416,9 +1417,9 @@
 	}
 
 	// Sanity check
-	if m.VendorProperties.Vendor_available != nil && mctx.InstallOnVendorPartition() {
+	if m.VendorProperties.Vendor_available != nil && (mctx.SocSpecific() || mctx.DeviceSpecific()) {
 		mctx.PropertyErrorf("vendor_available",
-			"doesn't make sense at the same time as `vendor: true` or `proprietary: true`")
+			"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
 		return
 	}
 	if vndk := m.vndkdep; vndk != nil {
@@ -1460,8 +1461,8 @@
 		vendor := mod[1].(*Module)
 		vendor.Properties.UseVndk = true
 		squashVendorSrcs(vendor)
-	} else if mctx.InstallOnVendorPartition() && String(m.Properties.Sdk_version) == "" {
-		// This will be available in /vendor only
+	} else if (mctx.SocSpecific() || mctx.DeviceSpecific()) && String(m.Properties.Sdk_version) == "" {
+		// This will be available in /vendor (or /odm) only
 		mod := mctx.CreateVariations(vendorMode)
 		vendor := mod[0].(*Module)
 		vendor.Properties.UseVndk = true