Merge changes from topic "llndk_cc_library"

* changes:
  Don't rewrite LLNDK dependencies with .llndk suffix
  Don't strip stub libraries
diff --git a/android/makevars.go b/android/makevars.go
index 5101436..546abcf 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -90,6 +90,7 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	ModuleType(module blueprint.Module) string
+	ModuleProvider(module blueprint.Module, key blueprint.ProviderKey) interface{}
 	BlueprintFile(module blueprint.Module) string
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 4a6aecf..cb43e24 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1355,9 +1355,9 @@
 			ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
 
 			mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
-			ensureContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
+			ensureContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
 			for _, ver := range tc.shouldNotLink {
-				ensureNotContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
+				ensureNotContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
 			}
 
 			mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ddacb70..c4b9cd5 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -269,7 +269,7 @@
 	if library.shared() && !library.buildStubs() {
 		ctx.subAndroidMk(entries, library.baseInstaller)
 	} else {
-		if library.buildStubs() {
+		if library.buildStubs() && library.stubsVersion() != "" {
 			entries.SubName = "." + library.stubsVersion()
 		}
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
@@ -471,18 +471,9 @@
 }
 
 func (c *llndkStubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "SHARED_LIBRARIES"
-	entries.OverrideName = c.implementationModuleName(ctx.BaseModuleName())
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		c.libraryDecorator.androidMkWriteExportedFlags(entries)
-		_, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
-
-		entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-		entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
-		entries.SetString("LOCAL_SOONG_TOC", c.toc().String())
-	})
+	// Don't write anything for an llndk_library module, the vendor variant of the cc_library
+	// module will write the Android.mk entries.
+	entries.Disabled = true
 }
 
 func (c *vndkPrebuiltLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
diff --git a/cc/cc.go b/cc/cc.go
index 1cc2bd5..a023f3f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -390,6 +390,13 @@
 	// explicitly marked as `double_loadable: true` by the owner, or the dependency
 	// from the LLNDK lib should be cut if the lib is not designed to be double loaded.
 	Double_loadable *bool
+
+	// IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
+	IsLLNDK bool `blueprint:"mutated"`
+
+	// IsLLNDKPrivate is set to true for the vendor variant of a cc_library module that has LLNDK
+	// stubs and also sets llndk.vendor_available: false.
+	IsLLNDKPrivate bool `blueprint:"mutated"`
 }
 
 // ModuleContextIntf is an interface (on a module context helper) consisting of functions related
@@ -408,9 +415,10 @@
 	sdkVersion() string
 	useVndk() bool
 	isNdk(config android.Config) bool
-	isLlndk(config android.Config) bool
-	isLlndkPublic(config android.Config) bool
-	isVndkPrivate(config android.Config) bool
+	IsLlndk() bool
+	IsLlndkPublic() bool
+	isImplementationForLLNDKPublic() bool
+	IsVndkPrivate() bool
 	isVndk() bool
 	isVndkSp() bool
 	IsVndkExt() bool
@@ -645,6 +653,7 @@
 	runtimeDepTag         = installDependencyTag{name: "runtime lib"}
 	testPerSrcDepTag      = dependencyTag{name: "test_per_src"}
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
+	llndkStubDepTag       = dependencyTag{name: "llndk stub"}
 )
 
 type copyDirectlyInAnyApexDependencyTag dependencyTag
@@ -1028,20 +1037,34 @@
 	return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
 }
 
-func (c *Module) isLlndk(config android.Config) bool {
-	// Returns true for both LLNDK (public) and LLNDK-private libs.
-	return isLlndkLibrary(c.BaseModuleName(), config)
+// isLLndk returns true for both LLNDK (public) and LLNDK-private libs.
+func (c *Module) IsLlndk() bool {
+	return c.VendorProperties.IsLLNDK
 }
 
-func (c *Module) isLlndkPublic(config android.Config) bool {
-	// Returns true only for LLNDK (public) libs.
-	name := c.BaseModuleName()
-	return isLlndkLibrary(name, config) && !isVndkPrivateLibrary(name, config)
+// IsLlndkPublic returns true only for LLNDK (public) libs.
+func (c *Module) IsLlndkPublic() bool {
+	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsLLNDKPrivate
 }
 
-func (c *Module) IsVndkPrivate(config android.Config) bool {
+// 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 {
+	library, _ := c.library.(*libraryDecorator)
+	return library != nil && library.hasLLNDKStubs() &&
+		(Bool(library.Properties.Llndk.Vendor_available) ||
+			// TODO(b/170784825): until the LLNDK properties are moved into the cc_library,
+			// the non-Vendor variants of the cc_library don't know if the corresponding
+			// llndk_library set vendor_available: false.  Since libft2 is the only
+			// private LLNDK library, hardcode it during the transition.
+			c.BaseModuleName() != "libft2")
+}
+
+func (c *Module) IsVndkPrivate() bool {
 	// Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
-	return isVndkPrivateLibrary(c.BaseModuleName(), config)
+	library, _ := c.library.(*libraryDecorator)
+	return library != nil && !Bool(library.Properties.Llndk.Vendor_available) &&
+		!Bool(c.VendorProperties.Vendor_available) && !c.IsVndkExt()
 }
 
 func (c *Module) IsVndk() bool {
@@ -1247,16 +1270,20 @@
 	return ctx.mod.IsNdk(config)
 }
 
-func (ctx *moduleContextImpl) isLlndk(config android.Config) bool {
-	return ctx.mod.isLlndk(config)
+func (ctx *moduleContextImpl) IsLlndk() bool {
+	return ctx.mod.IsLlndk()
 }
 
-func (ctx *moduleContextImpl) isLlndkPublic(config android.Config) bool {
-	return ctx.mod.isLlndkPublic(config)
+func (ctx *moduleContextImpl) IsLlndkPublic() bool {
+	return ctx.mod.IsLlndkPublic()
 }
 
-func (ctx *moduleContextImpl) isVndkPrivate(config android.Config) bool {
-	return ctx.mod.IsVndkPrivate(config)
+func (ctx *moduleContextImpl) isImplementationForLLNDKPublic() bool {
+	return ctx.mod.isImplementationForLLNDKPublic()
+}
+
+func (ctx *moduleContextImpl) IsVndkPrivate() bool {
+	return ctx.mod.IsVndkPrivate()
 }
 
 func (ctx *moduleContextImpl) isVndk() bool {
@@ -1407,7 +1434,7 @@
 	if vndkVersion == "current" {
 		vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
 	}
-	if c.Properties.VndkVersion != vndkVersion {
+	if c.Properties.VndkVersion != vndkVersion && c.Properties.VndkVersion != "" {
 		// add version suffix only if the module is using different vndk version than the
 		// version in product or vendor partition.
 		nameSuffix += "." + c.Properties.VndkVersion
@@ -1439,7 +1466,7 @@
 		c.Properties.SubName += nativeBridgeSuffix
 	}
 
-	_, llndk := c.linker.(*llndkStubDecorator)
+	llndk := c.IsLlndk()
 	_, llndkHeader := c.linker.(*llndkHeadersDecorator)
 	if llndk || llndkHeader || (c.UseVndk() && c.HasNonSystemVariants()) {
 		// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
@@ -1817,10 +1844,6 @@
 		vendorSnapshotSharedLibs := vendorSnapshotSharedLibs(actx.Config())
 
 		rewriteVendorLibs := func(lib string) string {
-			if isLlndkLibrary(lib, ctx.Config()) {
-				return lib + llndkLibrarySuffix
-			}
-
 			// only modules with BOARD_VNDK_VERSION uses snapshot.
 			if c.VndkVersion() != actx.DeviceConfig().VndkVersion() {
 				return lib
@@ -2237,7 +2260,7 @@
 			return true
 		}
 
-		if to.isVndkSp() || to.isLlndk(ctx.Config()) || Bool(to.VendorProperties.Double_loadable) {
+		if to.isVndkSp() || to.IsLlndk() || Bool(to.VendorProperties.Double_loadable) {
 			return false
 		}
 
@@ -2252,7 +2275,7 @@
 	}
 	if module, ok := ctx.Module().(*Module); ok {
 		if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
-			if module.isLlndk(ctx.Config()) || Bool(module.VendorProperties.Double_loadable) {
+			if lib.hasLLNDKStubs() || Bool(module.VendorProperties.Double_loadable) {
 				ctx.WalkDeps(check)
 			}
 		}
@@ -2372,9 +2395,6 @@
 		if depTag == android.ProtoPluginDepTag {
 			return
 		}
-		if depTag == llndkImplDep {
-			return
-		}
 
 		if dep.Target().Os != ctx.Os() {
 			ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
@@ -2744,7 +2764,8 @@
 	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
 
 	libName := baseLibName(depName)
-	isLLndk := isLlndkLibrary(libName, ctx.Config())
+	ccDepModule, _ := ccDep.(*Module)
+	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
 	isVendorPublicLib := inList(libName, *vendorPublicLibraries)
 	bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
 
@@ -2896,17 +2917,14 @@
 
 func GetMakeLinkType(actx android.ModuleContext, c LinkableInterface) string {
 	if c.UseVndk() {
-		if ccModule, ok := c.Module().(*Module); ok {
-			// Only CC modules provide stubs at the moment.
-			if lib, ok := ccModule.linker.(*llndkStubDecorator); ok {
-				if Bool(lib.Properties.Vendor_available) {
-					return "native:vndk"
-				}
+		if c.IsLlndk() {
+			if !c.IsLlndkPublic() {
 				return "native:vndk_private"
 			}
+			return "native:vndk"
 		}
 		if c.IsVndk() && !c.IsVndkExt() {
-			if c.IsVndkPrivate(actx.Config()) {
+			if c.IsVndkPrivate() {
 				return "native:vndk_private"
 			}
 			return "native:vndk"
@@ -3039,7 +3057,7 @@
 			return false
 		}
 	}
-	if depTag == stubImplDepTag || depTag == llndkImplDep {
+	if depTag == stubImplDepTag || depTag == llndkStubDepTag {
 		// We don't track beyond LLNDK or from an implementation library to its stubs.
 		return false
 	}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index c16cce8..fe9db37 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1049,6 +1049,16 @@
 		name: "obj",
 		vendor_available: true,
 	}
+
+	cc_library {
+		name: "libllndk",
+		llndk_stubs: "libllndk.llndk",
+	}
+
+	llndk_library {
+		name: "libllndk.llndk",
+		symbol_file: "",
+	}
 `
 	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -1080,6 +1090,9 @@
 			filepath.Join(sharedDir, "libvendor.so.json"),
 			filepath.Join(sharedDir, "libvendor_available.so.json"))
 
+		// LLNDK modules are not captured
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
+
 		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
 		// Also cfi variants are captured, except for prebuilts like toolchain_library
 		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
@@ -2899,7 +2912,7 @@
 		{vendorVariant, "libvndkprivate", "native:vndk_private"},
 		{vendorVariant, "libvendor", "native:vendor"},
 		{vendorVariant, "libvndkext", "native:vendor"},
-		{vendorVariant, "libllndk.llndk", "native:vndk"},
+		{vendorVariant, "libllndk", "native:vndk"},
 		{vendorVariant27, "prevndk.vndk.27.arm.binder32", "native:vndk"},
 		{coreVariant, "libvndk", "native:platform"},
 		{coreVariant, "libvndkprivate", "native:platform"},
@@ -3178,8 +3191,39 @@
 	llndk_library {
 		name: "libllndk.llndk",
 	}
+
+	cc_prebuilt_library_shared {
+		name: "libllndkprebuilt",
+		stubs: { versions: ["1", "2"] },
+		llndk_stubs: "libllndkprebuilt.llndk",
+	}
+	llndk_library {
+		name: "libllndkprebuilt.llndk",
+	}
+
+	cc_library {
+		name: "libllndk_with_external_headers",
+		stubs: { versions: ["1", "2"] },
+		llndk_stubs: "libllndk_with_external_headers.llndk",
+		header_libs: ["libexternal_headers"],
+		export_header_lib_headers: ["libexternal_headers"],
+	}
+	llndk_library {
+		name: "libllndk_with_external_headers.llndk",
+	}
+	cc_library_headers {
+		name: "libexternal_headers",
+		export_include_dirs: ["include"],
+		vendor_available: true,
+	}
 	`)
-	actual := ctx.ModuleVariantsForTests("libllndk.llndk")
+	actual := ctx.ModuleVariantsForTests("libllndk")
+	for i := 0; i < len(actual); i++ {
+		if !strings.HasPrefix(actual[i], "android_vendor.VER_") {
+			actual = append(actual[:i], actual[i+1:]...)
+			i--
+		}
+	}
 	expected := []string{
 		"android_vendor.VER_arm64_armv8-a_shared_1",
 		"android_vendor.VER_arm64_armv8-a_shared_2",
@@ -3190,10 +3234,10 @@
 	}
 	checkEquals(t, "variants for llndk stubs", expected, actual)
 
-	params := ctx.ModuleForTests("libllndk.llndk", "android_vendor.VER_arm_armv7-a-neon_shared").Description("generate stub")
+	params := ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared").Description("generate stub")
 	checkEquals(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"])
 
-	params = ctx.ModuleForTests("libllndk.llndk", "android_vendor.VER_arm_armv7-a-neon_shared_1").Description("generate stub")
+	params = ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared_1").Description("generate stub")
 	checkEquals(t, "override apiLevel for versioned stubs", "1", params.Args["apiLevel"])
 }
 
diff --git a/cc/image.go b/cc/image.go
index cca454a..380c1db 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -308,6 +308,18 @@
 		} else {
 			vendorVariants = append(vendorVariants, platformVndkVersion)
 		}
+	} else if lib := moduleLibraryInterface(m); lib != nil && lib.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.
+		coreVariantNeeded = true
+		vendorVariants = append(vendorVariants,
+			platformVndkVersion,
+			boardVndkVersion,
+		)
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
diff --git a/cc/library.go b/cc/library.go
index 11ee7dd..1d0c018 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -22,6 +22,7 @@
 	"strings"
 	"sync"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
@@ -114,6 +115,10 @@
 
 	// If this is an LLNDK library, the name of the equivalent llndk_library module.
 	Llndk_stubs *string
+
+	// If this is an LLNDK library, properties to describe the LLNDK stubs.  Will be copied from
+	// the module pointed to by llndk_stubs if it is set.
+	Llndk llndkLibraryProperties
 }
 
 // StaticProperties is a properties stanza to affect only attributes of the "static" variants of a
@@ -570,6 +575,12 @@
 	}
 
 	flags = library.baseCompiler.compilerFlags(ctx, flags, deps)
+	if ctx.IsLlndk() {
+		// LLNDK libraries ignore most of the properties on the cc_library and use the
+		// LLNDK-specific properties instead.
+		// Wipe all the module-local properties, leaving only the global properties.
+		flags.Local = LocalOrGlobalFlags{}
+	}
 	if library.buildStubs() {
 		// Remove -include <file> when compiling stubs. Otherwise, the force included
 		// headers might cause conflicting types error with the symbols in the
@@ -603,6 +614,22 @@
 }
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+	if ctx.IsLlndk() {
+		// This is the vendor variant of an LLNDK library, build the LLNDK stubs.
+		vndkVer := ctx.Module().(*Module).VndkVersion()
+		if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" {
+			// For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
+			vndkVer = "current"
+		}
+		if library.stubsVersion() != "" {
+			vndkVer = library.stubsVersion()
+		}
+		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Llndk.Symbol_file), vndkVer, "--llndk")
+		if !Bool(library.Properties.Llndk.Unversioned) {
+			library.versionScriptPath = android.OptionalPathForPath(versionScript)
+		}
+		return objs
+	}
 	if library.buildStubs() {
 		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
 		library.versionScriptPath = android.OptionalPathForPath(versionScript)
@@ -693,6 +720,7 @@
 	allStubsVersions() []string
 
 	implementationModuleName(name string) string
+	hasLLNDKStubs() bool
 }
 
 var _ libraryInterface = (*libraryDecorator)(nil)
@@ -768,12 +796,27 @@
 }
 
 func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	if ctx.IsLlndk() {
+		// LLNDK libraries ignore most of the properties on the cc_library and use the
+		// LLNDK-specific properties instead.
+		return deps
+	}
+
 	deps = library.baseCompiler.compilerDeps(ctx, deps)
 
 	return deps
 }
 
 func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	if ctx.IsLlndk() {
+		// LLNDK libraries ignore most of the properties on the cc_library and use the
+		// LLNDK-specific properties instead.
+		deps.HeaderLibs = append(deps.HeaderLibs, library.Properties.Llndk.Export_llndk_headers...)
+		deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
+			library.Properties.Llndk.Export_llndk_headers...)
+		return deps
+	}
+
 	if library.static() {
 		// Compare with nil because an empty list needs to be propagated.
 		if library.StaticProperties.Static.System_shared_libs != nil {
@@ -977,7 +1020,12 @@
 	transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
 
 	stripFlags := flagsToStripFlags(flags)
-	if library.stripper.NeedsStrip(ctx) {
+	needsStrip := library.stripper.NeedsStrip(ctx)
+	if library.buildStubs() {
+		// No need to strip stubs libraries
+		needsStrip = false
+	}
+	if needsStrip {
 		if ctx.Darwin() {
 			stripFlags.StripUseGnuStrip = true
 		}
@@ -1017,7 +1065,7 @@
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	linkerDeps = append(linkerDeps, objs.tidyFiles...)
 
-	if Bool(library.Properties.Sort_bss_symbols_by_size) {
+	if Bool(library.Properties.Sort_bss_symbols_by_size) && !library.buildStubs() {
 		unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
 		transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 			deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
@@ -1071,7 +1119,7 @@
 		ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
 			SharedStubLibraries: stubsInfo,
 
-			IsLLNDK: ctx.isLlndk(ctx.Config()),
+			IsLLNDK: ctx.IsLlndk(),
 		})
 	}
 
@@ -1100,7 +1148,7 @@
 func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
 	// The logic must be consistent with classifySourceAbiDump.
 	isNdk := ctx.isNdk(ctx.Config())
-	isLlndkOrVndk := ctx.isLlndkPublic(ctx.Config()) || (ctx.useVndk() && ctx.isVndk())
+	isLlndkOrVndk := ctx.IsLlndkPublic() || (ctx.useVndk() && ctx.isVndk())
 
 	refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
 	refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, true)
@@ -1153,17 +1201,64 @@
 			library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
 				refAbiDumpFile, fileName, exportedHeaderFlags,
 				Bool(library.Properties.Header_abi_checker.Check_all_apis),
-				ctx.isLlndk(ctx.Config()), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
+				ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
 		}
 	}
 }
 
+func processLLNDKHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path {
+	srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
+	srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
+
+	var installPaths []android.WritablePath
+	for _, header := range srcFiles {
+		headerDir := filepath.Dir(header.String())
+		relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
+		if err != nil {
+			ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
+				srcDir.String(), headerDir, err)
+			continue
+		}
+
+		installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
+	}
+
+	return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
+}
+
 // link registers actions to link this library, and sets various fields
 // on this library to reflect information that should be exported up the build
 // tree (for example, exported flags and include paths).
 func (library *libraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
+	if ctx.IsLlndk() {
+		if len(library.Properties.Llndk.Export_preprocessed_headers) > 0 {
+			// This is the vendor variant of an LLNDK library with preprocessed headers.
+			genHeaderOutDir := android.PathForModuleGen(ctx, "include")
+
+			var timestampFiles android.Paths
+			for _, dir := range library.Properties.Llndk.Export_preprocessed_headers {
+				timestampFiles = append(timestampFiles, processLLNDKHeaders(ctx, dir, genHeaderOutDir))
+			}
+
+			if Bool(library.Properties.Llndk.Export_headers_as_system) {
+				library.reexportSystemDirs(genHeaderOutDir)
+			} else {
+				library.reexportDirs(genHeaderOutDir)
+			}
+
+			library.reexportDeps(timestampFiles...)
+		}
+
+		if Bool(library.Properties.Llndk.Export_headers_as_system) {
+			library.flagExporter.Properties.Export_system_include_dirs = append(
+				library.flagExporter.Properties.Export_system_include_dirs,
+				library.flagExporter.Properties.Export_include_dirs...)
+			library.flagExporter.Properties.Export_include_dirs = nil
+		}
+	}
+
 	// Linking this library consists of linking `deps.Objs` (.o files in dependencies
 	// of this library), together with `objs` (.o files created by compiling this
 	// library).
@@ -1246,7 +1341,7 @@
 }
 
 func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
-	if library.buildStubs() && !library.skipAPIDefine {
+	if library.buildStubs() && library.stubsVersion() != "" && !library.skipAPIDefine {
 		name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
 		ver := library.stubsVersion()
 		library.reexportFlags("-D" + name + "=" + ver)
@@ -1334,7 +1429,7 @@
 				}
 				library.baseInstaller.subDir = "bootstrap"
 			}
-		} else if ctx.directlyInAnyApex() && ctx.isLlndk(ctx.Config()) && !isBionic(ctx.baseModuleName()) {
+		} else if ctx.directlyInAnyApex() && ctx.IsLlndk() && !isBionic(ctx.baseModuleName()) {
 			// Skip installing LLNDK (non-bionic) libraries moved to APEX.
 			ctx.Module().HideFromMake()
 		}
@@ -1411,6 +1506,11 @@
 	library.MutatedProperties.BuildStatic = false
 }
 
+// hasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
+func (library *libraryDecorator) hasLLNDKStubs() bool {
+	return String(library.Properties.Llndk_stubs) != ""
+}
+
 func (library *libraryDecorator) implementationModuleName(name string) string {
 	return name
 }
@@ -1423,6 +1523,9 @@
 	if library.Properties.Header_abi_checker.Symbol_file != nil {
 		return library.Properties.Header_abi_checker.Symbol_file
 	}
+	if ctx.Module().(*Module).IsLlndk() {
+		return library.Properties.Llndk.Symbol_file
+	}
 	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
 		return library.Properties.Stubs.Symbol_file
 	}
@@ -1587,7 +1690,9 @@
 		library := mctx.Module().(*Module).linker.(prebuiltLibraryInterface)
 
 		// Differentiate between header only and building an actual static/shared library
-		if library.buildStatic() || library.buildShared() {
+		buildStatic := library.buildStatic()
+		buildShared := library.buildShared()
+		if buildStatic || buildShared {
 			// Always create both the static and shared variants for prebuilt libraries, and then disable the one
 			// that is not being used.  This allows them to share the name of a cc_library module, which requires that
 			// all the variants of the cc_library also exist on the prebuilt.
@@ -1598,16 +1703,16 @@
 			static.linker.(prebuiltLibraryInterface).setStatic()
 			shared.linker.(prebuiltLibraryInterface).setShared()
 
-			if library.buildShared() {
+			if buildShared {
 				mctx.AliasVariation("shared")
-			} else if library.buildStatic() {
+			} else if buildStatic {
 				mctx.AliasVariation("static")
 			}
 
-			if !library.buildStatic() {
+			if !buildStatic {
 				static.linker.(prebuiltLibraryInterface).disablePrebuilt()
 			}
-			if !library.buildShared() {
+			if !buildShared {
 				shared.linker.(prebuiltLibraryInterface).disablePrebuilt()
 			}
 		} else {
@@ -1622,7 +1727,18 @@
 			variations = append(variations, "")
 		}
 
-		if library.BuildStaticVariant() && library.BuildSharedVariant() {
+		isLLNDK := false
+		if m, ok := mctx.Module().(*Module); ok {
+			isLLNDK = m.IsLlndk()
+			// Don't count the vestigial llndk_library module as isLLNDK, it needs a static
+			// variant so that a cc_library_prebuilt can depend on it.
+			if _, ok := m.linker.(*llndkStubDecorator); ok {
+				isLLNDK = false
+			}
+		}
+		buildStatic := library.BuildStaticVariant() && !isLLNDK
+		buildShared := library.BuildSharedVariant()
+		if buildStatic && buildShared {
 			variations := append([]string{"static", "shared"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
@@ -1636,13 +1752,13 @@
 				reuseStaticLibrary(mctx, static.(*Module), shared.(*Module))
 			}
 			mctx.AliasVariation("shared")
-		} else if library.BuildStaticVariant() {
+		} else if buildStatic {
 			variations := append([]string{"static"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
 			modules[0].(LinkableInterface).SetStatic()
 			mctx.AliasVariation("static")
-		} else if library.BuildSharedVariant() {
+		} else if buildShared {
 			variations := append([]string{"shared"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
@@ -1675,22 +1791,32 @@
 }
 
 func createVersionVariations(mctx android.BottomUpMutatorContext, versions []string) {
-	// "" is for the non-stubs (implementation) variant.
+	// "" is for the non-stubs (implementation) variant for system modules, or the LLNDK variant
+	// for LLNDK modules.
 	variants := append(android.CopyOf(versions), "")
 
+	m := mctx.Module().(*Module)
+	isLLNDK := m.IsLlndk()
+
 	modules := mctx.CreateLocalVariations(variants...)
 	for i, m := range modules {
-		if variants[i] != "" {
+
+		if variants[i] != "" || isLLNDK {
+			// A stubs or LLNDK stubs variant.
 			c := m.(*Module)
-			c.Properties.HideFromMake = true
 			c.sanitize = nil
 			c.stl = nil
 			c.Properties.PreventInstall = true
 			lib := moduleLibraryInterface(m)
 			lib.setBuildStubs()
-			lib.setStubsVersion(variants[i])
-			// The implementation depends on the stubs
-			mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
+
+			if variants[i] != "" {
+				// A non-LLNDK stubs module is hidden from make and has a dependency from the
+				// implementation module to the stubs module.
+				c.Properties.HideFromMake = true
+				lib.setStubsVersion(variants[i])
+				mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
+			}
 		}
 	}
 	mctx.AliasVariation("")
@@ -1737,7 +1863,7 @@
 		module.CcLibraryInterface() && module.Shared()
 }
 
-func moduleLibraryInterface(module android.Module) libraryInterface {
+func moduleLibraryInterface(module blueprint.Module) libraryInterface {
 	if m, ok := module.(*Module); ok {
 		return m.library
 	}
@@ -1758,7 +1884,15 @@
 				// Set the versions on the pre-mutated module so they can be read by any llndk modules that
 				// depend on the implementation library and haven't been mutated yet.
 				library.setAllStubsVersions(versions)
-				return
+			}
+
+			if mctx.Module().(*Module).UseVndk() && library.hasLLNDKStubs() {
+				// Propagate the version to the llndk stubs module.
+				mctx.VisitDirectDepsWithTag(llndkStubDepTag, func(stubs android.Module) {
+					if stubsLib := moduleLibraryInterface(stubs); stubsLib != nil {
+						stubsLib.setAllStubsVersions(library.allStubsVersions())
+					}
+				})
 			}
 		}
 	}
diff --git a/cc/linkable.go b/cc/linkable.go
index d010985..489063f 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -43,9 +43,11 @@
 	UseSdk() bool
 	UseVndk() bool
 	MustUseVendorVariant() bool
+	IsLlndk() bool
+	IsLlndkPublic() bool
 	IsVndk() bool
 	IsVndkExt() bool
-	IsVndkPrivate(config android.Config) bool
+	IsVndkPrivate() bool
 	HasVendorVariant() bool
 	InProduct() bool
 
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 212720b..d0fbc48 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -15,21 +15,21 @@
 package cc
 
 import (
-	"fmt"
-	"path/filepath"
 	"strings"
 
 	"android/soong/android"
 )
 
-var llndkImplDep = dependencyTag{name: "llndk impl"}
-
 var (
 	llndkLibrarySuffix = ".llndk"
 	llndkHeadersSuffix = ".llndk"
 )
 
-// Creates a stub shared library based on the provided version file.
+// Holds properties to describe a stub shared library based on the provided version file.
+// The stub library will actually be built by the cc_library module that points to this
+// module with the llndk_stubs property.
+// TODO(ccross): move the properties from llndk_library modules directly into the cc_library
+//  modules and remove the llndk_library modules.
 //
 // Example:
 //
@@ -64,43 +64,32 @@
 
 	// list of llndk headers to re-export include directories from.
 	Export_llndk_headers []string `android:"arch_variant"`
+
+	// whether this module can be directly depended upon by libs that are installed
+	// to /vendor and /product.
+	// When set to true, this module can only be depended on by VNDK libraries, not
+	// vendor nor product libraries. This effectively hides this module from
+	// non-system modules. Default value is false.
+	Private *bool
 }
 
 type llndkStubDecorator struct {
 	*libraryDecorator
 
 	Properties llndkLibraryProperties
-
-	movedToApex bool
 }
 
 var _ versionedInterface = (*llndkStubDecorator)(nil)
 
 func (stub *llndkStubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
-	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
-	return addStubLibraryCompilerFlags(flags)
+	return flags
 }
 
 func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	vndkVer := ctx.Module().(*Module).VndkVersion()
-	if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" {
-		// For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
-		vndkVer = "current"
-	}
-	if stub.stubsVersion() != "" {
-		vndkVer = stub.stubsVersion()
-	}
-	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), vndkVer, "--llndk")
-	if !Bool(stub.Properties.Unversioned) {
-		stub.versionScriptPath = android.OptionalPathForPath(versionScript)
-	}
-	return objs
+	return Objects{}
 }
 
 func (stub *llndkStubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	headers := addSuffix(stub.Properties.Export_llndk_headers, llndkHeadersSuffix)
-	deps.HeaderLibs = append(deps.HeaderLibs, headers...)
-	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, headers...)
 	return deps
 }
 
@@ -116,57 +105,9 @@
 	return stub.libraryDecorator.linkerFlags(ctx, flags)
 }
 
-func (stub *llndkStubDecorator) processHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path {
-	srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
-	srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
-
-	var installPaths []android.WritablePath
-	for _, header := range srcFiles {
-		headerDir := filepath.Dir(header.String())
-		relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
-		if err != nil {
-			ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
-				srcDir.String(), headerDir, err)
-			continue
-		}
-
-		installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
-	}
-
-	return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
-}
-
 func (stub *llndkStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
 	objs Objects) android.Path {
-
-	impl := ctx.GetDirectDepWithTag(stub.implementationModuleName(ctx.ModuleName()), llndkImplDep)
-	if implApexModule, ok := impl.(android.ApexModule); ok {
-		stub.movedToApex = implApexModule.DirectlyInAnyApex()
-	}
-
-	if len(stub.Properties.Export_preprocessed_headers) > 0 {
-		genHeaderOutDir := android.PathForModuleGen(ctx, "include")
-
-		var timestampFiles android.Paths
-		for _, dir := range stub.Properties.Export_preprocessed_headers {
-			timestampFiles = append(timestampFiles, stub.processHeaders(ctx, dir, genHeaderOutDir))
-		}
-
-		if Bool(stub.Properties.Export_headers_as_system) {
-			stub.reexportSystemDirs(genHeaderOutDir)
-		} else {
-			stub.reexportDirs(genHeaderOutDir)
-		}
-
-		stub.reexportDeps(timestampFiles...)
-	}
-
-	if Bool(stub.Properties.Export_headers_as_system) {
-		stub.exportIncludesAsSystem(ctx)
-		stub.libraryDecorator.flagExporter.Properties.Export_include_dirs = []string{}
-	}
-
-	return stub.libraryDecorator.link(ctx, flags, deps, objs)
+	return nil
 }
 
 func (stub *llndkStubDecorator) nativeCoverage() bool {
@@ -181,20 +122,8 @@
 	return true
 }
 
-func (stub *llndkStubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
-	// Get the versions from the implementation module.
-	impls := ctx.GetDirectDepsWithTag(llndkImplDep)
-	if len(impls) > 1 {
-		panic(fmt.Errorf("Expected single implmenetation library, got %d", len(impls)))
-	} else if len(impls) == 1 {
-		return moduleLibraryInterface(impls[0]).allStubsVersions()
-	}
-	return nil
-}
-
 func NewLLndkStubLibrary() *Module {
 	module, library := NewLibrary(android.DeviceSupported)
-	library.BuildOnlyShared()
 	module.stl = nil
 	module.sanitize = nil
 	library.disableStripping()
@@ -235,10 +164,6 @@
 	*libraryDecorator
 }
 
-func (headers *llndkHeadersDecorator) Name(name string) string {
-	return name + llndkHeadersSuffix
-}
-
 // llndk_headers contains a set of c/c++ llndk headers files which are imported
 // by other soongs cc modules.
 func llndkHeadersFactory() android.Module {
diff --git a/cc/sabi.go b/cc/sabi.go
index c357f8d..4a1ba3c 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -80,10 +80,10 @@
 	if m.IsNdk(ctx.Config()) {
 		return "NDK"
 	}
-	if m.isLlndkPublic(ctx.Config()) {
+	if m.isImplementationForLLNDKPublic() {
 		return "LLNDK"
 	}
-	if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate(ctx.Config()) {
+	if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate() {
 		if m.isVndkSp() {
 			if m.IsVndkExt() {
 				return "VNDK-SP-ext"
@@ -156,7 +156,7 @@
 	}
 
 	// Don't create ABI dump for stubs.
-	if m.isNDKStubLibrary() || m.IsStubs() {
+	if m.isNDKStubLibrary() || m.IsLlndk() || m.IsStubs() {
 		return false
 	}
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 22ee25f..d7df5dc 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1002,9 +1002,6 @@
 
 		if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
 			// UBSan is supported on non-bionic linux host builds as well
-			if isLlndkLibrary(runtimeLibrary, mctx.Config()) && !c.static() && c.UseVndk() {
-				runtimeLibrary = runtimeLibrary + llndkLibrarySuffix
-			}
 
 			// Adding dependency to the runtime library. We are using *FarVariation*
 			// because the runtime libraries themselves are not mutated by sanitizer
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index f5f8121..419b7cf 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -805,7 +805,7 @@
 	}
 
 	// .. and also filter out llndk library
-	if module.isLlndk(ctx.Config()) {
+	if module.IsLlndk() {
 		return
 	}
 
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index bca76dc..d2c29d6 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -210,6 +210,9 @@
 		return false
 	}
 	// skip llndk_library and llndk_headers which are backward compatible
+	if m.IsLlndk() {
+		return false
+	}
 	if _, ok := m.linker.(*llndkStubDecorator); ok {
 		return false
 	}
diff --git a/cc/vndk.go b/cc/vndk.go
index 1529ac5..c1bce16 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -156,9 +156,15 @@
 	}
 	if lib, ok := to.linker.(*libraryDecorator); !ok || !lib.shared() {
 		// Check only shared libraries.
-		// Other (static and LL-NDK) libraries are allowed to link.
+		// Other (static) libraries are allowed to link.
 		return
 	}
+
+	if to.IsLlndk() {
+		// LL-NDK libraries are allowed to link
+		return
+	}
+
 	if !to.UseVndk() {
 		ctx.ModuleErrorf("(%s) should not link to %q which is not a vendor-available library",
 			vndk.typeName(), to.Name())
@@ -250,22 +256,12 @@
 	}).(map[string]string)
 }
 
-func isLlndkLibrary(baseModuleName string, config android.Config) bool {
-	_, ok := llndkLibraries(config)[strings.TrimSuffix(baseModuleName, llndkLibrarySuffix)]
-	return ok
-}
-
 func llndkLibraries(config android.Config) map[string]string {
 	return config.Once(llndkLibrariesKey, func() interface{} {
 		return make(map[string]string)
 	}).(map[string]string)
 }
 
-func isVndkPrivateLibrary(baseModuleName string, config android.Config) bool {
-	_, ok := vndkPrivateLibraries(config)[baseModuleName]
-	return ok
-}
-
 func vndkPrivateLibraries(config android.Config) map[string]string {
 	return config.Once(vndkPrivateLibrariesKey, func() interface{} {
 		return make(map[string]string)
@@ -301,12 +297,10 @@
 	defer vndkLibrariesLock.Unlock()
 
 	llndkLibraries(mctx.Config())[name] = filename
+	m.VendorProperties.IsLLNDK = true
 	if !Bool(lib.Properties.Vendor_available) {
 		vndkPrivateLibraries(mctx.Config())[name] = filename
-	}
-
-	if mctx.OtherModuleExists(name) {
-		mctx.AddFarVariationDependencies(m.Target().Variations(), llndkImplDep, name)
+		m.VendorProperties.IsLLNDKPrivate = true
 	}
 }
 
@@ -410,9 +404,31 @@
 		return
 	}
 
+	// This is a temporary measure to copy the properties from an llndk_library into the cc_library
+	// that will actually build the stubs.  It will be removed once the properties are moved into
+	// the cc_library in the Android.bp files.
+	mergeLLNDKToLib := func(llndk *Module, llndkProperties *llndkLibraryProperties, flagExporter *flagExporter) {
+		if llndkLib := moduleLibraryInterface(llndk); llndkLib != nil {
+			*llndkProperties = llndkLib.(*llndkStubDecorator).Properties
+			flagExporter.Properties = llndkLib.(*llndkStubDecorator).flagExporter.Properties
+
+			m.VendorProperties.IsLLNDK = llndk.VendorProperties.IsLLNDK
+			m.VendorProperties.IsLLNDKPrivate = llndk.VendorProperties.IsLLNDKPrivate
+		}
+	}
+
 	lib, is_lib := m.linker.(*libraryDecorator)
 	prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
 
+	if m.UseVndk() && is_lib && lib.hasLLNDKStubs() {
+		llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(lib.Properties.Llndk_stubs))
+		mergeLLNDKToLib(llndk[0].(*Module), &lib.Properties.Llndk, &lib.flagExporter)
+	}
+	if m.UseVndk() && is_prebuilt_lib && prebuilt_lib.hasLLNDKStubs() {
+		llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(prebuilt_lib.Properties.Llndk_stubs))
+		mergeLLNDKToLib(llndk[0].(*Module), &prebuilt_lib.Properties.Llndk, &prebuilt_lib.flagExporter)
+	}
+
 	if (is_lib && lib.buildShared()) || (is_prebuilt_lib && prebuilt_lib.buildShared()) {
 		if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
 			processVndkLibrary(mctx, m)
@@ -819,13 +835,11 @@
 	// they been moved to an apex.
 	movedToApexLlndkLibraries := make(map[string]bool)
 	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(*Module); ok {
-			if llndk, ok := m.linker.(*llndkStubDecorator); ok {
-				// Skip bionic libs, they are handled in different manner
-				name := llndk.implementationModuleName(m.BaseModuleName())
-				if llndk.movedToApex && !isBionic(name) {
-					movedToApexLlndkLibraries[name] = true
-				}
+		if library := moduleLibraryInterface(module); library != nil && library.hasLLNDKStubs() {
+			// Skip bionic libs, they are handled in different manner
+			name := library.implementationModuleName(module.(*Module).BaseModuleName())
+			if module.(android.ApexModule).DirectlyInAnyApex() && !isBionic(name) {
+				movedToApexLlndkLibraries[name] = true
 			}
 		}
 	})
diff --git a/rust/rust.go b/rust/rust.go
index 21da5ae..2e78a14 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -189,7 +189,15 @@
 	return false
 }
 
-func (c *Module) IsVndkPrivate(config android.Config) bool {
+func (c *Module) IsVndkPrivate() bool {
+	return false
+}
+
+func (c *Module) IsLlndk() bool {
+	return false
+}
+
+func (c *Module) IsLlndkPublic() bool {
 	return false
 }