Merge "Add debug ramdisk variant."
diff --git a/android/arch.go b/android/arch.go
index a63d630..74fef3d 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1673,13 +1673,144 @@
 	return buildTargets, nil
 }
 
+func (m *ModuleBase) getArchPropertySet(propertySet interface{}, archType ArchType) interface{} {
+	archString := archType.Field
+	for i := range m.archProperties {
+		if m.archProperties[i] == nil {
+			// Skip over nil properties
+			continue
+		}
+
+		// Not archProperties are usable; this function looks for properties of a very specific
+		// form, and ignores the rest.
+		for _, archProperty := range m.archProperties[i] {
+			// archPropValue is a property struct, we are looking for the form:
+			// `arch: { arm: { key: value, ... }}`
+			archPropValue := reflect.ValueOf(archProperty).Elem()
+
+			// Unwrap src so that it should looks like a pointer to `arm: { key: value, ... }`
+			src := archPropValue.FieldByName("Arch").Elem()
+
+			// Step into non-nil pointers to structs in the src value.
+			if src.Kind() == reflect.Ptr {
+				if src.IsNil() {
+					continue
+				}
+				src = src.Elem()
+			}
+
+			// Find the requested field (e.g. arm, x86) in the src struct.
+			src = src.FieldByName(archString)
+
+			// We only care about structs.
+			if !src.IsValid() || src.Kind() != reflect.Struct {
+				continue
+			}
+
+			// If the value of the field is a struct then step into the
+			// BlueprintEmbed field. The special "BlueprintEmbed" name is
+			// used by createArchPropTypeDesc to embed the arch properties
+			// in the parent struct, so the src arch prop should be in this
+			// field.
+			//
+			// See createArchPropTypeDesc for more details on how Arch-specific
+			// module properties are processed from the nested props and written
+			// into the module's archProperties.
+			src = src.FieldByName("BlueprintEmbed")
+
+			// Clone the destination prop, since we want a unique prop struct per arch.
+			propertySetClone := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+
+			// Copy the located property struct into the cloned destination property struct.
+			err := proptools.ExtendMatchingProperties([]interface{}{propertySetClone}, src.Interface(), nil, proptools.OrderReplace)
+			if err != nil {
+				// This is fine, it just means the src struct doesn't match the type of propertySet.
+				continue
+			}
+
+			return propertySetClone
+		}
+	}
+	// No property set was found specific to the given arch, so return an empty
+	// property set.
+	return reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+}
+
+// getMultilibPropertySet returns a property set struct matching the type of
+// `propertySet`, containing multilib-specific module properties for the given architecture.
+// If no multilib-specific properties exist for the given architecture, returns an empty property
+// set matching `propertySet`'s type.
+func (m *ModuleBase) getMultilibPropertySet(propertySet interface{}, archType ArchType) interface{} {
+	// archType.Multilib is lowercase (for example, lib32) but property struct field is
+	// capitalized, such as Lib32, so use strings.Title to capitalize it.
+	multiLibString := strings.Title(archType.Multilib)
+
+	for i := range m.archProperties {
+		if m.archProperties[i] == nil {
+			// Skip over nil properties
+			continue
+		}
+
+		// Not archProperties are usable; this function looks for properties of a very specific
+		// form, and ignores the rest.
+		for _, archProperties := range m.archProperties[i] {
+			// archPropValue is a property struct, we are looking for the form:
+			// `multilib: { lib32: { key: value, ... }}`
+			archPropValue := reflect.ValueOf(archProperties).Elem()
+
+			// Unwrap src so that it should looks like a pointer to `lib32: { key: value, ... }`
+			src := archPropValue.FieldByName("Multilib").Elem()
+
+			// Step into non-nil pointers to structs in the src value.
+			if src.Kind() == reflect.Ptr {
+				if src.IsNil() {
+					// Ignore nil pointers.
+					continue
+				}
+				src = src.Elem()
+			}
+
+			// Find the requested field (e.g. lib32) in the src struct.
+			src = src.FieldByName(multiLibString)
+
+			// We only care about valid struct pointers.
+			if !src.IsValid() || src.Kind() != reflect.Ptr || src.Elem().Kind() != reflect.Struct {
+				continue
+			}
+
+			// Get the zero value for the requested property set.
+			propertySetClone := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+
+			// Copy the located property struct into the "zero" property set struct.
+			err := proptools.ExtendMatchingProperties([]interface{}{propertySetClone}, src.Interface(), nil, proptools.OrderReplace)
+
+			if err != nil {
+				// This is fine, it just means the src struct doesn't match.
+				continue
+			}
+
+			return propertySetClone
+		}
+	}
+
+	// There were no multilib properties specifically matching the given archtype.
+	// Return zeroed value.
+	return reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+}
+
 // GetArchProperties returns a map of architectures to the values of the
-// properties of the 'dst' struct that are specific to that architecture.
+// properties of the 'propertySet' struct that are specific to that architecture.
 //
 // For example, passing a struct { Foo bool, Bar string } will return an
 // interface{} that can be type asserted back into the same struct, containing
 // the arch specific property value specified by the module if defined.
-func (m *ModuleBase) GetArchProperties(dst interface{}) map[ArchType]interface{} {
+//
+// Arch-specific properties may come from an arch stanza or a multilib stanza; properties
+// in these stanzas are combined.
+// For example: `arch: { x86: { Foo: ["bar"] } }, multilib: { lib32: {` Foo: ["baz"] } }`
+// will result in `Foo: ["bar", "baz"]` being returned for architecture x86, if the given
+// propertyset contains `Foo []string`.
+func (m *ModuleBase) GetArchProperties(propertySet interface{}) map[ArchType]interface{} {
 	// Return value of the arch types to the prop values for that arch.
 	archToProp := map[ArchType]interface{}{}
 
@@ -1688,81 +1819,26 @@
 		return archToProp
 	}
 
-	// archProperties has the type of [][]interface{}. Looks complicated, so let's
-	// explain this step by step.
-	//
-	// Loop over the outer index, which determines the property struct that
-	// contains a matching set of properties in dst that we're interested in.
-	// For example, BaseCompilerProperties or BaseLinkerProperties.
-	for i := range m.archProperties {
-		if m.archProperties[i] == nil {
-			// Skip over nil arch props
-			continue
+	// For each arch (x86, arm64, etc.),
+	for _, arch := range ArchTypeList() {
+		// Find arch-specific properties matching that property set type. For example, any
+		// matching properties under `arch { x86 { ... } }`.
+		archPropertySet := m.getArchPropertySet(propertySet, arch)
+
+		// Find multilib-specific properties matching that property set type. For example, any
+		// matching properties under `multilib { lib32 { ... } }` for x86, as x86 is 32-bit.
+		multilibPropertySet := m.getMultilibPropertySet(propertySet, arch)
+
+		// Append the multilibPropertySet to archPropertySet. This combines the
+		// arch and multilib properties into a single property struct.
+		err := proptools.ExtendMatchingProperties([]interface{}{archPropertySet}, multilibPropertySet, nil, proptools.OrderAppend)
+		if err != nil {
+			// archPropertySet and multilibPropertySet must be of the same type, or
+			// something horrible went wrong.
+			panic(err)
 		}
 
-		// Non-nil arch prop, let's see if the props match up.
-		for _, arch := range ArchTypeList() {
-			// e.g X86, Arm
-			field := arch.Field
-
-			// If it's not nil, loop over the inner index, which determines the arch variant
-			// of the prop type. In an Android.bp file, this is like looping over:
-			//
-			// arch: { arm: { key: value, ... }, x86: { key: value, ... } }
-			for _, archProperties := range m.archProperties[i] {
-				archPropValues := reflect.ValueOf(archProperties).Elem()
-
-				// This is the archPropRoot struct. Traverse into the Arch nested struct.
-				src := archPropValues.FieldByName("Arch").Elem()
-
-				// Step into non-nil pointers to structs in the src value.
-				if src.Kind() == reflect.Ptr {
-					if src.IsNil() {
-						// Ignore nil pointers.
-						continue
-					}
-					src = src.Elem()
-				}
-
-				// Find the requested field (e.g. x86, x86_64) in the src struct.
-				src = src.FieldByName(field)
-				if !src.IsValid() {
-					continue
-				}
-
-				// We only care about structs. These are not the droids you are looking for.
-				if src.Kind() != reflect.Struct {
-					continue
-				}
-
-				// If the value of the field is a struct  then step into the
-				// BlueprintEmbed field. The special "BlueprintEmbed" name is
-				// used by createArchPropTypeDesc to embed the arch properties
-				// in the parent struct, so the src arch prop should be in this
-				// field.
-				//
-				// See createArchPropTypeDesc for more details on how Arch-specific
-				// module properties are processed from the nested props and written
-				// into the module's archProperties.
-				src = src.FieldByName("BlueprintEmbed")
-
-				// Clone the destination prop, since we want a unique prop struct per arch.
-				dstClone := reflect.New(reflect.ValueOf(dst).Elem().Type()).Interface()
-
-				// Copy the located property struct into the cloned destination property struct.
-				err := proptools.ExtendMatchingProperties([]interface{}{dstClone}, src.Interface(), nil, proptools.OrderReplace)
-				if err != nil {
-					// This is fine, it just means the src struct doesn't match.
-					continue
-				}
-
-				// Found the prop for the arch, you have.
-				archToProp[arch] = dstClone
-
-				// Go to the next prop.
-				break
-			}
-		}
+		archToProp[arch] = archPropertySet
 	}
 	return archToProp
 }
diff --git a/android/bazel.go b/android/bazel.go
index f50b31d..81ef578 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -168,68 +168,115 @@
 		"external/gwp_asan":     Bp2BuildDefaultTrueRecursively,
 		"system/core/libcutils": Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
-		"system/libbase":        Bp2BuildDefaultTrueRecursively,
-		"system/logging/liblog": Bp2BuildDefaultTrueRecursively,
+		"system/libbase":                  Bp2BuildDefaultTrueRecursively,
+		"system/logging/liblog":           Bp2BuildDefaultTrueRecursively,
+		"external/jemalloc_new":           Bp2BuildDefaultTrueRecursively,
+		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
+		"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
 	}
 
 	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
 	bp2buildModuleDoNotConvertList = []string{
-		// Things that transitively depend on //external/arm-optimized-routines. That one fails
-		// with a linker error: "ld.lld: no input files"
-		"libc_common",        // ruperts@, cc_library_static, depends on //bionic/libc:libc_nopthread
-		"libc_common_static", // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libc_common_shared", // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libc_nomalloc",      // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libc_nopthread",     // ruperts@, cc_library_static, depends on //external/arm-optimized-routine
+		// Things that transitively depend on unconverted libc_* modules.
+		"libc_nopthread", // http://b/186821550, cc_library_static, depends on //bionic/libc:libc_bionic_ndk (http://b/186822256)
+		//                                                     also depends on //bionic/libc:libc_tzcode (http://b/186822591)
+		//                                                     also depends on //bionic/libc:libstdc++ (http://b/186822597)
+		"libc_common",        // http://b/186821517, cc_library_static, depends on //bionic/libc:libc_nopthread (http://b/186821550)
+		"libc_common_static", // http://b/186824119, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517)
+		"libc_common_shared", // http://b/186824118, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517)
+		"libc_nomalloc",      // http://b/186825031, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517)
 
-		// Things that transitively depend on //system/libbase. libbase doesn't work because:
-		// fmtlib: fatal error: 'cassert' file not found
-		"libbase",                     // eakammer@, cc_library, no such target '//build/bazel/platforms/os:darwin': target 'darwin' not declared
-		"libbase_ndk",                 // eakammer@, cc_library, no such target '//build/bazel/platforms/os:darwin': target 'darwin' not declared
-		"libbionic_spawn_benchmark",   // ruperts@, cc_library_static, depends on libbase, libgoogle-benchmark
-		"libc_malloc_debug",           // ruperts@, cc_library_static, depends on libbase
-		"libc_malloc_debug_backtrace", // ruperts@, cc_library_static, depends on libbase
-		"libcutils",                   // eakammer@, cc_library, depends on libbase, liblog
-		"libcutils_sockets",           // eakammer@, cc_library, depends on libbase, liblog
-		"liblinker_debuggerd_stub",    // ruperts@, cc_library_static, depends on libbase, libz, libziparchive
-		"liblinker_main",              // ruperts@, cc_library_static, depends on libbase, libz, libziparchive
-		"liblinker_malloc",            // ruperts@, cc_library_static, depends on libziparchive, libz, libbase
+		"libbionic_spawn_benchmark", // http://b/186824595, cc_library_static, depends on //external/google-benchmark (http://b/186822740)
+		//                                                                also depends on //system/logging/liblog:liblog (http://b/186822772)
 
-		// Requires non-libc targets, but otherwise works
-		"libc_jemalloc_wrapper", // ruperts@, cc_library_static, depends on //external/jemalloc_new
+		"libc_malloc_debug",           // http://b/186824339, cc_library_static, depends on //system/libbase:libbase (http://b/186823646)
+		"libc_malloc_debug_backtrace", // http://b/186824112, cc_library_static, depends on //external/libcxxabi:libc++demangle (http://b/186823773)
 
-		// Compilation error, seems to be fixable by changing the toolchain definition
-		"libc_bionic_ndk", // ruperts@, cc_library_static, error: ISO C++ requires field designators...
-		"libc_tzcode",     // ruperts@, cc_library_static, error: expected expression
-		"libm",            // jingwen@, cc_library, error: "expected register here" (and many others)
+		"libcutils",         // http://b/186827426, cc_library, depends on //system/core/libprocessgroup:libprocessgroup_headers (http://b/186826841)
+		"libcutils_sockets", // http://b/186826853, cc_library, depends on //system/libbase:libbase (http://b/186826479)
 
-		// Linker error
-		"libc_malloc_hooks", // jingwen@, cc_library, undefined symbol: __malloc_hook, etc.
-		"libdl",             // jingwen@, cc_library, clang failed
-		"libstdc++",         // jingwen@, cc_library, undefined symbol: free
+		"liblinker_debuggerd_stub", // http://b/186824327, cc_library_static, depends on //external/zlib:libz (http://b/186823782)
+		//                                                               also depends on //system/libziparchive:libziparchive (http://b/186823656)
+		//                                                               also depends on //system/logging/liblog:liblog (http://b/186822772)
+		"liblinker_main", // http://b/186825989, cc_library_static, depends on //external/zlib:libz (http://b/186823782)
+		//                                                     also depends on //system/libziparchive:libziparchive (http://b/186823656)
+		//                                                     also depends on//system/logging/liblog:liblog (http://b/186822772)
+		"liblinker_malloc", // http://b/186826466, cc_library_static, depends on //external/zlib:libz (http://b/186823782)
+		//                                                       also depends on //system/libziparchive:libziparchive (http://b/186823656)
+		//                                                       also depends on //system/logging/liblog:liblog (http://b/186822772)
+		"libc_jemalloc_wrapper", // cc_library_static, depends on //external/jemalloc_new:libjemalloc5
+		"libc_ndk",              // cc_library_static, depends on libc_bionic_ndk, libc_jemalloc_wrapper, libc_tzcode, libstdc++
+		// libc: http://b/183064430
+		// cc_library, depends on libc_jemalloc_wrapper (and possibly many others)
+		// Also http://b/186816506: Handle static and shared props
+		// Also http://b/186650430: version_script prop support
+		// Also http://b/186651708: pack_relocations prop support
+		// Also http://b/186576099: multilib props support
+		"libc",
 
-		// Includes not found
-		"libbionic_tests_headers_posix", // ruperts@, cc_library_static, 'dirent.h' not found
-		"liblog",                        // eakammer@, cc_library, 'sys/cdefs.h' file not found, missing -isystem bionic/libc/include through the libc/libm/libdl default dependencies if system_shared_libs unset
-		"libseccomp_policy",             // jingwen@, cc_library, 'linux/filter.h' not found, missing -isystem bionic/libc/kernel/uapi/asm-arm, probably due to us not handling arch { ... { export_system_include_dirs } } correctly
-		"note_memtag_heap_async",        // lberki@, cc_library_static, error: feature.h not found, missing -isystem bionic/libc/include through the libc/libm/libdl default dependencies if system_shared_libs unset
-		"note_memtag_heap_sync",         // lberki@, cc_library_static, error: feature.h not found, missing -isystem bionic/libc/include through the libc/libm/libdl default dependencies if system_shared_libs unset
+		// Compilation or linker error from command line and toolchain inconsistencies.
+		// http://b/186388670: Make Bazel/Ninja command lines more similar.
+		// http://b/186628704: Incorporate Soong's Clang flags into Bazel's toolchains.
+		//
+		"libc_tzcode",  // http://b/186822591: cc_library_static, error: expected expression
+		"libjemalloc5", // http://b/186828626: cc_library, ld.lld: error: undefined symbol: memset, __stack_chk_fail, pthread_mutex_trylock..
+		// libc_bionic_ndk, cc_library_static
+		// Error: ISO C++ requires field designators...
+		// Also http://b/186576099: multilib props support
+		// Also http://b/183595873: product_variables support
+		"libc_bionic_ndk",
+		// libc_malloc_hooks, cc_library
+		// Error: undefined symbol: __malloc_hook, __realloc_hook, __free_hook, __memalign_hook, memset, __errno
+		// These symbols are defined in https://cs.android.com/android/platform/superproject/+/master:bionic/libc/bionic/malloc_common.cpp;l=57-60;drc=9cad8424ff7b0fa63b53cb9919eae31539b8561a
+		// Also http://b/186650430: version_script prop support
+		"libc_malloc_hooks",
+		// http://b/186822597, libstdc++, cc_library
+		// Error: undefined symbol: __errno, syscall, async_safe_fatal_no_abort, abort, malloc, free
+		// Also http://b/186024507: depends on libc through system_shared_libraries.
+		// Also http://b/186650430: version_script prop support
+		// Also http://b/186651708: pack_relocations prop support
+		"libstdc++",
+		// http://b/183064661, libm:
+		// cc_library, error: "expected register here" (and many others)
+		// Also http://b/186024507: depends on libc through system_shared_libraries.
+		// Also http://b/186650430: version_script prop support
+		// Also http://b/186651708: pack_relocations prop support
+		// Also http://b/186576099: multilib props support
+		"libm",
 
-		// Other
-		"libBionicBenchmarksUtils", // ruperts@, cc_library_static, 'map' file not found
-		"libc_ndk",                 // ruperts@, cc_library_static, depends on libc_bionic_ndk, libc_jemalloc_wrapper, libc_tzcode, libstdc++
+		// http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx
+		// c++_static
+		"fmtlib_ndk",  // cc_library, from c++_static
+		"libbase_ndk", // http://b/186826477, cc_library, depends on fmtlib_ndk, which depends on c++_static
+		// libcxx
+		"libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx
+		"fmtlib",                   // cc_library_static, fatal error: 'cassert' file not found, from libcxx
+		"libbase",                  // http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx
 
-		"libc", // jingwen@, cc_library, depends on //external/gwp_asan
+		// http://b/186024507: Includes errors because of the system_shared_libs default value.
+		// Missing -isystem bionic/libc/include through the libc/libm/libdl
+		// default dependencies if system_shared_libs is unset.
+		"liblog",                 // http://b/186822772: cc_library, 'sys/cdefs.h' file not found
+		"libjemalloc5_jet",       // cc_library, 'sys/cdefs.h' file not found
+		"libseccomp_policy",      // http://b/186476753: cc_library, 'linux/filter.h' not found
+		"note_memtag_heap_async", // http://b/185127353: cc_library_static, error: feature.h not found
+		"note_memtag_heap_sync",  // http://b/185127353: cc_library_static, error: feature.h not found
+
+		// Tests. Handle later.
+		"libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
+		"libjemalloc5_integrationtest",
+		"libjemalloc5_stresstestlib",
+		"libjemalloc5_unittest",
 	}
 
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
 	mixedBuildsDisabledList = []string{
-		"libc_gdtoa",            // ruperts@, cc_library_static, OK for bp2build but undefined symbol: __strtorQ for mixed builds
-		"libc_netbsd",           // lberki@, cc_library_static, version script assignment of 'LIBC_PRIVATE' to symbol 'SHA1Final' failed: symbol not defined
-		"libc_openbsd",          // ruperts@, cc_library_static, OK for bp2build but error: duplicate symbol: strcpy for mixed builds
-		"libsystemproperties",   // cparsons@, cc_library_static, wrong include paths
-		"libpropertyinfoparser", // cparsons@, cc_library_static, wrong include paths
+		"libc_netbsd",                      // lberki@, cc_library_static, version script assignment of 'LIBC_PRIVATE' to symbol 'SHA1Final' failed: symbol not defined
+		"libc_openbsd",                     // ruperts@, cc_library_static, OK for bp2build but error: duplicate symbol: strcpy for mixed builds
+		"libsystemproperties",              // cparsons@, cc_library_static, wrong include paths
+		"libpropertyinfoparser",            // cparsons@, cc_library_static, wrong include paths
+		"libarm-optimized-routines-string", // jingwen@, cc_library_static, OK for bp2build but b/186615213 (asflags not handled in  bp2build), version script assignment of 'LIBC' to symbol 'memcmp' failed: symbol not defined (also for memrchr, strnlen)
 	}
 
 	// Used for quicker lookups
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 4280bc3..f4b2a7c 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -100,6 +100,10 @@
 	return labels
 }
 
+func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+}
+
 // BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
 // references (":<module>") and returns a bazel.LabelList{} containing the resolved references in
 // paths, relative to the local module, or Bazel-labels (absolute if in a different package or
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 06d3b54..ebf0833 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -118,10 +118,6 @@
 	seenDataOutPaths := make(map[string]bool)
 
 	for _, fi := range a.filesInfo {
-		if ccMod, ok := fi.module.(*cc.Module); ok && ccMod.Properties.HideFromMake {
-			continue
-		}
-
 		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
 
 		moduleName := a.fullModuleName(apexBundleName, &fi)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index c2378fc..08d82e9 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1355,12 +1355,9 @@
 				system_shared_libs: [],
 				stl: "none",
 				stubs: { versions: ["29","30"] },
-				llndk_stubs: "libbar.llndk",
-			}
-
-			llndk_library {
-				name: "libbar.llndk",
-				symbol_file: "",
+				llndk: {
+					symbol_file: "libbar.map.txt",
+				}
 			}
 			`,
 				setUseVendorAllowListForTest([]string{"myapex"}),
@@ -6522,6 +6519,15 @@
 			min_sdk_version: "current",
 		}
 
+		bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["some-art-lib"],
+			apex_available: [
+				"com.android.art.debug",
+			],
+		}
+
 		apex_key {
 			name: "com.android.art.debug.key",
 		}
@@ -6654,14 +6660,14 @@
 	})
 
 	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
+		err := `ArtApexJars expects this to be in apex "some-updatable-apex" but this is only in apexes.*"com.android.art.debug"`
 		// Update the dexpreopt ArtApexJars directly.
 		preparer := prepareSetArtJars("some-updatable-apex:some-updatable-apex-lib")
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err := `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
+		err := `ArtApexJars expects this to be in apex "some-non-updatable-apex" but this is only in apexes.*"com.android.art.debug"`
 		// Update the dexpreopt ArtApexJars directly.
 		preparer := prepareSetArtJars("some-non-updatable-apex:some-non-updatable-apex-lib")
 		testNoUpdatableJarsInBootImage(t, err, preparer)
@@ -6691,7 +6697,7 @@
 	})
 
 	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
-		err := `module "some-platform-lib" is not allowed in the ART boot image`
+		err := `ArtApexJars is invalid as it requests a platform variant of "some-platform-lib"`
 		// Update the dexpreopt ArtApexJars directly.
 		preparer := prepareSetArtJars("platform:some-platform-lib")
 		testNoUpdatableJarsInBootImage(t, err, preparer)
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 3d39d34..e2b320c 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -94,6 +94,8 @@
 		bootclasspath_fragment {
 			name: "art-bootclasspath-fragment",
 			image_name: "art",
+			// Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above.
+			contents: ["baz", "quuz"],
 			apex_available: [
 				"com.android.art",
 			],
@@ -405,6 +407,8 @@
 		prebuilt_bootclasspath_fragment {
 			name: "mybootclasspathfragment",
 			image_name: "art",
+			// Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above.
+			contents: ["foo", "bar"],
 			apex_available: [
 				"com.android.art",
 			],
diff --git a/bazel/properties.go b/bazel/properties.go
index 037150d..12dfcaf 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -227,6 +227,15 @@
 	HasConfigurableValues() bool
 }
 
+// Represents an attribute whose value is a single label
+type LabelAttribute struct {
+	Value Label
+}
+
+func (LabelAttribute) HasConfigurableValues() bool {
+	return false
+}
+
 // Arch-specific label_list typed Bazel attribute values. This should correspond
 // to the types of architectures supported for compilation in arch.go.
 type labelListArchValues struct {
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 08790d1..bddc524 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -519,9 +519,7 @@
 	case reflect.Struct:
 		valueIsZero := true
 		for i := 0; i < value.NumField(); i++ {
-			if value.Field(i).CanSet() {
-				valueIsZero = valueIsZero && isZero(value.Field(i))
-			}
+			valueIsZero = valueIsZero && isZero(value.Field(i))
 		}
 		return valueIsZero
 	case reflect.Ptr:
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index d9cd22d..0551a18 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -50,6 +50,7 @@
 		expectedBazelTargets               []string
 		filesystem                         map[string]string
 		dir                                string
+		depsMutators                       []android.RegisterMutatorFunc
 	}{
 		{
 			description:                        "cc_library - simple example",
@@ -248,6 +249,59 @@
     srcs = ["math/cosf.c"],
 )`},
 		},
+		{
+			description:                        "cc_library shared/static props",
+			moduleTypeUnderTest:                "cc_library",
+			moduleTypeUnderTestFactory:         cc.LibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			dir:                                "foo/bar",
+			filesystem: map[string]string{
+				"foo/bar/a.cpp": "",
+				"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    shared: { whole_static_libs: ["b"] },
+    static: { srcs: ["a.cpp"] },
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static { name: "b" }
+`,
+			},
+			bp: soongCcLibraryPreamble,
+			expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = ["-Ifoo/bar"],
+    srcs = ["a.cpp"],
+    static_deps_for_shared = [":b"],
+)`},
+		},
+		{
+			description:                        "cc_library non-configured version script",
+			moduleTypeUnderTest:                "cc_library",
+			moduleTypeUnderTestFactory:         cc.LibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			dir:                                "foo/bar",
+			filesystem: map[string]string{
+				"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    srcs: ["a.cpp"],
+    version_script: "v.map",
+    bazel_module: { bp2build_available: true },
+}
+`,
+			},
+			bp: soongCcLibraryPreamble,
+			expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = ["-Ifoo/bar"],
+    srcs = ["a.cpp"],
+    version_script = "v.map",
+)`},
+		},
 	}
 
 	dir := "."
@@ -266,11 +320,15 @@
 		ctx := android.NewTestContext(config)
 
 		cc.RegisterCCBuildComponents(ctx)
+		ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 		ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
 		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
 		ctx.RegisterBp2BuildConfig(bp2buildConfig) // TODO(jingwen): make this the default for all tests
+		for _, m := range testCase.depsMutators {
+			ctx.DepsBp2BuildMutators(m)
+		}
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index e8235d5..bff9b07 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -746,6 +746,172 @@
     linkstatic = True,
 )`},
 		},
+		{
+			description:                        "cc_library_static 1 multilib srcs and exclude_srcs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem: map[string]string{
+				"common.c":        "",
+				"for-lib32.c":     "",
+				"not-for-lib32.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c", "not-for-*.c"],
+    multilib: {
+        lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
+    },
+} `,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = ["common.c"] + select({
+        "//build/bazel/platforms/arch:arm": ["for-lib32.c"],
+        "//build/bazel/platforms/arch:x86": ["for-lib32.c"],
+        "//conditions:default": ["not-for-lib32.c"],
+    }),
+)`},
+		},
+		{
+			description:                        "cc_library_static 2 multilib srcs and exclude_srcs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem: map[string]string{
+				"common.c":        "",
+				"for-lib32.c":     "",
+				"for-lib64.c":     "",
+				"not-for-lib32.c": "",
+				"not-for-lib64.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static2",
+    srcs: ["common.c", "not-for-*.c"],
+    multilib: {
+        lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
+        lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] },
+    },
+} `,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static2",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = ["common.c"] + select({
+        "//build/bazel/platforms/arch:arm": [
+            "for-lib32.c",
+            "not-for-lib64.c",
+        ],
+        "//build/bazel/platforms/arch:arm64": [
+            "for-lib64.c",
+            "not-for-lib32.c",
+        ],
+        "//build/bazel/platforms/arch:x86": [
+            "for-lib32.c",
+            "not-for-lib64.c",
+        ],
+        "//build/bazel/platforms/arch:x86_64": [
+            "for-lib64.c",
+            "not-for-lib32.c",
+        ],
+        "//conditions:default": [
+            "not-for-lib32.c",
+            "not-for-lib64.c",
+        ],
+    }),
+)`},
+		},
+		{
+			description:                        "cc_library_static arch and multilib srcs and exclude_srcs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem: map[string]string{
+				"common.c":             "",
+				"for-arm.c":            "",
+				"for-arm64.c":          "",
+				"for-x86.c":            "",
+				"for-x86_64.c":         "",
+				"for-lib32.c":          "",
+				"for-lib64.c":          "",
+				"not-for-arm.c":        "",
+				"not-for-arm64.c":      "",
+				"not-for-x86.c":        "",
+				"not-for-x86_64.c":     "",
+				"not-for-lib32.c":      "",
+				"not-for-lib64.c":      "",
+				"not-for-everything.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+   name: "foo_static3",
+   srcs: ["common.c", "not-for-*.c"],
+   exclude_srcs: ["not-for-everything.c"],
+   arch: {
+       arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] },
+       arm64: { srcs: ["for-arm64.c"], exclude_srcs: ["not-for-arm64.c"] },
+       x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] },
+       x86_64: { srcs: ["for-x86_64.c"], exclude_srcs: ["not-for-x86_64.c"] },
+   },
+   multilib: {
+       lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
+       lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] },
+   },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static3",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = ["common.c"] + select({
+        "//build/bazel/platforms/arch:arm": [
+            "for-arm.c",
+            "for-lib32.c",
+            "not-for-arm64.c",
+            "not-for-lib64.c",
+            "not-for-x86.c",
+            "not-for-x86_64.c",
+        ],
+        "//build/bazel/platforms/arch:arm64": [
+            "for-arm64.c",
+            "for-lib64.c",
+            "not-for-arm.c",
+            "not-for-lib32.c",
+            "not-for-x86.c",
+            "not-for-x86_64.c",
+        ],
+        "//build/bazel/platforms/arch:x86": [
+            "for-lib32.c",
+            "for-x86.c",
+            "not-for-arm.c",
+            "not-for-arm64.c",
+            "not-for-lib64.c",
+            "not-for-x86_64.c",
+        ],
+        "//build/bazel/platforms/arch:x86_64": [
+            "for-lib64.c",
+            "for-x86_64.c",
+            "not-for-arm.c",
+            "not-for-arm64.c",
+            "not-for-lib32.c",
+            "not-for-x86.c",
+        ],
+        "//conditions:default": [
+            "not-for-arm.c",
+            "not-for-arm64.c",
+            "not-for-lib32.c",
+            "not-for-lib64.c",
+            "not-for-x86.c",
+            "not-for-x86_64.c",
+        ],
+    }),
+)`},
+		},
 	}
 
 	dir := "."
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 52afb55..050679b 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -30,6 +30,11 @@
 	return value, archSelects, osSelects
 }
 
+func getLabelValue(label bazel.LabelAttribute) (reflect.Value, selects, selects) {
+	value := reflect.ValueOf(label.Value)
+	return value, nil, nil
+}
+
 func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) {
 	value := reflect.ValueOf(list.Value.Includes)
 	if !list.HasConfigurableValues() {
@@ -54,12 +59,13 @@
 func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
 	var value reflect.Value
 	var archSelects, osSelects selects
-
 	switch list := v.(type) {
 	case bazel.StringListAttribute:
 		value, archSelects, osSelects = getStringListValues(list)
 	case bazel.LabelListAttribute:
 		value, archSelects, osSelects = getLabelListValues(list)
+	case bazel.LabelAttribute:
+		value, archSelects, osSelects = getLabelValue(list)
 	default:
 		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
 	}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 8f3a652..e58d166 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -485,12 +485,6 @@
 	})
 }
 
-func (c *llndkStubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	// 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) {
 	entries.Class = "SHARED_LIBRARIES"
 
@@ -586,20 +580,6 @@
 	entries.Class = "SHARED_LIBRARIES"
 }
 
-func (c *vendorPublicLibraryStubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "SHARED_LIBRARIES"
-	entries.SubName = vendorPublicLibrarySuffix
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, 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)
-	})
-}
-
 func (p *prebuiltLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		if p.properties.Check_elf_files != nil {
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 9c4bf6e5..d52b817 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -68,9 +68,61 @@
 		}
 	}
 
+	// Deps in the static: { .. } and shared: { .. } props of a cc_library.
+	if lib, ok := module.compiler.(*libraryDecorator); ok {
+		allDeps = append(allDeps, lib.SharedProperties.Shared.Static_libs...)
+		allDeps = append(allDeps, lib.SharedProperties.Shared.Whole_static_libs...)
+		allDeps = append(allDeps, lib.SharedProperties.Shared.Shared_libs...)
+		allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...)
+
+		allDeps = append(allDeps, lib.StaticProperties.Static.Static_libs...)
+		allDeps = append(allDeps, lib.StaticProperties.Static.Whole_static_libs...)
+		allDeps = append(allDeps, lib.StaticProperties.Static.Shared_libs...)
+		allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...)
+	}
+
 	ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
 }
 
+type sharedAttributes struct {
+	staticDeps bazel.LabelListAttribute
+}
+
+// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
+func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) sharedAttributes {
+	lib, ok := module.compiler.(*libraryDecorator)
+	if !ok {
+		return sharedAttributes{}
+	}
+
+	var staticDeps bazel.LabelListAttribute
+
+	staticDeps.Value = android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Whole_static_libs)
+
+	return sharedAttributes{
+		staticDeps: staticDeps,
+	}
+}
+
+type staticAttributes struct {
+	srcs bazel.LabelListAttribute
+}
+
+// bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
+func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticAttributes {
+	lib, ok := module.compiler.(*libraryDecorator)
+	if !ok {
+		return staticAttributes{}
+	}
+
+	var srcs bazel.LabelListAttribute
+	srcs.Value = android.BazelLabelForModuleSrc(ctx, lib.StaticProperties.Static.Srcs)
+
+	return staticAttributes{
+		srcs: srcs,
+	}
+}
+
 // Convenience struct to hold all attributes parsed from compiler properties.
 type compilerAttributes struct {
 	copts    bazel.StringListAttribute
@@ -194,15 +246,17 @@
 
 // Convenience struct to hold all attributes parsed from linker properties.
 type linkerAttributes struct {
-	deps     bazel.LabelListAttribute
-	linkopts bazel.StringListAttribute
+	deps          bazel.LabelListAttribute
+	linkopts      bazel.StringListAttribute
+	versionScript bazel.LabelAttribute
 }
 
-// bp2BuildParseLinkerProps creates a label list attribute containing the header library deps of a module, including
+// bp2BuildParseLinkerProps parses the linker properties of a module, including
 // configurable attribute values.
 func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
 	var deps bazel.LabelListAttribute
 	var linkopts bazel.StringListAttribute
+	var versionScript bazel.LabelAttribute
 
 	for _, linkerProps := range module.linker.linkerProps() {
 		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
@@ -213,6 +267,12 @@
 			libs = android.SortedUniqueStrings(libs)
 			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
 			linkopts.Value = baseLinkerProps.Ldflags
+
+			if baseLinkerProps.Version_script != nil {
+				versionScript = bazel.LabelAttribute{
+					Value: android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script),
+				}
+			}
 			break
 		}
 	}
@@ -242,8 +302,9 @@
 	}
 
 	return linkerAttributes{
-		deps:     deps,
-		linkopts: linkopts,
+		deps:          deps,
+		linkopts:      linkopts,
+		versionScript: versionScript,
 	}
 }
 
diff --git a/cc/cc.go b/cc/cc.go
index c35f628..16a49d3 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -447,6 +447,10 @@
 
 	// IsVNDKProduct is set if a VNDK module sets the product_available property.
 	IsVNDKProduct bool `blueprint:"mutated"`
+
+	// IsVendorPublicLibrary is set for the core and product variants of a library that has
+	// vendor_public_library stubs.
+	IsVendorPublicLibrary bool `blueprint:"mutated"`
 }
 
 // ModuleContextIntf is an interface (on a module context helper) consisting of functions related
@@ -475,6 +479,7 @@
 	isVndk() bool
 	isVndkSp() bool
 	IsVndkExt() bool
+	IsVendorPublicLibrary() bool
 	inProduct() bool
 	inVendor() bool
 	inRamdisk() bool
@@ -1121,18 +1126,21 @@
 	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
 }
 
-func (c *Module) IsLlndkLibrary() bool {
-	if _, ok := c.linker.(*llndkStubDecorator); ok {
-		return true
-	}
-	return false
-}
-
 func (m *Module) NeedsLlndkVariants() bool {
 	lib := moduleLibraryInterface(m)
 	return lib != nil && (lib.hasLLNDKStubs() || lib.hasLLNDKHeaders())
 }
 
+func (m *Module) NeedsVendorPublicLibraryVariants() bool {
+	lib := moduleLibraryInterface(m)
+	return lib != nil && (lib.hasVendorPublicLibrary())
+}
+
+// IsVendorPublicLibrary returns true for vendor public libraries.
+func (c *Module) IsVendorPublicLibrary() bool {
+	return c.VendorProperties.IsVendorPublicLibrary
+}
+
 // 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 {
@@ -1444,6 +1452,10 @@
 	return ctx.mod.IsVndkExt()
 }
 
+func (ctx *moduleContextImpl) IsVendorPublicLibrary() bool {
+	return ctx.mod.IsVendorPublicLibrary()
+}
+
 func (ctx *moduleContextImpl) mustUseVendorVariant() bool {
 	return ctx.mod.MustUseVendorVariant()
 }
@@ -1606,6 +1618,8 @@
 		// added for product variant only when we have vendor and product variants with core
 		// variant. The suffix is not added for vendor-only or product-only module.
 		c.Properties.SubName += c.getNameSuffixWithVndkVersion(actx)
+	} else if c.IsVendorPublicLibrary() {
+		c.Properties.SubName += vendorPublicLibrarySuffix
 	} else if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
 		// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
 		// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
@@ -2050,8 +2064,6 @@
 		// The caller can then know to add the variantLibs dependencies differently from the
 		// nonvariantLibs
 
-		vendorPublicLibraries := vendorPublicLibraries(actx.Config())
-
 		rewriteLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) {
 			variantLibs = []string{}
 			nonvariantLibs = []string{}
@@ -2062,16 +2074,6 @@
 					nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
 				} else if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
 					variantLibs = append(variantLibs, name+ndkLibrarySuffix)
-				} else if (ctx.Platform() || ctx.ProductSpecific()) && inList(name, *vendorPublicLibraries) {
-					vendorPublicLib := name + vendorPublicLibrarySuffix
-					if actx.OtherModuleExists(vendorPublicLib) {
-						nonvariantLibs = append(nonvariantLibs, vendorPublicLib)
-					} else {
-						// This can happen if vendor_public_library module is defined in a
-						// namespace that isn't visible to the current module. In that case,
-						// link to the original library.
-						nonvariantLibs = append(nonvariantLibs, name)
-					}
 				} else if ctx.useVndk() {
 					nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
 				} else {
@@ -2928,13 +2930,9 @@
 }
 
 func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableInterface, depName string) string {
-
-	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
-
 	libName := baseLibName(depName)
 	ccDepModule, _ := ccDep.(*Module)
 	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
-	isVendorPublicLib := inList(libName, *vendorPublicLibraries)
 	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
 
 	if ccDepModule != nil {
@@ -2956,8 +2954,6 @@
 		// The vendor and product modules in Make will have been renamed to not conflict with the
 		// core module, so update the dependency name here accordingly.
 		return libName + ccDep.SubName()
-	} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
-		return libName + vendorPublicLibrarySuffix
 	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
 		return libName + ramdiskSuffix
 	} else if ccDep.InVendorRamdisk() && !ccDep.OnlyInVendorRamdisk() {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 49fffc9..e9daf33 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -548,13 +548,10 @@
 
 		cc_library {
 			name: "libllndk",
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
-			export_llndk_headers: ["libllndk_headers"],
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+				export_llndk_headers: ["libllndk_headers"],
+			}
 		}
 
 		cc_library_headers {
@@ -897,13 +894,10 @@
 
 		cc_library {
 			name: "libllndk",
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
-			export_llndk_headers: ["libllndk_headers"],
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+				export_llndk_headers: ["libllndk_headers"],
+			}
 		}
 
 		cc_library_headers {
@@ -1170,12 +1164,9 @@
 		cc_library {
 			name: "libllndk",
 			shared_libs: ["libdoubleloadable"],
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 
 		cc_library {
@@ -1193,12 +1184,9 @@
 		cc_library {
 			name: "libllndk",
 			shared_libs: ["libvndksp"],
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 
 		cc_library {
@@ -1255,12 +1243,9 @@
 		cc_library {
 			name: "libllndk",
 			shared_libs: ["libcoreonly"],
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 
 		cc_library {
@@ -1283,12 +1268,9 @@
 		cc_library {
 			name: "libllndk",
 			shared_libs: ["libnondoubleloadable"],
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 
 		cc_library {
@@ -1307,12 +1289,9 @@
 			name: "libllndk",
 			no_libcrt: true,
 			shared_libs: ["libnondoubleloadable"],
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 
 		cc_library {
@@ -1326,12 +1305,9 @@
 		cc_library {
 			name: "libllndk",
 			shared_libs: ["libcoreonly"],
-			llndk_stubs: "libllndk.llndk",
-		}
-
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 
 		cc_library {
@@ -1357,11 +1333,9 @@
 		cc_library {
 			name: "libllndk",
 			shared_libs: ["libnondoubleloadable"],
-			llndk_stubs: "libllndk.llndk",
-		}
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 		cc_library {
 			name: "libnondoubleloadable",
@@ -1388,11 +1362,6 @@
 			shared_libs: ["libanothervndksp"],
 		}
 
-		llndk_library {
-			name: "libllndk",
-			symbol_file: "",
-		}
-
 		cc_library {
 			name: "libanothervndksp",
 			vendor_available: true,
@@ -2158,11 +2127,9 @@
 	bp := `
 		cc_library {
 			name: "libllndk",
-			llndk_stubs: "libllndk.llndk",
-		}
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 		cc_library {
 			name: "libvndk",
@@ -2436,20 +2403,16 @@
 		}
 		cc_library {
 			name: "libllndk",
-			llndk_stubs: "libllndk.llndk",
-		}
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			}
 		}
 		cc_library {
 			name: "libllndkprivate",
-			llndk_stubs: "libllndkprivate.llndk",
-		}
-		llndk_library {
-			name: "libllndkprivate.llndk",
-			private: true,
-			symbol_file: "",
+			llndk: {
+				symbol_file: "libllndkprivate.map.txt",
+				private: true,
+			}
 		}
 
 		llndk_libraries_txt {
@@ -2769,68 +2732,6 @@
 }
 
 func TestLlndkLibrary(t *testing.T) {
-	ctx := testCc(t, `
-	cc_library {
-		name: "libllndk",
-		stubs: { versions: ["1", "2"] },
-		llndk_stubs: "libllndk.llndk",
-	}
-	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")
-	for i := 0; i < len(actual); i++ {
-		if !strings.HasPrefix(actual[i], "android_vendor.29_") {
-			actual = append(actual[:i], actual[i+1:]...)
-			i--
-		}
-	}
-	expected := []string{
-		"android_vendor.29_arm64_armv8-a_shared_1",
-		"android_vendor.29_arm64_armv8-a_shared_2",
-		"android_vendor.29_arm64_armv8-a_shared_current",
-		"android_vendor.29_arm64_armv8-a_shared",
-		"android_vendor.29_arm_armv7-a-neon_shared_1",
-		"android_vendor.29_arm_armv7-a-neon_shared_2",
-		"android_vendor.29_arm_armv7-a-neon_shared_current",
-		"android_vendor.29_arm_armv7-a-neon_shared",
-	}
-	checkEquals(t, "variants for llndk stubs", expected, actual)
-
-	params := ctx.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared").Description("generate stub")
-	checkEquals(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"])
-
-	params = ctx.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared_1").Description("generate stub")
-	checkEquals(t, "override apiLevel for versioned stubs", "1", params.Args["apiLevel"])
-}
-
-func TestEmbeddedLlndkLibrary(t *testing.T) {
 	result := prepareForCcTest.RunTestWithBp(t, `
 	cc_library {
 		name: "libllndk",
diff --git a/cc/image.go b/cc/image.go
index b853798..5d41717 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -437,15 +437,11 @@
 		productVndkVersion = platformVndkVersion
 	}
 
-	if m.IsLlndkLibrary() || m.NeedsLlndkVariants() {
+	if m.NeedsLlndkVariants() {
 		// 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.
 		// The LLNDK libraries need vendor variants even if there is no VNDK.
-		// The obsolete llndk_library and llndk_headers modules also need the vendor variants
-		// so the cc_library LLNDK stubs can depend on them.
-		if m.NeedsLlndkVariants() {
-			coreVariantNeeded = true
-		}
+		coreVariantNeeded = true
 		if platformVndkVersion != "" {
 			vendorVariants = append(vendorVariants, platformVndkVersion)
 			productVariants = append(productVariants, platformVndkVersion)
@@ -456,6 +452,17 @@
 		if productVndkVersion != "" {
 			productVariants = append(productVariants, productVndkVersion)
 		}
+	} else if m.NeedsVendorPublicLibraryVariants() {
+		// A vendor public library has the implementation on /vendor, with stub variants
+		// for system and product.
+		coreVariantNeeded = true
+		vendorVariants = append(vendorVariants, boardVndkVersion)
+		if platformVndkVersion != "" {
+			productVariants = append(productVariants, platformVndkVersion)
+		}
+		if productVndkVersion != "" {
+			productVariants = append(productVariants, productVndkVersion)
+		}
 	} else if boardVndkVersion == "" {
 		// If the device isn't compiling against the VNDK, we always
 		// use the core mode.
@@ -685,4 +692,9 @@
 		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
 		squashProductSrcs(m)
 	}
+
+	if c.NeedsVendorPublicLibraryVariants() &&
+		(variant == android.CoreVariation || strings.HasPrefix(variant, ProductVariationPrefix)) {
+		c.VendorProperties.IsVendorPublicLibrary = true
+	}
 }
diff --git a/cc/library.go b/cc/library.go
index 73215ed..50d3f67 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -117,12 +117,12 @@
 	// Inject boringssl hash into the shared library.  This is only intended for use by external/boringssl.
 	Inject_bssl_hash *bool `android:"arch_variant"`
 
-	// 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
+
+	// If this is a vendor public library, properties to describe the vendor public library stubs.
+	Vendor_public_library vendorPublicLibraryProperties
 }
 
 // StaticProperties is a properties stanza to affect only attributes of the "static" variants of a
@@ -220,13 +220,15 @@
 
 // For bp2build conversion.
 type bazelCcLibraryAttributes struct {
-	Srcs            bazel.LabelListAttribute
-	Hdrs            bazel.LabelListAttribute
-	Copts           bazel.StringListAttribute
-	Linkopts        bazel.StringListAttribute
-	Deps            bazel.LabelListAttribute
-	User_link_flags bazel.StringListAttribute
-	Includes        bazel.StringListAttribute
+	Srcs                   bazel.LabelListAttribute
+	Hdrs                   bazel.LabelListAttribute
+	Copts                  bazel.StringListAttribute
+	Linkopts               bazel.StringListAttribute
+	Deps                   bazel.LabelListAttribute
+	User_link_flags        bazel.StringListAttribute
+	Includes               bazel.StringListAttribute
+	Static_deps_for_shared bazel.LabelListAttribute
+	Version_script         bazel.LabelAttribute
 }
 
 type bazelCcLibrary struct {
@@ -257,16 +259,24 @@
 		return
 	}
 
+	sharedAttrs := bp2BuildParseSharedProps(ctx, m)
+	staticAttrs := bp2BuildParseStaticProps(ctx, m)
 	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
 
+	var srcs bazel.LabelListAttribute
+	srcs.Append(compilerAttrs.srcs)
+	srcs.Append(staticAttrs.srcs)
+
 	attrs := &bazelCcLibraryAttributes{
-		Srcs:     compilerAttrs.srcs,
-		Copts:    compilerAttrs.copts,
-		Linkopts: linkerAttrs.linkopts,
-		Deps:     linkerAttrs.deps,
-		Includes: exportedIncludes,
+		Srcs:                   srcs,
+		Copts:                  compilerAttrs.copts,
+		Linkopts:               linkerAttrs.linkopts,
+		Deps:                   linkerAttrs.deps,
+		Version_script:         linkerAttrs.versionScript,
+		Static_deps_for_shared: sharedAttrs.staticDeps,
+		Includes:               exportedIncludes,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -781,6 +791,13 @@
 		}
 		return objs
 	}
+	if ctx.IsVendorPublicLibrary() {
+		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Vendor_public_library.Symbol_file), "current", "")
+		if !Bool(library.Properties.Vendor_public_library.Unversioned) {
+			library.versionScriptPath = android.OptionalPathForPath(versionScript)
+		}
+		return objs
+	}
 	if library.buildStubs() {
 		symbolFile := String(library.Properties.Stubs.Symbol_file)
 		if symbolFile != "" && !strings.HasSuffix(symbolFile, ".map.txt") {
@@ -878,6 +895,7 @@
 	implementationModuleName(name string) string
 	hasLLNDKStubs() bool
 	hasLLNDKHeaders() bool
+	hasVendorPublicLibrary() bool
 }
 
 var _ libraryInterface = (*libraryDecorator)(nil)
@@ -973,6 +991,12 @@
 		deps.ReexportHeaderLibHeaders = append([]string(nil), library.Properties.Llndk.Export_llndk_headers...)
 		return deps
 	}
+	if ctx.IsVendorPublicLibrary() {
+		headers := library.Properties.Vendor_public_library.Export_public_headers
+		deps.HeaderLibs = append([]string(nil), headers...)
+		deps.ReexportHeaderLibHeaders = append([]string(nil), headers...)
+		return deps
+	}
 
 	if library.static() {
 		// Compare with nil because an empty list needs to be propagated.
@@ -1429,6 +1453,14 @@
 		}
 	}
 
+	if ctx.IsVendorPublicLibrary() {
+		// override the module's export_include_dirs with vendor_public_library.override_export_include_dirs
+		// if it is set.
+		if override := library.Properties.Vendor_public_library.Override_export_include_dirs; override != nil {
+			library.flagExporter.Properties.Export_include_dirs = override
+		}
+	}
+
 	// 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).
@@ -1682,22 +1714,20 @@
 
 // hasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
 func (library *libraryDecorator) hasLLNDKStubs() bool {
-	return library.hasVestigialLLNDKLibrary() || String(library.Properties.Llndk.Symbol_file) != ""
+	return String(library.Properties.Llndk.Symbol_file) != ""
 }
 
-// hasVestigialLLNDKLibrary returns true if this cc_library module has a corresponding llndk_library
-// module containing properties describing the LLNDK variant.
-// TODO(b/170784825): remove this once there are no more llndk_library modules.
-func (library *libraryDecorator) hasVestigialLLNDKLibrary() bool {
-	return String(library.Properties.Llndk_stubs) != ""
-}
-
-// hasLLNDKHeaders returns true if this cc_library module has a variant that provides headers
-// to a module that sets llndk.symbol_file.
+// hasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
 func (library *libraryDecorator) hasLLNDKHeaders() bool {
 	return Bool(library.Properties.Llndk.Llndk_headers)
 }
 
+// hasVendorPublicLibrary returns true if this cc_library module has a variant that will build
+// vendor public library stubs.
+func (library *libraryDecorator) hasVendorPublicLibrary() bool {
+	return String(library.Properties.Vendor_public_library.Symbol_file) != ""
+}
+
 func (library *libraryDecorator) implementationModuleName(name string) string {
 	return name
 }
@@ -1934,9 +1964,7 @@
 
 		isLLNDK := false
 		if m, ok := mctx.Module().(*Module); ok {
-			// 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.
-			isLLNDK = m.IsLlndk() && !isVestigialLLNDKModule(m)
+			isLLNDK = m.IsLlndk()
 		}
 		buildStatic := library.BuildStaticVariant() && !isLLNDK
 		buildShared := library.BuildSharedVariant()
@@ -1999,11 +2027,12 @@
 
 	m := mctx.Module().(*Module)
 	isLLNDK := m.IsLlndk()
+	isVendorPublicLibrary := m.IsVendorPublicLibrary()
 
 	modules := mctx.CreateLocalVariations(variants...)
 	for i, m := range modules {
 
-		if variants[i] != "" || isLLNDK {
+		if variants[i] != "" || isLLNDK || isVendorPublicLibrary {
 			// A stubs or LLNDK stubs variant.
 			c := m.(*Module)
 			c.sanitize = nil
diff --git a/cc/linkable.go b/cc/linkable.go
index 7b5b446..40a9d8b 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -106,12 +106,12 @@
 	// IsLlndkPublic returns true only for LLNDK (public) libs.
 	IsLlndkPublic() bool
 
-	// IsLlndkLibrary returns true if this module is an LLNDK library module.
-	IsLlndkLibrary() bool
-
 	// NeedsLlndkVariants returns true if this module has LLNDK stubs or provides LLNDK headers.
 	NeedsLlndkVariants() bool
 
+	// NeedsVendorPublicLibraryVariants returns true if this module has vendor public library stubs.
+	NeedsVendorPublicLibraryVariants() bool
+
 	UseVndk() bool
 	MustUseVendorVariant() bool
 	IsVndk() bool
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 88f3118..c307410 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -14,31 +14,12 @@
 
 package cc
 
-import (
-	"strings"
-
-	"android/soong/android"
-)
-
 var (
 	llndkLibrarySuffix = ".llndk"
 	llndkHeadersSuffix = ".llndk"
 )
 
 // 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:
-//
-// llndk_library {
-//     name: "libfoo",
-//     symbol_file: "libfoo.map.txt",
-//     export_include_dirs: ["include_vndk"],
-// }
-//
 type llndkLibraryProperties struct {
 	// Relative path to the symbol map.
 	// An example file can be seen here: TODO(danalbert): Make an example.
@@ -74,98 +55,3 @@
 	// llndk.symbol_file.
 	Llndk_headers *bool
 }
-
-type llndkStubDecorator struct {
-	*libraryDecorator
-
-	Properties llndkLibraryProperties
-}
-
-var _ versionedInterface = (*llndkStubDecorator)(nil)
-
-func (stub *llndkStubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
-	return flags
-}
-
-func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	return Objects{}
-}
-
-func (stub *llndkStubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	return deps
-}
-
-func (stub *llndkStubDecorator) Name(name string) string {
-	if strings.HasSuffix(name, llndkLibrarySuffix) {
-		return name
-	}
-	return name + llndkLibrarySuffix
-}
-
-func (stub *llndkStubDecorator) linkerProps() []interface{} {
-	props := stub.libraryDecorator.linkerProps()
-	return append(props, &stub.Properties)
-}
-
-func (stub *llndkStubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	stub.libraryDecorator.libName = stub.implementationModuleName(ctx.ModuleName())
-	return stub.libraryDecorator.linkerFlags(ctx, flags)
-}
-
-func (stub *llndkStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
-	objs Objects) android.Path {
-	return nil
-}
-
-func (stub *llndkStubDecorator) nativeCoverage() bool {
-	return false
-}
-
-func (stub *llndkStubDecorator) implementationModuleName(name string) string {
-	return strings.TrimSuffix(name, llndkLibrarySuffix)
-}
-
-func (stub *llndkStubDecorator) buildStubs() bool {
-	return true
-}
-
-func NewLLndkStubLibrary() *Module {
-	module, library := NewLibrary(android.DeviceSupported)
-	module.stl = nil
-	module.sanitize = nil
-	library.disableStripping()
-
-	stub := &llndkStubDecorator{
-		libraryDecorator: library,
-	}
-	module.compiler = stub
-	module.linker = stub
-	module.installer = nil
-	module.library = stub
-
-	return module
-}
-
-// llndk_library creates a stub llndk shared library based on the provided
-// version file. Example:
-//
-//    llndk_library {
-//        name: "libfoo",
-//        symbol_file: "libfoo.map.txt",
-//        export_include_dirs: ["include_vndk"],
-//    }
-func LlndkLibraryFactory() android.Module {
-	module := NewLLndkStubLibrary()
-	return module.Init()
-}
-
-// isVestigialLLNDKModule returns true if m is a vestigial llndk_library module used to provide
-// properties to the LLNDK variant of a cc_library.
-func isVestigialLLNDKModule(m *Module) bool {
-	_, ok := m.linker.(*llndkStubDecorator)
-	return ok
-}
-
-func init() {
-	android.RegisterModuleType("llndk_library", LlndkLibraryFactory)
-}
diff --git a/cc/makevars.go b/cc/makevars.go
index 923472a..fa0b2cc 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -71,8 +71,6 @@
 }
 
 func makeVarsProvider(ctx android.MakeVarsContext) {
-	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
-
 	ctx.Strict("LLVM_RELEASE_VERSION", "${config.ClangShortVersion}")
 	ctx.Strict("LLVM_PREBUILTS_VERSION", "${config.ClangVersion}")
 	ctx.Strict("LLVM_PREBUILTS_BASE", "${config.ClangBase}")
@@ -106,7 +104,7 @@
 	ctx.VisitAllModules(func(module android.Module) {
 		if ccModule, ok := module.(*Module); ok {
 			baseName := ccModule.BaseModuleName()
-			if inList(baseName, *vendorPublicLibraries) && module.ExportedToMake() {
+			if ccModule.IsVendorPublicLibrary() && module.ExportedToMake() {
 				if !inList(baseName, exportedVendorPublicLibraries) {
 					exportedVendorPublicLibraries = append(exportedVendorPublicLibraries, baseName)
 				}
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 81c8be7..1722c80 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -31,20 +31,7 @@
 
 // Check if the module defines stub, or itself is stub
 func IsStubTarget(m *Module) bool {
-	if m.IsStubs() || m.HasStubsVariants() {
-		return true
-	}
-
-	// Library which defines LLNDK Stub is also Stub target.
-	// Pure LLNDK Stub target would not contain any packaging
-	// with target file path.
-	if library, ok := m.linker.(*libraryDecorator); ok {
-		if library.Properties.Llndk_stubs != nil {
-			return true
-		}
-	}
-
-	return false
+	return m.IsStubs() || m.HasStubsVariants()
 }
 
 // Get target file name to be installed from this module
diff --git a/cc/testing.go b/cc/testing.go
index bf89f62..15f7ebb 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -27,7 +27,6 @@
 	RegisterLibraryHeadersBuildComponents(ctx)
 
 	ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
-	ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", genRuleFactory)
@@ -200,12 +199,9 @@
 			stubs: {
 				versions: ["27", "28", "29"],
 			},
-			llndk_stubs: "libc.llndk",
-		}
-		llndk_library {
-			name: "libc.llndk",
-			symbol_file: "",
-			sdk_version: "current",
+			llndk: {
+				symbol_file: "libc.map.txt",
+			},
 		}
 		cc_library {
 			name: "libm",
@@ -222,12 +218,9 @@
 				"//apex_available:platform",
 				"myapex"
 			],
-			llndk_stubs: "libm.llndk",
-		}
-		llndk_library {
-			name: "libm.llndk",
-			symbol_file: "",
-			sdk_version: "current",
+			llndk: {
+				symbol_file: "libm.map.txt",
+			},
 		}
 
 		// Coverage libraries
@@ -289,12 +282,9 @@
 				"//apex_available:platform",
 				"myapex"
 			],
-			llndk_stubs: "libdl.llndk",
-		}
-		llndk_library {
-			name: "libdl.llndk",
-			symbol_file: "",
-			sdk_version: "current",
+			llndk: {
+				symbol_file: "libdl.map.txt",
+			},
 		}
 		cc_library {
 			name: "libft2",
@@ -302,13 +292,10 @@
 			nocrt: true,
 			system_shared_libs: [],
 			recovery_available: true,
-			llndk_stubs: "libft2.llndk",
-		}
-		llndk_library {
-			name: "libft2.llndk",
-			symbol_file: "",
-			private: true,
-			sdk_version: "current",
+			llndk: {
+				symbol_file: "libft2.map.txt",
+				private: true,
+			}
 		}
 		cc_library {
 			name: "libc++_static",
@@ -569,7 +556,6 @@
 		ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
 		ctx.RegisterModuleType("cc_test", TestFactory)
 		ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
-		ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
 		ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
 		RegisterVndkLibraryTxtTypes(ctx)
@@ -685,7 +671,6 @@
 	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
 	ctx.RegisterModuleType("cc_test", TestFactory)
 	ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
-	ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
diff --git a/cc/vendor_public_library.go b/cc/vendor_public_library.go
index 394e322..65a2b0c 100644
--- a/cc/vendor_public_library.go
+++ b/cc/vendor_public_library.go
@@ -14,26 +14,10 @@
 
 package cc
 
-import (
-	"strings"
-	"sync"
-
-	"android/soong/android"
-)
-
 var (
 	vendorPublicLibrarySuffix = ".vendorpublic"
-
-	vendorPublicLibrariesKey  = android.NewOnceKey("vendorPublicLibraries")
-	vendorPublicLibrariesLock sync.Mutex
 )
 
-func vendorPublicLibraries(config android.Config) *[]string {
-	return config.Once(vendorPublicLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
 // Creates a stub shared library for a vendor public library. Vendor public libraries
 // are vendor libraries (owned by them and installed to /vendor partition) that are
 // exposed to Android apps via JNI. The libraries are made public by being listed in
@@ -64,105 +48,9 @@
 
 	// list of header libs to re-export include directories from.
 	Export_public_headers []string `android:"arch_variant"`
-}
 
-type vendorPublicLibraryStubDecorator struct {
-	*libraryDecorator
-
-	Properties vendorPublicLibraryProperties
-
-	versionScriptPath android.ModuleGenPath
-}
-
-func (stub *vendorPublicLibraryStubDecorator) Name(name string) string {
-	return name + vendorPublicLibrarySuffix
-}
-
-func (stub *vendorPublicLibraryStubDecorator) compilerInit(ctx BaseModuleContext) {
-	stub.baseCompiler.compilerInit(ctx)
-
-	name := ctx.baseModuleName()
-	if strings.HasSuffix(name, vendorPublicLibrarySuffix) {
-		ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", vendorPublicLibrarySuffix)
-	}
-
-	vendorPublicLibrariesLock.Lock()
-	defer vendorPublicLibrariesLock.Unlock()
-	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
-	for _, lib := range *vendorPublicLibraries {
-		if lib == name {
-			return
-		}
-	}
-	*vendorPublicLibraries = append(*vendorPublicLibraries, name)
-}
-
-func (stub *vendorPublicLibraryStubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
-	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
-	return addStubLibraryCompilerFlags(flags)
-}
-
-func (stub *vendorPublicLibraryStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), "current", "")
-	stub.versionScriptPath = versionScript
-	return objs
-}
-
-func (stub *vendorPublicLibraryStubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	headers := stub.Properties.Export_public_headers
-	deps.HeaderLibs = append(deps.HeaderLibs, headers...)
-	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, headers...)
-	return deps
-}
-
-func (stub *vendorPublicLibraryStubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	stub.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), vendorPublicLibrarySuffix)
-	return stub.libraryDecorator.linkerFlags(ctx, flags)
-}
-
-func (stub *vendorPublicLibraryStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
-	objs Objects) android.Path {
-	if !Bool(stub.Properties.Unversioned) {
-		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
-		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
-		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
-	}
-	return stub.libraryDecorator.link(ctx, flags, deps, objs)
-}
-
-// vendor_public_library creates a stub shared library for a vendor public
-// library. This stub library is a build-time only artifact that provides
-// symbols that are exposed from a vendor public library. Example:
-//
-//    vendor_public_library {
-//        name: "libfoo",
-//        symbol_file: "libfoo.map.txt",
-//        export_public_headers: ["libfoo_headers"],
-//    }
-func vendorPublicLibraryFactory() android.Module {
-	module, library := NewLibrary(android.DeviceSupported)
-	library.BuildOnlyShared()
-	module.stl = nil
-	module.sanitize = nil
-	library.disableStripping()
-
-	stub := &vendorPublicLibraryStubDecorator{
-		libraryDecorator: library,
-	}
-	module.compiler = stub
-	module.linker = stub
-	module.installer = nil
-
-	module.AddProperties(
-		&stub.Properties,
-		&module.VendorProperties,
-		&library.MutatedProperties,
-		&library.flagExporter.Properties)
-
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
-	return module
-}
-
-func init() {
-	android.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
+	// list of directories relative to the Blueprints file that willbe added to the include path
+	// (using -I) for any module that links against the LLNDK variant of this module, replacing
+	// any that were listed outside the llndk clause.
+	Override_export_include_dirs []string
 }
diff --git a/cc/vendor_public_library_test.go b/cc/vendor_public_library_test.go
index 9f2accf..01959b4 100644
--- a/cc/vendor_public_library_test.go
+++ b/cc/vendor_public_library_test.go
@@ -26,18 +26,16 @@
 		product_available: true,
 		export_include_dirs: ["my_include"],
 	}
-	vendor_public_library {
-		name: "libvendorpublic",
-		product_available: true,
-		symbol_file: "",
-		export_public_headers: ["libvendorpublic_headers"],
-	}
 	cc_library {
 		name: "libvendorpublic",
 		srcs: ["foo.c"],
 		vendor: true,
 		no_libcrt: true,
 		nocrt: true,
+		vendor_public_library: {
+			symbol_file: "libvendorpublic.map.txt",
+			export_public_headers: ["libvendorpublic_headers"],
+		},
 	}
 
 	cc_library {
@@ -81,7 +79,7 @@
 	// test if libsystem is linked to the stub
 	ld := ctx.ModuleForTests("libsystem", coreVariant).Rule("ld")
 	libflags := ld.Args["libFlags"]
-	stubPaths := getOutputPaths(ctx, coreVariant, []string{"libvendorpublic" + vendorPublicLibrarySuffix})
+	stubPaths := getOutputPaths(ctx, coreVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libsystem must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
@@ -89,7 +87,7 @@
 	// test if libsystem is linked to the stub
 	ld = ctx.ModuleForTests("libproduct", productVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = getOutputPaths(ctx, productVariant, []string{"libvendorpublic" + vendorPublicLibrarySuffix})
+	stubPaths = getOutputPaths(ctx, productVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libproduct must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index c0082fb..9ad51ad 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -184,9 +184,6 @@
 	if m.IsLlndk() {
 		return false
 	}
-	if _, ok := m.linker.(*llndkStubDecorator); ok {
-		return false
-	}
 
 	// Libraries
 	if l, ok := m.linker.(snapshotLibraryInterface); ok {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 66396f7..fddd72a 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -78,14 +78,12 @@
 
 	cc_library {
 		name: "libllndk",
-		llndk_stubs: "libllndk.llndk",
-	}
-
-	llndk_library {
-		name: "libllndk.llndk",
-		symbol_file: "",
+		llndk: {
+			symbol_file: "libllndk.map.txt",
+		},
 	}
 `
+
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
diff --git a/cc/vndk.go b/cc/vndk.go
index e224e66..8b3788b 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -233,11 +233,11 @@
 type moduleListerFunc func(ctx android.SingletonContext) (moduleNames, fileNames []string)
 
 var (
-	llndkLibraries                = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsLLNDK && !isVestigialLLNDKModule(m) && !m.Header() })
+	llndkLibraries                = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsLLNDK && !m.Header() })
 	llndkLibrariesWithoutHWASAN   = vndkModuleListRemover(llndkLibraries, "libclang_rt.hwasan-")
 	vndkSPLibraries               = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
 	vndkCoreLibraries             = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
-	vndkPrivateLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate && !isVestigialLLNDKModule(m) })
+	vndkPrivateLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate })
 	vndkProductLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
 	vndkUsingCoreVariantLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKUsingCoreVariant })
 )
@@ -298,15 +298,6 @@
 	})
 }
 
-func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
-	lib := m.linker.(*llndkStubDecorator)
-
-	m.VendorProperties.IsLLNDK = true
-	if Bool(lib.Properties.Private) {
-		m.VendorProperties.IsVNDKPrivate = true
-	}
-}
-
 func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
 	if m.InProduct() {
 		// We may skip the steps for the product variants because they
@@ -402,41 +393,14 @@
 		return
 	}
 
-	if _, ok := m.linker.(*llndkStubDecorator); ok {
-		processLlndkLibrary(mctx, m)
-		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.IsVNDKPrivate = llndk.VendorProperties.IsVNDKPrivate
-		}
-	}
-
 	lib, isLib := m.linker.(*libraryDecorator)
 	prebuiltLib, isPrebuiltLib := m.linker.(*prebuiltLibraryLinker)
 
-	if m.UseVndk() && isLib && lib.hasVestigialLLNDKLibrary() {
-		llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(lib.Properties.Llndk_stubs))
-		mergeLLNDKToLib(llndk[0].(*Module), &lib.Properties.Llndk, &lib.flagExporter)
-	}
-	if m.UseVndk() && isPrebuiltLib && prebuiltLib.hasVestigialLLNDKLibrary() {
-		llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(prebuiltLib.Properties.Llndk_stubs))
-		mergeLLNDKToLib(llndk[0].(*Module), &prebuiltLib.Properties.Llndk, &prebuiltLib.flagExporter)
-	}
-
-	if m.UseVndk() && isLib && lib.hasLLNDKStubs() && !lib.hasVestigialLLNDKLibrary() {
+	if m.UseVndk() && isLib && lib.hasLLNDKStubs() {
 		m.VendorProperties.IsLLNDK = true
 		m.VendorProperties.IsVNDKPrivate = Bool(lib.Properties.Llndk.Private)
 	}
-	if m.UseVndk() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() && !prebuiltLib.hasVestigialLLNDKLibrary() {
+	if m.UseVndk() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() {
 		m.VendorProperties.IsLLNDK = true
 		m.VendorProperties.IsVNDKPrivate = Bool(prebuiltLib.Properties.Llndk.Private)
 	}
@@ -874,9 +838,6 @@
 	if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
 		return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
 	}
-	if library, ok := m.linker.(*llndkStubDecorator); ok {
-		return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
-	}
 	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
 }
 
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 3848205..628197c 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -134,7 +134,8 @@
 	profileInstalledPath := module.DexLocation + ".prof"
 
 	if !module.ProfileIsTextListing {
-		rule.Command().FlagWithOutput("touch ", profilePath)
+		rule.Command().Text("rm -f").Output(profilePath)
+		rule.Command().Text("touch").Output(profilePath)
 	}
 
 	cmd := rule.Command().
@@ -174,7 +175,8 @@
 	profileInstalledPath := module.DexLocation + ".bprof"
 
 	if !module.ProfileIsTextListing {
-		rule.Command().FlagWithOutput("touch ", profilePath)
+		rule.Command().Text("rm -f").Output(profilePath)
+		rule.Command().Text("touch").Output(profilePath)
 	}
 
 	cmd := rule.Command().
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index cb31c09..de9dc45 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -54,6 +54,7 @@
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
+	ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory)
 }
 
 var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
@@ -453,3 +454,16 @@
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	return module
 }
+
+// prebuilt_rfsa installs a firmware file that will be available through Qualcomm's RFSA
+// to the <partition>/lib/rfsa directory.
+func PrebuiltRFSAFactory() android.Module {
+	module := &PrebuiltEtc{}
+	// Ideally these would go in /vendor/dsp, but the /vendor/lib/rfsa paths are hardcoded in too
+	// many places outside of the application processor.  They could be moved to /vendor/dsp once
+	// that is cleaned up.
+	InitPrebuiltEtcModule(module, "lib/rfsa")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index fdb5648..354f6bb 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -312,3 +312,37 @@
 		})
 	}
 }
+
+func TestPrebuiltRFSADirPath(t *testing.T) {
+	targetPath := "out/soong/target/product/test_device"
+	tests := []struct {
+		description  string
+		config       string
+		expectedPath string
+	}{{
+		description: "prebuilt: system rfsa",
+		config: `
+			prebuilt_rfsa {
+				name: "foo.conf",
+				src: "foo.conf",
+			}`,
+		expectedPath: filepath.Join(targetPath, "system/lib/rfsa"),
+	}, {
+		description: "prebuilt: vendor rfsa",
+		config: `
+			prebuilt_rfsa {
+				name: "foo.conf",
+				src: "foo.conf",
+				soc_specific: true,
+				sub_dir: "sub_dir",
+			}`,
+		expectedPath: filepath.Join(targetPath, "vendor/lib/rfsa/sub_dir"),
+	}}
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
+			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
+		})
+	}
+}
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index c16193d..02833ab 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -69,13 +69,15 @@
 // addDependencyOntoApexModulePair adds a dependency onto the specified APEX specific variant or the
 // specified module.
 //
-// If apex="platform" then this adds a dependency onto the platform variant of the module. This adds
-// dependencies onto the prebuilt and source modules with the specified name, depending on which
-// ones are available. Visiting must use isActiveModule to select the preferred module when both
-// source and prebuilt modules are available.
+// If apex="platform" or "system_ext" then this adds a dependency onto the platform variant of the
+// module. This adds dependencies onto the prebuilt and source modules with the specified name,
+// depending on which ones are available. Visiting must use isActiveModule to select the preferred
+// module when both source and prebuilt modules are available.
+//
+// Use gatherApexModulePairDepsWithTag to retrieve the dependencies.
 func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) {
 	var variations []blueprint.Variation
-	if apex != "platform" {
+	if apex != "platform" && apex != "system_ext" {
 		// Pick the correct apex variant.
 		variations = []blueprint.Variation{
 			{Mutator: "apex", Variation: apex},
@@ -118,12 +120,28 @@
 	ctx.AddFarVariationDependencies(variations, nil, name)
 }
 
+// gatherApexModulePairDepsWithTag returns the list of dependencies with the supplied tag that was
+// added by addDependencyOntoApexModulePair.
+func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprint.DependencyTag) []android.Module {
+	var modules []android.Module
+	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+		t := ctx.OtherModuleDependencyTag(module)
+		if t == tag {
+			modules = append(modules, module)
+		}
+	})
+	return modules
+}
+
 // ApexVariantReference specifies a particular apex variant of a module.
 type ApexVariantReference struct {
 	// The name of the module apex variant, i.e. the apex containing the module variant.
 	//
 	// If this is not specified then it defaults to "platform" which will cause a dependency to be
 	// added to the module's platform variant.
+	//
+	// A value of system_ext should be used for any module that will be part of the system_ext
+	// partition.
 	Apex *string
 
 	// The name of the module.
@@ -161,3 +179,59 @@
 
 // The tag used for dependencies onto bootclasspath_fragments.
 var bootclasspathFragmentDepTag = bootclasspathDependencyTag{name: "fragment"}
+
+// BootclasspathNestedAPIProperties defines properties related to the API provided by parts of the
+// bootclasspath that are nested within the main BootclasspathAPIProperties.
+type BootclasspathNestedAPIProperties struct {
+	// java_library or preferably, java_sdk_library modules providing stub classes that define the
+	// APIs provided by this bootclasspath_fragment.
+	Stub_libs []string
+}
+
+// BootclasspathAPIProperties defines properties for defining the API provided by parts of the
+// bootclasspath.
+type BootclasspathAPIProperties struct {
+	// Api properties provide information about the APIs provided by the bootclasspath_fragment.
+	// Properties in this section apply to public, system and test api scopes. They DO NOT apply to
+	// core_platform as that is a special, ART specific scope, that does not follow the pattern and so
+	// has its own section. It is in the process of being deprecated and replaced by the system scope
+	// but this will remain for the foreseeable future to maintain backwards compatibility.
+	//
+	// Every bootclasspath_fragment must specify at least one stubs_lib in this section and must
+	// specify stubs for all the APIs provided by its contents. Failure to do so will lead to those
+	// methods being inaccessible to other parts of Android, including but not limited to
+	// applications.
+	Api BootclasspathNestedAPIProperties
+
+	// Properties related to the core platform API surface.
+	//
+	// This must only be used by the following modules:
+	// * ART
+	// * Conscrypt
+	// * I18N
+	//
+	// The bootclasspath_fragments for each of the above modules must specify at least one stubs_lib
+	// and must specify stubs for all the APIs provided by its contents. Failure to do so will lead to
+	// those methods being inaccessible to the other modules in the list.
+	Core_platform_api BootclasspathNestedAPIProperties
+}
+
+// sdkKindToStubLibs calculates the stub library modules for each relevant android.SdkKind from the
+// Stub_libs properties.
+func (p BootclasspathAPIProperties) sdkKindToStubLibs() map[android.SdkKind][]string {
+	m := map[android.SdkKind][]string{}
+	for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} {
+		m[kind] = p.Api.Stub_libs
+	}
+	m[android.SdkCorePlatform] = p.Core_platform_api.Stub_libs
+	return m
+}
+
+// bootclasspathApiInfo contains paths resolved from BootclasspathAPIProperties
+type bootclasspathApiInfo struct {
+	// stubJarsByKind maps from the android.SdkKind to the paths containing dex stub jars for each
+	// kind.
+	stubJarsByKind map[android.SdkKind]android.Paths
+}
+
+var bootclasspathApiInfoProvider = blueprint.NewProvider(bootclasspathApiInfo{})
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 5c1c5f0..8fe362a 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -88,6 +88,9 @@
 	//
 	// The order of this list matters as it is the order that is used in the bootclasspath.
 	Contents []string
+
+	// The properties for specifying the API stubs provided by this fragment.
+	BootclasspathAPIProperties
 }
 
 type bootclasspathFragmentProperties struct {
@@ -142,11 +145,6 @@
 		ctx.ModuleErrorf(`neither of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
 	}
 
-	if len(contents) != 0 {
-		// Nothing to do.
-		return
-	}
-
 	imageName := proptools.String(m.properties.Image_name)
 	if imageName == "art" {
 		// TODO(b/177892522): Prebuilts (versioned or not) should not use the image_name property.
@@ -178,7 +176,7 @@
 				continue
 			}
 			if !m.AvailableFor(apex) {
-				ctx.ModuleErrorf("incompatible with ArtApexJars which expects this to be in apex %q but this is only in apexes %q",
+				ctx.ModuleErrorf("ArtApexJars configuration incompatible with this module, ArtApexJars expects this to be in apex %q but this is only in apexes %q",
 					apex, m.ApexAvailable())
 				continue
 			}
@@ -190,6 +188,11 @@
 			}
 		}
 
+		if len(contents) != 0 {
+			// Nothing to do.
+			return
+		}
+
 		// Store the jars in the Contents property so that they can be used to add dependencies.
 		m.properties.Contents = modules.CopyOfJars()
 	}
@@ -316,6 +319,9 @@
 }
 
 func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Add dependencies onto all the modules that provide the API stubs for classes on this
+	// bootclasspath fragment.
+	hiddenAPIAddStubLibDependencies(ctx, b.properties.sdkKindToStubLibs())
 
 	if SkipDexpreoptBootJars(ctx) {
 		return
@@ -384,6 +390,13 @@
 
 	// Store the information for use by platform_bootclasspath.
 	ctx.SetProvider(hiddenAPIFlagFileInfoProvider, flagFileInfo)
+
+	// Convert the kind specific lists of modules into kind specific lists of jars.
+	stubJarsByKind := hiddenAPIGatherStubLibDexJarPaths(ctx)
+
+	// Store the information for use by other modules.
+	bootclasspathApiInfo := bootclasspathApiInfo{stubJarsByKind: stubJarsByKind}
+	ctx.SetProvider(bootclasspathApiInfoProvider, bootclasspathApiInfo)
 }
 
 type bootclasspathFragmentMemberType struct {
@@ -420,6 +433,10 @@
 	// Contents of the bootclasspath fragment
 	Contents []string
 
+	// Stub_libs properties.
+	Stub_libs               []string
+	Core_platform_stub_libs []string
+
 	// Flag files by *hiddenAPIFlagFileCategory
 	Flag_files_by_category map[*hiddenAPIFlagFileCategory]android.Paths
 }
@@ -434,6 +451,10 @@
 	mctx := ctx.SdkModuleContext()
 	flagFileInfo := mctx.OtherModuleProvider(module, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
 	b.Flag_files_by_category = flagFileInfo.categoryToPaths
+
+	// Copy stub_libs properties.
+	b.Stub_libs = module.properties.Api.Stub_libs
+	b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs
 }
 
 func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -441,11 +462,22 @@
 		propertySet.AddProperty("image_name", *b.Image_name)
 	}
 
+	builder := ctx.SnapshotBuilder()
+	requiredMemberDependency := builder.SdkMemberReferencePropertyTag(true)
+
 	if len(b.Contents) > 0 {
-		propertySet.AddPropertyWithTag("contents", b.Contents, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(true))
+		propertySet.AddPropertyWithTag("contents", b.Contents, requiredMemberDependency)
 	}
 
-	builder := ctx.SnapshotBuilder()
+	if len(b.Stub_libs) > 0 {
+		apiPropertySet := propertySet.AddPropertySet("api")
+		apiPropertySet.AddPropertyWithTag("stub_libs", b.Stub_libs, requiredMemberDependency)
+	}
+	if len(b.Core_platform_stub_libs) > 0 {
+		corePlatformApiPropertySet := propertySet.AddPropertySet("core_platform_api")
+		corePlatformApiPropertySet.AddPropertyWithTag("stub_libs", b.Core_platform_stub_libs, requiredMemberDependency)
+	}
+
 	if b.Flag_files_by_category != nil {
 		hiddenAPISet := propertySet.AddPropertySet("hidden_api")
 		for _, category := range hiddenAPIFlagFileCategories {
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 0419a46..32ed7ea 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -125,10 +125,20 @@
 			contents: [
 				"mybootlib",
 			],
+			api: {
+				stub_libs: [
+					"mysdklibrary",
+				],
+			},
 			coverage: {
 				contents: [
 					"coveragelib",
 				],
+				api: {
+					stub_libs: [
+						"mycoveragestubs",
+					],
+				},
 			},
 		}
 
@@ -147,6 +157,21 @@
 			sdk_version: "none",
 			compile_dex: true,
 		}
+
+		java_sdk_library {
+			name: "mysdklibrary",
+			srcs: ["Test.java"],
+			compile_dex: true,
+			public: {enabled: true},
+			system: {enabled: true},
+		}
+
+		java_sdk_library {
+			name: "mycoveragestubs",
+			srcs: ["Test.java"],
+			compile_dex: true,
+			public: {enabled: true},
+		}
 	`)
 
 	checkContents := func(t *testing.T, result *android.TestResult, expected ...string) {
@@ -154,20 +179,88 @@
 		android.AssertArrayString(t, "contents property", expected, module.properties.Contents)
 	}
 
+	preparer := android.GroupFixturePreparers(
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary", "mycoveragestubs"),
+		prepareWithBp,
+	)
+
 	t.Run("without coverage", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			prepareForTestWithBootclasspathFragment,
-			prepareWithBp,
-		).RunTest(t)
+		result := preparer.RunTest(t)
 		checkContents(t, result, "mybootlib")
 	})
 
 	t.Run("with coverage", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
-			prepareForTestWithBootclasspathFragment,
 			prepareForTestWithFrameworkCoverage,
-			prepareWithBp,
+			preparer,
 		).RunTest(t)
 		checkContents(t, result, "mybootlib", "coveragelib")
 	})
 }
+
+func TestBootclasspathFragment_StubLibs(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary", "mycoreplatform"),
+	).RunTestWithBp(t, `
+		bootclasspath_fragment {
+			name: "myfragment",
+			contents: ["mysdklibrary"],
+			api: {
+				stub_libs: [
+					"mystublib",
+					"mysdklibrary",
+				],
+			},
+			core_platform_api: {
+				stub_libs: ["mycoreplatform"],
+			},
+		}
+
+		java_library {
+			name: "mystublib",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+
+		java_sdk_library {
+			name: "mysdklibrary",
+			srcs: ["a.java"],
+			compile_dex: true,
+			public: {enabled: true},
+			system: {enabled: true},
+		}
+
+		java_sdk_library {
+			name: "mycoreplatform",
+			srcs: ["a.java"],
+			compile_dex: true,
+			public: {enabled: true},
+		}
+	`)
+
+	fragment := result.Module("myfragment", "android_common")
+	info := result.ModuleProvider(fragment, bootclasspathApiInfoProvider).(bootclasspathApiInfo)
+
+	stubsJar := "out/soong/.intermediates/mystublib/android_common/dex/mystublib.jar"
+
+	// Check that SdkPublic uses public stubs.
+	publicStubsJar := "out/soong/.intermediates/mysdklibrary.stubs/android_common/dex/mysdklibrary.stubs.jar"
+	android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{stubsJar, publicStubsJar}, info.stubJarsByKind[android.SdkPublic])
+
+	// Check that SdkSystem uses system stubs.
+	systemStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.system/android_common/dex/mysdklibrary.stubs.system.jar"
+	android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{stubsJar, systemStubsJar}, info.stubJarsByKind[android.SdkSystem])
+
+	// Check that SdkTest also uses system stubs as the mysdklibrary does not provide test stubs.
+	android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{stubsJar, systemStubsJar}, info.stubJarsByKind[android.SdkTest])
+
+	// Check that SdkCorePlatform uses public stubs from the mycoreplatform library.
+	corePlatformStubsJar := "out/soong/.intermediates/mycoreplatform.stubs/android_common/dex/mycoreplatform.stubs.jar"
+	android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.stubJarsByKind[android.SdkCorePlatform])
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 8a6f3d1..ce5155f 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -431,9 +431,6 @@
 	defaultImageConfig := defaultBootImageConfig(ctx)
 	profile := bootImageProfileRule(ctx, defaultImageConfig)
 
-	// Generate the updatable bootclasspath packages rule.
-	updatableBcpPackagesRule(ctx, defaultImageConfig)
-
 	// Create the default boot image.
 	d.defaultBootImage = buildBootImage(ctx, defaultImageConfig, profile)
 
@@ -441,8 +438,6 @@
 	d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx), profile))
 
 	copyUpdatableBootJars(ctx)
-
-	dumpOatRules(ctx, d.defaultBootImage)
 }
 
 // shouldBuildBootImages determines whether boot images should be built.
@@ -457,129 +452,79 @@
 	return true
 }
 
-// Inspect this module to see if it contains a bootclasspath dex jar.
-// Note that the same jar may occur in multiple modules.
-// This logic is tested in the apex package to avoid import cycle apex <-> java.
+// A copy of isModuleInConfiguredList created to work with singleton context.
 //
-// This is similar to logic in isModuleInConfiguredList() so any changes needed here are likely to
-// be needed there too.
-//
-// TODO(b/177892522): Avoid having to perform this type of check or if necessary dedup it.
-func getBootJar(ctx android.SingletonContext, bootjars android.ConfiguredJarList,
-	module android.Module, fromWhere string) (int, android.Path, *android.ApexInfo) {
-
+// TODO(b/177892522): Remove this.
+func isModuleInConfiguredListForSingleton(ctx android.SingletonContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool {
 	name := ctx.ModuleName(module)
 
-	// Strip a prebuilt_ prefix so that this can access the dex jar from a prebuilt module.
+	// Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed.
 	name = android.RemoveOptionalPrebuiltPrefix(name)
 
 	// Ignore any module that is not listed in the boot image configuration.
-	index := bootjars.IndexOfJar(name)
+	index := configuredBootJars.IndexOfJar(name)
 	if index == -1 {
-		return -1, nil, nil
+		return false
 	}
 
-	// It is an error if a module configured in the boot image does not support accessing the dex jar.
-	// This is safe because every module that has the same name has to have the same module type.
-	jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
-	if !hasJar {
-		ctx.Errorf("module %q %sdoes not support accessing dex jar", module, fromWhere)
-		return -1, nil, nil
-	}
-
-	// It is also an error if the module is not an ApexModule.
+	// It is an error if the module is not an ApexModule.
 	if _, ok := module.(android.ApexModule); !ok {
-		ctx.Errorf("module %q %sdoes not support being added to an apex", module, fromWhere)
-		return -1, nil, nil
+		ctx.Errorf("%s is configured in boot jars but does not support being added to an apex", ctx.ModuleName(module))
+		return false
 	}
 
 	apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
 
 	// Now match the apex part of the boot image configuration.
-	requiredApex := bootjars.Apex(index)
+	requiredApex := configuredBootJars.Apex(index)
 	if requiredApex == "platform" || requiredApex == "system_ext" {
 		if len(apexInfo.InApexes) != 0 {
 			// A platform variant is required but this is for an apex so ignore it.
-			return -1, nil, nil
+			return false
 		}
 	} else if !apexInfo.InApexByBaseName(requiredApex) {
 		// An apex variant for a specific apex is required but this is the wrong apex.
-		return -1, nil, nil
+		return false
 	}
 
-	return index, jar.DexJarBuildPath(), &apexInfo
+	return true
 }
 
-// Inspect this module to see if it contains a bootclasspath dex jar from a given boot image.
-func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
-	fromImage := fmt.Sprintf("configured in boot image %q ", image.name)
-	index, jarPath, apexInfo := getBootJar(ctx, image.modules, module, fromImage)
-	if index == -1 {
-		return -1, nil
-	}
-
-	name := ctx.ModuleName(module)
-
-	// Check that this module satisfies any boot image specific constraints.
-	fromUpdatableApex := apexInfo.Updatable
-
-	switch image.name {
-	case artBootImageName:
-		inArtApex := false
-		for _, n := range artApexNames {
-			if apexInfo.InApexByBaseName(n) {
-				inArtApex = true
-				break
-			}
-		}
-		if inArtApex {
-			// ok: found the jar in the ART apex
-		} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-			// exception (skip and continue): Jacoco platform variant for a coverage build
-			return -1, nil
-		} else if fromUpdatableApex {
-			// error: this jar is part of an updatable apex other than ART
-			ctx.Errorf("module %q from updatable apexes %q is not allowed in the ART boot image", name, apexInfo.InApexes)
-		} else {
-			// error: this jar is part of the platform or a non-updatable apex
-			ctx.Errorf("module %q is not allowed in the ART boot image", name)
-		}
-
-	case frameworkBootImageName:
-		if !fromUpdatableApex {
-			// ok: this jar is part of the platform or a non-updatable apex
-		} else {
-			// error: this jar is part of an updatable apex
-			ctx.Errorf("module %q from updatable apexes %q is not allowed in the framework boot image", name, apexInfo.InApexes)
-		}
-	default:
-		panic("unknown boot image: " + image.name)
-	}
-
-	return index, jarPath
-}
-
-// Generate commands that will copy boot jars to predefined paths in the global config.
-func findAndCopyBootJars(ctx android.SingletonContext, bootjars android.ConfiguredJarList,
-	jarPathsPredefined android.WritablePaths,
-	getBootJar func(module android.Module) (int, android.Path)) {
+// findBootJarModules finds the boot jar module variants specified in the bootjars parameter.
+//
+// It returns a list of modules such that the module at index i corresponds to the configured jar
+// at index i.
+func findBootJarModules(ctx android.SingletonContext, bootjars android.ConfiguredJarList) []android.Module {
+	modules := make([]android.Module, bootjars.Len())
 
 	// This logic is tested in the apex package to avoid import cycle apex <-> java.
-	jarPaths := make(android.Paths, bootjars.Len())
-
 	ctx.VisitAllModules(func(module android.Module) {
-		if !isActiveModule(module) {
+		if !isActiveModule(module) || !isModuleInConfiguredListForSingleton(ctx, module, bootjars) {
 			return
 		}
-		if i, j := getBootJar(module); i != -1 {
-			if existing := jarPaths[i]; existing != nil {
-				ctx.Errorf("Multiple dex jars found for %s:%s - %q and %q",
-					bootjars.Apex(i), bootjars.Jar(i), existing, j)
-				return
-			}
-			jarPaths[i] = j
+
+		name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
+		index := bootjars.IndexOfJar(name)
+		if existing := modules[index]; existing != nil {
+			ctx.Errorf("Multiple boot jar modules found for %s:%s - %q and %q",
+				bootjars.Apex(index), bootjars.Jar(index), existing, module)
+			return
 		}
+		modules[index] = module
 	})
+	return modules
+}
+
+// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to
+// predefined paths in the global config.
+func copyBootJarsToPredefinedLocations(ctx android.SingletonContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) {
+	jarPaths := make(android.Paths, bootjars.Len())
+	for i, module := range bootModules {
+		if module != nil {
+			bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
+			jarPaths[i] = bootDexJar
+		}
+	}
 
 	// The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction
 	// time, before the boot images are built (these paths are used in dexpreopt rule generation for
@@ -619,10 +564,8 @@
 
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
 func buildBootImage(ctx android.SingletonContext, image *bootImageConfig, profile android.WritablePath) *bootImageConfig {
-	getBootJarFunc := func(module android.Module) (int, android.Path) {
-		return getBootImageJar(ctx, image, module)
-	}
-	findAndCopyBootJars(ctx, image.modules, image.dexPaths, getBootJarFunc)
+	bootModules := findBootJarModules(ctx, image.modules)
+	copyBootJarsToPredefinedLocations(ctx, bootModules, image.modules, image.dexPaths)
 
 	var zipFiles android.Paths
 	for _, variant := range image.variants {
@@ -649,11 +592,8 @@
 // Generate commands that will copy updatable boot jars to predefined paths in the global config.
 func copyUpdatableBootJars(ctx android.SingletonContext) {
 	config := GetUpdatableBootConfig(ctx)
-	getBootJarFunc := func(module android.Module) (int, android.Path) {
-		index, jar, _ := getBootJar(ctx, config.modules, module, "configured in updatable boot jars ")
-		return index, jar
-	}
-	findAndCopyBootJars(ctx, config.modules, config.dexPaths, getBootJarFunc)
+	bootModules := findBootJarModules(ctx, config.modules)
+	copyBootJarsToPredefinedLocations(ctx, bootModules, config.modules, config.dexPaths)
 }
 
 // Generate boot image build rules for a specific target.
@@ -887,32 +827,9 @@
 	return profile
 }
 
-func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConfig) android.WritablePath {
-	if ctx.Config().UnbundledBuild() {
-		return nil
-	}
-
-	global := dexpreopt.GetGlobalConfig(ctx)
-	var modules []android.Module
-	updatableModules := global.UpdatableBootJars.CopyOfJars()
-	ctx.VisitAllModules(func(module android.Module) {
-		if !isActiveModule(module) {
-			return
-		}
-		name := ctx.ModuleName(module)
-		if i := android.IndexList(name, updatableModules); i != -1 {
-			modules = append(modules, module)
-			// Do not match the same library repeatedly.
-			updatableModules = append(updatableModules[:i], updatableModules[i+1:]...)
-		}
-	})
-
-	return generateUpdatableBcpPackagesRule(ctx, image, modules)
-}
-
 // generateUpdatableBcpPackagesRule generates the rule to create the updatable-bcp-packages.txt file
 // and returns a path to the generated file.
-func generateUpdatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConfig, updatableModules []android.Module) android.WritablePath {
+func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImageConfig, updatableModules []android.Module) android.WritablePath {
 	// Collect `permitted_packages` for updatable boot jars.
 	var updatablePackages []string
 	for _, module := range updatableModules {
@@ -921,7 +838,7 @@
 			if len(pp) > 0 {
 				updatablePackages = append(updatablePackages, pp...)
 			} else {
-				ctx.Errorf("Missing permitted_packages for %s", ctx.ModuleName(module))
+				ctx.ModuleErrorf("Missing permitted_packages")
 			}
 		}
 	}
@@ -944,7 +861,7 @@
 	return updatableBcpPackages
 }
 
-func dumpOatRules(ctx android.SingletonContext, image *bootImageConfig) {
+func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
 	var allPhonies android.Paths
 	for _, image := range image.variants {
 		arch := image.target.Arch.ArchType
@@ -985,7 +902,6 @@
 		Inputs:      allPhonies,
 		Description: "dump-oat-boot",
 	})
-
 }
 
 func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 2676f3d..90d9896 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -283,6 +283,8 @@
 	if Bool(d.properties.Annotations_enabled) {
 		cmd.Flag("--include-annotations")
 
+		cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi")
+
 		validatingNullability :=
 			strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
 				String(d.properties.Validate_nullability_from_list) != ""
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index bc3b474..a34044f 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -121,13 +121,6 @@
 
 var _ hiddenAPIIntf = (*hiddenAPI)(nil)
 
-// hiddenAPISupportingModule is the interface that is implemented by any module that supports
-// contributing to the hidden API processing.
-type hiddenAPISupportingModule interface {
-	android.Module
-	hiddenAPIIntf
-}
-
 // Initialize the hiddenapi structure
 func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) {
 	// If hiddenapi processing is disabled treat this as inactive.
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 793d63a..335f5b8 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -127,7 +127,7 @@
 		tag := ctx.OtherModuleDependencyTag(module)
 		if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
 			kind := hiddenAPIStubsTag.sdkKind
-			dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module)
+			dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind)
 			if dexJar != nil {
 				m[kind] = append(m[kind], dexJar)
 			}
@@ -138,17 +138,21 @@
 
 // hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if
 // available, or reports an error.
-func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module) android.Path {
-	if j, ok := module.(UsesLibraryDependency); ok {
-		dexJar := j.DexJarBuildPath()
-		if dexJar != nil {
-			return dexJar
-		}
-		ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module)
+func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path {
+	var dexJar android.Path
+	if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
+		dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
+	} else if j, ok := module.(UsesLibraryDependency); ok {
+		dexJar = j.DexJarBuildPath()
 	} else {
 		ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module))
+		return nil
 	}
-	return nil
+
+	if dexJar == nil {
+		ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module)
+	}
+	return dexJar
 }
 
 var sdkKindToHiddenapiListOption = map[android.SdkKind]string{
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 3cc88e6..f6af501 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -149,19 +149,9 @@
 	}
 }
 
-// Export paths to Make.  INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
-func (h *hiddenAPISingleton) MakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		return
-	}
-
-	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
-}
-
 // Checks to see whether the supplied module variant is in the list of boot jars.
 //
-// This is similar to logic in getBootImageJar() so any changes needed here are likely to be needed
-// there too.
+// Apart from the context this is identical to isModuleInConfiguredListForSingleton.
 //
 // TODO(b/179354495): Avoid having to perform this type of check or if necessary dedup it.
 func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool {
@@ -178,7 +168,7 @@
 
 	// It is an error if the module is not an ApexModule.
 	if _, ok := module.(android.ApexModule); !ok {
-		ctx.ModuleErrorf("is configured in boot jars but does not support being added to an apex")
+		ctx.ModuleErrorf("%s is configured in boot jars but does not support being added to an apex", ctx.OtherModuleName(module))
 		return false
 	}
 
@@ -186,7 +176,7 @@
 
 	// Now match the apex part of the boot image configuration.
 	requiredApex := configuredBootJars.Apex(index)
-	if requiredApex == "platform" {
+	if requiredApex == "platform" || requiredApex == "system_ext" {
 		if len(apexInfo.InApexes) != 0 {
 			// A platform variant is required but this is for an apex so ignore it.
 			return false
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 6503eca..4cb02e3 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -26,14 +26,19 @@
 }
 
 func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory)
+	ctx.RegisterSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
 }
 
-// The tag used for the dependency between the platform bootclasspath and any configured boot jars.
-var platformBootclasspathModuleDepTag = bootclasspathDependencyTag{name: "module"}
+// The tags used for the dependencies between the platform bootclasspath and any configured boot
+// jars.
+var (
+	platformBootclasspathArtBootJarDepTag          = bootclasspathDependencyTag{name: "art-boot-jar"}
+	platformBootclasspathNonUpdatableBootJarDepTag = bootclasspathDependencyTag{name: "non-updatable-boot-jar"}
+	platformBootclasspathUpdatableBootJarDepTag    = bootclasspathDependencyTag{name: "updatable-boot-jar"}
+)
 
 type platformBootclasspathModule struct {
-	android.ModuleBase
+	android.SingletonModuleBase
 	ClasspathFragmentBase
 
 	properties platformBootclasspathProperties
@@ -64,7 +69,7 @@
 	Hidden_api HiddenAPIFlagFileProperties
 }
 
-func platformBootclasspathFactory() android.Module {
+func platformBootclasspathFactory() android.SingletonModule {
 	m := &platformBootclasspathModule{}
 	m.AddProperties(&m.properties)
 	// TODO(satayev): split systemserver and apex jars into separate configs.
@@ -125,41 +130,64 @@
 func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies on all the modules configured in the "art" boot image.
 	artImageConfig := genBootImageConfigs(ctx)[artBootImageName]
-	addDependenciesOntoBootImageModules(ctx, artImageConfig.modules)
+	addDependenciesOntoBootImageModules(ctx, artImageConfig.modules, platformBootclasspathArtBootJarDepTag)
 
-	// Add dependencies on all the modules configured in the "boot" boot image. That does not
-	// include modules configured in the "art" boot image.
+	// Add dependencies on all the non-updatable module configured in the "boot" boot image. That does
+	// not include modules configured in the "art" boot image.
 	bootImageConfig := b.getImageConfig(ctx)
-	addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules)
+	addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathNonUpdatableBootJarDepTag)
 
 	// Add dependencies on all the updatable modules.
 	updatableModules := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
-	addDependenciesOntoBootImageModules(ctx, updatableModules)
+	addDependenciesOntoBootImageModules(ctx, updatableModules, platformBootclasspathUpdatableBootJarDepTag)
 
 	// Add dependencies on all the fragments.
 	b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx)
 }
 
-func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList) {
+func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tag bootclasspathDependencyTag) {
 	for i := 0; i < modules.Len(); i++ {
 		apex := modules.Apex(i)
 		name := modules.Jar(i)
 
-		addDependencyOntoApexModulePair(ctx, apex, name, platformBootclasspathModuleDepTag)
+		addDependencyOntoApexModulePair(ctx, apex, name, tag)
 	}
 }
 
+// GenerateSingletonBuildActions does nothing and must never do anything.
+//
+// This module only implements android.SingletonModule so that it can implement
+// android.SingletonMakeVarsProvider.
+func (b *platformBootclasspathModule) GenerateSingletonBuildActions(android.SingletonContext) {
+	// Keep empty
+}
+
+func (d *platformBootclasspathModule) MakeVars(ctx android.MakeVarsContext) {
+	d.generateHiddenApiMakeVars(ctx)
+}
+
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	b.classpathFragmentBase().generateAndroidBuildActions(ctx)
 
-	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
-		tag := ctx.OtherModuleDependencyTag(module)
-		if tag == platformBootclasspathModuleDepTag {
-			b.configuredModules = append(b.configuredModules, module)
-		} else if tag == bootclasspathFragmentDepTag {
-			b.fragments = append(b.fragments, module)
-		}
-	})
+	// Gather all the dependencies from the art, updatable and non-updatable boot jars.
+	artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag)
+	nonUpdatableModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathNonUpdatableBootJarDepTag)
+	updatableModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathUpdatableBootJarDepTag)
+
+	// Concatenate them all, in order as they would appear on the bootclasspath.
+	var allModules []android.Module
+	allModules = append(allModules, artModules...)
+	allModules = append(allModules, nonUpdatableModules...)
+	allModules = append(allModules, updatableModules...)
+	b.configuredModules = allModules
+
+	// Gather all the fragments dependencies.
+	b.fragments = gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
+
+	// Check the configuration of the boot modules.
+	// ART modules are checked by the art-bootclasspath-fragment.
+	b.checkNonUpdatableModules(ctx, nonUpdatableModules)
+	b.checkUpdatableModules(ctx, updatableModules)
 
 	b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 
@@ -168,13 +196,60 @@
 		return
 	}
 
-	b.generateBootImageBuildActions(ctx)
+	b.generateBootImageBuildActions(ctx, updatableModules)
+}
+
+// checkNonUpdatableModules ensures that the non-updatable modules supplied are not part of an
+// updatable module.
+func (b *platformBootclasspathModule) checkNonUpdatableModules(ctx android.ModuleContext, modules []android.Module) {
+	for _, m := range modules {
+		apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
+		fromUpdatableApex := apexInfo.Updatable
+		if fromUpdatableApex {
+			// error: this jar is part of an updatable apex
+			ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the framework boot image", ctx.OtherModuleName(m), apexInfo.InApexes)
+		} else {
+			// ok: this jar is part of the platform or a non-updatable apex
+		}
+	}
+}
+
+// checkUpdatableModules ensures that the updatable modules supplied are not from the platform.
+func (b *platformBootclasspathModule) checkUpdatableModules(ctx android.ModuleContext, modules []android.Module) {
+	for _, m := range modules {
+		apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
+		fromUpdatableApex := apexInfo.Updatable
+		if fromUpdatableApex {
+			// ok: this jar is part of an updatable apex
+		} else {
+			name := ctx.OtherModuleName(m)
+			if apexInfo.IsForPlatform() {
+				// error: this jar is part of the platform
+				ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name)
+			} else {
+				// TODO(b/177892522): Treat this as an error.
+				// Cannot do that at the moment because framework-wifi and framework-tethering are in the
+				// PRODUCT_UPDATABLE_BOOT_JARS but not marked as updatable in AOSP.
+			}
+		}
+	}
 }
 
 func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
 	return defaultBootImageConfig(ctx)
 }
 
+// hiddenAPISupportingModule encapsulates the information provided by any module that contributes to
+// the hidden API processing.
+type hiddenAPISupportingModule struct {
+	module android.Module
+
+	bootDexJar  android.Path
+	flagsCSV    android.Path
+	indexCSV    android.Path
+	metadataCSV android.Path
+}
+
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) {
 
@@ -197,27 +272,55 @@
 		return
 	}
 
+	// nilPathHandler will check the supplied path and if it is nil then it will either immediately
+	// report an error, or it will defer the error reporting until it is actually used, depending
+	// whether missing dependencies are allowed.
+	var nilPathHandler func(path android.Path, name string, module android.Module) android.Path
+	if ctx.Config().AllowMissingDependencies() {
+		nilPathHandler = func(path android.Path, name string, module android.Module) android.Path {
+			if path == nil {
+				outputPath := android.PathForModuleOut(ctx, "missing", module.Name(), name)
+				path = outputPath
+
+				// Create an error rule that pretends to create the output file but will actually fail if it
+				// is run.
+				ctx.Build(pctx, android.BuildParams{
+					Rule:   android.ErrorRule,
+					Output: outputPath,
+					Args: map[string]string{
+						"error": fmt.Sprintf("missing hidden API file: %s for %s", name, module),
+					},
+				})
+			}
+			return path
+		}
+	} else {
+		nilPathHandler = func(path android.Path, name string, module android.Module) android.Path {
+			if path == nil {
+				ctx.ModuleErrorf("module %s does not provide a %s file", module, name)
+			}
+			return path
+		}
+	}
+
 	hiddenAPISupportingModules := []hiddenAPISupportingModule{}
 	for _, module := range modules {
-		if h, ok := module.(hiddenAPISupportingModule); ok {
-			if h.bootDexJar() == nil {
-				ctx.ModuleErrorf("module %s does not provide a bootDexJar file", module)
-			}
-			if h.flagsCSV() == nil {
-				ctx.ModuleErrorf("module %s does not provide a flagsCSV file", module)
-			}
-			if h.indexCSV() == nil {
-				ctx.ModuleErrorf("module %s does not provide an indexCSV file", module)
-			}
-			if h.metadataCSV() == nil {
-				ctx.ModuleErrorf("module %s does not provide a metadataCSV file", module)
+		if h, ok := module.(hiddenAPIIntf); ok {
+			hiddenAPISupportingModule := hiddenAPISupportingModule{
+				module:      module,
+				bootDexJar:  nilPathHandler(h.bootDexJar(), "bootDexJar", module),
+				flagsCSV:    nilPathHandler(h.flagsCSV(), "flagsCSV", module),
+				indexCSV:    nilPathHandler(h.indexCSV(), "indexCSV", module),
+				metadataCSV: nilPathHandler(h.metadataCSV(), "metadataCSV", module),
 			}
 
+			// If any errors were reported when trying to populate the hiddenAPISupportingModule struct
+			// then don't add it to the list.
 			if ctx.Failed() {
 				continue
 			}
 
-			hiddenAPISupportingModules = append(hiddenAPISupportingModules, h)
+			hiddenAPISupportingModules = append(hiddenAPISupportingModules, hiddenAPISupportingModule)
 		} else {
 			ctx.ModuleErrorf("module %s of type %s does not support hidden API processing", module, ctx.OtherModuleType(module))
 		}
@@ -225,7 +328,7 @@
 
 	moduleSpecificFlagsPaths := android.Paths{}
 	for _, module := range hiddenAPISupportingModules {
-		moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV())
+		moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV)
 	}
 
 	flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
@@ -251,7 +354,7 @@
 func (b *platformBootclasspathModule) generateHiddenAPIStubFlagsRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
 	bootDexJars := android.Paths{}
 	for _, module := range modules {
-		bootDexJars = append(bootDexJars, module.bootDexJar())
+		bootDexJars = append(bootDexJars, module.bootDexJar)
 	}
 
 	sdkKindToStubPaths := hiddenAPIGatherStubLibDexJarPaths(ctx)
@@ -264,7 +367,7 @@
 func (b *platformBootclasspathModule) generateHiddenAPIIndexRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
 	indexes := android.Paths{}
 	for _, module := range modules {
-		indexes = append(indexes, module.indexCSV())
+		indexes = append(indexes, module.indexCSV)
 	}
 
 	rule := android.NewRuleBuilder(pctx, ctx)
@@ -280,7 +383,7 @@
 func (b *platformBootclasspathModule) generatedHiddenAPIMetadataRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
 	metadataCSVFiles := android.Paths{}
 	for _, module := range modules {
-		metadataCSVFiles = append(metadataCSVFiles, module.metadataCSV())
+		metadataCSVFiles = append(metadataCSVFiles, module.metadataCSV)
 	}
 
 	rule := android.NewRuleBuilder(pctx, ctx)
@@ -296,8 +399,18 @@
 	rule.Build("platform-bootclasspath-monolithic-hiddenapi-metadata", "monolithic hidden API metadata")
 }
 
+// generateHiddenApiMakeVars generates make variables needed by hidden API related make rules, e.g.
+// veridex and run-appcompat.
+func (b *platformBootclasspathModule) generateHiddenApiMakeVars(ctx android.MakeVarsContext) {
+	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+		return
+	}
+	// INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
+	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", b.hiddenAPIFlagsCSV.String())
+}
+
 // generateBootImageBuildActions generates ninja rules related to the boot image creation.
-func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext) {
+func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, updatableModules []android.Module) {
 	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
 	// GenerateSingletonBuildActions method as it cannot create it for itself.
 	dexpreopt.GetGlobalSoongConfig(ctx)
@@ -314,4 +427,9 @@
 
 	// Generate the framework profile rule
 	bootFrameworkProfileRule(ctx, imageConfig)
+
+	// Generate the updatable bootclasspath packages rule.
+	generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules)
+
+	dumpOatRules(ctx, imageConfig)
 }
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 955e387..2216b11 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -33,7 +33,7 @@
 func TestPlatformBootclasspath(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
-		FixtureConfigureBootJars("platform:foo", "platform:bar"),
+		FixtureConfigureBootJars("platform:foo", "system_ext:bar"),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
@@ -45,6 +45,7 @@
 				system_modules: "none",
 				sdk_version: "none",
 				compile_dex: true,
+				system_ext_specific: true,
 			}
 		`),
 	)
diff --git a/rust/rust.go b/rust/rust.go
index 74d2828..bb97142 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -262,10 +262,6 @@
 	return false
 }
 
-func (m *Module) IsLlndkLibrary() bool {
-	return false
-}
-
 func (mod *Module) KernelHeadersDecorator() bool {
 	return false
 }
@@ -274,6 +270,10 @@
 	return false
 }
 
+func (m *Module) NeedsVendorPublicLibraryVariants() bool {
+	return false
+}
+
 func (mod *Module) SdkVersion() string {
 	return ""
 }
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 5658f16..0f2fd54 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -68,6 +68,7 @@
 			bootclasspath_fragment {
 				name: "mybootclasspathfragment",
 				image_name: "art",
+				contents: ["mybootlib"],
 				apex_available: ["com.android.art"],
 			}
 
@@ -165,15 +166,25 @@
 func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
+		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("mysdklibrary", "mycoreplatform"),
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
 				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_sdk_libs: ["mysdklibrary", "mycoreplatform"],
 			}
 
 			bootclasspath_fragment {
 				name: "mybootclasspathfragment",
 				contents: ["mybootlib"],
+				api: {
+					stub_libs: ["mysdklibrary"],
+				},
+				core_platform_api: {
+					stub_libs: ["mycoreplatform"],
+				},
 			}
 
 			java_library {
@@ -183,6 +194,20 @@
 				sdk_version: "none",
 				compile_dex: true,
 			}
+
+			java_sdk_library {
+				name: "mysdklibrary",
+				srcs: ["Test.java"],
+				compile_dex: true,
+				public: {enabled: true},
+			}
+
+			java_sdk_library {
+				name: "mycoreplatform",
+				srcs: ["Test.java"],
+				compile_dex: true,
+				public: {enabled: true},
+			}
 		`),
 	).RunTest(t)
 
@@ -196,6 +221,12 @@
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     contents: ["mybootlib"],
+    api: {
+        stub_libs: ["mysdklibrary"],
+    },
+    core_platform_api: {
+        stub_libs: ["mycoreplatform"],
+    },
 }
 
 java_import {
@@ -205,6 +236,38 @@
     apex_available: ["//apex_available:platform"],
     jars: ["java/mybootlib.jar"],
 }
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: true,
+    compile_dex: true,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_sdk_library_import {
+    name: "mycoreplatform",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: true,
+    compile_dex: true,
+    public: {
+        jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
+        current_api: "sdk_library/public/mycoreplatform.txt",
+        removed_api: "sdk_library/public/mycoreplatform-removed.txt",
+        sdk_version: "current",
+    },
+}
 `),
 		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
@@ -215,6 +278,12 @@
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     contents: ["mysdk_mybootlib@current"],
+    api: {
+        stub_libs: ["mysdk_mysdklibrary@current"],
+    },
+    core_platform_api: {
+        stub_libs: ["mysdk_mycoreplatform@current"],
+    },
 }
 
 java_import {
@@ -225,15 +294,57 @@
     jars: ["java/mybootlib.jar"],
 }
 
+java_sdk_library_import {
+    name: "mysdk_mysdklibrary@current",
+    sdk_member_name: "mysdklibrary",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: true,
+    compile_dex: true,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_sdk_library_import {
+    name: "mysdk_mycoreplatform@current",
+    sdk_member_name: "mycoreplatform",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: true,
+    compile_dex: true,
+    public: {
+        jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
+        current_api: "sdk_library/public/mycoreplatform.txt",
+        removed_api: "sdk_library/public/mycoreplatform-removed.txt",
+        sdk_version: "current",
+    },
+}
+
 sdk_snapshot {
     name: "mysdk@current",
     visibility: ["//visibility:public"],
     bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
     java_boot_libs: ["mysdk_mybootlib@current"],
+    java_sdk_libs: [
+        "mysdk_mysdklibrary@current",
+        "mysdk_mycoreplatform@current",
+    ],
 }
 `),
 		checkAllCopyRules(`
 .intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
+.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
 `))
 }
 
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 6d35f9c..f935f06 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -52,12 +52,9 @@
 			system_shared_libs: [],
 			recovery_available: true,
 			host_supported: true,
-			llndk_stubs: "liblog.llndk",
-		}
-
-		llndk_library {
-			name: "liblog.llndk",
-			symbol_file: "",
+			llndk: {
+				symbol_file: "liblog.map.txt",
+			}
 		}
 
 		java_library {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 6ba497c..19b5690 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -144,7 +144,8 @@
 		productOut("odm"),
 		productOut("odm_dlkm"),
 		productOut("sysloader"),
-		productOut("testcases"))
+		productOut("testcases"),
+		productOut("symbols"))
 }
 
 // Since products and build variants (unfortunately) shared the same