cc: Create a common image mutator interface

As part of adding Vendor support to Rust, refactor the image mutator in
CC to a new common image mutator interface so this logic can be reused
across both CC and Rust.

Bug: 184042776
Test: m nothing
Change-Id: Ia55d5ad840db7cf1a64d6c65ed86487230cb8742
diff --git a/cc/cc.go b/cc/cc.go
index 9176bc3..4413df0 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1113,16 +1113,33 @@
 	return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
 }
 
-// isLLndk returns true for both LLNDK (public) and LLNDK-private libs.
 func (c *Module) IsLlndk() bool {
 	return c.VendorProperties.IsLLNDK
 }
 
-// IsLlndkPublic returns true only for LLNDK (public) libs.
 func (c *Module) IsLlndkPublic() bool {
 	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
 }
 
+func (c *Module) IsLlndkHeaders() bool {
+	if _, ok := c.linker.(*llndkHeadersDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) IsLlndkLibrary() bool {
+	if _, ok := c.linker.(*llndkStubDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (m *Module) HasLlndkStubs() bool {
+	lib := moduleLibraryInterface(m)
+	return lib != nil && lib.hasLLNDKStubs()
+}
+
 // isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs
 // and does not set llndk.vendor_available: false.
 func (c *Module) isImplementationForLLNDKPublic() bool {
@@ -1246,7 +1263,7 @@
 	return c.linker != nil && c.linker.nativeCoverage()
 }
 
-func (c *Module) isSnapshotPrebuilt() bool {
+func (c *Module) IsSnapshotPrebuilt() bool {
 	if p, ok := c.linker.(snapshotInterface); ok {
 		return p.isSnapshotPrebuilt()
 	}
diff --git a/cc/image.go b/cc/image.go
index afe6a0e..cb8589d 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -187,40 +187,73 @@
 	return true
 }
 
+// ImageMutatableModule provides a common image mutation interface for  LinkableInterface modules.
+type ImageMutatableModule interface {
+	android.Module
+	LinkableInterface
+
+	// AndroidModuleBase returns the android.ModuleBase for this module
+	AndroidModuleBase() *android.ModuleBase
+
+	// VendorAvailable returns true if this module is available on the vendor image.
+	VendorAvailable() bool
+
+	// OdmAvailable returns true if this module is available on the odm image.
+	OdmAvailable() bool
+
+	// ProductAvailable returns true if this module is available on the product image.
+	ProductAvailable() bool
+
+	// RamdiskAvailable returns true if this module is available on the ramdisk image.
+	RamdiskAvailable() bool
+
+	// RecoveryAvailable returns true if this module is available on the recovery image.
+	RecoveryAvailable() bool
+
+	// VendorRamdiskAvailable returns true if this module is available on the vendor ramdisk image.
+	VendorRamdiskAvailable() bool
+
+	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
+	IsSnapshotPrebuilt() bool
+
+	// SnapshotVersion returns the snapshot version for this module.
+	SnapshotVersion(mctx android.BaseModuleContext) string
+
+	// SdkVersion returns the SDK version for this module.
+	SdkVersion() string
+
+	// ExtraVariants returns the list of extra variants this module requires.
+	ExtraVariants() []string
+
+	// AppendExtraVariant returns an extra variant to the list of extra variants this module requires.
+	AppendExtraVariant(extraVariant string)
+
+	// SetRamdiskVariantNeeded sets whether the Ramdisk Variant is needed.
+	SetRamdiskVariantNeeded(b bool)
+
+	// SetVendorRamdiskVariantNeeded sets whether the Vendor Ramdisk Variant is needed.
+	SetVendorRamdiskVariantNeeded(b bool)
+
+	// SetRecoveryVariantNeeded sets whether the Recovery Variant is needed.
+	SetRecoveryVariantNeeded(b bool)
+
+	// SetCoreVariantNeeded sets whether the Core Variant is needed.
+	SetCoreVariantNeeded(b bool)
+}
+
+var _ ImageMutatableModule = (*Module)(nil)
+
 func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	// Validation check
+	m.CheckVndkProperties(mctx)
+	MutateImage(mctx, m)
+}
+
+// CheckVndkProperties checks whether the VNDK-related properties are set correctly.
+// If properties are not set correctly, results in a module context property error.
+func (m *Module) CheckVndkProperties(mctx android.BaseModuleContext) {
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 	productSpecific := mctx.ProductSpecific()
 
-	if Bool(m.VendorProperties.Vendor_available) {
-		if vendorSpecific {
-			mctx.PropertyErrorf("vendor_available",
-				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
-		}
-		if Bool(m.VendorProperties.Odm_available) {
-			mctx.PropertyErrorf("vendor_available",
-				"doesn't make sense at the same time as `odm_available: true`")
-		}
-	}
-
-	if Bool(m.VendorProperties.Odm_available) {
-		if vendorSpecific {
-			mctx.PropertyErrorf("odm_available",
-				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
-		}
-	}
-
-	if Bool(m.VendorProperties.Product_available) {
-		if productSpecific {
-			mctx.PropertyErrorf("product_available",
-				"doesn't make sense at the same time as `product_specific: true`")
-		}
-		if vendorSpecific {
-			mctx.PropertyErrorf("product_available",
-				"cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
-		}
-	}
-
 	if vndkdep := m.vndkdep; vndkdep != nil {
 		if vndkdep.isVndk() {
 			if vendorSpecific || productSpecific {
@@ -265,6 +298,111 @@
 			}
 		}
 	}
+}
+
+func (m *Module) VendorAvailable() bool {
+	return Bool(m.VendorProperties.Vendor_available)
+}
+
+func (m *Module) OdmAvailable() bool {
+	return Bool(m.VendorProperties.Odm_available)
+}
+
+func (m *Module) ProductAvailable() bool {
+	return Bool(m.VendorProperties.Product_available)
+}
+
+func (m *Module) RamdiskAvailable() bool {
+	return Bool(m.Properties.Ramdisk_available)
+}
+
+func (m *Module) VendorRamdiskAvailable() bool {
+	return Bool(m.Properties.Vendor_ramdisk_available)
+}
+
+func (m *Module) AndroidModuleBase() *android.ModuleBase {
+	return &m.ModuleBase
+}
+
+func (m *Module) RecoveryAvailable() bool {
+	return Bool(m.Properties.Recovery_available)
+}
+
+func (m *Module) ExtraVariants() []string {
+	return m.Properties.ExtraVariants
+}
+
+func (m *Module) AppendExtraVariant(extraVariant string) {
+	m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, extraVariant)
+}
+
+func (m *Module) SetRamdiskVariantNeeded(b bool) {
+	m.Properties.RamdiskVariantNeeded = b
+}
+
+func (m *Module) SetVendorRamdiskVariantNeeded(b bool) {
+	m.Properties.VendorRamdiskVariantNeeded = b
+}
+
+func (m *Module) SetRecoveryVariantNeeded(b bool) {
+	m.Properties.RecoveryVariantNeeded = b
+}
+
+func (m *Module) SetCoreVariantNeeded(b bool) {
+	m.Properties.CoreVariantNeeded = b
+}
+
+func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
+	if snapshot, ok := m.linker.(snapshotInterface); ok {
+		return snapshot.version()
+	} else {
+		mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+		// Should we be panicking here instead?
+		return ""
+	}
+}
+
+func (m *Module) KernelHeadersDecorator() bool {
+	if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+		return true
+	}
+	return false
+}
+
+// MutateImage handles common image mutations for ImageMutatableModule interfaces.
+func MutateImage(mctx android.BaseModuleContext, m ImageMutatableModule) {
+	// Validation check
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+	productSpecific := mctx.ProductSpecific()
+
+	if m.VendorAvailable() {
+		if vendorSpecific {
+			mctx.PropertyErrorf("vendor_available",
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
+		}
+		if m.OdmAvailable() {
+			mctx.PropertyErrorf("vendor_available",
+				"doesn't make sense at the same time as `odm_available: true`")
+		}
+	}
+
+	if m.OdmAvailable() {
+		if vendorSpecific {
+			mctx.PropertyErrorf("odm_available",
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
+		}
+	}
+
+	if m.ProductAvailable() {
+		if productSpecific {
+			mctx.PropertyErrorf("product_available",
+				"doesn't make sense at the same time as `product_specific: true`")
+		}
+		if vendorSpecific {
+			mctx.PropertyErrorf("product_available",
+				"cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
+		}
+	}
 
 	var coreVariantNeeded bool = false
 	var ramdiskVariantNeeded bool = false
@@ -287,18 +425,13 @@
 		productVndkVersion = platformVndkVersion
 	}
 
-	_, isLLNDKLibrary := m.linker.(*llndkStubDecorator)
-	_, isLLNDKHeaders := m.linker.(*llndkHeadersDecorator)
-	lib := moduleLibraryInterface(m)
-	hasLLNDKStubs := lib != nil && lib.hasLLNDKStubs()
-
-	if isLLNDKLibrary || isLLNDKHeaders || hasLLNDKStubs {
+	if m.IsLlndkLibrary() || m.IsLlndkHeaders() || m.HasLlndkStubs() {
 		// This is an LLNDK library.  The implementation of the library will be on /system,
 		// and vendor and product variants will be created with LLNDK stubs.
 		// The LLNDK libraries need vendor variants even if there is no VNDK.
 		// The obsolete llndk_library and llndk_headers modules also need the vendor variants
 		// so the cc_library LLNDK stubs can depend on them.
-		if hasLLNDKStubs {
+		if m.HasLlndkStubs() {
 			coreVariantNeeded = true
 		}
 		if platformVndkVersion != "" {
@@ -315,17 +448,13 @@
 		// If the device isn't compiling against the VNDK, we always
 		// use the core mode.
 		coreVariantNeeded = true
-	} else if m.isSnapshotPrebuilt() {
+	} else if m.IsSnapshotPrebuilt() {
 		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
 		// PRODUCT_EXTRA_VNDK_VERSIONS.
-		if snapshot, ok := m.linker.(snapshotInterface); ok {
-			if m.InstallInRecovery() {
-				recoveryVariantNeeded = true
-			} else {
-				vendorVariants = append(vendorVariants, snapshot.version())
-			}
+		if m.InstallInRecovery() {
+			recoveryVariantNeeded = true
 		} else {
-			mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+			vendorVariants = append(vendorVariants, m.SnapshotVersion(mctx))
 		}
 	} else if m.HasNonSystemVariants() && !m.IsVndkExt() {
 		// This will be available to /system unless it is product_specific
@@ -351,7 +480,7 @@
 				productVariants = append(productVariants, productVndkVersion)
 			}
 		}
-	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
+	} else if vendorSpecific && m.SdkVersion() == "" {
 		// This will be available in /vendor (or /odm) only
 
 		// kernel_headers is a special module type whose exported headers
@@ -360,7 +489,7 @@
 		// For other modules, we assume that modules under proprietary
 		// paths are compatible for BOARD_VNDK_VERSION. The other modules
 		// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
-		if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+		if m.KernelHeadersDecorator() {
 			vendorVariants = append(vendorVariants,
 				platformVndkVersion,
 				boardVndkVersion,
@@ -378,7 +507,7 @@
 	}
 
 	if boardVndkVersion != "" && productVndkVersion != "" {
-		if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
+		if coreVariantNeeded && productSpecific && m.SdkVersion() == "" {
 			// The module has "product_specific: true" that does not create core variant.
 			coreVariantNeeded = false
 			productVariants = append(productVariants, productVndkVersion)
@@ -390,60 +519,60 @@
 		productVariants = []string{}
 	}
 
-	if Bool(m.Properties.Ramdisk_available) {
+	if m.RamdiskAvailable() {
 		ramdiskVariantNeeded = true
 	}
 
-	if m.ModuleBase.InstallInRamdisk() {
+	if m.AndroidModuleBase().InstallInRamdisk() {
 		ramdiskVariantNeeded = true
 		coreVariantNeeded = false
 	}
 
-	if Bool(m.Properties.Vendor_ramdisk_available) {
+	if m.VendorRamdiskAvailable() {
 		vendorRamdiskVariantNeeded = true
 	}
 
-	if m.ModuleBase.InstallInVendorRamdisk() {
+	if m.AndroidModuleBase().InstallInVendorRamdisk() {
 		vendorRamdiskVariantNeeded = true
 		coreVariantNeeded = false
 	}
 
-	if Bool(m.Properties.Recovery_available) {
+	if m.RecoveryAvailable() {
 		recoveryVariantNeeded = true
 	}
 
-	if m.ModuleBase.InstallInRecovery() {
+	if m.AndroidModuleBase().InstallInRecovery() {
 		recoveryVariantNeeded = true
 		coreVariantNeeded = false
 	}
 
 	// If using a snapshot, the recovery variant under AOSP directories is not needed,
 	// except for kernel headers, which needs all variants.
-	if _, ok := m.linker.(*kernelHeadersDecorator); !ok &&
-		!m.isSnapshotPrebuilt() &&
+	if m.KernelHeadersDecorator() &&
+		!m.IsSnapshotPrebuilt() &&
 		usingRecoverySnapshot &&
 		!isRecoveryProprietaryModule(mctx) {
 		recoveryVariantNeeded = false
 	}
 
 	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
+		m.AppendExtraVariant(VendorVariationPrefix + variant)
 	}
 
 	for _, variant := range android.FirstUniqueStrings(productVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
+		m.AppendExtraVariant(ProductVariationPrefix + variant)
 	}
 
-	m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
-	m.Properties.VendorRamdiskVariantNeeded = vendorRamdiskVariantNeeded
-	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
-	m.Properties.CoreVariantNeeded = coreVariantNeeded
+	m.SetRamdiskVariantNeeded(ramdiskVariantNeeded)
+	m.SetVendorRamdiskVariantNeeded(vendorRamdiskVariantNeeded)
+	m.SetRecoveryVariantNeeded(recoveryVariantNeeded)
+	m.SetCoreVariantNeeded(coreVariantNeeded)
 
 	// Disable the module if no variants are needed.
 	if !ramdiskVariantNeeded &&
 		!recoveryVariantNeeded &&
 		!coreVariantNeeded &&
-		len(m.Properties.ExtraVariants) == 0 {
+		len(m.ExtraVariants()) == 0 {
 		m.Disable()
 	}
 }
diff --git a/cc/linkable.go b/cc/linkable.go
index 6aa238b..17bafcc 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -98,10 +98,24 @@
 	InVendor() bool
 
 	UseSdk() bool
+
+	// IsLlndk returns true for both LLNDK (public) and LLNDK-private libs.
+	IsLlndk() bool
+
+	// IsLlndkPublic returns true only for LLNDK (public) libs.
+	IsLlndkPublic() bool
+
+	// IsLlndkHeaders returns true if this module is an LLNDK headers module.
+	IsLlndkHeaders() bool
+
+	// IsLlndkLibrary returns true if this module is an LLNDK library module.
+	IsLlndkLibrary() bool
+
+	// HasLlndkStubs returns true if this module has LLNDK stubs.
+	HasLlndkStubs() bool
+
 	UseVndk() bool
 	MustUseVendorVariant() bool
-	IsLlndk() bool
-	IsLlndkPublic() bool
 	IsVndk() bool
 	IsVndkExt() bool
 	IsVndkPrivate() bool
@@ -121,6 +135,10 @@
 	SetPreventInstall()
 	// SetHideFromMake sets the HideFromMake property to 'true' for this module.
 	SetHideFromMake()
+
+	// KernelHeadersDecorator returns true if this is a kernel headers decorator module.
+	// This is specific to cc and should always return false for all other packages.
+	KernelHeadersDecorator() bool
 }
 
 var (
diff --git a/cc/sabi.go b/cc/sabi.go
index 4a1ba3c..c0eb57c 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -141,7 +141,7 @@
 	}
 
 	// Don't create ABI dump for prebuilts.
-	if m.Prebuilt() != nil || m.isSnapshotPrebuilt() {
+	if m.Prebuilt() != nil || m.IsSnapshotPrebuilt() {
 		return false
 	}
 
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 3437d77..2f68cca 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -173,7 +173,7 @@
 		return false
 	}
 	// the module must be installed in target image
-	if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !image.inImage(m)() {
+	if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.inImage(m)() {
 		return false
 	}
 	// skip kernel_headers which always depend on vendor
diff --git a/cc/vndk.go b/cc/vndk.go
index b7047e9..1a8a454 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -609,8 +609,8 @@
 	}
 	// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
 	// !installable: Snapshot only cares about "installable" modules.
-	// isSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
-	if !m.InVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
+	// IsSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
+	if !m.InVendor() || !m.installable(apexInfo) || m.IsSnapshotPrebuilt() {
 		return nil, "", false
 	}
 	l, ok := m.linker.(snapshotLibraryInterface)
diff --git a/rust/rust.go b/rust/rust.go
index 34e197a..f0da2ce 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -255,6 +255,22 @@
 	return false
 }
 
+func (m *Module) IsLlndkHeaders() bool {
+	return false
+}
+
+func (m *Module) IsLlndkLibrary() bool {
+	return false
+}
+
+func (mod *Module) KernelHeadersDecorator() bool {
+	return false
+}
+
+func (m *Module) HasLlndkStubs() bool {
+	return false
+}
+
 func (mod *Module) SdkVersion() string {
 	return ""
 }