Stub/Impl selection for sdk variants

If a dependency has ndk stubs, then the sdk variant of the library
should link against the ndk stub variant and not the impl/apex_stubs variant

Unlike module-libapi, the depdendency does not go through an
@api_surfaces external repo indirection. This indirection was created to
support Multi-tree, and will likely  be removed in the future

Test: Added a unit test
Bug: 298085502
Change-Id: Ie081e153fa586b6c22db0b8e42f91149fd8e5d9b
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 1fe8442..a28432c 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -71,6 +71,7 @@
 
 	AndroidAndInApex = "android-in_apex"
 	AndroidPlatform  = "system"
+	Unbundled_app    = "unbundled_app"
 
 	InApex  = "in_apex"
 	NonApex = "non_apex"
@@ -207,6 +208,7 @@
 	osAndInApexMap = map[string]string{
 		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
 		AndroidPlatform:            "//build/bazel/rules/apex:system",
+		Unbundled_app:              "//build/bazel/rules/apex:unbundled_app",
 		OsDarwin:                   "//build/bazel/platforms/os:darwin",
 		OsLinux:                    "//build/bazel/platforms/os:linux_glibc",
 		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 921e6e3..b3dd6b1 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -32,6 +32,7 @@
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
 	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+	ctx.RegisterModuleType("ndk_library", cc.NdkLibraryFactory)
 }
 
 func runCcLibrarySharedTestCase(t *testing.T, tc Bp2buildTestCase) {
@@ -1593,3 +1594,62 @@
     ]`,
 			})}})
 }
+
+func TestCcLibrarySdkVariantUsesStubs(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library_shared stubs",
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+	name: "libUsesSdk",
+	sdk_version: "current",
+	shared_libs: [
+		"libNoStubs",
+		"libHasApexStubs",
+		"libHasApexAndNdkStubs",
+	]
+}
+cc_library_shared {
+	name: "libNoStubs",
+	bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+	name: "libHasApexStubs",
+	stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+	apex_available: ["apex_a"],
+}
+cc_library_shared {
+	name: "libHasApexAndNdkStubs",
+	stubs: { symbol_file: "b.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+	apex_available: ["apex_b"],
+}
+ndk_library {
+	name: "libHasApexAndNdkStubs",
+	bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "libUsesSdk", AttrNameToString{
+				"implementation_dynamic_deps": `[":libNoStubs"] + select({
+        "//build/bazel/rules/apex:system": [
+            "@api_surfaces//module-libapi/current:libHasApexStubs",
+            "@api_surfaces//module-libapi/current:libHasApexAndNdkStubs",
+        ],
+        "//build/bazel/rules/apex:unbundled_app": [
+            ":libHasApexStubs",
+            "//.:libHasApexAndNdkStubs.ndk_stub_libs",
+        ],
+        "//conditions:default": [
+            ":libHasApexStubs",
+            ":libHasApexAndNdkStubs",
+        ],
+    })`,
+				"local_includes": `["."]`,
+				"sdk_version":    `"current"`,
+			}),
+		},
+	})
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index ce8c96d..aacc088 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -1578,6 +1578,12 @@
 	}
 }
 
+// hasNdkStubs returns true for libfoo if there exists a libfoo.ndk of type ndk_library
+func hasNdkStubs(ctx android.BazelConversionPathContext, c *Module) bool {
+	mod, exists := ctx.ModuleFromName(c.Name() + ndkLibrarySuffix)
+	return exists && ctx.OtherModuleType(mod) == "ndk_library"
+}
+
 func SetStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
 	config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int, buildNonApexWithStubs bool) {
 
@@ -1638,6 +1644,27 @@
 			useStubOrImplInApexWithName(ssi)
 		}
 	}
+
+	// If the library has an sdk variant, create additional selects to build this variant against the ndk
+	// The config setting for this variant will be //build/bazel/rules/apex:unbundled_app
+	if c, ok := ctx.Module().(*Module); ok && c.Properties.Sdk_version != nil {
+		for _, l := range dynamicLibs.Includes {
+			dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
+			label := l // use the implementation by default
+			if depC, ok := dep.(*Module); ok && hasNdkStubs(ctx, depC) {
+				// If the dependency has ndk stubs, build against the ndk stubs
+				// https://cs.android.com/android/_/android/platform/build/soong/+/main:cc/cc.go;l=2642-2643;drc=e12d252e22dd8afa654325790d3298a0d67bd9d6;bpv=1;bpt=0
+				ndkLibModule, _ := ctx.ModuleFromName(dep.Name() + ndkLibrarySuffix)
+				label = bazel.Label{
+					Label: "//" + ctx.OtherModuleDir(ndkLibModule) + ":" + ndkLibModule.Name() + "_stub_libs",
+				}
+			}
+			// add the ndk lib label to this axis
+			existingValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, "unbundled_app")
+			existingValue.Append(bazel.MakeLabelList([]bazel.Label{label}))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, "unbundled_app", bazel.FirstUniqueBazelLabelList(existingValue))
+		}
+	}
 }
 
 func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {