Handle prebuilt vs source selection in bp2build

Test: enable mainline modules build from prebuilts and build
Bug: 300640274
Change-Id: Ib1d6bbca7e0ab459515d3cf6378741e8368e7327
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 5b77ba9..9dc5878 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -340,6 +340,7 @@
 		"prebuilts/clang/host/linux-x86":                   Bp2BuildDefaultTrueRecursively,
 		"prebuilts/gradle-plugin":                          Bp2BuildDefaultTrueRecursively,
 		"prebuilts/runtime/mainline/platform/sdk":          Bp2BuildDefaultTrueRecursively,
+		"prebuilts/module_sdk":                             Bp2BuildDefaultTrueRecursively,
 		"prebuilts/sdk":                                    Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/androidx":                   Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/androidx-legacy":            Bp2BuildDefaultTrue,
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 86829ce..c0eabdd 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -487,6 +487,9 @@
 	if moduleDir == Bp2BuildTopLevel {
 		moduleDir = ""
 	}
+	if a, ok := module.(Module); ok && IsModulePrebuilt(a) {
+		moduleName = RemoveOptionalPrebuiltPrefix(moduleName)
+	}
 	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
 }
 
diff --git a/android/config.go b/android/config.go
index 445c6cd..a70fad0 100644
--- a/android/config.go
+++ b/android/config.go
@@ -2073,6 +2073,11 @@
 	return c.apiLibraries
 }
 
+// Bp2buildMode indicates whether the config is for bp2build mode of Soong
+func (c *config) Bp2buildMode() bool {
+	return c.BuildMode == Bp2build
+}
+
 func (c *deviceConfig) CheckVendorSeappViolations() bool {
 	return Bool(c.config.productVariables.CheckVendorSeappViolations)
 }
diff --git a/android/module.go b/android/module.go
index f4b51ea..f48af4a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -3100,11 +3100,21 @@
 	if !b.isBazelConversionMode() {
 		panic("cannot call ModuleFromName if not in bazel conversion mode")
 	}
+	var m blueprint.Module
+	var ok bool
 	if moduleName, _ := SrcIsModuleWithTag(name); moduleName != "" {
-		return b.bp.ModuleFromName(moduleName)
+		m, ok = b.bp.ModuleFromName(moduleName)
 	} else {
-		return b.bp.ModuleFromName(name)
+		m, ok = b.bp.ModuleFromName(name)
 	}
+	if !ok {
+		return m, ok
+	}
+	// If this module is not preferred, tried to get the prebuilt version instead
+	if a, aOk := m.(Module); aOk && !IsModulePrebuilt(a) && !IsModulePreferred(a) {
+		return b.ModuleFromName("prebuilt_" + name)
+	}
+	return m, ok
 }
 
 func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
diff --git a/android/mutator.go b/android/mutator.go
index 41477b8..e185cce 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -55,6 +55,7 @@
 		// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
 		// evaluate the impact on conversion.
 		RegisterPrebuiltsPreArchMutators,
+		RegisterPrebuiltsPostDepsMutators,
 	},
 		bp2buildMutators...)
 
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 95b772d..e7b7979 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -483,20 +483,55 @@
 // usePrebuilt returns true if a prebuilt should be used instead of the source module.  The prebuilt
 // will be used if it is marked "prefer" or if the source module is disabled.
 func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module, prebuilt Module) bool {
-	if p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 {
-		return false
-	}
+	if !ctx.Config().Bp2buildMode() {
+		if p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 {
+			return false
+		}
 
-	// Skip prebuilt modules under unexported namespaces so that we won't
-	// end up shadowing non-prebuilt module when prebuilt module under same
-	// name happens to have a `Prefer` property set to true.
-	if ctx.Config().KatiEnabled() && !prebuilt.ExportedToMake() {
-		return false
+		// Skip prebuilt modules under unexported namespaces so that we won't
+		// end up shadowing non-prebuilt module when prebuilt module under same
+		// name happens to have a `Prefer` property set to true.
+		if ctx.Config().KatiEnabled() && !prebuilt.ExportedToMake() {
+			return false
+		}
 	}
 
 	// If source is not available or is disabled then always use the prebuilt.
 	if source == nil || !source.Enabled() {
-		return true
+		// If in bp2build mode, we need to check product variables & Soong config variables, which may
+		// have overridden the "enabled" property but have not been merged into the property value as
+		// they would in a non-bp2build mode invocation
+		if ctx.Config().Bp2buildMode() && source != nil {
+			productVariableProps, errs := ProductVariableProperties(ctx, source)
+			if productConfigProps, exists := productVariableProps["Enabled"]; len(errs) == 0 && exists && len(productConfigProps) == 1 {
+				var prop ProductConfigOrSoongConfigProperty
+				var value bool
+				for p, v := range productConfigProps {
+					prop = p
+					actual, ok := v.(*bool)
+					if ok {
+						value = proptools.Bool(actual)
+					}
+				}
+				if scv, ok := prop.(SoongConfigProperty); ok {
+					// If the product config var is enabled but the value of enabled is false still, the
+					// prebuilt is preferred. Otherwise, check if the prebulit is explicitly preferred
+					if ctx.Config().VendorConfig(scv.namespace).Bool(strings.ToLower(scv.name)) && !value {
+						return true
+					}
+				} else {
+					// TODO: b/300998219 - handle product vars
+					// We don't handle product variables yet, so return based on the non-product specific
+					// value of enabled
+					return true
+				}
+			} else {
+				// No "enabled" property override, return true since this module isn't enabled
+				return true
+			}
+		} else {
+			return true
+		}
 	}
 
 	// If the use_source_config_var property is set then it overrides the prefer property setting.
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index bf3351a..fde4c97 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -58,6 +58,7 @@
 func registerCcLibraryHeadersModuleTypes(ctx android.RegistrationContext) {
 	cc.RegisterCCBuildComponents(ctx)
 	cc.RegisterLibraryHeadersBuildComponents(ctx)
+	ctx.RegisterModuleType("cc_library_shared", cc.LibrarySharedFactory)
 }
 
 func runCcLibraryHeadersTestCase(t *testing.T, tc Bp2buildTestCase) {
@@ -417,3 +418,67 @@
 		},
 	})
 }
+
+func TestPrebuiltCcLibraryHeadersPreferredRdepUpdated(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+		Description:             "cc_library_headers prebuilt preferred is used as rdep",
+		StubbedBuildDefinitions: []string{"foo_export"},
+		Filesystem: map[string]string{
+			"foo/bar/Android.bp": simpleModule("cc_library_headers", "foo_headers"),
+		},
+		Blueprint: soongCcLibraryHeadersPreamble + `
+cc_prebuilt_library_headers {
+		name: "foo_headers",
+		whole_static_libs: ["foo_export"],
+		bazel_module: { bp2build_available: true },
+		prefer: true,
+}
+
+cc_library_shared {
+	name: "foo",
+	header_libs: ["foo_headers"],
+	include_build_directory: false,
+}
+` + simpleModule("cc_library_headers", "foo_export"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"implementation_deps": `[":foo_headers"]`,
+			}),
+		},
+	})
+}
+
+func TestPrebuiltCcLibraryHeadersRdepUpdated(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+		Description:             "cc_library_headers not preferred is not used for rdep",
+		StubbedBuildDefinitions: []string{"foo_export"},
+		Filesystem: map[string]string{
+			"foo/bar/Android.bp": simpleModule("cc_library_headers", "foo_headers"),
+		},
+		Blueprint: soongCcLibraryHeadersPreamble + `
+cc_prebuilt_library_headers {
+		name: "foo_headers",
+		whole_static_libs: ["foo_export"],
+		bazel_module: { bp2build_available: true },
+		prefer: false,
+}
+
+cc_library_shared {
+	name: "foo",
+	header_libs: ["foo_headers"],
+	include_build_directory: false,
+}
+` + simpleModule("cc_library_headers", "foo_export"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"implementation_deps": `["//foo/bar:foo_headers"]`,
+			}),
+		},
+	})
+}