Merge "Rename fields in dexpreopt config"
diff --git a/android/apex.go b/android/apex.go
index 25a07b8..60da45b 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -847,7 +847,6 @@
 	"libprocpartition":                                         30,
 	"libprotobuf-java-lite":                                    30,
 	"libprotoutil":                                             30,
-	"libqemu_pipe":                                             30,
 	"libsync":                                                  30,
 	"libtextclassifier_hash_headers":                           30,
 	"libtextclassifier_hash_static":                            30,
diff --git a/android/arch.go b/android/arch.go
index d7b12bc..c1b2c33 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -665,7 +665,7 @@
 	}
 
 	// only the primary arch in the ramdisk / vendor_ramdisk / recovery partition
-	if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk()) {
+	if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk() || module.InstallInDebugRamdisk()) {
 		osTargets = []Target{osTargets[0]}
 	}
 
@@ -1014,35 +1014,19 @@
 	base.customizableProperties = m.GetProperties()
 }
 
-// appendProperties squashes properties from the given field of the given src property struct
-// into the dst property struct.  Returns the reflect.Value of the field in the src property
-// struct to be used for further appendProperties calls on fields of that property struct.
-func (m *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
-	dst interface{}, src reflect.Value, field, srcPrefix string) reflect.Value {
-
-	// Step into non-nil pointers to structs in the src value.
-	if src.Kind() == reflect.Ptr {
-		if src.IsNil() {
-			return src
-		}
-		src = src.Elem()
-	}
-
-	// Find the requested field in the src struct.
-	src = src.FieldByName(field)
-	if !src.IsValid() {
-		ctx.ModuleErrorf("field %q does not exist", srcPrefix)
-		return src
-	}
-
-	// Save the value of the field in the src struct to return.
-	ret := src
-
+func maybeBlueprintEmbed(src reflect.Value) reflect.Value {
 	// If the value of the field is a struct (as opposed to a pointer to a struct) then step
 	// into the BlueprintEmbed field.
 	if src.Kind() == reflect.Struct {
-		src = src.FieldByName("BlueprintEmbed")
+		return src.FieldByName("BlueprintEmbed")
+	} else {
+		return src
 	}
+}
+
+// Merges the property struct in srcValue into dst.
+func mergePropertyStruct(ctx BaseMutatorContext, dst interface{}, srcValue reflect.Value) {
+	src := maybeBlueprintEmbed(srcValue).Interface()
 
 	// order checks the `android:"variant_prepend"` tag to handle properties where the
 	// arch-specific value needs to come before the generic value, for example for lists of
@@ -1058,7 +1042,7 @@
 	}
 
 	// Squash the located property struct into the destination property struct.
-	err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, order)
+	err := proptools.ExtendMatchingProperties([]interface{}{dst}, src, nil, order)
 	if err != nil {
 		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
 			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -1066,8 +1050,29 @@
 			panic(err)
 		}
 	}
+}
 
-	return ret
+// Returns the immediate child of the input property struct that corresponds to
+// the sub-property "field".
+func getChildPropertyStruct(ctx BaseMutatorContext,
+	src reflect.Value, field, userFriendlyField string) reflect.Value {
+
+	// Step into non-nil pointers to structs in the src value.
+	if src.Kind() == reflect.Ptr {
+		if src.IsNil() {
+			return src
+		}
+		src = src.Elem()
+	}
+
+	// Find the requested field in the src struct.
+	src = src.FieldByName(proptools.FieldNameForProperty(field))
+	if !src.IsValid() {
+		ctx.ModuleErrorf("field %q does not exist", userFriendlyField)
+		return src
+	}
+
+	return src
 }
 
 // Squash the appropriate OS-specific property structs into the matching top level property structs
@@ -1094,7 +1099,8 @@
 			if os.Class == Host {
 				field := "Host"
 				prefix := "target.host"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				hostProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, hostProperties)
 			}
 
 			// Handle target OS generalities of the form:
@@ -1106,13 +1112,15 @@
 			if os.Linux() {
 				field := "Linux"
 				prefix := "target.linux"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				linuxProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, linuxProperties)
 			}
 
 			if os.Bionic() {
 				field := "Bionic"
 				prefix := "target.bionic"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				bionicProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, bionicProperties)
 			}
 
 			// Handle target OS properties in the form:
@@ -1129,12 +1137,14 @@
 			// },
 			field := os.Field
 			prefix := "target." + os.Name
-			m.appendProperties(ctx, genProps, targetProp, field, prefix)
+			osProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+			mergePropertyStruct(ctx, genProps, osProperties)
 
 			if os.Class == Host && os != Windows {
 				field := "Not_windows"
 				prefix := "target.not_windows"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				notWindowsProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, notWindowsProperties)
 			}
 
 			// Handle 64-bit device properties in the form:
@@ -1154,17 +1164,189 @@
 				if ctx.Config().Android64() {
 					field := "Android64"
 					prefix := "target.android64"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
+					android64Properties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+					mergePropertyStruct(ctx, genProps, android64Properties)
 				} else {
 					field := "Android32"
 					prefix := "target.android32"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
+					android32Properties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+					mergePropertyStruct(ctx, genProps, android32Properties)
 				}
 			}
 		}
 	}
 }
 
+// Returns the struct containing the properties specific to the given
+// architecture type. These look like this in Blueprint files:
+// arch: {
+//     arm64: {
+//         key: value,
+//     },
+// },
+// This struct will also contain sub-structs containing to the architecture/CPU
+// variants and features that themselves contain properties specific to those.
+func getArchTypeStruct(ctx BaseMutatorContext, archProperties interface{}, archType ArchType) reflect.Value {
+	archPropValues := reflect.ValueOf(archProperties).Elem()
+	archProp := archPropValues.FieldByName("Arch").Elem()
+	prefix := "arch." + archType.Name
+	archStruct := getChildPropertyStruct(ctx, archProp, archType.Name, prefix)
+	return archStruct
+}
+
+// Returns the struct containing the properties specific to a given multilib
+// value. These look like this in the Blueprint file:
+// multilib: {
+//     lib32: {
+//         key: value,
+//     },
+// },
+func getMultilibStruct(ctx BaseMutatorContext, archProperties interface{}, archType ArchType) reflect.Value {
+	archPropValues := reflect.ValueOf(archProperties).Elem()
+	multilibProp := archPropValues.FieldByName("Multilib").Elem()
+	multilibProperties := getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
+	return multilibProperties
+}
+
+// Returns the structs corresponding to the properties specific to the given
+// architecture and OS in archProperties.
+func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch Arch, os OsType, nativeBridgeEnabled bool) []reflect.Value {
+	result := make([]reflect.Value, 0)
+	archPropValues := reflect.ValueOf(archProperties).Elem()
+
+	targetProp := archPropValues.FieldByName("Target").Elem()
+
+	archType := arch.ArchType
+
+	if arch.ArchType != Common {
+		archStruct := getArchTypeStruct(ctx, archProperties, arch.ArchType)
+		result = append(result, archStruct)
+
+		// Handle arch-variant-specific properties in the form:
+		// arch: {
+		//     arm: {
+		//         variant: {
+		//             key: value,
+		//         },
+		//     },
+		// },
+		v := variantReplacer.Replace(arch.ArchVariant)
+		if v != "" {
+			prefix := "arch." + archType.Name + "." + v
+			variantProperties := getChildPropertyStruct(ctx, archStruct, v, prefix)
+			result = append(result, variantProperties)
+		}
+
+		// Handle cpu-variant-specific properties in the form:
+		// arch: {
+		//     arm: {
+		//         variant: {
+		//             key: value,
+		//         },
+		//     },
+		// },
+		if arch.CpuVariant != arch.ArchVariant {
+			c := variantReplacer.Replace(arch.CpuVariant)
+			if c != "" {
+				prefix := "arch." + archType.Name + "." + c
+				cpuVariantProperties := getChildPropertyStruct(ctx, archStruct, c, prefix)
+				result = append(result, cpuVariantProperties)
+			}
+		}
+
+		// Handle arch-feature-specific properties in the form:
+		// arch: {
+		//     arm: {
+		//         feature: {
+		//             key: value,
+		//         },
+		//     },
+		// },
+		for _, feature := range arch.ArchFeatures {
+			prefix := "arch." + archType.Name + "." + feature
+			featureProperties := getChildPropertyStruct(ctx, archStruct, feature, prefix)
+			result = append(result, featureProperties)
+		}
+
+		multilibProperties := getMultilibStruct(ctx, archProperties, archType)
+		result = append(result, multilibProperties)
+
+		// Handle combined OS-feature and arch specific properties in the form:
+		// target: {
+		//     bionic_x86: {
+		//         key: value,
+		//     },
+		// }
+		if os.Linux() {
+			field := "Linux_" + arch.ArchType.Name
+			userFriendlyField := "target.linux_" + arch.ArchType.Name
+			linuxProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, linuxProperties)
+		}
+
+		if os.Bionic() {
+			field := "Bionic_" + archType.Name
+			userFriendlyField := "target.bionic_" + archType.Name
+			bionicProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, bionicProperties)
+		}
+
+		// Handle combined OS and arch specific properties in the form:
+		// target: {
+		//     linux_glibc_x86: {
+		//         key: value,
+		//     },
+		//     linux_glibc_arm: {
+		//         key: value,
+		//     },
+		//     android_arm {
+		//         key: value,
+		//     },
+		//     android_x86 {
+		//         key: value,
+		//     },
+		// },
+		field := os.Field + "_" + archType.Name
+		userFriendlyField := "target." + os.Name + "_" + archType.Name
+		osArchProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+		result = append(result, osArchProperties)
+	}
+
+	// Handle arm on x86 properties in the form:
+	// target {
+	//     arm_on_x86 {
+	//         key: value,
+	//     },
+	//     arm_on_x86_64 {
+	//         key: value,
+	//     },
+	// },
+	if os.Class == Device {
+		if arch.ArchType == X86 && (hasArmAbi(arch) ||
+			hasArmAndroidArch(ctx.Config().Targets[Android])) {
+			field := "Arm_on_x86"
+			userFriendlyField := "target.arm_on_x86"
+			armOnX86Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, armOnX86Properties)
+		}
+		if arch.ArchType == X86_64 && (hasArmAbi(arch) ||
+			hasArmAndroidArch(ctx.Config().Targets[Android])) {
+			field := "Arm_on_x86_64"
+			userFriendlyField := "target.arm_on_x86_64"
+			armOnX8664Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, armOnX8664Properties)
+		}
+		if os == Android && nativeBridgeEnabled {
+			userFriendlyField := "Native_bridge"
+			prefix := "target.native_bridge"
+			nativeBridgeProperties := getChildPropertyStruct(ctx, targetProp, userFriendlyField, prefix)
+			result = append(result, nativeBridgeProperties)
+		}
+	}
+
+	return result
+}
+
 // Squash the appropriate arch-specific property structs into the matching top level property
 // structs based on the CompileTarget value that was annotated on the variant.
 func (m *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
@@ -1176,144 +1358,15 @@
 		if m.archProperties[i] == nil {
 			continue
 		}
-		for _, archProperties := range m.archProperties[i] {
-			archPropValues := reflect.ValueOf(archProperties).Elem()
 
-			archProp := archPropValues.FieldByName("Arch").Elem()
-			multilibProp := archPropValues.FieldByName("Multilib").Elem()
-			targetProp := archPropValues.FieldByName("Target").Elem()
+		propStructs := make([]reflect.Value, 0)
+		for _, archProperty := range m.archProperties[i] {
+			propStructShard := getArchProperties(ctx, archProperty, arch, os, m.Target().NativeBridge == NativeBridgeEnabled)
+			propStructs = append(propStructs, propStructShard...)
+		}
 
-			// Handle arch-specific properties in the form:
-			// arch: {
-			//     arm64: {
-			//         key: value,
-			//     },
-			// },
-			t := arch.ArchType
-
-			if arch.ArchType != Common {
-				field := proptools.FieldNameForProperty(t.Name)
-				prefix := "arch." + t.Name
-				archStruct := m.appendProperties(ctx, genProps, archProp, field, prefix)
-
-				// Handle arch-variant-specific properties in the form:
-				// arch: {
-				//     variant: {
-				//         key: value,
-				//     },
-				// },
-				v := variantReplacer.Replace(arch.ArchVariant)
-				if v != "" {
-					field := proptools.FieldNameForProperty(v)
-					prefix := "arch." + t.Name + "." + v
-					m.appendProperties(ctx, genProps, archStruct, field, prefix)
-				}
-
-				// Handle cpu-variant-specific properties in the form:
-				// arch: {
-				//     variant: {
-				//         key: value,
-				//     },
-				// },
-				if arch.CpuVariant != arch.ArchVariant {
-					c := variantReplacer.Replace(arch.CpuVariant)
-					if c != "" {
-						field := proptools.FieldNameForProperty(c)
-						prefix := "arch." + t.Name + "." + c
-						m.appendProperties(ctx, genProps, archStruct, field, prefix)
-					}
-				}
-
-				// Handle arch-feature-specific properties in the form:
-				// arch: {
-				//     feature: {
-				//         key: value,
-				//     },
-				// },
-				for _, feature := range arch.ArchFeatures {
-					field := proptools.FieldNameForProperty(feature)
-					prefix := "arch." + t.Name + "." + feature
-					m.appendProperties(ctx, genProps, archStruct, field, prefix)
-				}
-
-				// Handle multilib-specific properties in the form:
-				// multilib: {
-				//     lib32: {
-				//         key: value,
-				//     },
-				// },
-				field = proptools.FieldNameForProperty(t.Multilib)
-				prefix = "multilib." + t.Multilib
-				m.appendProperties(ctx, genProps, multilibProp, field, prefix)
-			}
-
-			// Handle combined OS-feature and arch specific properties in the form:
-			// target: {
-			//     bionic_x86: {
-			//         key: value,
-			//     },
-			// }
-			if os.Linux() && arch.ArchType != Common {
-				field := "Linux_" + arch.ArchType.Name
-				prefix := "target.linux_" + arch.ArchType.Name
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			if os.Bionic() && arch.ArchType != Common {
-				field := "Bionic_" + t.Name
-				prefix := "target.bionic_" + t.Name
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			// Handle combined OS and arch specific properties in the form:
-			// target: {
-			//     linux_glibc_x86: {
-			//         key: value,
-			//     },
-			//     linux_glibc_arm: {
-			//         key: value,
-			//     },
-			//     android_arm {
-			//         key: value,
-			//     },
-			//     android_x86 {
-			//         key: value,
-			//     },
-			// },
-			if arch.ArchType != Common {
-				field := os.Field + "_" + t.Name
-				prefix := "target." + os.Name + "_" + t.Name
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			// Handle arm on x86 properties in the form:
-			// target {
-			//     arm_on_x86 {
-			//         key: value,
-			//     },
-			//     arm_on_x86_64 {
-			//         key: value,
-			//     },
-			// },
-			if os.Class == Device {
-				if arch.ArchType == X86 && (hasArmAbi(arch) ||
-					hasArmAndroidArch(ctx.Config().Targets[Android])) {
-					field := "Arm_on_x86"
-					prefix := "target.arm_on_x86"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
-				if arch.ArchType == X86_64 && (hasArmAbi(arch) ||
-					hasArmAndroidArch(ctx.Config().Targets[Android])) {
-					field := "Arm_on_x86_64"
-					prefix := "target.arm_on_x86_64"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
-				if os == Android && m.Target().NativeBridge == NativeBridgeEnabled {
-					field := "Native_bridge"
-					prefix := "target.native_bridge"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
-			}
+		for _, propStruct := range propStructs {
+			mergePropertyStruct(ctx, genProps, propStruct)
 		}
 	}
 }
@@ -1673,13 +1726,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(ctx BaseMutatorContext, propertySet interface{}) map[ArchType]interface{} {
 	// Return value of the arch types to the prop values for that arch.
 	archToProp := map[ArchType]interface{}{}
 
@@ -1688,82 +1872,47 @@
 		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
-		}
+	dstType := reflect.ValueOf(propertySet).Type()
+	var archProperties []interface{}
 
-		// 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
-			}
+	// First find the property set in the module that corresponds to the requested
+	// one. m.archProperties[i] corresponds to m.generalProperties[i].
+	for i, generalProp := range m.generalProperties {
+		srcType := reflect.ValueOf(generalProp).Type()
+		if srcType == dstType {
+			archProperties = m.archProperties[i]
+			break
 		}
 	}
+
+	if archProperties == nil {
+		// This module does not have the property set requested
+		return archToProp
+	}
+
+	// For each arch type (x86, arm64, etc.)
+	for _, arch := range ArchTypeList() {
+		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
+		// Iterate over ever shard and extract a struct with the same type as the
+		// input one that contains the data specific to that arch.
+		propertyStructs := make([]reflect.Value, 0)
+		for _, archProperty := range archProperties {
+			archTypeStruct := getArchTypeStruct(ctx, archProperty, arch)
+			multilibStruct := getMultilibStruct(ctx, archProperty, arch)
+			propertyStructs = append(propertyStructs, archTypeStruct, multilibStruct)
+		}
+
+		// Create a new instance of the requested property set
+		value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+
+		// Merge all the structs together
+		for _, propertyStruct := range propertyStructs {
+			mergePropertyStruct(ctx, value, propertyStruct)
+		}
+
+		archToProp[arch] = value
+	}
+
 	return archToProp
 }
 
diff --git a/android/bazel.go b/android/bazel.go
index ba5ae31..6c476a7 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -167,66 +167,96 @@
 		"bionic":                Bp2BuildDefaultTrueRecursively,
 		"external/gwp_asan":     Bp2BuildDefaultTrueRecursively,
 		"system/core/libcutils": Bp2BuildDefaultTrueRecursively,
-		"system/logging/liblog": Bp2BuildDefaultTrueRecursively,
+		"system/core/property_service/libpropertyinfoparser": 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{
-		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static, 'map' file not found
-		"libbionic_spawn_benchmark",     // ruperts@, cc_library_static, depends on //system/libbase
-		"libc_jemalloc_wrapper",         // ruperts@, cc_library_static, depends on //external/jemalloc_new
-		"libc_bootstrap",                // ruperts@, cc_library_static, 'private/bionic_auxv.h' file not found
-		"libc_init_static",              // ruperts@, cc_library_static, 'private/bionic_elf_tls.h' file not found
-		"libc_init_dynamic",             // ruperts@, cc_library_static, 'private/bionic_defs.h' file not found
-		"libc_tzcode",                   // ruperts@, cc_library_static, error: expected expression
-		"libc_netbsd",                   // ruperts@, cc_library_static, 'engine.c' file not found
-		"libc_fortify",                  // ruperts@, cc_library_static, 'private/bionic_fortify.h' file not found
-		"libc_bionic",                   // ruperts@, cc_library_static, 'private/bionic_asm.h' file not found
-		"libc_bionic_ndk",               // ruperts@, cc_library_static, depends on //bionic/libc/system_properties
-		"libc_bionic_systrace",          // ruperts@, cc_library_static, 'private/bionic_systrace.h' file not found
-		"libc_pthread",                  // ruperts@, cc_library_static, 'private/bionic_defs.h' file not found
-		"libc_syscalls",                 // eakammer@, cc_library_static,  'private/bionic_asm.h' file not found
-		"libc_ndk",                      // ruperts@, cc_library_static, depends on //bionic/libm:libm
-		"libc_nopthread",                // ruperts@, cc_library_static, depends on //external/arm-optimized-routines
-		"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_unwind_static",            // ruperts@, cc_library_static, 'private/bionic_elf_tls.h' file not found
-		"libc_nomalloc",                 // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libasync_safe",                 // ruperts@, cc_library_static, 'private/CachedProperty.h' file not found
-		"libc_malloc_debug_backtrace",   // ruperts@, cc_library_static, depends on //system/libbase
-		"libsystemproperties",           // ruperts@, cc_library_static, depends on //system/core/property_service/libpropertyinfoparser
-		"libdl_static",                  // ruperts@, cc_library_static, 'private/CFIShadow.h' file not found
-		"liblinker_main",                // ruperts@, cc_library_static, depends on //system/libbase
-		"liblinker_malloc",              // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
-		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
-		"libbionic_tests_headers_posix", // ruperts@, cc_library_static, 'complex.h' file not found
-		"libc_dns",                      // ruperts@, cc_library_static, 'private/android_filesystem_config.h' file not found
-		"libc_static_dispatch",          // eakammer@, cc_library_static, 'private/bionic_asm.h' file not found
-		"libc_dynamic_dispatch",         // eakammer@, cc_library_static, 'private/bionic_ifuncs.h' file not found
-		"note_memtag_heap_async",        // jingwen@, cc_library_static, 'private/bionic_asm.h' file not found (arm64)
-		"note_memtag_heap_sync",         // jingwen@, cc_library_static, 'private/bionic_asm.h' file not found (arm64)
+		// 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)
 
-		// List of all full_cc_libraries in //bionic, with their immediate failures
-		"libc",              // jingwen@, cc_library, depends on //external/gwp_asan
-		"libc_malloc_debug", // jingwen@, cc_library, fatal error: 'assert.h' file not found
-		"libc_malloc_hooks", // jingwen@, cc_library, fatal error: 'errno.h' file not found
-		"libdl",             // jingwen@, cc_library, ld.lld: error: no input files
-		"libm",              // lberki@, cc_library, compiler error: "Unexpected token in argument list"
-		"libseccomp_policy", // lberki@, cc_library, 'linux/filter.h' not found, caused by missing -isystem bionic/libc/kernel/uapi, dunno where it comes from in Soong
-		"libstdc++",         // jingwen@, cc_library, depends on //external/gwp_asan
+		"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)
+
+		"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)
+
+		"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)
+
+		"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", // http://b/187012490, cc_library_static, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
+		"libc_ndk",              // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
+		"libc",                  // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
+		"libc_tzcode",           // http://b/186822591, cc_library_static, localtime.c:84:46: error: expected expression
+		"libc_bionic_ndk",       // http://b/186822256, cc_library_static, signal.cpp:186:52: error: ISO C++ requires field designators to be specified in declaration order
+		"libc_malloc_hooks",     // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
+		"libm",                  // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list
+
+		// http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx
+		// c++_static
+		"libbase_ndk", // http://b/186826477, cc_library, no such target '//build/bazel/platforms/os:darwin' when --platforms //build/bazel/platforms:android_x86 is added
+		// 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
+
+		// 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 of cc_library modules to only generate the static
+	// variant if their shared variant isn't ready or buildable by Bazel.
+	bp2buildCcLibraryStaticOnlyList = []string{
+		"libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno
 	}
 
 	// 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_openbsd", // ruperts@, cc_library_static, OK for bp2build but error: duplicate symbol: strcpy 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
+		"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)
+		"fmtlib_ndk",                       // http://b/187040371, cc_library_static, OK for bp2build but format-inl.h:11:10: fatal error: 'cassert' file not found for mixed builds
 	}
 
 	// Used for quicker lookups
 	bp2buildDoNotWriteBuildFile = map[string]bool{}
 	bp2buildModuleDoNotConvert  = map[string]bool{}
+	bp2buildCcLibraryStaticOnly = map[string]bool{}
 	mixedBuildsDisabled         = map[string]bool{}
 )
 
@@ -239,11 +269,19 @@
 		bp2buildModuleDoNotConvert[moduleName] = true
 	}
 
+	for _, moduleName := range bp2buildCcLibraryStaticOnlyList {
+		bp2buildCcLibraryStaticOnly[moduleName] = true
+	}
+
 	for _, moduleName := range mixedBuildsDisabledList {
 		mixedBuildsDisabled[moduleName] = true
 	}
 }
 
+func GenerateCcLibraryStaticOnly(ctx BazelConversionPathContext) bool {
+	return bp2buildCcLibraryStaticOnly[ctx.Module().Name()]
+}
+
 func ShouldWriteBuildFileForDir(dir string) bool {
 	if _, ok := bp2buildDoNotWriteBuildFile[dir]; ok {
 		return false
@@ -261,6 +299,12 @@
 	if len(b.GetBazelLabel(ctx, ctx.Module())) == 0 {
 		return false
 	}
+	if GenerateCcLibraryStaticOnly(ctx) {
+		// Don't use partially-converted cc_library targets in mixed builds,
+		// since mixed builds would generally rely on both static and shared
+		// variants of a cc_library.
+		return false
+	}
 	return !mixedBuildsDisabled[ctx.Module().Name()]
 }
 
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 63e2c50..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
@@ -243,6 +247,14 @@
 	labels := bazel.LabelList{
 		Includes: []bazel.Label{},
 	}
+
+	// expandedExcludes contain module-dir relative paths, but root-relative paths
+	// are needed for GlobFiles later.
+	var rootRelativeExpandedExcludes []string
+	for _, e := range expandedExcludes {
+		rootRelativeExpandedExcludes = append(rootRelativeExpandedExcludes, filepath.Join(ctx.ModuleDir(), e))
+	}
+
 	for _, p := range paths {
 		if m, tag := SrcIsModuleWithTag(p); m != "" {
 			l := getOtherModuleLabel(ctx, m, tag)
@@ -253,7 +265,10 @@
 		} else {
 			var expandedPaths []bazel.Label
 			if pathtools.IsGlob(p) {
-				globbedPaths := GlobFiles(ctx, pathForModuleSrc(ctx, p).String(), expandedExcludes)
+				// e.g. turn "math/*.c" in
+				// external/arm-optimized-routines to external/arm-optimized-routines/math/*.c
+				rootRelativeGlobPath := pathForModuleSrc(ctx, p).String()
+				globbedPaths := GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes)
 				globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "")
 				for _, path := range globbedPaths {
 					s := path.Rel()
diff --git a/android/image.go b/android/image.go
index 1a1a423..66101be 100644
--- a/android/image.go
+++ b/android/image.go
@@ -30,6 +30,10 @@
 	// vendor ramdisk partition).
 	VendorRamdiskVariantNeeded(ctx BaseModuleContext) bool
 
+	// DebugRamdiskVariantNeeded should return true if the module needs a debug ramdisk variant (installed on the
+	// debug ramdisk partition: $(PRODUCT_OUT)/debug_ramdisk).
+	DebugRamdiskVariantNeeded(ctx BaseModuleContext) bool
+
 	// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
 	// recovery partition).
 	RecoveryVariantNeeded(ctx BaseModuleContext) bool
@@ -60,6 +64,9 @@
 
 	// VendorRamdiskVariation means a module to be installed to vendor ramdisk image.
 	VendorRamdiskVariation string = "vendor_ramdisk"
+
+	// DebugRamdiskVariation means a module to be installed to debug ramdisk image.
+	DebugRamdiskVariation string = "debug_ramdisk"
 )
 
 // imageMutator creates variants for modules that implement the ImageInterface that
@@ -83,6 +90,9 @@
 		if m.VendorRamdiskVariantNeeded(ctx) {
 			variations = append(variations, VendorRamdiskVariation)
 		}
+		if m.DebugRamdiskVariantNeeded(ctx) {
+			variations = append(variations, DebugRamdiskVariation)
+		}
 		if m.RecoveryVariantNeeded(ctx) {
 			variations = append(variations, RecoveryVariation)
 		}
diff --git a/android/license.go b/android/license.go
index 3bc6199..cb375a2 100644
--- a/android/license.go
+++ b/android/license.go
@@ -51,6 +51,7 @@
 type licenseModule struct {
 	ModuleBase
 	DefaultableModuleBase
+	SdkBase
 
 	properties licenseProperties
 }
@@ -75,6 +76,7 @@
 	// The visibility property needs to be checked and parsed by the visibility module.
 	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
 
+	InitSdkAwareModule(module)
 	initAndroidModuleBase(module)
 	InitDefaultableModule(module)
 
diff --git a/android/module.go b/android/module.go
index 9f923e2..99606d1 100644
--- a/android/module.go
+++ b/android/module.go
@@ -213,7 +213,7 @@
 
 	// GetDirectDep returns the Module and DependencyTag for the  direct dependency with the specified
 	// name, or nil if none exists.  If there are multiple dependencies on the same module it returns
-	// the first DependencyTag.  It skips any dependencies that are not an android.Module.
+	// the first DependencyTag.
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 
 	// VisitDirectDepsBlueprint calls visit for each direct dependency.  If there are multiple
@@ -393,6 +393,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -450,6 +451,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -753,6 +755,9 @@
 	// Whether this module is installed to vendor ramdisk
 	Vendor_ramdisk *bool
 
+	// Whether this module is installed to debug ramdisk
+	Debug_ramdisk *bool
+
 	// Whether this module is built for non-native architectures (also known as native bridge binary)
 	Native_bridge_supported *bool `android:"arch_variant"`
 
@@ -1540,6 +1545,10 @@
 	return Bool(m.commonProperties.Vendor_ramdisk)
 }
 
+func (m *ModuleBase) InstallInDebugRamdisk() bool {
+	return Bool(m.commonProperties.Debug_ramdisk)
+}
+
 func (m *ModuleBase) InstallInRecovery() bool {
 	return Bool(m.commonProperties.Recovery)
 }
@@ -1593,6 +1602,10 @@
 	return m.base().commonProperties.ImageVariation == VendorRamdiskVariation
 }
 
+func (m *ModuleBase) InDebugRamdisk() bool {
+	return m.base().commonProperties.ImageVariation == DebugRamdiskVariation
+}
+
 func (m *ModuleBase) InRecovery() bool {
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
@@ -2244,11 +2257,12 @@
 	return aModule
 }
 
-func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) {
-	type dep struct {
-		mod blueprint.Module
-		tag blueprint.DependencyTag
-	}
+type dep struct {
+	mod blueprint.Module
+	tag blueprint.DependencyTag
+}
+
+func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep {
 	var deps []dep
 	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
 		if aModule, _ := module.(Module); aModule != nil {
@@ -2265,6 +2279,11 @@
 			}
 		}
 	})
+	return deps
+}
+
+func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) {
+	deps := b.getDirectDepsInternal(name, tag)
 	if len(deps) == 1 {
 		return deps[0].mod, deps[0].tag
 	} else if len(deps) >= 2 {
@@ -2275,6 +2294,25 @@
 	}
 }
 
+func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) {
+	foundDeps := b.getDirectDepsInternal(name, nil)
+	deps := map[blueprint.Module]bool{}
+	for _, dep := range foundDeps {
+		deps[dep.mod] = true
+	}
+	if len(deps) == 1 {
+		return foundDeps[0].mod, foundDeps[0].tag
+	} else if len(deps) >= 2 {
+		// this could happen if two dependencies have the same name in different namespaces
+		// TODO(b/186554727): this should not occur if namespaces are handled within
+		// getDirectDepsInternal.
+		panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
+			name, b.ModuleName()))
+	} else {
+		return nil, nil
+	}
+}
+
 func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module {
 	var deps []Module
 	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
@@ -2292,8 +2330,11 @@
 	return module
 }
 
+// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
+// name, or nil if none exists. If there are multiple dependencies on the same module it returns the
+// first DependencyTag.
 func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) {
-	return b.getDirectDepInternal(name, nil)
+	return b.getDirectDepFirstTag(name)
 }
 
 func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
@@ -2548,6 +2589,10 @@
 	return m.module.InstallInVendorRamdisk()
 }
 
+func (m *moduleContext) InstallInDebugRamdisk() bool {
+	return m.module.InstallInDebugRamdisk()
+}
+
 func (m *moduleContext) InstallInRecovery() bool {
 	return m.module.InstallInRecovery()
 }
diff --git a/android/neverallow.go b/android/neverallow.go
index a385bbc..41b399a 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -63,8 +63,7 @@
 }
 
 func createIncludeDirsRules() []Rule {
-	// The list of paths that cannot be referenced using include_dirs
-	paths := []string{
+	notInIncludeDir := []string{
 		"art",
 		"art/libnativebridge",
 		"art/libnativeloader",
@@ -80,12 +79,22 @@
 		"external/vixl",
 		"external/wycheproof",
 	}
+	noUseIncludeDir := []string{
+		"frameworks/av/apex",
+		"frameworks/av/tools",
+		"frameworks/native/cmds",
+		"system/apex",
+		"system/bpf",
+		"system/gatekeeper",
+		"system/hwservicemanager",
+		"system/libbase",
+		"system/libfmq",
+		"system/libvintf",
+	}
 
-	// Create a composite matcher that will match if the value starts with any of the restricted
-	// paths. A / is appended to the prefix to ensure that restricting path X does not affect paths
-	// XY.
-	rules := make([]Rule, 0, len(paths))
-	for _, path := range paths {
+	rules := make([]Rule, 0, len(notInIncludeDir)+len(noUseIncludeDir))
+
+	for _, path := range notInIncludeDir {
 		rule :=
 			NeverAllow().
 				WithMatcher("include_dirs", StartsWith(path+"/")).
@@ -95,6 +104,13 @@
 		rules = append(rules, rule)
 	}
 
+	for _, path := range noUseIncludeDir {
+		rule := NeverAllow().In(path+"/").WithMatcher("include_dirs", isSetMatcherInstance).
+			Because("include_dirs is deprecated, all usages of them in '" + path + "' have been migrated" +
+				" to use alternate mechanisms and so can no longer be used.")
+		rules = append(rules, rule)
+	}
+
 	return rules
 }
 
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 268346a..35aadd8 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -76,7 +76,20 @@
 		},
 	},
 	{
-		name: "include_dir can reference another location",
+		name: "include_dir not allowed to reference art",
+		fs: map[string][]byte{
+			"system/libfmq/Android.bp": []byte(`
+				cc_library {
+					name: "libother",
+					include_dirs: ["any/random/file"],
+				}`),
+		},
+		expectedErrors: []string{
+			"all usages of them in 'system/libfmq' have been migrated",
+		},
+	},
+	{
+		name: "include_dir can work",
 		fs: map[string][]byte{
 			"other/Android.bp": []byte(`
 				cc_library {
diff --git a/android/override_module.go b/android/override_module.go
index 97acc5c..0a7e294 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -244,11 +244,11 @@
 		// See if there's a prebuilt module that overrides this override module with prefer flag,
 		// in which case we call HideFromMake on the corresponding variant later.
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
-			prebuilt, ok := dep.(PrebuiltInterface)
-			if !ok {
+			prebuilt := GetEmbeddedPrebuilt(dep)
+			if prebuilt == nil {
 				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
 			}
-			if prebuilt.Prebuilt().UsePrebuilt() {
+			if prebuilt.UsePrebuilt() {
 				module.setOverriddenByPrebuilt(true)
 				return
 			}
diff --git a/android/paths.go b/android/paths.go
index 93c5684..5d458cb 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -24,6 +24,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 )
 
@@ -106,6 +107,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -448,6 +450,12 @@
 		return outputFiles, nil
 	} else if tag != "" {
 		return nil, fmt.Errorf("path dependency %q is not an output file producing module", path)
+	} else if goBinary, ok := module.(bootstrap.GoBinaryTool); ok {
+		if rel, err := filepath.Rel(PathForOutput(ctx).String(), goBinary.InstallPath()); err == nil {
+			return Paths{PathForOutput(ctx, rel).WithoutRel()}, nil
+		} else {
+			return nil, fmt.Errorf("cannot find output path for %q: %w", goBinary.InstallPath(), err)
+		}
 	} else if srcProducer, ok := module.(SourceFileProducer); ok {
 		return srcProducer.Srcs(), nil
 	} else {
@@ -1689,6 +1697,8 @@
 			if !ctx.InstallInRoot() {
 				partition += "/system"
 			}
+		} else if ctx.InstallInDebugRamdisk() {
+			partition = "debug_ramdisk"
 		} else if ctx.InstallInRecovery() {
 			if ctx.InstallInRoot() {
 				partition = "recovery/root"
@@ -1859,6 +1869,7 @@
 	inSanitizerDir  bool
 	inRamdisk       bool
 	inVendorRamdisk bool
+	inDebugRamdisk  bool
 	inRecovery      bool
 	inRoot          bool
 	forceOS         *OsType
@@ -1891,6 +1902,10 @@
 	return m.inVendorRamdisk
 }
 
+func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool {
+	return m.inDebugRamdisk
+}
+
 func (m testModuleInstallPathContext) InstallInRecovery() bool {
 	return m.inRecovery
 }
diff --git a/android/paths_test.go b/android/paths_test.go
index 6ec75b4..f8ccc77 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -395,6 +395,19 @@
 			partitionDir: "target/product/test_device/vendor_ramdisk",
 		},
 		{
+			name: "debug_ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inDebugRamdisk: true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/debug_ramdisk/my_test",
+			partitionDir: "target/product/test_device/debug_ramdisk",
+		},
+		{
 			name: "system native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 40bcdfd..43b7cbe 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -242,14 +242,30 @@
 		// A source module that has been replaced by a prebuilt counterpart.
 		return false
 	}
-	if prebuilt, ok := module.(PrebuiltInterface); ok {
-		if p := prebuilt.Prebuilt(); p != nil {
-			return p.UsePrebuilt()
-		}
+	if p := GetEmbeddedPrebuilt(module); p != nil {
+		return p.UsePrebuilt()
 	}
 	return true
 }
 
+// IsModulePrebuilt returns true if the module implements PrebuiltInterface and
+// has been initialized as a prebuilt and so returns a non-nil value from the
+// PrebuiltInterface.Prebuilt() method.
+func IsModulePrebuilt(module Module) bool {
+	return GetEmbeddedPrebuilt(module) != nil
+}
+
+// GetEmbeddedPrebuilt returns a pointer to the embedded Prebuilt structure or
+// nil if the module does not implement PrebuiltInterface or has not been
+// initialized as a prebuilt module.
+func GetEmbeddedPrebuilt(module Module) *Prebuilt {
+	if p, ok := module.(PrebuiltInterface); ok {
+		return p.Prebuilt()
+	}
+
+	return nil
+}
+
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
 }
@@ -263,11 +279,12 @@
 // PrebuiltRenameMutator ensures that there always is a module with an
 // undecorated name.
 func PrebuiltRenameMutator(ctx BottomUpMutatorContext) {
-	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+	m := ctx.Module()
+	if p := GetEmbeddedPrebuilt(m); p != nil {
 		name := m.base().BaseModuleName()
 		if !ctx.OtherModuleExists(name) {
 			ctx.Rename(name)
-			m.Prebuilt().properties.PrebuiltRenamedToSource = true
+			p.properties.PrebuiltRenamedToSource = true
 		}
 	}
 }
@@ -275,14 +292,14 @@
 // PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the
 // corresponding source module, if one exists for the same variant.
 func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
-	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Enabled() && m.Prebuilt() != nil {
-		p := m.Prebuilt()
-		if !p.properties.PrebuiltRenamedToSource {
-			name := m.base().BaseModuleName()
-			if ctx.OtherModuleReverseDependencyVariantExists(name) {
-				ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
-				p.properties.SourceExists = true
-			}
+	m := ctx.Module()
+	// If this module is a prebuilt, is enabled and has not been renamed to source then add a
+	// dependency onto the source if it is present.
+	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled() && !p.properties.PrebuiltRenamedToSource {
+		name := m.base().BaseModuleName()
+		if ctx.OtherModuleReverseDependencyVariantExists(name) {
+			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
+			p.properties.SourceExists = true
 		}
 	}
 }
@@ -290,8 +307,8 @@
 // PrebuiltSelectModuleMutator marks prebuilts that are used, either overriding source modules or
 // because the source module doesn't exist.  It also disables installing overridden source modules.
 func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
-	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
-		p := m.Prebuilt()
+	m := ctx.Module()
+	if p := GetEmbeddedPrebuilt(m); p != nil {
 		if p.srcsSupplier == nil {
 			panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
 		}
@@ -299,9 +316,9 @@
 			p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil, m)
 		}
 	} else if s, ok := ctx.Module().(Module); ok {
-		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(m Module) {
-			p := m.(PrebuiltInterface).Prebuilt()
-			if p.usePrebuilt(ctx, s, m) {
+		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(prebuiltModule Module) {
+			p := GetEmbeddedPrebuilt(prebuiltModule)
+			if p.usePrebuilt(ctx, s, prebuiltModule) {
 				p.properties.UsePrebuilt = true
 				s.ReplacedByPrebuilt()
 			}
@@ -313,8 +330,8 @@
 // prebuilt when both modules exist and the prebuilt should be used.  When the prebuilt should not
 // be used, disable installing it.
 func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) {
-	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
-		p := m.Prebuilt()
+	m := ctx.Module()
+	if p := GetEmbeddedPrebuilt(m); p != nil {
 		name := m.base().BaseModuleName()
 		if p.properties.UsePrebuilt {
 			if p.properties.SourceExists {
diff --git a/android/sdk.go b/android/sdk.go
index b4ef8aa..0adfd89 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -41,6 +41,11 @@
 	sdkBase() *SdkBase
 	MakeMemberOf(sdk SdkRef)
 	IsInAnySdk() bool
+
+	// IsVersioned determines whether the module is versioned, i.e. has a name of the form
+	// <name>@<version>
+	IsVersioned() bool
+
 	ContainingSdk() SdkRef
 	MemberName() string
 	BuildWithSdks(sdks SdkRefs)
@@ -82,7 +87,7 @@
 func ParseSdkRef(ctx BaseModuleContext, str string, property string) SdkRef {
 	tokens := strings.Split(str, string(SdkVersionSeparator))
 	if len(tokens) < 1 || len(tokens) > 2 {
-		ctx.PropertyErrorf(property, "%q does not follow name#version syntax", str)
+		ctx.PropertyErrorf(property, "%q does not follow name@version syntax", str)
 		return SdkRef{Name: "invalid sdk name", Version: "invalid sdk version"}
 	}
 
@@ -140,6 +145,11 @@
 	return s.properties.ContainingSdk != nil
 }
 
+// IsVersioned returns true if this module is versioned.
+func (s *SdkBase) IsVersioned() bool {
+	return strings.Contains(s.module.Name(), "@")
+}
+
 // ContainingSdk returns the SDK that this module is a member of
 func (s *SdkBase) ContainingSdk() SdkRef {
 	if s.properties.ContainingSdk != nil {
@@ -171,6 +181,16 @@
 	m.AddProperties(&base.properties)
 }
 
+// IsModuleInVersionedSdk returns true if the module is an versioned sdk.
+func IsModuleInVersionedSdk(module Module) bool {
+	if s, ok := module.(SdkAware); ok {
+		if !s.ContainingSdk().Unversioned() {
+			return true
+		}
+	}
+	return false
+}
+
 // Provide support for generating the build rules which will build the snapshot.
 type SnapshotBuilder interface {
 	// Copy src to the dest (which is a snapshot relative path) and add the dest
@@ -282,15 +302,13 @@
 }
 
 // SdkMemberTypeDependencyTag is the interface that a tag must implement in order to allow the
-// dependent module to be automatically added to the sdk. In order for this to work the
-// SdkMemberType of the depending module must return true from
-// SdkMemberType.HasTransitiveSdkMembers.
+// dependent module to be automatically added to the sdk.
 type SdkMemberTypeDependencyTag interface {
 	blueprint.DependencyTag
 
 	// SdkMemberType returns the SdkMemberType that will be used to automatically add the child module
 	// to the sdk.
-	SdkMemberType() SdkMemberType
+	SdkMemberType(child Module) SdkMemberType
 
 	// ExportMember determines whether a module added to the sdk through this tag will be exported
 	// from the sdk or not.
@@ -317,7 +335,7 @@
 	export     bool
 }
 
-func (t *sdkMemberDependencyTag) SdkMemberType() SdkMemberType {
+func (t *sdkMemberDependencyTag) SdkMemberType(_ Module) SdkMemberType {
 	return t.memberType
 }
 
@@ -365,13 +383,6 @@
 	// True if the member type supports the sdk/sdk_snapshot, false otherwise.
 	UsableWithSdkAndSdkSnapshot() bool
 
-	// Return true if modules of this type can have dependencies which should be
-	// treated as if they are sdk members.
-	//
-	// Any dependency that is to be treated as a member of the sdk needs to implement
-	// SdkAware and be added with an SdkMemberTypeDependencyTag tag.
-	HasTransitiveSdkMembers() bool
-
 	// Return true if prebuilt host artifacts may be specific to the host OS. Only
 	// applicable to modules where HostSupported() is true. If this is true,
 	// snapshots will list each host OS variant explicitly and disable all other
@@ -437,10 +448,9 @@
 
 // Base type for SdkMemberType implementations.
 type SdkMemberTypeBase struct {
-	PropertyName         string
-	SupportsSdk          bool
-	TransitiveSdkMembers bool
-	HostOsDependent      bool
+	PropertyName    string
+	SupportsSdk     bool
+	HostOsDependent bool
 }
 
 func (b *SdkMemberTypeBase) SdkPropertyName() string {
@@ -451,10 +461,6 @@
 	return b.SupportsSdk
 }
 
-func (b *SdkMemberTypeBase) HasTransitiveSdkMembers() bool {
-	return b.TransitiveSdkMembers
-}
-
 func (b *SdkMemberTypeBase) IsHostOsDependent() bool {
 	return b.HostOsDependent
 }
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/apex/prebuilt.go b/apex/prebuilt.go
index 7830f95..8996352 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -110,6 +110,13 @@
 	}
 }
 
+// Implements android.DepInInSameApex
+func (p *prebuiltCommon) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	tag := ctx.OtherModuleDependencyTag(dep)
+	_, ok := tag.(exportedDependencyTag)
+	return ok
+}
+
 // apexInfoMutator marks any modules for which this apex exports a file as requiring an apex
 // specific variant and checks that they are supported.
 //
@@ -142,34 +149,43 @@
 
 	// Collect the list of dependencies.
 	var dependencies []android.ApexModule
-	mctx.VisitDirectDeps(func(m android.Module) {
-		tag := mctx.OtherModuleDependencyTag(m)
+	mctx.WalkDeps(func(child, parent android.Module) bool {
+		// If the child is not in the same apex as the parent then exit immediately and do not visit
+		// any of the child's dependencies.
+		if !android.IsDepInSameApex(mctx, parent, child) {
+			return false
+		}
+
+		tag := mctx.OtherModuleDependencyTag(child)
+		depName := mctx.OtherModuleName(child)
 		if exportedTag, ok := tag.(exportedDependencyTag); ok {
 			propertyName := exportedTag.name
-			depName := mctx.OtherModuleName(m)
 
 			// It is an error if the other module is not a prebuilt.
-			if _, ok := m.(android.PrebuiltInterface); !ok {
+			if !android.IsModulePrebuilt(child) {
 				mctx.PropertyErrorf(propertyName, "%q is not a prebuilt module", depName)
-				return
+				return false
 			}
 
 			// It is an error if the other module is not an ApexModule.
-			if _, ok := m.(android.ApexModule); !ok {
+			if _, ok := child.(android.ApexModule); !ok {
 				mctx.PropertyErrorf(propertyName, "%q is not usable within an apex", depName)
-				return
+				return false
 			}
-
-			// Strip off the prebuilt_ prefix if present before storing content to ensure consistent
-			// behavior whether there is a corresponding source module present or not.
-			depName = android.RemoveOptionalPrebuiltPrefix(depName)
-
-			// Remember that this module was added as a direct dependency.
-			contents[depName] = contents[depName].Add(true)
-
-			// Add the module to the list of dependencies that need to have an APEX variant.
-			dependencies = append(dependencies, m.(android.ApexModule))
 		}
+
+		// Strip off the prebuilt_ prefix if present before storing content to ensure consistent
+		// behavior whether there is a corresponding source module present or not.
+		depName = android.RemoveOptionalPrebuiltPrefix(depName)
+
+		// Remember if this module was added as a direct dependency.
+		direct := parent == mctx.Module()
+		contents[depName] = contents[depName].Add(direct)
+
+		// Add the module to the list of dependencies that need to have an APEX variant.
+		dependencies = append(dependencies, child.(android.ApexModule))
+
+		return true
 	})
 
 	// Create contents for the prebuilt_apex and store it away for later use.
diff --git a/bazel/properties.go b/bazel/properties.go
index a03b027..a71b12b 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -183,6 +183,15 @@
 	OS_LINUX        = "linux_glibc"
 	OS_LINUX_BIONIC = "linux_bionic"
 	OS_WINDOWS      = "windows"
+
+	// This is the string representation of the default condition wherever a
+	// configurable attribute is used in a select statement, i.e.
+	// //conditions:default for Bazel.
+	//
+	// This is consistently named "conditions_default" to mirror the Soong
+	// config variable default key in an Android.bp file, although there's no
+	// integration with Soong config variables (yet).
+	CONDITIONS_DEFAULT = "conditions_default"
 )
 
 var (
@@ -194,21 +203,23 @@
 	// A map of architectures to the Bazel label of the constraint_value
 	// for the @platforms//cpu:cpu constraint_setting
 	PlatformArchMap = map[string]string{
-		ARCH_ARM:    "//build/bazel/platforms/arch:arm",
-		ARCH_ARM64:  "//build/bazel/platforms/arch:arm64",
-		ARCH_X86:    "//build/bazel/platforms/arch:x86",
-		ARCH_X86_64: "//build/bazel/platforms/arch:x86_64",
+		ARCH_ARM:           "//build/bazel/platforms/arch:arm",
+		ARCH_ARM64:         "//build/bazel/platforms/arch:arm64",
+		ARCH_X86:           "//build/bazel/platforms/arch:x86",
+		ARCH_X86_64:        "//build/bazel/platforms/arch:x86_64",
+		CONDITIONS_DEFAULT: "//conditions:default", // The default condition of as arch select map.
 	}
 
 	// A map of target operating systems to the Bazel label of the
 	// constraint_value for the @platforms//os:os constraint_setting
 	PlatformOsMap = map[string]string{
-		OS_ANDROID:      "//build/bazel/platforms/os:android",
-		OS_DARWIN:       "//build/bazel/platforms/os:darwin",
-		OS_FUCHSIA:      "//build/bazel/platforms/os:fuchsia",
-		OS_LINUX:        "//build/bazel/platforms/os:linux",
-		OS_LINUX_BIONIC: "//build/bazel/platforms/os:linux_bionic",
-		OS_WINDOWS:      "//build/bazel/platforms/os:windows",
+		OS_ANDROID:         "//build/bazel/platforms/os:android",
+		OS_DARWIN:          "//build/bazel/platforms/os:darwin",
+		OS_FUCHSIA:         "//build/bazel/platforms/os:fuchsia",
+		OS_LINUX:           "//build/bazel/platforms/os:linux",
+		OS_LINUX_BIONIC:    "//build/bazel/platforms/os:linux_bionic",
+		OS_WINDOWS:         "//build/bazel/platforms/os:windows",
+		CONDITIONS_DEFAULT: "//conditions:default", // The default condition of an os select map.
 	}
 )
 
@@ -216,6 +227,51 @@
 	HasConfigurableValues() bool
 }
 
+// Represents an attribute whose value is a single label
+type LabelAttribute struct {
+	Value  Label
+	X86    Label
+	X86_64 Label
+	Arm    Label
+	Arm64  Label
+}
+
+func (attr *LabelAttribute) GetValueForArch(arch string) Label {
+	switch arch {
+	case ARCH_ARM:
+		return attr.Arm
+	case ARCH_ARM64:
+		return attr.Arm64
+	case ARCH_X86:
+		return attr.X86
+	case ARCH_X86_64:
+		return attr.X86_64
+	case CONDITIONS_DEFAULT:
+		return attr.Value
+	default:
+		panic("Invalid arch type")
+	}
+}
+
+func (attr *LabelAttribute) SetValueForArch(arch string, value Label) {
+	switch arch {
+	case ARCH_ARM:
+		attr.Arm = value
+	case ARCH_ARM64:
+		attr.Arm64 = value
+	case ARCH_X86:
+		attr.X86 = value
+	case ARCH_X86_64:
+		attr.X86_64 = value
+	default:
+		panic("Invalid arch type")
+	}
+}
+
+func (attr LabelAttribute) HasConfigurableValues() bool {
+	return attr.Arm.Label != "" || attr.Arm64.Label != "" || attr.X86.Label != "" || attr.X86_64.Label != ""
+}
+
 // 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 {
@@ -224,6 +280,8 @@
 	Arm    LabelList
 	Arm64  LabelList
 	Common LabelList
+
+	ConditionsDefault LabelList
 }
 
 type labelListOsValues struct {
@@ -233,6 +291,8 @@
 	Linux       LabelList
 	LinuxBionic LabelList
 	Windows     LabelList
+
+	ConditionsDefault LabelList
 }
 
 // LabelListAttribute is used to represent a list of Bazel labels as an
@@ -296,10 +356,11 @@
 
 func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
 	return map[string]*LabelList{
-		ARCH_X86:    &attrs.ArchValues.X86,
-		ARCH_X86_64: &attrs.ArchValues.X86_64,
-		ARCH_ARM:    &attrs.ArchValues.Arm,
-		ARCH_ARM64:  &attrs.ArchValues.Arm64,
+		ARCH_X86:           &attrs.ArchValues.X86,
+		ARCH_X86_64:        &attrs.ArchValues.X86_64,
+		ARCH_ARM:           &attrs.ArchValues.Arm,
+		ARCH_ARM64:         &attrs.ArchValues.Arm64,
+		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
 	}
 }
 
@@ -323,12 +384,13 @@
 
 func (attrs *LabelListAttribute) osValuePtrs() map[string]*LabelList {
 	return map[string]*LabelList{
-		OS_ANDROID:      &attrs.OsValues.Android,
-		OS_DARWIN:       &attrs.OsValues.Darwin,
-		OS_FUCHSIA:      &attrs.OsValues.Fuchsia,
-		OS_LINUX:        &attrs.OsValues.Linux,
-		OS_LINUX_BIONIC: &attrs.OsValues.LinuxBionic,
-		OS_WINDOWS:      &attrs.OsValues.Windows,
+		OS_ANDROID:         &attrs.OsValues.Android,
+		OS_DARWIN:          &attrs.OsValues.Darwin,
+		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
+		OS_LINUX:           &attrs.OsValues.Linux,
+		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
+		OS_WINDOWS:         &attrs.OsValues.Windows,
+		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
 	}
 }
 
@@ -381,6 +443,8 @@
 	Arm    []string
 	Arm64  []string
 	Common []string
+
+	ConditionsDefault []string
 }
 
 type stringListOsValues struct {
@@ -390,6 +454,8 @@
 	Linux       []string
 	LinuxBionic []string
 	Windows     []string
+
+	ConditionsDefault []string
 }
 
 // HasConfigurableValues returns true if the attribute contains
@@ -411,10 +477,11 @@
 
 func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
 	return map[string]*[]string{
-		ARCH_X86:    &attrs.ArchValues.X86,
-		ARCH_X86_64: &attrs.ArchValues.X86_64,
-		ARCH_ARM:    &attrs.ArchValues.Arm,
-		ARCH_ARM64:  &attrs.ArchValues.Arm64,
+		ARCH_X86:           &attrs.ArchValues.X86,
+		ARCH_X86_64:        &attrs.ArchValues.X86_64,
+		ARCH_ARM:           &attrs.ArchValues.Arm,
+		ARCH_ARM64:         &attrs.ArchValues.Arm64,
+		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
 	}
 }
 
@@ -438,12 +505,13 @@
 
 func (attrs *StringListAttribute) osValuePtrs() map[string]*[]string {
 	return map[string]*[]string{
-		OS_ANDROID:      &attrs.OsValues.Android,
-		OS_DARWIN:       &attrs.OsValues.Darwin,
-		OS_FUCHSIA:      &attrs.OsValues.Fuchsia,
-		OS_LINUX:        &attrs.OsValues.Linux,
-		OS_LINUX_BIONIC: &attrs.OsValues.LinuxBionic,
-		OS_WINDOWS:      &attrs.OsValues.Windows,
+		OS_ANDROID:         &attrs.OsValues.Android,
+		OS_DARWIN:          &attrs.OsValues.Darwin,
+		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
+		OS_LINUX:           &attrs.OsValues.Linux,
+		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
+		OS_WINDOWS:         &attrs.OsValues.Windows,
+		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
 	}
 }
 
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index f1bf648..cf6994f 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -18,7 +18,6 @@
 	"android/soong/android"
 	"fmt"
 	"os"
-	"strings"
 )
 
 // Codegen is the backend of bp2build. The code generator is responsible for
@@ -36,18 +35,12 @@
 	for _, f := range filesToWrite {
 		p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
 		if err := writeFile(ctx, p, f.Contents); err != nil {
-			fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
+			panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err))
 		}
 		// if these generated files are modified, regenerate on next run.
 		generatedBuildFiles = append(generatedBuildFiles, p.String())
 	}
 
-	// The MANIFEST file contains the full list of files generated by bp2build, excluding itself.
-	// Its purpose is for downstream tools to understand the set of files converted by bp2build.
-	manifestFile := outputDir.Join(ctx, "MANIFEST")
-	writeFile(ctx, manifestFile, strings.Join(generatedBuildFiles, "\n"))
-	generatedBuildFiles = append(generatedBuildFiles, manifestFile.String())
-
 	return metrics
 }
 
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/build_conversion_test.go b/bp2build/build_conversion_test.go
index 21d7062..63a6c2e 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1512,3 +1512,126 @@
 		}
 	}
 }
+
+func TestGlobExcludeSrcs(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		bp                                 string
+		expectedBazelTargets               []string
+		fs                                 map[string]string
+		dir                                string
+	}{
+		{
+			description:                        "filegroup top level exclude_srcs",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    exclude_srcs: ["c.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`filegroup(
+    name = "fg_foo",
+    srcs = [
+        "//dir:e.txt",
+        "//dir:f.txt",
+        "a.txt",
+        "b.txt",
+    ],
+)`,
+			},
+			fs: map[string]string{
+				"a.txt":          "",
+				"b.txt":          "",
+				"c.txt":          "",
+				"dir/Android.bp": "",
+				"dir/e.txt":      "",
+				"dir/f.txt":      "",
+			},
+		},
+		{
+			description:                        "filegroup in subdir exclude_srcs",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp:                                 "",
+			dir:                                "dir",
+			fs: map[string]string{
+				"dir/Android.bp": `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    exclude_srcs: ["b.txt"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+				"dir/a.txt":             "",
+				"dir/b.txt":             "",
+				"dir/subdir/Android.bp": "",
+				"dir/subdir/e.txt":      "",
+				"dir/subdir/f.txt":      "",
+			},
+			expectedBazelTargets: []string{`filegroup(
+    name = "fg_foo",
+    srcs = [
+        "//dir/subdir:e.txt",
+        "//dir/subdir:f.txt",
+        "a.txt",
+    ],
+)`,
+			},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		fs := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.fs {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			fs[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.bp, fs)
+		ctx := android.NewTestContext(config)
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+		_, errs = ctx.ResolveDependencies(config)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+
+		checkDir := dir
+		if testCase.dir != "" {
+			checkDir = testCase.dir
+		}
+		bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
+		}
+	}
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 6762416..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",
@@ -199,6 +200,108 @@
     srcs = ["ld_android.cpp"],
 )`},
 		},
+		{
+			description:                        "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
+			moduleTypeUnderTest:                "cc_library",
+			moduleTypeUnderTestFactory:         cc.LibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+			dir:                                "external",
+			filesystem: map[string]string{
+				"external/math/cosf.c":      "",
+				"external/math/erf.c":       "",
+				"external/math/erf_data.c":  "",
+				"external/math/erff.c":      "",
+				"external/math/erff_data.c": "",
+				"external/Android.bp": `
+cc_library {
+    name: "fake-libarm-optimized-routines-math",
+    exclude_srcs: [
+        // Provided by:
+        // bionic/libm/upstream-freebsd/lib/msun/src/s_erf.c
+        // bionic/libm/upstream-freebsd/lib/msun/src/s_erff.c
+        "math/erf.c",
+        "math/erf_data.c",
+        "math/erff.c",
+        "math/erff_data.c",
+    ],
+    srcs: [
+        "math/*.c",
+    ],
+    // arch-specific settings
+    arch: {
+        arm64: {
+            cflags: [
+                "-DHAVE_FAST_FMA=1",
+            ],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+`,
+			},
+			bp: soongCcLibraryPreamble,
+			expectedBazelTargets: []string{`cc_library(
+    name = "fake-libarm-optimized-routines-math",
+    copts = ["-Iexternal"] + select({
+        "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
+        "//conditions:default": [],
+    }),
+    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 := "."
@@ -217,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 207a080..bff9b07 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -526,6 +526,392 @@
     linkstatic = True,
 )`},
 		},
+		{
+			description:                        "cc_library_static simple exclude_srcs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem: map[string]string{
+				"common.c":       "",
+				"foo-a.c":        "",
+				"foo-excluded.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c", "foo-*.c"],
+    exclude_srcs: ["foo-excluded.c"],
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = [
+        "common.c",
+        "foo-a.c",
+    ],
+)`},
+		},
+		{
+			description:                        "cc_library_static one arch specific srcs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem: map[string]string{
+				"common.c":  "",
+				"foo-arm.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c"],
+    arch: { arm: { srcs: ["foo-arm.c"] } }
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = ["common.c"] + select({
+        "//build/bazel/platforms/arch:arm": ["foo-arm.c"],
+        "//conditions:default": [],
+    }),
+)`},
+		},
+		{
+			description:                        "cc_library_static one arch specific 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":          "",
+				"not-for-arm.c":      "",
+				"not-for-anything.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c", "not-for-*.c"],
+    exclude_srcs: ["not-for-anything.c"],
+    arch: {
+        arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] },
+    },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = ["common.c"] + select({
+        "//build/bazel/platforms/arch:arm": ["for-arm.c"],
+        "//conditions:default": ["not-for-arm.c"],
+    }),
+)`},
+		},
+		{
+			description:                        "cc_library_static arch specific exclude_srcs for 2 architectures",
+			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-x86.c":     "",
+				"not-for-arm.c": "",
+				"not-for-x86.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c", "not-for-*.c"],
+    exclude_srcs: ["not-for-everything.c"],
+    arch: {
+        arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] },
+        x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] },
+    },
+} `,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = ["common.c"] + select({
+        "//build/bazel/platforms/arch:arm": [
+            "for-arm.c",
+            "not-for-x86.c",
+        ],
+        "//build/bazel/platforms/arch:x86": [
+            "for-x86.c",
+            "not-for-arm.c",
+        ],
+        "//conditions:default": [
+            "not-for-arm.c",
+            "not-for-x86.c",
+        ],
+    }),
+)`},
+		},
+		{
+			description:                        "cc_library_static arch specific exclude_srcs for 4 architectures",
+			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":         "",
+				"not-for-arm.c":        "",
+				"not-for-arm64.c":      "",
+				"not-for-x86.c":        "",
+				"not-for-x86_64.c":     "",
+				"not-for-everything.c": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    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"] },
+	},
+} `,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    linkstatic = True,
+    srcs = ["common.c"] + select({
+        "//build/bazel/platforms/arch:arm": [
+            "for-arm.c",
+            "not-for-arm64.c",
+            "not-for-x86.c",
+            "not-for-x86_64.c",
+        ],
+        "//build/bazel/platforms/arch:arm64": [
+            "for-arm64.c",
+            "not-for-arm.c",
+            "not-for-x86.c",
+            "not-for-x86_64.c",
+        ],
+        "//build/bazel/platforms/arch:x86": [
+            "for-x86.c",
+            "not-for-arm.c",
+            "not-for-arm64.c",
+            "not-for-x86_64.c",
+        ],
+        "//build/bazel/platforms/arch:x86_64": [
+            "for-x86_64.c",
+            "not-for-arm.c",
+            "not-for-arm64.c",
+            "not-for-x86.c",
+        ],
+        "//conditions:default": [
+            "not-for-arm.c",
+            "not-for-arm64.c",
+            "not-for-x86.c",
+            "not-for-x86_64.c",
+        ],
+    }),
+)`},
+		},
+		{
+			description:                        "cc_library_static multiple dep same name panic",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem:                         map[string]string{},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static { name: "static_dep" }
+cc_library_static {
+    name: "foo_static",
+    static_libs: ["static_dep"],
+    whole_static_libs: ["static_dep"],
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    deps = [":static_dep"],
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep",
+    copts = ["-I."],
+    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 b9ffc04..95a2747 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,21 +59,30 @@
 func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
 	var value reflect.Value
 	var archSelects, osSelects selects
-
+	var defaultSelectValue string
 	switch list := v.(type) {
 	case bazel.StringListAttribute:
 		value, archSelects, osSelects = getStringListValues(list)
+		defaultSelectValue = "[]"
 	case bazel.LabelListAttribute:
 		value, archSelects, osSelects = getLabelListValues(list)
+		defaultSelectValue = "[]"
+	case bazel.LabelAttribute:
+		value, archSelects, osSelects = getLabelValue(list)
+		defaultSelectValue = "None"
 	default:
 		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
 	}
 
-	ret, err := prettyPrint(value, indent)
-	if err != nil {
-		return ret, err
-	}
+	ret := ""
+	if value.Kind() != reflect.Invalid {
+		s, err := prettyPrint(value, indent)
+		if err != nil {
+			return ret, err
+		}
 
+		ret += s
+	}
 	// Convenience function to append selects components to an attribute value.
 	appendSelects := func(selectsData selects, defaultValue, s string) (string, error) {
 		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
@@ -83,12 +97,12 @@
 		return s, nil
 	}
 
-	ret, err = appendSelects(archSelects, "[]", ret)
+	ret, err := appendSelects(archSelects, defaultSelectValue, ret)
 	if err != nil {
 		return "", err
 	}
 
-	ret, err = appendSelects(osSelects, "[]", ret)
+	ret, err = appendSelects(osSelects, defaultSelectValue, ret)
 	return ret, err
 }
 
@@ -99,8 +113,15 @@
 		return "", nil
 	}
 
+	// addConditionsDefault := false
+	conditionsDefaultKey := bazel.PlatformArchMap[bazel.CONDITIONS_DEFAULT]
+
 	var selects string
 	for _, selectKey := range android.SortedStringKeys(selectMap) {
+		if selectKey == conditionsDefaultKey {
+			// Handle default condition later.
+			continue
+		}
 		value := selectMap[selectKey]
 		if isZero(value) {
 			// Ignore zero values to not generate empty lists.
@@ -125,8 +146,22 @@
 	// Create the map.
 	ret := "select({\n"
 	ret += selects
-	// default condition comes last.
-	ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), "//conditions:default", defaultValue)
+
+	// Handle the default condition
+	s, err := prettyPrintSelectEntry(selectMap[conditionsDefaultKey], conditionsDefaultKey, indent)
+	if err != nil {
+		return "", err
+	}
+	if s == "" {
+		// Print an explicit empty list (the default value) even if the value is
+		// empty, to avoid errors about not finding a configuration that matches.
+		ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), "//conditions:default", defaultValue)
+	} else {
+		// Print the custom default value.
+		ret += s
+		ret += ",\n"
+	}
+
 	ret += makeIndent(indent)
 	ret += "})"
 
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 452f6ed..b925682 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -142,7 +142,7 @@
 
 		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.props.Arch_paths))
 
-		for arch, props := range m.GetArchProperties(&customProps{}) {
+		for arch, props := range m.GetArchProperties(ctx, &customProps{}) {
 			if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
 				paths.SetValueForArch(arch.Name, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
 			}
diff --git a/build_kzip.bash b/build_kzip.bash
index a4659d4..a09335e 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -5,6 +5,7 @@
 # It is assumed that the current directory is the top of the source tree.
 # The following environment variables affect the result:
 #   BUILD_NUMBER          build number, used to generate unique ID (will use UUID if not set)
+#   SUPERPROJECT_SHA      superproject sha, used to generate unique id (will use BUILD_NUMBER if not set)
 #   DIST_DIR              where the resulting all.kzip will be placed
 #   KYTHE_KZIP_ENCODING   proto or json (proto is default)
 #   KYTHE_JAVA_SOURCE_BATCH_SIZE maximum number of the Java source files in a compilation unit
@@ -14,6 +15,7 @@
 #   XREF_CORPUS           source code repository URI, e.g., 'android.googlesource.com/platform/superproject'
 
 : ${BUILD_NUMBER:=$(uuidgen)}
+: ${SUPERPROJECT_SHA:=$BUILD_NUMBER}
 : ${KYTHE_JAVA_SOURCE_BATCH_SIZE:=500}
 : ${KYTHE_KZIP_ENCODING:=proto}
 : ${XREF_CORPUS:?should be set}
@@ -48,6 +50,6 @@
 
 # Pack
 # TODO(asmundak): this should be done by soong.
-declare -r allkzip="$BUILD_NUMBER.kzip"
+declare -r allkzip="$SUPERPROJECT_SHA.kzip"
 "$out/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip')
 
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 b11602d..1433f6f 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -58,7 +58,7 @@
 		}
 	}
 
-	for _, p := range module.GetArchProperties(&BaseLinkerProperties{}) {
+	for _, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
 		// arch specific linker props
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
@@ -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
@@ -90,11 +142,6 @@
 		return "-I" + filepath.Join(ctx.ModuleDir(), dir)
 	}
 
-	// Parse the list of srcs, excluding files from exclude_srcs.
-	parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
-		return android.BazelLabelForModuleSrcExcludes(ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
-	}
-
 	// Parse the list of module-relative include directories (-I).
 	parseLocalIncludeDirs := func(baseCompilerProps *BaseCompilerProperties) []string {
 		// include_dirs are root-relative, not module-relative.
@@ -111,32 +158,82 @@
 		return copts
 	}
 
+	// baseSrcs contain the list of src files that are used for every configuration.
+	var baseSrcs []string
+	// baseExcludeSrcs contain the list of src files that are excluded for every configuration.
+	var baseExcludeSrcs []string
+	// baseSrcsLabelList is a clone of the base srcs LabelList, used for computing the
+	// arch or os specific srcs later.
+	var baseSrcsLabelList bazel.LabelList
+
+	// Parse srcs from an arch or OS's props value, taking the base srcs and
+	// exclude srcs into account.
+	parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
+		// Combine the base srcs and arch-specific srcs
+		allSrcs := append(baseSrcs, baseCompilerProps.Srcs...)
+		// Combine the base exclude_srcs and configuration-specific exclude_srcs
+		allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...)
+		return android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs)
+	}
+
 	for _, props := range module.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			srcs.Value = parseSrcs(baseCompilerProps)
 			copts.Value = parseCopts(baseCompilerProps)
+
+			// Used for arch-specific srcs later.
+			baseSrcs = baseCompilerProps.Srcs
+			baseExcludeSrcs = baseCompilerProps.Exclude_srcs
+			baseSrcsLabelList = parseSrcs(baseCompilerProps)
 			break
 		}
 	}
 
+	// Handle include_build_directory prop. If the property is true, then the
+	// target has access to all headers recursively in the package, and has
+	// "-I<module-dir>" in its copts.
 	if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
 		copts.Value = append(copts.Value, includeFlag("."))
 	} else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() {
 		copts.Value = append(copts.Value, includeFlag("."))
 	}
 
-	for arch, props := range module.GetArchProperties(&BaseCompilerProperties{}) {
+	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			srcsList := parseSrcs(baseCompilerProps)
-			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcsList, srcs.Value))
+			// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
+			// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
+			if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
+				srcsList := parseSrcs(baseCompilerProps)
+				srcs.SetValueForArch(arch.Name, srcsList)
+				// The base srcs value should not contain any arch-specific excludes.
+				srcs.Value = bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes})
+			}
+
 			copts.SetValueForArch(arch.Name, parseCopts(baseCompilerProps))
 		}
 	}
 
+	// After going through all archs, delete the duplicate files in the arch
+	// values that are already in the base srcs.Value.
+	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
+		if _, ok := props.(*BaseCompilerProperties); ok {
+			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcs.GetValueForArch(arch.Name), srcs.Value))
+		}
+	}
+
+	// Now that the srcs.Value list is finalized, compare it with the original
+	// list, and put the difference into the default condition for the arch
+	// select.
+	defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
+	// TODO(b/186153868): handle the case with multiple variant types, e.g. when arch and os are both used.
+	srcs.SetValueForArch(bazel.CONDITIONS_DEFAULT, defaultsSrcs)
+
+	// Handle OS specific props.
 	for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			srcsList := parseSrcs(baseCompilerProps)
-			srcs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(srcsList, srcs.Value))
+			// TODO(b/186153868): add support for os-specific srcs and exclude_srcs
+			srcs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
 			copts.SetValueForOS(os.Name, parseCopts(baseCompilerProps))
 		}
 	}
@@ -149,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 {
@@ -168,11 +267,15 @@
 			libs = android.SortedUniqueStrings(libs)
 			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
 			linkopts.Value = baseLinkerProps.Ldflags
+
+			if baseLinkerProps.Version_script != nil {
+				versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
+			}
 			break
 		}
 	}
 
-	for arch, p := range module.GetArchProperties(&BaseLinkerProperties{}) {
+	for arch, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			libs := baseLinkerProps.Header_libs
 			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
@@ -181,6 +284,10 @@
 			libs = android.SortedUniqueStrings(libs)
 			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
 			linkopts.SetValueForArch(arch.Name, baseLinkerProps.Ldflags)
+			if baseLinkerProps.Version_script != nil {
+				versionScript.SetValueForArch(arch.Name,
+					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
+			}
 		}
 	}
 
@@ -197,8 +304,9 @@
 	}
 
 	return linkerAttributes{
-		deps:     deps,
-		linkopts: linkopts,
+		deps:          deps,
+		linkopts:      linkopts,
+		versionScript: versionScript,
 	}
 }
 
@@ -237,7 +345,7 @@
 	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
 	includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
 
-	for arch, props := range module.GetArchProperties(&FlagExporterProperties{}) {
+	for arch, props := range module.GetArchProperties(ctx, &FlagExporterProperties{}) {
 		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
 			archIncludeDirs := flagExporterProperties.Export_system_include_dirs
 			archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...)
diff --git a/cc/builder.go b/cc/builder.go
index ad7e1e6..29cde9d 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -126,17 +126,24 @@
 
 	_ = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
 	_ = pctx.SourcePathVariable("xzCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/xz")
+	_ = pctx.SourcePathVariable("createMiniDebugInfo", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/create_minidebuginfo")
 
 	// Rule to invoke `strip` (to discard symbols and data from object files).
 	strip = pctx.AndroidStaticRule("strip",
 		blueprint.RuleParams{
-			Depfile:     "${out}.d",
-			Deps:        blueprint.DepsGCC,
-			Command:     "XZ=$xzCmd CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
-			CommandDeps: []string{"$stripPath", "$xzCmd"},
-			Pool:        darwinStripPool,
+			Depfile: "${out}.d",
+			Deps:    blueprint.DepsGCC,
+			Command: "XZ=$xzCmd CREATE_MINIDEBUGINFO=$createMiniDebugInfo CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
+			CommandDeps: func() []string {
+				if runtime.GOOS != "darwin" {
+					return []string{"$stripPath", "$xzCmd", "$createMiniDebugInfo"}
+				} else {
+					return []string{"$stripPath", "$xzCmd"}
+				}
+			}(),
+			Pool: darwinStripPool,
 		},
-		"args", "crossCompile")
+		"args")
 
 	// Rule to invoke `strip` (to discard symbols and data from object files) on darwin architecture.
 	darwinStrip = pctx.AndroidStaticRule("darwinStrip",
@@ -986,7 +993,6 @@
 func transformStrip(ctx android.ModuleContext, inputFile android.Path,
 	outputFile android.WritablePath, flags StripFlags) {
 
-	crossCompile := gccCmd(flags.Toolchain, "")
 	args := ""
 	if flags.StripAddGnuDebuglink {
 		args += " --add-gnu-debuglink"
@@ -1003,9 +1009,6 @@
 	if flags.StripKeepSymbolsAndDebugFrame {
 		args += " --keep-symbols-and-debug-frame"
 	}
-	if flags.StripUseGnuStrip {
-		args += " --use-gnu-strip"
-	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        strip,
@@ -1013,8 +1016,7 @@
 		Output:      outputFile,
 		Input:       inputFile,
 		Args: map[string]string{
-			"crossCompile": crossCompile,
-			"args":         args,
+			"args": args,
 		},
 	})
 }
diff --git a/cc/cc.go b/cc/cc.go
index 260fcf1..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,23 +1126,19 @@
 	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
 }
 
-func (c *Module) IsLlndkHeaders() bool {
-	if _, ok := c.linker.(*llndkHeadersDecorator); ok {
-		return true
-	}
-	return false
-}
-
-func (c *Module) IsLlndkLibrary() bool {
-	if _, ok := c.linker.(*llndkStubDecorator); ok {
-		return true
-	}
-	return false
-}
-
-func (m *Module) HasLlndkStubs() bool {
+func (m *Module) NeedsLlndkVariants() bool {
 	lib := moduleLibraryInterface(m)
-	return lib != nil && lib.hasLLNDKStubs()
+	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
@@ -1451,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()
 }
@@ -1608,12 +1613,13 @@
 	}
 
 	llndk := c.IsLlndk()
-	_, llndkHeader := c.linker.(*llndkHeadersDecorator)
-	if llndk || llndkHeader || (c.UseVndk() && c.HasNonSystemVariants()) {
+	if llndk || (c.UseVndk() && c.HasNonSystemVariants()) {
 		// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
 		// 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.
@@ -2058,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{}
@@ -2070,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 {
@@ -2936,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 {
@@ -2964,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 3d2160f..e9daf33 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -548,17 +548,17 @@
 
 		cc_library {
 			name: "libllndk",
-			llndk_stubs: "libllndk.llndk",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+				export_llndk_headers: ["libllndk_headers"],
+			}
 		}
 
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
-			export_llndk_headers: ["libllndk_headers"],
-		}
-
-		llndk_headers {
+		cc_library_headers {
 			name: "libllndk_headers",
+			llndk: {
+				llndk_headers: true,
+			},
 			export_include_dirs: ["include"],
 		}
 
@@ -894,17 +894,17 @@
 
 		cc_library {
 			name: "libllndk",
-			llndk_stubs: "libllndk.llndk",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+				export_llndk_headers: ["libllndk_headers"],
+			}
 		}
 
-		llndk_library {
-			name: "libllndk.llndk",
-			symbol_file: "",
-			export_llndk_headers: ["libllndk_headers"],
-		}
-
-		llndk_headers {
+		cc_library_headers {
 			name: "libllndk_headers",
+			llndk: {
+				symbol_file: "libllndk.map.txt",
+			},
 			export_include_dirs: ["include"],
 		}
 	`)
@@ -1164,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 {
@@ -1187,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 {
@@ -1249,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 {
@@ -1277,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 {
@@ -1301,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 {
@@ -1320,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 {
@@ -1351,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",
@@ -1382,11 +1362,6 @@
 			shared_libs: ["libanothervndksp"],
 		}
 
-		llndk_library {
-			name: "libllndk",
-			symbol_file: "",
-		}
-
 		cc_library {
 			name: "libanothervndksp",
 			vendor_available: true,
@@ -2152,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",
@@ -2430,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 {
@@ -2763,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",
@@ -2920,17 +2827,19 @@
 
 func TestLlndkHeaders(t *testing.T) {
 	ctx := testCc(t, `
-	llndk_headers {
+	cc_library_headers {
 		name: "libllndk_headers",
 		export_include_dirs: ["my_include"],
-	}
-	llndk_library {
-		name: "libllndk.llndk",
-		export_llndk_headers: ["libllndk_headers"],
+		llndk: {
+			llndk_headers: true,
+		},
 	}
 	cc_library {
 		name: "libllndk",
-		llndk_stubs: "libllndk.llndk",
+		llndk: {
+			symbol_file: "libllndk.map.txt",
+			export_llndk_headers: ["libllndk_headers"],
+		}
 	}
 
 	cc_library {
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 5219ebc..c780b6f 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -221,8 +221,7 @@
 	// If the same library is present both as source and a prebuilt we must pick
 	// only one to avoid a conflict. Always prefer the source since the prebuilt
 	// probably won't be built with sanitizers enabled.
-	if prebuilt, ok := dependency.(android.PrebuiltInterface); ok &&
-		prebuilt.Prebuilt() != nil && prebuilt.Prebuilt().SourceExists() {
+	if prebuilt := android.GetEmbeddedPrebuilt(dependency); prebuilt != nil && prebuilt.SourceExists() {
 		return false
 	}
 
diff --git a/cc/genrule.go b/cc/genrule.go
index ca4fda7..82d7205 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -75,6 +75,10 @@
 	return Bool(g.Vendor_ramdisk_available)
 }
 
+func (g *GenruleExtraProperties) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	// If the build is using a snapshot, the recovery variant under AOSP directories
 	// is not needed.
diff --git a/cc/image.go b/cc/image.go
index c1e5dfe..5d41717 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -437,15 +437,11 @@
 		productVndkVersion = platformVndkVersion
 	}
 
-	if m.IsLlndkLibrary() || m.IsLlndkHeaders() || m.HasLlndkStubs() {
+	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.HasLlndkStubs() {
-			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.
@@ -601,6 +608,10 @@
 	return c.Properties.VendorRamdiskVariantNeeded
 }
 
+func (c *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return c.Properties.RecoveryVariantNeeded
 }
@@ -681,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 f49698e..7b631fa 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,32 @@
 		return
 	}
 
+	// For some cc_library modules, their static variants are ready to be
+	// converted, but not their shared variants. For these modules, delegate to
+	// the cc_library_static bp2build converter temporarily instead.
+	if android.GenerateCcLibraryStaticOnly(ctx) {
+		ccLibraryStaticBp2BuildInternal(ctx, m)
+		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{
@@ -402,11 +420,18 @@
 
 func (f *flagExporter) setProvider(ctx android.ModuleContext) {
 	ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
-		IncludeDirs:       android.FirstUniquePaths(f.dirs),
+		// Comes from Export_include_dirs property, and those of exported transitive deps
+		IncludeDirs: android.FirstUniquePaths(f.dirs),
+		// Comes from Export_system_include_dirs property, and those of exported transitive deps
 		SystemIncludeDirs: android.FirstUniquePaths(f.systemDirs),
-		Flags:             f.flags,
-		Deps:              f.deps,
-		GeneratedHeaders:  f.headers,
+		// Used in very few places as a one-off way of adding extra defines.
+		Flags: f.flags,
+		// Used sparingly, for extra files that need to be explicitly exported to dependers,
+		// or for phony files to minimize ninja.
+		Deps: f.deps,
+		// For exported generated headers, such as exported aidl headers, proto headers, or
+		// sysprop headers.
+		GeneratedHeaders: f.headers,
 	})
 }
 
@@ -524,6 +549,8 @@
 			Direct(outputFilePath).
 			Build(),
 	})
+
+	ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfoFromCcInfo(ctx, ccInfo))
 	if i, ok := handler.module.linker.(snapshotLibraryInterface); ok {
 		// Dependencies on this library will expect collectedSnapshotHeaders to
 		// be set, otherwise validation will fail. For now, set this to an empty
@@ -772,6 +799,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") {
@@ -868,6 +902,8 @@
 
 	implementationModuleName(name string) string
 	hasLLNDKStubs() bool
+	hasLLNDKHeaders() bool
+	hasVendorPublicLibrary() bool
 }
 
 var _ libraryInterface = (*libraryDecorator)(nil)
@@ -963,6 +999,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.
@@ -1419,6 +1461,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).
@@ -1672,14 +1722,18 @@
 
 // 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) != ""
+// 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 {
@@ -1918,9 +1972,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()
@@ -1983,11 +2035,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
@@ -2160,6 +2213,28 @@
 	return module
 }
 
+func ccLibraryStaticBp2BuildInternal(ctx android.TopDownMutatorContext, module *Module) {
+	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
+	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
+
+	attrs := &bazelCcLibraryStaticAttributes{
+		Copts:      compilerAttrs.copts,
+		Srcs:       compilerAttrs.srcs,
+		Deps:       linkerAttrs.deps,
+		Linkopts:   linkerAttrs.linkopts,
+		Linkstatic: true,
+		Includes:   exportedIncludes,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_library_static",
+		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(BazelCcLibraryStaticFactory, module.Name(), props, attrs)
+}
+
 func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
 	module, ok := ctx.Module().(*Module)
 	if !ok {
@@ -2173,24 +2248,7 @@
 		return
 	}
 
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
-	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
-	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
-
-	attrs := &bazelCcLibraryStaticAttributes{
-		Copts:      compilerAttrs.copts,
-		Srcs:       compilerAttrs.srcs,
-		Deps:       linkerAttrs.deps,
-		Linkstatic: true,
-		Includes:   exportedIncludes,
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "cc_library_static",
-		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
-	}
-
-	ctx.CreateBazelTargetModule(BazelCcLibraryStaticFactory, module.Name(), props, attrs)
+	ccLibraryStaticBp2BuildInternal(ctx, module)
 }
 
 func (m *bazelCcLibraryStatic) Name() string {
diff --git a/cc/linkable.go b/cc/linkable.go
index 2fa12f6..40a9d8b 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -106,14 +106,11 @@
 	// IsLlndkPublic returns true only for LLNDK (public) libs.
 	IsLlndkPublic() bool
 
-	// IsLlndkHeaders returns true if this module is an LLNDK headers module.
-	IsLlndkHeaders() bool
+	// NeedsLlndkVariants returns true if this module has LLNDK stubs or provides LLNDK headers.
+	NeedsLlndkVariants() bool
 
-	// IsLlndkLibrary returns true if this module is an LLNDK library module.
-	IsLlndkLibrary() bool
-
-	// HasLlndkStubs returns true if this module has LLNDK stubs.
-	HasLlndkStubs() bool
+	// NeedsVendorPublicLibraryVariants returns true if this module has vendor public library stubs.
+	NeedsVendorPublicLibraryVariants() bool
 
 	UseVndk() bool
 	MustUseVendorVariant() bool
@@ -283,7 +280,7 @@
 	systemIncludes := android.PathsForBazelOut(ctx, ccInfo.SystemIncludes)
 
 	return FlagExporterInfo{
-		IncludeDirs:       includes,
-		SystemIncludeDirs: systemIncludes,
+		IncludeDirs:       android.FirstUniquePaths(includes),
+		SystemIncludeDirs: android.FirstUniquePaths(systemIncludes),
 	}
 }
diff --git a/cc/linker.go b/cc/linker.go
index ae33356..73fc4f0 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -322,7 +322,7 @@
 
 	if ctx.toolchain().Bionic() {
 		// libclang_rt.builtins has to be last on the command line
-		if !Bool(linker.Properties.No_libcrt) {
+		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
 			deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index d05dbce..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.
@@ -69,133 +50,8 @@
 	// vendor nor product libraries. This effectively hides this module from
 	// non-system modules. Default value is false.
 	Private *bool
-}
 
-type llndkStubDecorator struct {
-	*libraryDecorator
-
-	Properties llndkLibraryProperties
-}
-
-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
-}
-
-type llndkHeadersDecorator struct {
-	*libraryDecorator
-}
-
-func (llndk *llndkHeadersDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.HeaderLibs = append(deps.HeaderLibs, llndk.Properties.Llndk.Export_llndk_headers...)
-	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
-		llndk.Properties.Llndk.Export_llndk_headers...)
-	return deps
-}
-
-// llndk_headers contains a set of c/c++ llndk headers files which are imported
-// by other soongs cc modules.
-func llndkHeadersFactory() android.Module {
-	module, library := NewLibrary(android.DeviceSupported)
-	library.HeaderOnly()
-	module.stl = nil
-	module.sanitize = nil
-
-	decorator := &llndkHeadersDecorator{
-		libraryDecorator: library,
-	}
-
-	module.compiler = nil
-	module.linker = decorator
-	module.installer = nil
-	module.library = decorator
-
-	module.Init()
-
-	return module
-}
-
-func init() {
-	android.RegisterModuleType("llndk_library", LlndkLibraryFactory)
-	android.RegisterModuleType("llndk_headers", llndkHeadersFactory)
+	// if true, make this module available to provide headers to other modules that set
+	// llndk.symbol_file.
+	Llndk_headers *bool
 }
diff --git a/cc/makevars.go b/cc/makevars.go
index 923472a..da5f1fd 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)
 				}
@@ -153,6 +151,7 @@
 
 	ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
 	ctx.Strict("XZ", "${xzCmd}")
+	ctx.Strict("CREATE_MINIDEBUGINFO", "${createMiniDebugInfo}")
 
 	includeFlags, err := ctx.Eval("${config.CommonGlobalIncludes}")
 	if err != nil {
@@ -286,11 +285,14 @@
 	}
 
 	if target.Os.Class == android.Device {
-		ctx.Strict(makePrefix+"OBJCOPY", gccCmd(toolchain, "objcopy"))
-		ctx.Strict(makePrefix+"LD", gccCmd(toolchain, "ld"))
-		ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
+		ctx.Strict(makePrefix+"OBJCOPY", "${config.ClangBin}/llvm-objcopy")
+		ctx.Strict(makePrefix+"LD", "${config.ClangBin}/lld")
 		ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain))
+		// TODO: work out whether to make this "${config.ClangBin}/llvm-", which
+		// should mostly work, or remove it.
 		ctx.Strict(makePrefix+"TOOLS_PREFIX", gccCmd(toolchain, ""))
+		// TODO: GCC version is obsolete now that GCC has been removed.
+		ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
 	}
 
 	if target.Os.Class == android.Host {
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index c12ad79..885a0ce 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -308,6 +308,10 @@
 	return false
 }
 
+func (s *snapshot) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (s *snapshot) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
diff --git a/cc/stl.go b/cc/stl.go
index 4f8865f..75921c6 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -62,6 +62,8 @@
 		s := ""
 		if stl.Properties.Stl != nil {
 			s = *stl.Properties.Stl
+		} else if ctx.header() {
+			s = "none"
 		}
 		if ctx.useSdk() && ctx.Device() {
 			switch s {
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/test.go b/cc/test.go
index 9b77e45..d4c23d7 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -46,6 +46,14 @@
 
 	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
 	Unit_test *bool
+
+	// Add ShippingApiLevelModuleController to auto generated test config. If the device properties
+	// for the shipping api level is less than the test_min_api_level, skip this module.
+	Test_min_api_level *int64
+
+	// Add MinApiLevelModuleController with ro.vndk.version property. If ro.vndk.version has an
+	// integer value and the value is less than the test_min_vndk_version, skip this module.
+	Test_min_vndk_version *int64
 }
 
 type TestBinaryProperties struct {
@@ -89,6 +97,7 @@
 
 	// Add ShippingApiLevelModuleController to auto generated test config. If the device properties
 	// for the shipping api level is less than the test_min_api_level, skip this module.
+	// Deprecated (b/187258404). Use test_options.test_min_api_level instead.
 	Test_min_api_level *int64
 
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
@@ -395,11 +404,22 @@
 	for _, tag := range test.Properties.Test_options.Test_suite_tag {
 		configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag})
 	}
-	if test.Properties.Test_min_api_level != nil {
+	if test.Properties.Test_options.Test_min_api_level != nil {
+		var options []tradefed.Option
+		options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Test_min_api_level), 10)})
+		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
+	} else if test.Properties.Test_min_api_level != nil {
+		// TODO: (b/187258404) Remove test.Properties.Test_min_api_level
 		var options []tradefed.Option
 		options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)})
 		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
 	}
+	if test.Properties.Test_options.Test_min_vndk_version != nil {
+		var options []tradefed.Option
+		options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Test_min_vndk_version), 10)})
+		options = append(options, tradefed.Option{Name: "api-level-prop", Value: "ro.vndk.version"})
+		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
+	}
 
 	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
 		test.Properties.Test_config_template, test.Properties.Test_suites, configs, test.Properties.Auto_gen_config, testInstallBase)
diff --git a/cc/testing.go b/cc/testing.go
index ff32bff..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,8 +556,6 @@
 		ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
 		ctx.RegisterModuleType("cc_test", TestFactory)
 		ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
-		ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
-		ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
 		ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
 		RegisterVndkLibraryTxtTypes(ctx)
@@ -686,8 +671,6 @@
 	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
 	ctx.RegisterModuleType("cc_test", TestFactory)
 	ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
-	ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
-	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 2f68cca..9ad51ad 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -180,16 +180,10 @@
 	if _, ok := m.linker.(*kernelHeadersDecorator); ok {
 		return false
 	}
-	// skip llndk_library and llndk_headers which are backward compatible
+	// skip LLNDK libraries which are backward compatible
 	if m.IsLlndk() {
 		return false
 	}
-	if _, ok := m.linker.(*llndkStubDecorator); ok {
-		return false
-	}
-	if _, ok := m.linker.(*llndkHeadersDecorator); 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/cmd/run_with_timeout/Android.bp b/cmd/run_with_timeout/Android.bp
new file mode 100644
index 0000000..76262cc
--- /dev/null
+++ b/cmd/run_with_timeout/Android.bp
@@ -0,0 +1,27 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "run_with_timeout",
+    srcs: [
+        "run_with_timeout.go",
+    ],
+    testSrcs: [
+        "run_with_timeout_test.go",
+    ],
+}
diff --git a/cmd/run_with_timeout/run_with_timeout.go b/cmd/run_with_timeout/run_with_timeout.go
new file mode 100644
index 0000000..f2caaab
--- /dev/null
+++ b/cmd/run_with_timeout/run_with_timeout.go
@@ -0,0 +1,143 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// run_with_timeout is a utility that can kill a wrapped command after a configurable timeout,
+// optionally running a command to collect debugging information first.
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"os/exec"
+	"sync"
+	"syscall"
+	"time"
+)
+
+var (
+	timeout      = flag.Duration("timeout", 0, "time after which to kill command (example: 60s)")
+	onTimeoutCmd = flag.String("on_timeout", "", "command to run with `PID=<pid> sh -c` after timeout.")
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: %s [--timeout N] [--on_timeout CMD] -- command [args...]\n", os.Args[0])
+	flag.PrintDefaults()
+	fmt.Fprintln(os.Stderr, "run_with_timeout is a utility that can kill a wrapped command after a configurable timeout,")
+	fmt.Fprintln(os.Stderr, "optionally running a command to collect debugging information first.")
+
+	os.Exit(2)
+}
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+
+	if flag.NArg() < 1 {
+		fmt.Fprintln(os.Stderr, "command is required")
+		usage()
+	}
+
+	err := runWithTimeout(flag.Arg(0), flag.Args()[1:], *timeout, *onTimeoutCmd,
+		os.Stdin, os.Stdout, os.Stderr)
+	if err != nil {
+		if exitErr, ok := err.(*exec.ExitError); ok {
+			fmt.Fprintln(os.Stderr, "process exited with error:", exitErr.Error())
+		} else {
+			fmt.Fprintln(os.Stderr, "error:", err.Error())
+		}
+		os.Exit(1)
+	}
+}
+
+// concurrentWriter wraps a writer to make it thread-safe to call Write.
+type concurrentWriter struct {
+	w io.Writer
+	sync.Mutex
+}
+
+// Write writes the data to the wrapped writer with a lock to allow for concurrent calls.
+func (c *concurrentWriter) Write(data []byte) (n int, err error) {
+	c.Lock()
+	defer c.Unlock()
+	if c.w == nil {
+		return 0, nil
+	}
+	return c.w.Write(data)
+}
+
+// Close ends the concurrentWriter, causing future calls to Write to be no-ops.  It does not close
+// the underlying writer.
+func (c *concurrentWriter) Close() {
+	c.Lock()
+	defer c.Unlock()
+	c.w = nil
+}
+
+func runWithTimeout(command string, args []string, timeout time.Duration, onTimeoutCmdStr string,
+	stdin io.Reader, stdout, stderr io.Writer) error {
+	cmd := exec.Command(command, args...)
+
+	// Wrap the writers in a locking writer so that cmd and onTimeoutCmd don't try to write to
+	// stdout or stderr concurrently.
+	concurrentStdout := &concurrentWriter{w: stdout}
+	concurrentStderr := &concurrentWriter{w: stderr}
+	defer concurrentStdout.Close()
+	defer concurrentStderr.Close()
+
+	cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, concurrentStdout, concurrentStderr
+	err := cmd.Start()
+	if err != nil {
+		return err
+	}
+
+	// waitCh will signal the subprocess exited.
+	waitCh := make(chan error)
+	go func() {
+		waitCh <- cmd.Wait()
+	}()
+
+	// timeoutCh will signal the subprocess timed out if timeout was set.
+	var timeoutCh <-chan time.Time = make(chan time.Time)
+	if timeout > 0 {
+		timeoutCh = time.After(timeout)
+	}
+
+	select {
+	case err := <-waitCh:
+		if exitErr, ok := err.(*exec.ExitError); ok {
+			return fmt.Errorf("process exited with error: %w", exitErr)
+		}
+		return err
+	case <-timeoutCh:
+		// Continue below.
+	}
+
+	// Process timed out before exiting.
+	defer cmd.Process.Signal(syscall.SIGKILL)
+
+	if onTimeoutCmdStr != "" {
+		onTimeoutCmd := exec.Command("sh", "-c", onTimeoutCmdStr)
+		onTimeoutCmd.Stdin, onTimeoutCmd.Stdout, onTimeoutCmd.Stderr = stdin, concurrentStdout, concurrentStderr
+		onTimeoutCmd.Env = append(os.Environ(), fmt.Sprintf("PID=%d", cmd.Process.Pid))
+		err := onTimeoutCmd.Run()
+		if err != nil {
+			return fmt.Errorf("on_timeout command %q exited with error: %w", onTimeoutCmdStr, err)
+		}
+	}
+
+	return fmt.Errorf("timed out after %s", timeout.String())
+}
diff --git a/cmd/run_with_timeout/run_with_timeout_test.go b/cmd/run_with_timeout/run_with_timeout_test.go
new file mode 100644
index 0000000..aebd336
--- /dev/null
+++ b/cmd/run_with_timeout/run_with_timeout_test.go
@@ -0,0 +1,94 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bytes"
+	"io"
+	"testing"
+	"time"
+)
+
+func Test_runWithTimeout(t *testing.T) {
+	type args struct {
+		command      string
+		args         []string
+		timeout      time.Duration
+		onTimeoutCmd string
+		stdin        io.Reader
+	}
+	tests := []struct {
+		name       string
+		args       args
+		wantStdout string
+		wantStderr string
+		wantErr    bool
+	}{
+		{
+			name: "no timeout",
+			args: args{
+				command: "echo",
+				args:    []string{"foo"},
+			},
+			wantStdout: "foo\n",
+		},
+		{
+			name: "timeout not reached",
+			args: args{
+				command: "echo",
+				args:    []string{"foo"},
+				timeout: 1 * time.Second,
+			},
+			wantStdout: "foo\n",
+		},
+		{
+			name: "timed out",
+			args: args{
+				command: "sh",
+				args:    []string{"-c", "sleep 1 && echo foo"},
+				timeout: 1 * time.Millisecond,
+			},
+			wantErr: true,
+		},
+		{
+			name: "on_timeout command",
+			args: args{
+				command:      "sh",
+				args:         []string{"-c", "sleep 1 && echo foo"},
+				timeout:      1 * time.Millisecond,
+				onTimeoutCmd: "echo bar",
+			},
+			wantStdout: "bar\n",
+			wantErr:    true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			stdout := &bytes.Buffer{}
+			stderr := &bytes.Buffer{}
+			err := runWithTimeout(tt.args.command, tt.args.args, tt.args.timeout, tt.args.onTimeoutCmd, tt.args.stdin, stdout, stderr)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("runWithTimeout() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if gotStdout := stdout.String(); gotStdout != tt.wantStdout {
+				t.Errorf("runWithTimeout() gotStdout = %v, want %v", gotStdout, tt.wantStdout)
+			}
+			if gotStderr := stderr.String(); gotStderr != tt.wantStderr {
+				t.Errorf("runWithTimeout() gotStderr = %v, want %v", gotStderr, tt.wantStderr)
+			}
+		})
+	}
+}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 7a41861..1844bce 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -49,7 +49,7 @@
 
 	ArtApexJars android.ConfiguredJarList // modules for jars that are in the ART APEX
 
-	SystemServerJars          []string                  // jars that form the system server
+	SystemServerJars          android.ConfiguredJarList // jars that form the system server
 	SystemServerApps          []string                  // apps that are loaded into system server
 	UpdatableSystemServerJars android.ConfiguredJarList // jars within apex that are loaded into system server
 	SpeedApps                 []string                  // apps that should be speed optimized
@@ -404,14 +404,14 @@
 		if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
 			// Found the source module, or prebuilt module that has replaced the source.
 			dex2oatModule = child
-			if p, ok := child.(android.PrebuiltInterface); ok && p.Prebuilt() != nil {
+			if android.IsModulePrebuilt(child) {
 				return false // If it's the prebuilt we're done.
 			} else {
 				return true // Recurse to check if the source has a prebuilt dependency.
 			}
 		}
 		if parent == dex2oatModule && ctx.OtherModuleDependencyTag(child) == android.PrebuiltDepTag {
-			if p, ok := child.(android.PrebuiltInterface); ok && p.Prebuilt() != nil && p.Prebuilt().UsePrebuilt() {
+			if p := android.GetEmbeddedPrebuilt(child); p != nil && p.UsePrebuilt() {
 				dex2oatModule = child // Found a prebuilt that should be used.
 			}
 		}
@@ -604,7 +604,7 @@
 		BootJars:                           android.EmptyConfiguredJarList(),
 		UpdatableBootJars:                  android.EmptyConfiguredJarList(),
 		ArtApexJars:                        android.EmptyConfiguredJarList(),
-		SystemServerJars:                   nil,
+		SystemServerJars:                   android.EmptyConfiguredJarList(),
 		SystemServerApps:                   nil,
 		UpdatableSystemServerJars:          android.EmptyConfiguredJarList(),
 		SpeedApps:                          nil,
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 84e486f..9d9234f 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -120,7 +120,7 @@
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
 	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
 	if global.OnlyPreoptBootImageAndSystemServer && !global.BootJars.ContainsJar(module.Name) &&
-		!contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
+		!global.SystemServerJars.ContainsJar(module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
 
@@ -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().
@@ -360,7 +362,7 @@
 
 	if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
 		var compilerFilter string
-		if contains(global.SystemServerJars, module.Name) {
+		if global.SystemServerJars.ContainsJar(module.Name) {
 			// Jars of system server, use the product option if it is set, speed otherwise.
 			if global.SystemServerCompilerFilter != "" {
 				compilerFilter = global.SystemServerCompilerFilter
@@ -414,7 +416,7 @@
 
 	// PRODUCT_SYSTEM_SERVER_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
 	// PRODUCT_OTHER_JAVA_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
-	if contains(global.SystemServerJars, module.Name) {
+	if global.SystemServerJars.ContainsJar(module.Name) {
 		if global.AlwaysSystemServerDebugInfo {
 			debugInfo = true
 		} else if global.NeverSystemServerDebugInfo {
@@ -522,7 +524,7 @@
 // from java subpackage to dexpreopt.
 func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
 	return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
-		return android.RemoveListFromList(global.SystemServerJars, global.UpdatableSystemServerJars.CopyOfJars())
+		return android.RemoveListFromList(global.SystemServerJars.CopyOfJars(), global.UpdatableSystemServerJars.CopyOfJars())
 	}).([]string)
 }
 
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 6e502b7..4dd383d 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -54,6 +54,9 @@
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
+	ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory)
+
+	ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
 }
 
 var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
@@ -84,6 +87,9 @@
 	// the recovery variant instead.
 	Vendor_ramdisk_available *bool
 
+	// Make this module available when building for debug ramdisk.
+	Debug_ramdisk_available *bool
+
 	// Make this module available when building for recovery.
 	Recovery_available *bool
 
@@ -120,6 +126,7 @@
 
 type PrebuiltEtc struct {
 	android.ModuleBase
+	android.DefaultableModuleBase
 
 	properties       prebuiltEtcProperties
 	subdirProperties prebuiltSubdirProperties
@@ -135,6 +142,11 @@
 	additionalDependencies *android.Paths
 }
 
+type Defaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
 func (p *PrebuiltEtc) inRamdisk() bool {
 	return p.ModuleBase.InRamdisk() || p.ModuleBase.InstallInRamdisk()
 }
@@ -159,6 +171,18 @@
 	return p.inVendorRamdisk()
 }
 
+func (p *PrebuiltEtc) inDebugRamdisk() bool {
+	return p.ModuleBase.InDebugRamdisk() || p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) onlyInDebugRamdisk() bool {
+	return p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) InstallInDebugRamdisk() bool {
+	return p.inDebugRamdisk()
+}
+
 func (p *PrebuiltEtc) inRecovery() bool {
 	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
 }
@@ -177,7 +201,7 @@
 
 func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk() &&
-		!p.ModuleBase.InstallInVendorRamdisk()
+		!p.ModuleBase.InstallInVendorRamdisk() && !p.ModuleBase.InstallInDebugRamdisk()
 }
 
 func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -188,6 +212,10 @@
 	return proptools.Bool(p.properties.Vendor_ramdisk_available) || p.ModuleBase.InstallInVendorRamdisk()
 }
 
+func (p *PrebuiltEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(p.properties.Debug_ramdisk_available) || p.ModuleBase.InstallInDebugRamdisk()
+}
+
 func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return proptools.Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
 }
@@ -313,6 +341,9 @@
 	if p.inVendorRamdisk() && !p.onlyInVendorRamdisk() {
 		nameSuffix = ".vendor_ramdisk"
 	}
+	if p.inDebugRamdisk() && !p.onlyInDebugRamdisk() {
+		nameSuffix = ".debug_ramdisk"
+	}
 	if p.inRecovery() && !p.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
@@ -355,6 +386,25 @@
 	InitPrebuiltEtcModule(module, "etc")
 	// This module is device-only
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+func defaultsFactory() android.Module {
+	return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+	module := &Defaults{}
+
+	module.AddProperties(props...)
+	module.AddProperties(
+		&prebuiltEtcProperties{},
+		&prebuiltSubdirProperties{},
+	)
+
+	android.InitDefaultsModule(module)
+
 	return module
 }
 
@@ -431,3 +481,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/genrule/genrule.go b/genrule/genrule.go
index b426b20..77dae75 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -626,6 +626,7 @@
 func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool            { return false }
 func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool         { return false }
 func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool   { return false }
+func (x noopImageInterface) DebugRamdiskVariantNeeded(android.BaseModuleContext) bool    { return false }
 func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool        { return false }
 func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
 func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
diff --git a/java/Android.bp b/java/Android.bp
index a17140c..623a6c5 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -65,6 +65,7 @@
         "sdk_library_external.go",
         "support_libraries.go",
         "system_modules.go",
+        "systemserver_classpath_fragment.go",
         "testing.go",
         "tradefed.go",
     ],
@@ -91,6 +92,7 @@
         "rro_test.go",
         "sdk_test.go",
         "system_modules_test.go",
+        "systemserver_classpath_fragment_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 6ca0f75..02833ab 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -15,9 +15,8 @@
 package java
 
 import (
-	"fmt"
-
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -70,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},
@@ -116,15 +117,20 @@
 // reportMissingVariationDependency intentionally adds a dependency on a missing variation in order
 // to generate an appropriate error message with information about the available variations.
 func reportMissingVariationDependency(ctx android.BottomUpMutatorContext, variations []blueprint.Variation, name string) {
-	modules := ctx.AddFarVariationDependencies(variations, nil, name)
-	if len(modules) != 1 {
-		panic(fmt.Errorf("Internal Error: expected one module, found %d", len(modules)))
-		return
-	}
-	if modules[0] != nil {
-		panic(fmt.Errorf("Internal Error: expected module to be missing but was found: %q", modules[0]))
-		return
-	}
+	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.
@@ -133,6 +139,9 @@
 	//
 	// 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.
@@ -170,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 2e33c06..6270b5b 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -32,9 +32,8 @@
 
 	android.RegisterSdkMemberType(&bootclasspathFragmentMemberType{
 		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName:         "bootclasspath_fragments",
-			SupportsSdk:          true,
-			TransitiveSdkMembers: true,
+			PropertyName: "bootclasspath_fragments",
+			SupportsSdk:  true,
 		},
 	})
 }
@@ -63,7 +62,7 @@
 
 // SdkMemberType causes dependencies added with this tag to be automatically added to the sdk as if
 // they were specified using java_boot_libs.
-func (b bootclasspathFragmentContentDependencyTag) SdkMemberType() android.SdkMemberType {
+func (b bootclasspathFragmentContentDependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType {
 	return javaBootLibsSdkMemberType
 }
 
@@ -88,6 +87,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,15 +144,10 @@
 		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.
-		if m.MemberName() != "" {
+		if android.IsModuleInVersionedSdk(m) {
 			// The module is a versioned prebuilt so ignore it. This is done for a couple of reasons:
 			// 1. There is no way to use this at the moment so ignoring it is safe.
 			// 2. Attempting to initialize the contents property from the configuration will end up having
@@ -178,7 +175,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 +187,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()
 	}
@@ -205,7 +207,7 @@
 	imageName := proptools.String(b.properties.Image_name)
 	if imageName == "art" {
 		// TODO(b/177892522): Prebuilts (versioned or not) should not use the image_name property.
-		if b.MemberName() != "" {
+		if android.IsModuleInVersionedSdk(b) {
 			// The module is a versioned prebuilt so ignore it. This is done for a couple of reasons:
 			// 1. There is no way to use this at the moment so ignoring it is safe.
 			// 2. Attempting to initialize the contents property from the configuration will end up having
@@ -316,6 +318,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 +389,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 +432,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 +450,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 +461,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/classpath_fragment.go b/java/classpath_fragment.go
index d497460..2e05823 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -51,6 +51,10 @@
 	android.Module
 
 	classpathFragmentBase() *ClasspathFragmentBase
+
+	// ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all
+	// the jars in this classpath fragment.
+	ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList
 }
 
 // ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
@@ -58,6 +62,8 @@
 type ClasspathFragmentBase struct {
 	properties classpathFragmentProperties
 
+	classpathType classpathType
+
 	outputFilepath android.OutputPath
 	installDirPath android.InstallPath
 }
@@ -67,8 +73,9 @@
 }
 
 // Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase.
-func initClasspathFragment(c classpathFragment) {
+func initClasspathFragment(c classpathFragment, classpathType classpathType) {
 	base := c.classpathFragmentBase()
+	base.classpathType = classpathType
 	c.AddProperties(&base.properties)
 }
 
@@ -81,16 +88,26 @@
 	maxSdkVersion int32
 }
 
-func (c *ClasspathFragmentBase) generateAndroidBuildActions(ctx android.ModuleContext) {
+// Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
+func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar {
+	paths := configuredJars.DevicePaths(ctx.Config(), android.Android)
+	jars := make([]classpathJar, 0, len(paths)*len(classpaths))
+	for i := 0; i < len(paths); i++ {
+		for _, classpathType := range classpaths {
+			jars = append(jars, classpathJar{
+				classpath: classpathType,
+				path:      paths[i],
+			})
+		}
+	}
+	return jars
+}
+
+func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) {
 	outputFilename := ctx.ModuleName() + ".pb"
 	c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
 	c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
 
-	var jars []classpathJar
-	jars = appendClasspathJar(jars, BOOTCLASSPATH, defaultBootclasspath(ctx)...)
-	jars = appendClasspathJar(jars, DEX2OATBOOTCLASSPATH, defaultBootImageConfig(ctx).getAnyAndroidVariant().dexLocationsDeps...)
-	jars = appendClasspathJar(jars, SYSTEMSERVERCLASSPATH, systemServerClasspath(ctx)...)
-
 	generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
 	writeClasspathsJson(ctx, generatedJson, jars)
 
@@ -126,19 +143,8 @@
 	android.WriteFileRule(ctx, output, content.String())
 }
 
-func appendClasspathJar(slice []classpathJar, classpathType classpathType, paths ...string) (result []classpathJar) {
-	result = append(result, slice...)
-	for _, path := range paths {
-		result = append(result, classpathJar{
-			path:      path,
-			classpath: classpathType,
-		})
-	}
-	return
-}
-
-func (c *ClasspathFragmentBase) getAndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
+func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(c.outputFilepath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
diff --git a/java/config/config.go b/java/config/config.go
index 30c6f91..273084c 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -69,6 +69,8 @@
 	pctx.StaticVariable("JavacHeapSize", "2048M")
 	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
 	pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
+	// TODO(b/181095653): remove duplicated flags.
+	pctx.StaticVariable("DexJavaFlags", "-XX:OnError='cat hs_err_pid%p.log' -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads -Xmx2G")
 
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
 		`-Xmaxerrs 9999999`,
diff --git a/java/dex.go b/java/dex.go
index 7898e9d..6bf0143 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -84,6 +84,11 @@
 	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
 }
 
+func init() {
+	pctx.HostBinToolVariable("runWithTimeoutCmd", "run_with_timeout")
+	pctx.SourcePathVariable("jstackCmd", "${config.JavaToolchain}/jstack")
+}
+
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -117,7 +122,10 @@
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
 			`mkdir -p $$(dirname ${outUsage}) && ` +
-			`$r8Template${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
+			// TODO(b/181095653): remove R8 timeout and go back to config.R8Cmd.
+			`${runWithTimeoutCmd} -timeout 30m -on_timeout '${jstackCmd} $$PID' -- ` +
+			`$r8Template${config.JavaCmd} ${config.DexJavaFlags} -cp ${config.R8Jar} ` +
+			`com.android.tools.r8.compatproguard.CompatProguard -injars $in --output $outDir ` +
 			`--no-data-resources ` +
 			`-printmapping ${outDict} ` +
 			`-printusage ${outUsage} ` +
@@ -128,9 +136,10 @@
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
-			"${config.R8Cmd}",
+			"${config.R8Jar}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
+			"${runWithTimeoutCmd}",
 		},
 	}, map[string]*remoteexec.REParams{
 		"$r8Template": &remoteexec.REParams{
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index ef3ca2c..8d23ad6 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -150,7 +150,7 @@
 
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	isSystemServerJar := inList(ctx.ModuleName(), global.SystemServerJars)
+	isSystemServerJar := global.SystemServerJars.ContainsJar(ctx.ModuleName())
 
 	bootImage := defaultBootImageConfig(ctx)
 	if global.UseArtImage {
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 19d1075..ce68c48 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -423,164 +423,106 @@
 	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
 
 	global := dexpreopt.GetGlobalConfig(ctx)
+	if !shouldBuildBootImages(ctx.Config(), global) {
+		return
+	}
 
+	// Generate the profile rule from the default boot image.
+	defaultImageConfig := defaultBootImageConfig(ctx)
+	profile := bootImageProfileRule(ctx, defaultImageConfig)
+
+	// Create the default boot image.
+	d.defaultBootImage = buildBootImage(ctx, defaultImageConfig, profile)
+
+	// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
+	d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx), profile))
+
+	copyUpdatableBootJars(ctx)
+}
+
+// shouldBuildBootImages determines whether boot images should be built.
+func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig) bool {
 	// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
 	// and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
 	// Note: this is technically incorrect. Compiled code contains stack checks which may depend
 	//       on ASAN settings.
-	if len(ctx.Config().SanitizeDevice()) == 1 &&
-		ctx.Config().SanitizeDevice()[0] == "address" &&
-		global.SanitizeLite {
-		return
+	if len(config.SanitizeDevice()) == 1 && config.SanitizeDevice()[0] == "address" && global.SanitizeLite {
+		return false
 	}
-
-	// Always create the default boot image first, to get a unique profile rule for all images.
-	d.defaultBootImage = buildBootImage(ctx, defaultBootImageConfig(ctx))
-	// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
-	d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx)))
-
-	copyUpdatableBootJars(ctx)
-
-	dumpOatRules(ctx, d.defaultBootImage)
+	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)) []string {
+// 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
-		}
-	})
 
-	var missingDeps []string
-	// Ensure all modules were converted to paths
-	for i := range jarPaths {
-		if jarPaths[i] == nil {
-			m := bootjars.Jar(i)
-			if ctx.Config().AllowMissingDependencies() {
-				missingDeps = append(missingDeps, m)
-				jarPaths[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex",
-					bootjars.Apex(i))
-			} else {
-				ctx.Errorf("failed to find a dex jar path for module '%s'"+
-					", note that some jars may be filtered out by module constraints", m)
-			}
+		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
 		}
 	}
 
@@ -589,30 +531,45 @@
 	// Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined
 	// paths.
 	for i := range jarPaths {
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Input:  jarPaths[i],
-			Output: jarPathsPredefined[i],
-		})
-	}
+		input := jarPaths[i]
+		output := jarPathsPredefined[i]
+		module := bootjars.Jar(i)
+		if input == nil {
+			if ctx.Config().AllowMissingDependencies() {
+				apex := bootjars.Apex(i)
 
-	return missingDeps
+				// 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: output,
+					Args: map[string]string{
+						"error": fmt.Sprintf("missing dependencies: dex jar for %s:%s", module, apex),
+					},
+				})
+			} else {
+				ctx.Errorf("failed to find a dex jar path for module '%s'"+
+					", note that some jars may be filtered out by module constraints", module)
+			}
+
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Cp,
+				Input:  input,
+				Output: output,
+			})
+		}
+	}
 }
 
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
-func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
-	getBootJarFunc := func(module android.Module) (int, android.Path) {
-		return getBootImageJar(ctx, image, module)
-	}
-	missingDeps := findAndCopyBootJars(ctx, image.modules, image.dexPaths, getBootJarFunc)
-
-	profile := bootImageProfileRule(ctx, image, missingDeps)
-	bootFrameworkProfileRule(ctx, image, missingDeps)
-	updatableBcpPackagesRule(ctx, image, missingDeps)
+func buildBootImage(ctx android.SingletonContext, image *bootImageConfig, profile android.WritablePath) *bootImageConfig {
+	bootModules := findBootJarModules(ctx, image.modules)
+	copyBootJarsToPredefinedLocations(ctx, bootModules, image.modules, image.dexPaths)
 
 	var zipFiles android.Paths
 	for _, variant := range image.variants {
-		files := buildBootImageVariant(ctx, variant, profile, missingDeps)
+		files := buildBootImageVariant(ctx, variant, profile)
 		if variant.target.Os == android.Android {
 			zipFiles = append(zipFiles, files.Paths()...)
 		}
@@ -635,21 +592,12 @@
 // 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
-	}
-	missingDeps := findAndCopyBootJars(ctx, config.modules, config.dexPaths, getBootJarFunc)
-	// Ignoring missing dependencies here. Ideally they should be added to the dexpreopt rule, but
-	// that is not possible as this rule is created after dexpreopt rules (it's in a singleton
-	// context, and they are in a module context). The true fix is to add dependencies from the
-	// dexpreopted modules on updatable boot jars and avoid this copying altogether.
-	_ = missingDeps
+	bootModules := findBootJarModules(ctx, config.modules)
+	copyBootJarsToPredefinedLocations(ctx, bootModules, config.modules, config.dexPaths)
 }
 
 // Generate boot image build rules for a specific target.
-func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant,
-	profile android.Path, missingDeps []string) android.WritablePaths {
+func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant, profile android.Path) android.WritablePaths {
 
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
@@ -664,7 +612,6 @@
 	imagePath := outputPath.ReplaceExtension(ctx, "art")
 
 	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.MissingDeps(missingDeps)
 
 	rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String())
 	rule.Command().Text("rm").Flag("-f").
@@ -800,160 +747,121 @@
 It is likely that the boot classpath is inconsistent.
 Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
 
-func bootImageProfileRule(ctx android.SingletonContext, image *bootImageConfig, missingDeps []string) android.WritablePath {
+func bootImageProfileRule(ctx android.SingletonContext, image *bootImageConfig) android.WritablePath {
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	if global.DisableGenerateProfile {
 		return nil
 	}
-	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
-		defaultProfile := "frameworks/base/config/boot-image-profile.txt"
 
-		rule := android.NewRuleBuilder(pctx, ctx)
-		rule.MissingDeps(missingDeps)
+	defaultProfile := "frameworks/base/config/boot-image-profile.txt"
 
-		var bootImageProfile android.Path
-		if len(global.BootImageProfiles) > 1 {
-			combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
-			rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile)
-			bootImageProfile = combinedBootImageProfile
-		} else if len(global.BootImageProfiles) == 1 {
-			bootImageProfile = global.BootImageProfiles[0]
-		} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
-			bootImageProfile = path.Path()
-		} else {
-			// No profile (not even a default one, which is the case on some branches
-			// like master-art-host that don't have frameworks/base).
-			// Return nil and continue without profile.
-			return nil
-		}
+	rule := android.NewRuleBuilder(pctx, ctx)
 
-		profile := image.dir.Join(ctx, "boot.prof")
-
-		rule.Command().
-			Text(`ANDROID_LOG_TAGS="*:e"`).
-			Tool(globalSoong.Profman).
-			Flag("--output-profile-type=boot").
-			FlagWithInput("--create-profile-from=", bootImageProfile).
-			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
-			FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
-			FlagWithOutput("--reference-profile-file=", profile)
-
-		rule.Install(profile, "/system/etc/boot-image.prof")
-
-		rule.Build("bootJarsProfile", "profile boot jars")
-
-		image.profileInstalls = rule.Installs()
-
-		return profile
-	})
-	if profile == nil {
-		return nil // wrap nil into a typed pointer with value nil
+	var bootImageProfile android.Path
+	if len(global.BootImageProfiles) > 1 {
+		combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
+		rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile)
+		bootImageProfile = combinedBootImageProfile
+	} else if len(global.BootImageProfiles) == 1 {
+		bootImageProfile = global.BootImageProfiles[0]
+	} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
+		bootImageProfile = path.Path()
+	} else {
+		// No profile (not even a default one, which is the case on some branches
+		// like master-art-host that don't have frameworks/base).
+		// Return nil and continue without profile.
+		return nil
 	}
-	return profile.(android.WritablePath)
+
+	profile := image.dir.Join(ctx, "boot.prof")
+
+	rule.Command().
+		Text(`ANDROID_LOG_TAGS="*:e"`).
+		Tool(globalSoong.Profman).
+		Flag("--output-profile-type=boot").
+		FlagWithInput("--create-profile-from=", bootImageProfile).
+		FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
+		FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
+		FlagWithOutput("--reference-profile-file=", profile)
+
+	rule.Install(profile, "/system/etc/boot-image.prof")
+
+	rule.Build("bootJarsProfile", "profile boot jars")
+
+	image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+
+	return profile
 }
 
-var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
-
-func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImageConfig, missingDeps []string) android.WritablePath {
-	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+// bootFrameworkProfileRule generates the rule to create the boot framework profile and
+// returns a path to the generated file.
+func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
+	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
 		return nil
 	}
-	return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
-		rule := android.NewRuleBuilder(pctx, ctx)
-		rule.MissingDeps(missingDeps)
 
-		// Some branches like master-art-host don't have frameworks/base, so manually
-		// handle the case that the default is missing.  Those branches won't attempt to build the profile rule,
-		// and if they do they'll get a missing deps error.
-		defaultProfile := "frameworks/base/config/boot-profile.txt"
-		path := android.ExistentPathForSource(ctx, defaultProfile)
-		var bootFrameworkProfile android.Path
-		if path.Valid() {
-			bootFrameworkProfile = path.Path()
-		} else {
-			missingDeps = append(missingDeps, defaultProfile)
-			bootFrameworkProfile = android.PathForOutput(ctx, "missing", defaultProfile)
-		}
+	defaultProfile := "frameworks/base/config/boot-profile.txt"
+	bootFrameworkProfile := android.PathForSource(ctx, defaultProfile)
 
-		profile := image.dir.Join(ctx, "boot.bprof")
+	profile := image.dir.Join(ctx, "boot.bprof")
 
-		rule.Command().
-			Text(`ANDROID_LOG_TAGS="*:e"`).
-			Tool(globalSoong.Profman).
-			Flag("--output-profile-type=bprof").
-			FlagWithInput("--create-profile-from=", bootFrameworkProfile).
-			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
-			FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
-			FlagWithOutput("--reference-profile-file=", profile)
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		Text(`ANDROID_LOG_TAGS="*:e"`).
+		Tool(globalSoong.Profman).
+		Flag("--output-profile-type=bprof").
+		FlagWithInput("--create-profile-from=", bootFrameworkProfile).
+		FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
+		FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
+		FlagWithOutput("--reference-profile-file=", profile)
 
-		rule.Install(profile, "/system/etc/boot-image.bprof")
-		rule.Build("bootFrameworkProfile", "profile boot framework jars")
-		image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+	rule.Install(profile, "/system/etc/boot-image.bprof")
+	rule.Build("bootFrameworkProfile", "profile boot framework jars")
+	image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
 
-		return profile
-	}).(android.WritablePath)
+	return profile
 }
 
-var bootFrameworkProfileRuleKey = android.NewOnceKey("bootFrameworkProfileRule")
-
-func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConfig, missingDeps []string) android.WritablePath {
-	if ctx.Config().UnbundledBuild() {
-		return nil
+// generateUpdatableBcpPackagesRule generates the rule to create the updatable-bcp-packages.txt file
+// and returns a path to the generated file.
+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 {
+		if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
+			pp := j.PermittedPackagesForUpdatableBootJars()
+			if len(pp) > 0 {
+				updatablePackages = append(updatablePackages, pp...)
+			} else {
+				ctx.ModuleErrorf("Missing permitted_packages")
+			}
+		}
 	}
 
-	return ctx.Config().Once(updatableBcpPackagesRuleKey, func() interface{} {
-		global := dexpreopt.GetGlobalConfig(ctx)
-		updatableModules := global.UpdatableBootJars.CopyOfJars()
+	// Sort updatable packages to ensure deterministic ordering.
+	sort.Strings(updatablePackages)
 
-		// Collect `permitted_packages` for updatable boot jars.
-		var updatablePackages []string
-		ctx.VisitAllModules(func(module android.Module) {
-			if !isActiveModule(module) {
-				return
-			}
-			if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
-				name := ctx.ModuleName(module)
-				if i := android.IndexList(name, updatableModules); i != -1 {
-					pp := j.PermittedPackagesForUpdatableBootJars()
-					if len(pp) > 0 {
-						updatablePackages = append(updatablePackages, pp...)
-					} else {
-						ctx.Errorf("Missing permitted_packages for %s", name)
-					}
-					// Do not match the same library repeatedly.
-					updatableModules = append(updatableModules[:i], updatableModules[i+1:]...)
-				}
-			}
-		})
+	updatableBcpPackagesName := "updatable-bcp-packages.txt"
+	updatableBcpPackages := image.dir.Join(ctx, updatableBcpPackagesName)
 
-		// Sort updatable packages to ensure deterministic ordering.
-		sort.Strings(updatablePackages)
+	// WriteFileRule automatically adds the last end-of-line.
+	android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n"))
 
-		updatableBcpPackagesName := "updatable-bcp-packages.txt"
-		updatableBcpPackages := image.dir.Join(ctx, updatableBcpPackagesName)
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Install(updatableBcpPackages, "/system/etc/"+updatableBcpPackagesName)
+	// TODO: Rename `profileInstalls` to `extraInstalls`?
+	// Maybe even move the field out of the bootImageConfig into some higher level type?
+	image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
 
-		// WriteFileRule automatically adds the last end-of-line.
-		android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n"))
-
-		rule := android.NewRuleBuilder(pctx, ctx)
-		rule.MissingDeps(missingDeps)
-		rule.Install(updatableBcpPackages, "/system/etc/"+updatableBcpPackagesName)
-		// TODO: Rename `profileInstalls` to `extraInstalls`?
-		// Maybe even move the field out of the bootImageConfig into some higher level type?
-		image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
-
-		return updatableBcpPackages
-	}).(android.WritablePath)
+	return updatableBcpPackages
 }
 
-var updatableBcpPackagesRuleKey = android.NewOnceKey("updatableBcpPackagesRule")
-
-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
@@ -994,7 +902,6 @@
 		Inputs:      allPhonies,
 		Description: "dump-oat-boot",
 	})
-
 }
 
 func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 9eb3f49..7fb0444 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -39,10 +39,9 @@
 		// 2) The jars that are from an updatable apex.
 		systemServerClasspathLocations = append(systemServerClasspathLocations,
 			global.UpdatableSystemServerJars.DevicePaths(ctx.Config(), android.Android)...)
-		if len(systemServerClasspathLocations) != len(global.SystemServerJars)+global.UpdatableSystemServerJars.Len() {
-			panic(fmt.Errorf("Wrong number of system server jars, got %d, expected %d",
-				len(systemServerClasspathLocations),
-				len(global.SystemServerJars)+global.UpdatableSystemServerJars.Len()))
+
+		if expectedLen := global.SystemServerJars.Len() + global.UpdatableSystemServerJars.Len(); expectedLen != len(systemServerClasspathLocations) {
+			panic(fmt.Errorf("wrong number of system server jars, got %d, expected %d", len(systemServerClasspathLocations), expectedLen))
 		}
 		return systemServerClasspathLocations
 	})
@@ -164,18 +163,6 @@
 	return genBootImageConfigs(ctx)[frameworkBootImageName]
 }
 
-func defaultBootclasspath(ctx android.PathContext) []string {
-	return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string {
-		global := dexpreopt.GetGlobalConfig(ctx)
-		image := defaultBootImageConfig(ctx)
-
-		updatableBootclasspath := global.UpdatableBootJars.DevicePaths(ctx.Config(), android.Android)
-
-		bootclasspath := append(copyOf(image.getAnyAndroidVariant().dexLocationsDeps), updatableBootclasspath...)
-		return bootclasspath
-	})
-}
-
 // Updatable boot config allows to access build/install paths of updatable boot jars without going
 // through the usual trouble of registering dependencies on those modules and extracting build paths
 // from those dependencies.
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 01c0f16..56e6247 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -193,23 +193,6 @@
 	return false
 }
 
-// Used by xsd_config
-type ApiFilePath interface {
-	ApiFilePath() android.Path
-}
-
-type ApiStubsSrcProvider interface {
-	StubsSrcJar() android.Path
-}
-
-// Provider of information about API stubs, used by java_sdk_library.
-type ApiStubsProvider interface {
-	ApiFilePath
-	RemovedApiFilePath() android.Path
-
-	ApiStubsSrcProvider
-}
-
 //
 // Javadoc
 //
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 2676f3d..566f7e3 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -146,6 +146,23 @@
 	Write_sdk_values *bool
 }
 
+// Used by xsd_config
+type ApiFilePath interface {
+	ApiFilePath() android.Path
+}
+
+type ApiStubsSrcProvider interface {
+	StubsSrcJar() android.Path
+}
+
+// Provider of information about API stubs, used by java_sdk_library.
+type ApiStubsProvider interface {
+	ApiFilePath
+	RemovedApiFilePath() android.Path
+
+	ApiStubsSrcProvider
+}
+
 // droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
 // documented, filtering out hidden classes and methods.  The resulting .java files are intended to be passed to
 // a droiddoc module to generate documentation.
@@ -283,6 +300,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 8cc6f8f..335f5b8 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -33,6 +33,21 @@
 	return false
 }
 
+func (b hiddenAPIStubsDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType {
+	// If the module is a java_sdk_library then treat it as if it was specific in the java_sdk_libs
+	// property, otherwise treat if it was specified in the java_header_libs property.
+	if javaSdkLibrarySdkMemberType.IsInstance(child) {
+		return javaSdkLibrarySdkMemberType
+	}
+
+	return javaHeaderLibsSdkMemberType
+}
+
+func (b hiddenAPIStubsDependencyTag) ExportMember() bool {
+	// Export the module added via this dependency tag from the sdk.
+	return true
+}
+
 // Avoid having to make stubs content explicitly visible to dependent modules.
 //
 // This is a temporary workaround to make it easier to migrate to bootclasspath_fragment modules
@@ -44,6 +59,7 @@
 var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
 var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
 var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
+var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{}
 
 // hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden
 // API processing.
@@ -111,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)
 			}
@@ -122,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/java.go b/java/java.go
index 7258dce..d74bf68 100644
--- a/java/java.go
+++ b/java/java.go
@@ -669,6 +669,7 @@
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
 	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostSupported)
 	return module
 }
@@ -923,6 +924,7 @@
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.test = true
 
+	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
 }
@@ -1296,9 +1298,10 @@
 		if ai.ForPrebuiltApex {
 			if deapexerModule == nil {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
-				// deapxer module has been configured to export the dex implementation jar for this module.
+				// deapexer module has been configured to export the dex implementation jar for this module.
 				ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
 					j.Name(), ai.ApexVariationName)
+				return
 			}
 
 			// Get the path of the dex implementation jar from the `deapexer` module.
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 05b8e2f..1caffe4 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,11 +69,11 @@
 	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.
-	initClasspathFragment(m)
+	// TODO(satayev): split apex jars into separate configs.
+	initClasspathFragment(m, BOOTCLASSPATH)
 	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	return m
 }
@@ -82,7 +87,7 @@
 		OutputFile: android.OptionalPathForPath(b.hiddenAPIFlagsCSV),
 		Include:    "$(BUILD_PHONY_PACKAGE)",
 	})
-	entries = append(entries, b.classpathFragmentBase().getAndroidMkEntries()...)
+	entries = append(entries, b.classpathFragmentBase().androidMkEntries()...)
 	return
 }
 
@@ -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)
 	}
 }
 
-func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	b.classpathFragmentBase().generateAndroidBuildActions(ctx)
+// 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
+}
 
-	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)
-		}
-	})
+func (d *platformBootclasspathModule) MakeVars(ctx android.MakeVarsContext) {
+	d.generateHiddenApiMakeVars(ctx)
+}
+
+func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// 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.generateClasspathProtoBuildActions(ctx)
 
 	b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 
@@ -168,15 +196,77 @@
 		return
 	}
 
-	// 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)
+	b.generateBootImageBuildActions(ctx, updatableModules)
+}
+
+// Generate classpaths.proto config
+func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
+	// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
+	classpathJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+	// TODO(satayev): remove updatable boot jars once each apex has its own fragment
+	global := dexpreopt.GetGlobalConfig(ctx)
+	classpathJars = append(classpathJars, configuredJarListToClasspathJars(ctx, global.UpdatableBootJars, BOOTCLASSPATH)...)
+
+	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+}
+
+func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	// TODO(satayev): split ART apex jars into their own classpathFragment
+	return global.BootJars
+}
+
+// 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) {
 
@@ -199,27 +289,57 @@
 		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 if _, ok := module.(*DexImport); ok {
+			// Ignore this for the purposes of hidden API processing
 		} else {
 			ctx.ModuleErrorf("module %s of type %s does not support hidden API processing", module, ctx.OtherModuleType(module))
 		}
@@ -227,7 +347,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)
@@ -253,7 +373,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)
@@ -266,7 +386,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)
@@ -282,7 +402,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)
@@ -297,3 +417,38 @@
 
 	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, 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)
+
+	imageConfig := b.getImageConfig(ctx)
+	if imageConfig == nil {
+		return
+	}
+
+	global := dexpreopt.GetGlobalConfig(ctx)
+	if !shouldBuildBootImages(ctx.Config(), global) {
+		return
+	}
+
+	// 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..98d4614 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,
 			}
 		`),
 	)
@@ -132,6 +133,23 @@
 			"platform:bar",
 		})
 	})
+
+	t.Run("dex import", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			preparer,
+			android.FixtureAddTextFile("deximport/Android.bp", `
+				dex_import {
+					name: "foo",
+					jars: ["a.jar"],
+				}
+			`),
+		).RunTest(t)
+
+		CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{
+			"platform:prebuilt_foo",
+			"platform:bar",
+		})
+	})
 }
 
 func TestPlatformBootclasspath_Fragments(t *testing.T) {
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index edfa146..712c2a2 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -226,10 +226,8 @@
 func isModulePreferredByCompatConfig(module android.Module) bool {
 	// A versioned prebuilt_platform_compat_config, i.e. foo-platform-compat-config@current should be
 	// ignored.
-	if s, ok := module.(android.SdkAware); ok {
-		if !s.ContainingSdk().Unversioned() {
-			return false
-		}
+	if android.IsModuleInVersionedSdk(module) {
+		return false
 	}
 
 	return android.IsModulePreferred(module)
diff --git a/java/sdk.go b/java/sdk.go
index 1c097d5..cbd873d 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -356,6 +356,7 @@
 			"frameworks-base-api-current.txt",
 			"frameworks-base-api-system-current.txt",
 			"frameworks-base-api-module-lib-current.txt",
+			"services-system-server-current.txt",
 		}
 		count := 0
 		ctx.VisitAllModules(func(module android.Module) {
@@ -369,8 +370,7 @@
 			ctx.Errorf("Could not find all the expected API modules %v, found %d\n", apiTxtFileModules, count)
 			return
 		}
-		cmd.Input(android.PathForSource(ctx, "frameworks/base/services/api/current.txt")).
-			Text("| md5sum | cut -d' ' -f1 >").
+		cmd.Text("| md5sum | cut -d' ' -f1 >").
 			Output(out)
 	} else {
 		// Unbundled build
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 05ce97a..aff4539 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -339,12 +339,7 @@
 	})
 
 	// Register sdk member types.
-	android.RegisterSdkMemberType(&sdkLibrarySdkMemberType{
-		android.SdkMemberTypeBase{
-			PropertyName: "java_sdk_libs",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(javaSdkLibrarySdkMemberType)
 }
 
 func RegisterSdkLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -1761,6 +1756,7 @@
 
 	module.InitSdkLibraryProperties()
 	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 
 	// Initialize the map from scope to scope specific properties.
@@ -2377,15 +2373,19 @@
 	return &sdkLibrarySdkMemberProperties{}
 }
 
+var javaSdkLibrarySdkMemberType = &sdkLibrarySdkMemberType{
+	android.SdkMemberTypeBase{
+		PropertyName: "java_sdk_libs",
+		SupportsSdk:  true,
+	},
+}
+
 type sdkLibrarySdkMemberProperties struct {
 	android.SdkMemberPropertiesBase
 
 	// Scope to per scope properties.
 	Scopes map[*apiScope]scopeProperties
 
-	// Additional libraries that the exported stubs libraries depend upon.
-	Libs []string
-
 	// The Java stubs source files.
 	Stub_srcs []string
 
@@ -2437,7 +2437,6 @@
 		}
 	}
 
-	s.Libs = sdk.properties.Libs
 	s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme
 	s.Shared_library = proptools.BoolPtr(sdk.sharedLibrary())
 	s.Compile_dex = sdk.dexProperties.Compile_dex
@@ -2502,8 +2501,4 @@
 		}
 		propertySet.AddProperty("doctag_files", dests)
 	}
-
-	if len(s.Libs) > 0 {
-		propertySet.AddPropertyWithTag("libs", s.Libs, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(false))
-	}
 }
diff --git a/java/system_modules.go b/java/system_modules.go
index a09778c..d0dc74a 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -35,9 +35,8 @@
 	// Register sdk member types.
 	android.RegisterSdkMemberType(&systemModulesSdkMemberType{
 		android.SdkMemberTypeBase{
-			PropertyName:         "java_system_modules",
-			SupportsSdk:          true,
-			TransitiveSdkMembers: true,
+			PropertyName: "java_system_modules",
+			SupportsSdk:  true,
 		},
 	})
 }
@@ -115,6 +114,7 @@
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitSdkAwareModule(module)
 	return module
 }
 
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
new file mode 100644
index 0000000..82cdb89
--- /dev/null
+++ b/java/systemserver_classpath_fragment.go
@@ -0,0 +1,67 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"android/soong/dexpreopt"
+)
+
+func init() {
+	registerSystemserverClasspathBuildComponents(android.InitRegistrationContext)
+}
+
+func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) {
+	// TODO(satayev): add systemserver_classpath_fragment module
+	ctx.RegisterModuleType("platform_systemserverclasspath", platformSystemServerClasspathFactory)
+}
+
+type platformSystemServerClasspathModule struct {
+	android.ModuleBase
+
+	ClasspathFragmentBase
+}
+
+func platformSystemServerClasspathFactory() android.Module {
+	m := &platformSystemServerClasspathModule{}
+	initClasspathFragment(m, SYSTEMSERVERCLASSPATH)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+func (b *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) {
+	return b.classpathFragmentBase().androidMkEntries()
+}
+
+func (b *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	configuredJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType)
+	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars)
+}
+
+var platformSystemServerClasspathKey = android.NewOnceKey("platform_systemserverclasspath")
+
+func (b *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+	return ctx.Config().Once(platformSystemServerClasspathKey, func() interface{} {
+		global := dexpreopt.GetGlobalConfig(ctx)
+
+		jars := global.SystemServerJars
+
+		// TODO(satayev): split apex jars into separate configs.
+		for i := 0; i < global.UpdatableSystemServerJars.Len(); i++ {
+			jars = jars.Append(global.UpdatableSystemServerJars.Apex(i), global.UpdatableSystemServerJars.Jar(i))
+		}
+		return jars
+	}).(android.ConfiguredJarList)
+}
diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go
new file mode 100644
index 0000000..6126d5e
--- /dev/null
+++ b/java/systemserver_classpath_fragment_test.go
@@ -0,0 +1,97 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+var prepareForTestWithSystemserverClasspath = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+)
+
+func TestSystemserverClasspathVariant(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithSystemserverClasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_systemserverclasspath {
+				name: "platform-systemserverclasspath",
+			}
+		`),
+	).RunTest(t)
+
+	variants := result.ModuleVariantsForTests("platform-systemserverclasspath")
+	android.AssertIntEquals(t, "expect 1 variant", 1, len(variants))
+}
+
+func TestSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithSystemserverClasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_systemserverclasspath {
+				name: "platform-systemserverclasspath",
+			}
+		`),
+	).RunTest(t)
+
+	p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
+	android.AssertStringEquals(t, "output filepath", p.Name()+".pb", p.ClasspathFragmentBase.outputFilepath.Base())
+	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+}
+
+func TestSystemserverClasspathModule_AndroidMkEntries(t *testing.T) {
+	preparer := android.GroupFixturePreparers(
+		prepareForTestWithSystemserverClasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_systemserverclasspath {
+				name: "platform-systemserverclasspath",
+			}
+		`),
+	)
+
+	t.Run("AndroidMkEntries", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
+
+		entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+		android.AssertIntEquals(t, "AndroidMkEntries count", 1, len(entries))
+	})
+
+	t.Run("classpath-fragment-entry", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		want := map[string][]string{
+			"LOCAL_MODULE":                {"platform-systemserverclasspath"},
+			"LOCAL_MODULE_CLASS":          {"ETC"},
+			"LOCAL_INSTALLED_MODULE_STEM": {"platform-systemserverclasspath.pb"},
+			// Output and Install paths are tested separately in TestSystemserverClasspath_ClasspathFragmentPaths
+		}
+
+		p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
+
+		entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+		got := entries[0]
+		for k, expectedValue := range want {
+			if value, ok := got.EntryMap[k]; ok {
+				android.AssertDeepEquals(t, k, expectedValue, value)
+			} else {
+				t.Errorf("No %s defined, saw %q", k, got.EntryMap)
+			}
+		}
+	})
+}
diff --git a/java/testing.go b/java/testing.go
index 649d27b..37e63f5 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -244,6 +244,7 @@
 	RegisterSdkLibraryBuildComponents(ctx)
 	RegisterStubsBuildComponents(ctx)
 	RegisterSystemModulesBuildComponents(ctx)
+	registerSystemserverClasspathBuildComponents(ctx)
 }
 
 // gatherRequiredDepsForTest gathers the module definitions used by
diff --git a/python/python.go b/python/python.go
index 4444a70..0f5b788 100644
--- a/python/python.go
+++ b/python/python.go
@@ -444,11 +444,10 @@
 		var sharedLibs []string
 		// if embedded launcher is enabled, we need to collect the shared library depenendencies of the
 		// launcher
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			if ctx.OtherModuleDependencyTag(dep) == launcherSharedLibTag {
-				sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
-			}
-		})
+		for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
+			sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+		}
+
 		p.installer.setAndroidMkSharedLibs(sharedLibs)
 
 		// Install the par file from installSource
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 5f89d73..ea45ebd 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -91,7 +91,6 @@
 	if binary.distFile.Valid() {
 		ret.DistFiles = android.MakeDefaultDistFiles(binary.distFile.Path())
 	}
-
 	ret.Class = "EXECUTABLES"
 }
 
@@ -201,3 +200,36 @@
 			entries.SetString("LOCAL_MODULE_STEM", stem)
 		})
 }
+
+func (fuzz *fuzzDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	ctx.SubAndroidMk(entries, fuzz.binaryDecorator)
+
+	var fuzzFiles []string
+	for _, d := range fuzz.corpus {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.corpusIntermediateDir.String())+":corpus/"+d.Base())
+	}
+
+	for _, d := range fuzz.data {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.dataIntermediateDir.String())+":data/"+d.Rel())
+	}
+
+	if fuzz.dictionary != nil {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.dictionary.String())+":"+fuzz.dictionary.Base())
+	}
+
+	if fuzz.config != nil {
+		fuzzFiles = append(fuzzFiles,
+			filepath.Dir(fuzz.config.String())+":config.json")
+	}
+
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
+		entries *android.AndroidMkEntries) {
+		entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
+		if len(fuzzFiles) > 0 {
+			entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
+		}
+	})
+}
diff --git a/rust/builder.go b/rust/builder.go
index 1fcce38..6db508d 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -47,8 +47,7 @@
 	_       = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
 	rustdoc = pctx.AndroidStaticRule("rustdoc",
 		blueprint.RuleParams{
-			Command: "rm -rf $outDir && " +
-				"$envVars $rustdocCmd $rustdocFlags $in -o $outDir && " +
+			Command: "$envVars $rustdocCmd $rustdocFlags $in -o $outDir && " +
 				"touch $out",
 			CommandDeps: []string{"$rustdocCmd"},
 		},
@@ -307,6 +306,10 @@
 	rustdocFlags := append([]string{}, flags.RustdocFlags...)
 	rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null")
 
+	// Build an index for all our crates. -Z unstable options is required to use
+	// this flag.
+	rustdocFlags = append(rustdocFlags, "-Z", "unstable-options", "--enable-index-page")
+
 	targetTriple := ctx.toolchain().RustTriple()
 
 	// Collect rustc flags
@@ -315,13 +318,17 @@
 	}
 
 	crateName := ctx.RustModule().CrateName()
-	if crateName != "" {
-		rustdocFlags = append(rustdocFlags, "--crate-name "+crateName)
-	}
+	rustdocFlags = append(rustdocFlags, "--crate-name "+crateName)
 
 	rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...)
 	docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp")
-	docDir := android.PathForOutput(ctx, "rustdoc", ctx.ModuleName())
+
+	// Yes, the same out directory is used simultaneously by all rustdoc builds.
+	// This is what cargo does. The docs for individual crates get generated to
+	// a subdirectory named for the crate, and rustdoc synchronizes writes to
+	// shared pieces like the index and search data itself.
+	// https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/render/write_shared.rs#L144-L146
+	docDir := android.PathForOutput(ctx, "rustdoc")
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rustdoc,
diff --git a/rust/fuzz.go b/rust/fuzz.go
index d699971..7e1c55a 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -15,6 +15,10 @@
 package rust
 
 import (
+	"path/filepath"
+	"sort"
+	"strings"
+
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/rust/config"
@@ -22,6 +26,7 @@
 
 func init() {
 	android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
+	android.RegisterSingletonType("rust_fuzz_packaging", rustFuzzPackagingFactory)
 }
 
 type fuzzDecorator struct {
@@ -93,3 +98,204 @@
 func (fuzzer *fuzzDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
 	return rlibAutoDep
 }
+
+// Responsible for generating GNU Make rules that package fuzz targets into
+// their architecture & target/host specific zip file.
+type rustFuzzPackager struct {
+	packages    android.Paths
+	fuzzTargets map[string]bool
+}
+
+func rustFuzzPackagingFactory() android.Singleton {
+	return &rustFuzzPackager{}
+}
+
+type fileToZip struct {
+	SourceFilePath        android.Path
+	DestinationPathPrefix string
+}
+
+type archOs struct {
+	hostOrTarget string
+	arch         string
+	dir          string
+}
+
+func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+
+	// Map between each architecture + host/device combination.
+	archDirs := make(map[archOs][]fileToZip)
+
+	// List of individual fuzz targets.
+	s.fuzzTargets = make(map[string]bool)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		// Discard non-fuzz targets.
+		rustModule, ok := module.(*Module)
+		if !ok {
+			return
+		}
+
+		fuzzModule, ok := rustModule.compiler.(*fuzzDecorator)
+		if !ok {
+			return
+		}
+
+		// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
+		// fuzz targets we're going to package anyway.
+		if !rustModule.Enabled() || rustModule.Properties.PreventInstall ||
+			rustModule.InRamdisk() || rustModule.InVendorRamdisk() || rustModule.InRecovery() {
+			return
+		}
+
+		// Discard modules that are in an unavailable namespace.
+		if !rustModule.ExportedToMake() {
+			return
+		}
+
+		hostOrTargetString := "target"
+		if rustModule.Host() {
+			hostOrTargetString = "host"
+		}
+
+		archString := rustModule.Arch().ArchType.String()
+		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
+		archOs := archOs{hostOrTarget: hostOrTargetString, arch: archString, dir: archDir.String()}
+
+		var files []fileToZip
+		builder := android.NewRuleBuilder(pctx, ctx)
+
+		// Package the corpora into a zipfile.
+		if fuzzModule.corpus != nil {
+			corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
+			command := builder.Command().BuiltTool("soong_zip").
+				Flag("-j").
+				FlagWithOutput("-o ", corpusZip)
+			rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
+			command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus)
+			files = append(files, fileToZip{corpusZip, ""})
+		}
+
+		// Package the data into a zipfile.
+		if fuzzModule.data != nil {
+			dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
+			command := builder.Command().BuiltTool("soong_zip").
+				FlagWithOutput("-o ", dataZip)
+			for _, f := range fuzzModule.data {
+				intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
+				command.FlagWithArg("-C ", intermediateDir)
+				command.FlagWithInput("-f ", f)
+			}
+			files = append(files, fileToZip{dataZip, ""})
+		}
+
+		// The executable.
+		files = append(files, fileToZip{rustModule.unstrippedOutputFile.Path(), ""})
+
+		// The dictionary.
+		if fuzzModule.dictionary != nil {
+			files = append(files, fileToZip{fuzzModule.dictionary, ""})
+		}
+
+		// Additional fuzz config.
+		if fuzzModule.config != nil {
+			files = append(files, fileToZip{fuzzModule.config, ""})
+		}
+
+		fuzzZip := archDir.Join(ctx, module.Name()+".zip")
+
+		command := builder.Command().BuiltTool("soong_zip").
+			Flag("-j").
+			FlagWithOutput("-o ", fuzzZip)
+
+		for _, file := range files {
+			if file.DestinationPathPrefix != "" {
+				command.FlagWithArg("-P ", file.DestinationPathPrefix)
+			} else {
+				command.Flag("-P ''")
+			}
+			command.FlagWithInput("-f ", file.SourceFilePath)
+		}
+
+		builder.Build("create-"+fuzzZip.String(),
+			"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
+
+		// Don't add modules to 'make haiku-rust' that are set to not be
+		// exported to the fuzzing infrastructure.
+		if config := fuzzModule.Properties.Fuzz_config; config != nil {
+			if rustModule.Host() && !BoolDefault(config.Fuzz_on_haiku_host, true) {
+				return
+			} else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
+				return
+			}
+		}
+
+		s.fuzzTargets[module.Name()] = true
+		archDirs[archOs] = append(archDirs[archOs], fileToZip{fuzzZip, ""})
+	})
+
+	var archOsList []archOs
+	for archOs := range archDirs {
+		archOsList = append(archOsList, archOs)
+	}
+	sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].dir < archOsList[j].dir })
+
+	for _, archOs := range archOsList {
+		filesToZip := archDirs[archOs]
+		arch := archOs.arch
+		hostOrTarget := archOs.hostOrTarget
+		builder := android.NewRuleBuilder(pctx, ctx)
+		outputFile := android.PathForOutput(ctx, "fuzz-rust-"+hostOrTarget+"-"+arch+".zip")
+		s.packages = append(s.packages, outputFile)
+
+		command := builder.Command().BuiltTool("soong_zip").
+			Flag("-j").
+			FlagWithOutput("-o ", outputFile).
+			Flag("-L 0") // No need to try and re-compress the zipfiles.
+
+		for _, fileToZip := range filesToZip {
+			if fileToZip.DestinationPathPrefix != "" {
+				command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
+			} else {
+				command.Flag("-P ''")
+			}
+			command.FlagWithInput("-f ", fileToZip.SourceFilePath)
+		}
+		builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
+			"Create fuzz target packages for "+arch+"-"+hostOrTarget)
+	}
+
+}
+
+func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+	packages := s.packages.Strings()
+	sort.Strings(packages)
+
+	ctx.Strict("SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+
+	// Preallocate the slice of fuzz targets to minimise memory allocations.
+	fuzzTargets := make([]string, 0, len(s.fuzzTargets))
+	for target, _ := range s.fuzzTargets {
+		fuzzTargets = append(fuzzTargets, target)
+	}
+	sort.Strings(fuzzTargets)
+	ctx.Strict("ALL_RUST_FUZZ_TARGETS", strings.Join(fuzzTargets, " "))
+}
+
+func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
+	fuzz.binaryDecorator.baseCompiler.dir = filepath.Join(
+		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzz.binaryDecorator.baseCompiler.dir64 = filepath.Join(
+		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzz.binaryDecorator.baseCompiler.install(ctx)
+
+	if fuzz.Properties.Corpus != nil {
+		fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus)
+	}
+	if fuzz.Properties.Data != nil {
+		fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data)
+	}
+	if fuzz.Properties.Dictionary != nil {
+		fuzz.dictionary = android.PathForModuleSrc(ctx, *fuzz.Properties.Dictionary)
+	}
+}
diff --git a/rust/image.go b/rust/image.go
index 7eb49d9..900842e 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -97,6 +97,10 @@
 	return mod.InRamdisk()
 }
 
+func (mod *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
 	return mod.InRecovery()
 }
diff --git a/rust/rust.go b/rust/rust.go
index 78a793d..bb97142 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -262,19 +262,15 @@
 	return false
 }
 
-func (m *Module) IsLlndkHeaders() bool {
-	return false
-}
-
-func (m *Module) IsLlndkLibrary() bool {
-	return false
-}
-
 func (mod *Module) KernelHeadersDecorator() bool {
 	return false
 }
 
-func (m *Module) HasLlndkStubs() bool {
+func (m *Module) NeedsLlndkVariants() bool {
+	return false
+}
+
+func (m *Module) NeedsVendorPublicLibraryVariants() bool {
 	return false
 }
 
diff --git a/scripts/strip.sh b/scripts/strip.sh
index e3e5273..d09c187 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -71,7 +71,7 @@
     "${CLANG_BIN}/llvm-objcopy" -w "${infile}" "${outfile}.tmp" ${KEEP_SYMBOLS}
 }
 
-do_strip_keep_mini_debug_info() {
+do_strip_keep_mini_debug_info_darwin() {
     rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
     local fail=
     "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
@@ -92,6 +92,32 @@
     fi
 }
 
+do_strip_keep_mini_debug_info_linux() {
+    rm -f "${outfile}.mini_debuginfo.xz"
+    local fail=
+    "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+
+    if [ -z $fail ]; then
+        "${CREATE_MINIDEBUGINFO}" "${infile}" "${outfile}.mini_debuginfo.xz"
+        "${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
+        rm -f "${outfile}.mini_debuginfo.xz"
+    else
+        cp -f "${infile}" "${outfile}.tmp"
+    fi
+}
+
+do_strip_keep_mini_debug_info() {
+  case $(uname) in
+      Linux)
+          do_strip_keep_mini_debug_info_linux
+          ;;
+      Darwin)
+          do_strip_keep_mini_debug_info_darwin
+          ;;
+      *) echo "unknown OS:" $(uname) >&2 && exit 1;;
+  esac
+}
+
 do_add_gnu_debuglink() {
     "${CLANG_BIN}/llvm-objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
 }
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index dcdf852..0f2fd54 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -46,6 +46,8 @@
 				],
 			}
 		`),
+		// Needed for platform_bootclasspath
+		android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
 
 		java.FixtureConfigureBootJars("com.android.art:mybootlib"),
 		android.FixtureWithRootAndroidBp(`
@@ -66,6 +68,7 @@
 			bootclasspath_fragment {
 				name: "mybootclasspathfragment",
 				image_name: "art",
+				contents: ["mybootlib"],
 				apex_available: ["com.android.art"],
 			}
 
@@ -163,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 {
@@ -181,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)
 
@@ -194,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 {
@@ -203,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.
@@ -213,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 {
@@ -223,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/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index a4f985b..31555c0 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -191,12 +191,12 @@
 
 		sdk_snapshot {
 			name: "mysdk@1",
-			native_shared_libs: ["sdkmember_mysdk_1"],
+			native_shared_libs: ["sdkmember_mysdk@1"],
 		}
 
 		sdk_snapshot {
 			name: "mysdk@2",
-			native_shared_libs: ["sdkmember_mysdk_2"],
+			native_shared_libs: ["sdkmember_mysdk@2"],
 		}
 
 		cc_prebuilt_library_shared {
@@ -208,7 +208,7 @@
 		}
 
 		cc_prebuilt_library_shared {
-			name: "sdkmember_mysdk_1",
+			name: "sdkmember_mysdk@1",
 			sdk_member_name: "sdkmember",
 			srcs: ["libfoo.so"],
 			system_shared_libs: [],
@@ -221,7 +221,7 @@
 		}
 
 		cc_prebuilt_library_shared {
-			name: "sdkmember_mysdk_2",
+			name: "sdkmember_mysdk@2",
 			sdk_member_name: "sdkmember",
 			srcs: ["libfoo.so"],
 			system_shared_libs: [],
@@ -272,8 +272,8 @@
 		}
 	`)
 
-	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_shared_apex10000_mysdk_1").Rule("toc").Output
-	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_shared_apex10000_mysdk_2").Rule("toc").Output
+	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk@1", "android_arm64_armv8-a_shared_apex10000_mysdk_1").Rule("toc").Output
+	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk@2", "android_arm64_armv8-a_shared_apex10000_mysdk_2").Rule("toc").Output
 
 	cpplibForMyApex := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_apex10000_mysdk_1")
 	cpplibForMyApex2 := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_apex10000_mysdk_2")
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 6016981..dc58d93 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -83,12 +83,12 @@
 
 		sdk_snapshot {
 			name: "mysdk@1",
-			java_header_libs: ["sdkmember_mysdk_1"],
+			java_header_libs: ["sdkmember_mysdk@1"],
 		}
 
 		sdk_snapshot {
 			name: "mysdk@2",
-			java_header_libs: ["sdkmember_mysdk_2"],
+			java_header_libs: ["sdkmember_mysdk@2"],
 		}
 
 		java_library {
@@ -100,13 +100,13 @@
 		}
 
 		java_import {
-			name: "sdkmember_mysdk_1",
+			name: "sdkmember_mysdk@1",
 			sdk_member_name: "sdkmember",
 			host_supported: true,
 		}
 
 		java_import {
-			name: "sdkmember_mysdk_2",
+			name: "sdkmember_mysdk@2",
 			sdk_member_name: "sdkmember",
 			host_supported: true,
 		}
@@ -144,8 +144,8 @@
 		}
 	`)
 
-	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_common").Rule("combineJar").Output
-	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_common").Rule("combineJar").Output
+	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk@1", "android_common").Rule("combineJar").Output
+	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk@2", "android_common").Rule("combineJar").Output
 
 	javalibForMyApex := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_1")
 	javalibForMyApex2 := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_2")
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 95a4930..624c0fa 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -307,8 +307,8 @@
 
 		// Generate the snapshot from the member info.
 		p := s.buildSnapshot(ctx, sdkVariants)
-		s.snapshotFile = android.OptionalPathForPath(p)
-		ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), s.Name()+"-current.zip", p)
+		zip := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), p.Base(), p)
+		s.snapshotFile = android.OptionalPathForPath(zip)
 	}
 }
 
@@ -444,7 +444,7 @@
 // built with libfoo.mysdk.11 and libfoo.mysdk.12, respectively depending on which sdk they are
 // using.
 func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() && m.IsVersioned() {
 		if !m.ContainingSdk().Unversioned() {
 			memberName := m.MemberName()
 			tag := sdkMemberVersionedDepTag{member: memberName, version: m.ContainingSdk().Version}
@@ -483,7 +483,7 @@
 // Step 5: if libfoo.mysdk.11 is in the context where version 11 of mysdk is requested, the
 // versioned module is used instead of the un-versioned (in-development) module libfoo
 func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) {
-	if versionedSdkMember, ok := mctx.Module().(android.SdkAware); ok && versionedSdkMember.IsInAnySdk() {
+	if versionedSdkMember, ok := mctx.Module().(android.SdkAware); ok && versionedSdkMember.IsInAnySdk() && versionedSdkMember.IsVersioned() {
 		if sdk := versionedSdkMember.ContainingSdk(); !sdk.Unversioned() {
 			// Only replace dependencies to <sdkmember> with <sdkmember@required-version>
 			// if the depending module requires it. e.g.
@@ -499,7 +499,7 @@
 			// TODO(b/183204176): Remove this after fixing.
 			defer func() {
 				if r := recover(); r != nil {
-					mctx.ModuleErrorf("%s", r)
+					mctx.ModuleErrorf("sdkDepsReplaceMutator %s", r)
 				}
 			}()
 
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index b7da95c..e9129e0 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -464,3 +464,65 @@
     "struct-0" has value "should-be-but-is-not-common0"
     "struct-1" has value "should-be-but-is-not-common1"`, err)
 }
+
+// Ensure that sdk snapshot related environment variables work correctly.
+func TestSnapshot_EnvConfiguration(t *testing.T) {
+	bp := `
+		sdk {
+			name: "mysdk",
+			java_header_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+	`
+	preparer := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureWithRootAndroidBp(bp),
+	)
+
+	checkZipFile := func(t *testing.T, result *android.TestResult, expected string) {
+		zipRule := result.ModuleForTests("mysdk", "common_os").Rule("SnapshotZipFiles")
+		android.AssertStringEquals(t, "snapshot zip file", expected, zipRule.Output.String())
+	}
+
+	t.Run("no env variables", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
+
+		CheckSnapshot(t, result, "mysdk", "",
+			checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+			`),
+		)
+	})
+}
diff --git a/sdk/update.go b/sdk/update.go
index 3668b46..457cbd9 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -120,7 +120,7 @@
 	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 		tag := ctx.OtherModuleDependencyTag(child)
 		if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
-			memberType := memberTag.SdkMemberType()
+			memberType := memberTag.SdkMemberType(child)
 
 			// Make sure that the resolved module is allowed in the member list property.
 			if !memberType.IsInstance(child) {
@@ -133,9 +133,9 @@
 			export := memberTag.ExportMember()
 			s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{s, memberType, child.(android.SdkAware), export})
 
-			// If the member type supports transitive sdk members then recurse down into
-			// its dependencies, otherwise exit traversal.
-			return memberType.HasTransitiveSdkMembers()
+			// Recurse down into the member's dependencies as it may have dependencies that need to be
+			// automatically added to the sdk.
+			return true
 		}
 
 		return false
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 6623381..42d5680 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -210,6 +210,10 @@
 	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.ModuleBase.InstallInVendorRamdisk()
 }
 
+func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return proptools.Bool(s.properties.Recovery_available) || s.ModuleBase.InstallInRecovery()
 }
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