Allow hwasan variant for vendor_snapshot_static modules

vendor_snapshot_static modules can define hwasan variant as below:

vendor_snapshot_static {
    name: "libsnapshot",
    vendor: true,
    version: "33",
    arch: {
        arm64: {
            src: "libsnapshot.a",
            hwasan: {
                src: "libsnapshot.hwasan.a",
            },
        },
    },
}

Bug: 234772527
Test: m nothing
Change-Id: I0725028a4fdc302005d86f1e36561b98c0af623b
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 58bb57c..4ae099f 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -540,8 +540,10 @@
 
 	entries.SubName = ""
 
-	if c.sanitizerProperties.CfiEnabled {
+	if c.isSanitizerEnabled(cfi) {
 		entries.SubName += ".cfi"
+	} else if c.isSanitizerEnabled(Hwasan) {
+		entries.SubName += ".hwasan"
 	}
 
 	entries.SubName += c.baseProperties.Androidmk_suffix
diff --git a/cc/sanitize.go b/cc/sanitize.go
index d3fc221..c7e98c0 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1065,7 +1065,7 @@
 		//TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
 
 		// Check if it's a snapshot module supporting sanitizer
-		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
 			return []string{"", s.sanitizer.variationName()}
 		} else {
 			return []string{""}
@@ -1097,7 +1097,7 @@
 func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
 	if d, ok := ctx.Module().(PlatformSanitizeable); ok {
 		if dm, ok := ctx.Module().(*Module); ok {
-			if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+			if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
 				return incomingVariation
 			}
 		}
@@ -1212,14 +1212,23 @@
 			sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name())
 		}
 	} else if c, ok := mctx.Module().(*Module); ok {
-		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
+			if !ss.isUnsanitizedVariant() {
+				// Snapshot sanitizer may have only one variantion.
+				// Skip exporting the module if it already has a sanitizer variation.
+				c.SetPreventInstall()
+				c.SetHideFromMake()
+				return
+			}
 			c.linker.(snapshotSanitizer).setSanitizerVariation(s.sanitizer, sanitizerVariation)
 
 			// Export the static lib name to make
 			if c.static() && c.ExportedToMake() {
+				// use BaseModuleName which is the name for Make.
 				if s.sanitizer == cfi {
-					// use BaseModuleName which is the name for Make.
 					cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+				} else if s.sanitizer == Hwasan {
+					hwasanStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
 				}
 			}
 		}
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 792ffe3..570300b 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,6 +18,7 @@
 // snapshot mutators and snapshot information maps which are also defined in this file.
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -399,8 +400,10 @@
 }
 
 type snapshotSanitizer interface {
-	isSanitizerEnabled(t SanitizerType) bool
+	isSanitizerAvailable(t SanitizerType) bool
 	setSanitizerVariation(t SanitizerType, enabled bool)
+	isSanitizerEnabled(t SanitizerType) bool
+	isUnsanitizedVariant() bool
 }
 
 type snapshotLibraryDecorator struct {
@@ -408,10 +411,13 @@
 	*libraryDecorator
 	properties          SnapshotLibraryProperties
 	sanitizerProperties struct {
-		CfiEnabled bool `blueprint:"mutated"`
+		SanitizerVariation SanitizerType `blueprint:"mutated"`
 
 		// Library flags for cfi variant.
 		Cfi SnapshotLibraryProperties `android:"arch_variant"`
+
+		// Library flags for hwasan variant.
+		Hwasan SnapshotLibraryProperties `android:"arch_variant"`
 	}
 }
 
@@ -450,8 +456,10 @@
 		return p.libraryDecorator.link(ctx, flags, deps, objs)
 	}
 
-	if p.sanitizerProperties.CfiEnabled {
+	if p.isSanitizerEnabled(cfi) {
 		p.properties = p.sanitizerProperties.Cfi
+	} else if p.isSanitizerEnabled(Hwasan) {
+		p.properties = p.sanitizerProperties.Hwasan
 	}
 
 	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
@@ -514,25 +522,34 @@
 	return false
 }
 
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+func (p *snapshotLibraryDecorator) isSanitizerAvailable(t SanitizerType) bool {
 	switch t {
 	case cfi:
 		return p.sanitizerProperties.Cfi.Src != nil
+	case Hwasan:
+		return p.sanitizerProperties.Hwasan.Src != nil
 	default:
 		return false
 	}
 }
 
 func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
-	if !enabled {
+	if !enabled || p.isSanitizerEnabled(t) {
 		return
 	}
-	switch t {
-	case cfi:
-		p.sanitizerProperties.CfiEnabled = true
-	default:
-		return
+	if !p.isUnsanitizedVariant() {
+		panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both"))
 	}
+	p.sanitizerProperties.SanitizerVariation = t
+}
+
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+	return p.sanitizerProperties.SanitizerVariation == t
+}
+
+func (p *snapshotLibraryDecorator) isUnsanitizedVariant() bool {
+	return !p.isSanitizerEnabled(Asan) &&
+		!p.isSanitizerEnabled(Hwasan)
 }
 
 func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 6a98778..79405e9 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -1053,6 +1053,7 @@
 			},
 		},
 	}
+
 	vendor_snapshot_static {
 		name: "libsnapshot",
 		vendor: true,
@@ -1063,7 +1064,10 @@
 				src: "libsnapshot.a",
 				cfi: {
 					src: "libsnapshot.cfi.a",
-				}
+				},
+				hwasan: {
+					src: "libsnapshot.hwasan.a",
+				},
 			},
 		},
 	}
@@ -1098,6 +1102,7 @@
 		"vendor/libc++demangle.a":        nil,
 		"vendor/libsnapshot.a":           nil,
 		"vendor/libsnapshot.cfi.a":       nil,
+		"vendor/libsnapshot.hwasan.a":    nil,
 		"vendor/note_memtag_heap_sync.a": nil,
 	}
 
@@ -1106,15 +1111,25 @@
 	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
 	ctx := testCcWithConfig(t, config)
 
-	// Check non-cfi and cfi variant.
+	// Check non-cfi, cfi and hwasan variant.
 	staticVariant := "android_vendor.28_arm64_armv8-a_static"
 	staticCfiVariant := "android_vendor.28_arm64_armv8-a_static_cfi"
+	staticHwasanVariant := "android_vendor.28_arm64_armv8-a_static_hwasan"
+	staticHwasanCfiVariant := "android_vendor.28_arm64_armv8-a_static_hwasan_cfi"
 
 	staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticVariant).Module().(*Module)
 	assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
 
 	staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticCfiVariant).Module().(*Module)
 	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+
+	staticHwasanModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanVariant).Module().(*Module)
+	assertString(t, staticHwasanModule.outputFile.Path().Base(), "libsnapshot.hwasan.a")
+
+	staticHwasanCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanCfiVariant).Module().(*Module)
+	if !staticHwasanCfiModule.HiddenFromMake() || !staticHwasanCfiModule.PreventInstall() {
+		t.Errorf("Hwasan and Cfi cannot enabled at the same time.")
+	}
 }
 
 func TestVendorSnapshotExclude(t *testing.T) {