Refactor MixedBuildsEnabled and add --ensure-allowlist-integrity.

Currently, there is little verification around allowlisted modules
actually being mixed-built. This flag would allow us to verify
that a module allowlisted is mixed-built for at least one variant.

Bug: 278910100
Test: m nothing --bazel-mode-staging --ensure-allowlist-integrity
Test: m nothing --bazel-mode-staging --ensure-allowlist-integrity
--bazel-force-enabled-modules=com.google.android.neuralnetworks (This
fails, as expected)
Test: build/soong/test/mixed_mode_test.sh

Change-Id: Icd5976f4f44f1a8caca1e5247d986642f7995f97
diff --git a/android/bazel.go b/android/bazel.go
index 1646883..58d9d87 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -32,6 +32,22 @@
 	Bp2BuildTopLevel = "."
 )
 
+type MixedBuildEnabledStatus int
+
+const (
+	// This module can be mixed_built.
+	MixedBuildEnabled = iota
+
+	// There is a technical incompatibility preventing this module from being
+	// bazel-analyzed. Note: the module might also be incompatible.
+	TechnicalIncompatibility
+
+	// This module cannot be mixed_built due to some incompatibility with it
+	// that is not a platform incompatibility. Example: the module-type is not
+	// enabled, or is not bp2build-converted.
+	ModuleIncompatibility
+)
+
 // FileGroupAsLibrary describes a filegroup module that is converted to some library
 // such as aidl_library or proto_library.
 type FileGroupAsLibrary interface {
@@ -346,24 +362,31 @@
 	}).(Bp2BuildConversionAllowlist)
 }
 
-// MixedBuildsEnabled returns true if a module is ready to be replaced by a
-// converted or handcrafted Bazel target. As a side effect, calling this
-// method will also log whether this module is mixed build enabled for
-// metrics reporting.
-func MixedBuildsEnabled(ctx BaseModuleContext) bool {
+// MixedBuildsEnabled returns a MixedBuildEnabledStatus regarding whether
+// a module is ready to be replaced by a converted or handcrafted Bazel target.
+// As a side effect, calling this method will also log whether this module is
+// mixed build enabled for metrics reporting.
+func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus {
 	module := ctx.Module()
 	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
 	withinApex := !apexInfo.IsForPlatform()
+
+	platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType)
+	if platformIncompatible {
+		ctx.Config().LogMixedBuild(ctx, false)
+		return TechnicalIncompatibility
+	}
+
 	mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
-		ctx.Os() != Windows && // Windows toolchains are not currently supported.
-		ctx.Os() != LinuxBionic && // Linux Bionic toolchains are not currently supported.
-		ctx.Os() != LinuxMusl && // Linux musl toolchains are not currently supported (b/259266326).
-		ctx.Arch().ArchType != Riscv64 && // TODO(b/262192655) Riscv64 toolchains are not currently supported.
 		module.Enabled() &&
 		convertedToBazel(ctx, module) &&
 		ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
 	ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
-	return mixedBuildEnabled
+
+	if mixedBuildEnabled {
+		return MixedBuildEnabled
+	}
+	return ModuleIncompatibility
 }
 
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
@@ -388,6 +411,13 @@
 	OtherModuleDir(m blueprint.Module) string
 }
 
+func isPlatformIncompatible(osType OsType, arch ArchType) bool {
+	return osType == Windows || // Windows toolchains are not currently supported.
+		osType == LinuxBionic || // Linux Bionic toolchains are not currently supported.
+		osType == LinuxMusl || // Linux musl toolchains are not currently supported (b/259266326).
+		arch == Riscv64 // TODO(b/262192655) Riscv64 toolchains are not currently supported.
+}
+
 func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool {
 	if !b.bazelProps().Bazel_module.CanConvertToBazel {
 		return false
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 9c273d9..debc7a2 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -86,12 +86,10 @@
 func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
 	if m := ctx.Module(); m.Enabled() {
 		if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
-			queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx)
+			mixedBuildEnabled := MixedBuildsEnabled(ctx)
+			queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && mixedBuildEnabled == MixedBuildEnabled
 			if queueMixedBuild {
 				mixedBuildMod.QueueBazelCall(ctx)
-			} else if _, ok := ctx.Config().bazelForceEnabledModules[m.Name()]; ok {
-				// TODO(b/273910287) - remove this once --ensure_allowlist_integrity is added
-				ctx.ModuleErrorf("Attempted to force enable an unready module: %s. Did you forget to Bp2BuildDefaultTrue its directory?\n", m.Name())
 			}
 		}
 	}
diff --git a/android/config.go b/android/config.go
index 032172d..7141e54 100644
--- a/android/config.go
+++ b/android/config.go
@@ -102,6 +102,8 @@
 	UseBazelProxy bool
 
 	BuildFromTextStub bool
+
+	EnsureAllowlistIntegrity bool
 }
 
 // Build modes that soong_build can run as.
@@ -278,6 +280,11 @@
 	// If buildFromTextStub is true then the Java API stubs are
 	// built from the signature text files, not the source Java files.
 	buildFromTextStub bool
+
+	// If ensureAllowlistIntegrity is true, then the presence of any allowlisted
+	// modules that aren't mixed-built for at least one variant will cause a build
+	// failure
+	ensureAllowlistIntegrity bool
 }
 
 type deviceConfig struct {
@@ -1904,6 +1911,10 @@
 	return Bool(c.productVariables.HostMusl)
 }
 
+func (c *config) GetMixedBuildsEnabledModules() map[string]struct{} {
+	return c.mixedBuildEnabledModules
+}
+
 func (c *config) LogMixedBuild(ctx BaseModuleContext, useBazel bool) {
 	moduleName := ctx.Module().Name()
 	c.mixedBuildsLock.Lock()
diff --git a/android/module.go b/android/module.go
index ba47453..c8670c3 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2438,7 +2438,7 @@
 
 func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
 	if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok {
-		if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
+		if mixedBuildMod.IsMixedBuildSupported(ctx) && (MixedBuildsEnabled(ctx) == MixedBuildEnabled) {
 			return mixedBuildMod, true
 		}
 	}