Merge "Revert "Include system/core/liblog/include instead of using symlinks""
diff --git a/android/androidmk.go b/android/androidmk.go
index fafbfd6..ddd51ff 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -304,15 +304,16 @@
 	host := false
 	switch amod.Os().Class {
 	case Host:
-		// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
-		if amod.Arch().ArchType != Common {
-			a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
-		}
-		host = true
-	case HostCross:
-		// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
-		if amod.Arch().ArchType != Common {
-			a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+		if amod.Target().HostCross {
+			// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
+			if amod.Arch().ArchType != Common {
+				a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+			}
+		} else {
+			// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
+			if amod.Arch().ArchType != Common {
+				a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
+			}
 		}
 		host = true
 	case Device:
@@ -359,9 +360,11 @@
 	if amod.ArchSpecific() {
 		switch amod.Os().Class {
 		case Host:
-			prefix = "HOST_"
-		case HostCross:
-			prefix = "HOST_CROSS_"
+			if amod.Target().HostCross {
+				prefix = "HOST_CROSS_"
+			} else {
+				prefix = "HOST_"
+			}
 		case Device:
 			prefix = "TARGET_"
 
@@ -563,9 +566,11 @@
 	if amod.ArchSpecific() {
 		switch amod.Os().Class {
 		case Host:
-			prefix = "HOST_"
-		case HostCross:
-			prefix = "HOST_CROSS_"
+			if amod.Target().HostCross {
+				prefix = "HOST_CROSS_"
+			} else {
+				prefix = "HOST_"
+			}
 		case Device:
 			prefix = "TARGET_"
 
@@ -634,3 +639,21 @@
 		// Make does not understand LinuxBionic
 		module.Os() == LinuxBionic
 }
+
+func AndroidMkDataPaths(data []DataPath) []string {
+	var testFiles []string
+	for _, d := range data {
+		rel := d.SrcPath.Rel()
+		path := d.SrcPath.String()
+		if !strings.HasSuffix(path, rel) {
+			panic(fmt.Errorf("path %q does not end with %q", path, rel))
+		}
+		path = strings.TrimSuffix(path, rel)
+		testFileString := path + ":" + rel
+		if len(d.RelativeInstallPath) > 0 {
+			testFileString += ":" + d.RelativeInstallPath
+		}
+		testFiles = append(testFiles, testFileString)
+	}
+	return testFiles
+}
diff --git a/android/apex.go b/android/apex.go
index f857ec6..c886962 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -24,29 +24,36 @@
 	"github.com/google/blueprint"
 )
 
-const (
-	SdkVersion_Android10 = 29
+var (
+	SdkVersion_Android10 = uncheckedFinalApiLevel(29)
 )
 
 type ApexInfo struct {
 	// Name of the apex variation that this module is mutated into
 	ApexVariationName string
 
-	MinSdkVersion int
-	Updatable     bool
-	RequiredSdks  SdkRefs
+	// Serialized ApiLevel. Use via MinSdkVersion() method. Cannot be stored in
+	// its struct form because this is cloned into properties structs, and
+	// ApiLevel has private members.
+	MinSdkVersionStr string
+	Updatable        bool
+	RequiredSdks     SdkRefs
 
 	InApexes []string
 }
 
-func (i ApexInfo) mergedName() string {
-	name := "apex" + strconv.Itoa(i.MinSdkVersion)
+func (i ApexInfo) mergedName(ctx EarlyModuleContext) string {
+	name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt())
 	for _, sdk := range i.RequiredSdks {
 		name += "_" + sdk.Name + "_" + sdk.Version
 	}
 	return name
 }
 
+func (this *ApexInfo) MinSdkVersion(ctx EarlyModuleContext) ApiLevel {
+	return ApiLevelOrPanic(ctx, this.MinSdkVersionStr)
+}
+
 // Extracted from ApexModule to make it easier to define custom subsets of the
 // ApexModule interface and improve code navigation within the IDE.
 type DepIsInSameApex interface {
@@ -129,7 +136,7 @@
 	// Returns the highest version which is <= maxSdkVersion.
 	// For example, with maxSdkVersion is 10 and versionList is [9,11]
 	// it returns 9 as string
-	ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error)
+	ChooseSdkVersion(ctx BaseModuleContext, versionList []string, maxSdkVersion ApiLevel) (string, error)
 
 	// Tests if the module comes from an updatable APEX.
 	Updatable() bool
@@ -141,7 +148,7 @@
 
 	// Returns nil if this module supports sdkVersion
 	// Otherwise, returns error with reason
-	ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion int) error
+	ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error
 
 	// Returns true if this module needs a unique variation per apex, for example if
 	// use_apex_name_macro is set.
@@ -313,14 +320,18 @@
 	return true
 }
 
-func (m *ApexModuleBase) ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error) {
+func (m *ApexModuleBase) ChooseSdkVersion(ctx BaseModuleContext, versionList []string, maxSdkVersion ApiLevel) (string, error) {
 	for i := range versionList {
-		ver, _ := strconv.Atoi(versionList[len(versionList)-i-1])
-		if ver <= maxSdkVersion {
-			return versionList[len(versionList)-i-1], nil
+		version := versionList[len(versionList)-i-1]
+		ver, err := ApiLevelFromUser(ctx, version)
+		if err != nil {
+			return "", err
+		}
+		if ver.LessThanOrEqualTo(maxSdkVersion) {
+			return version, nil
 		}
 	}
-	return "", fmt.Errorf("not found a version(<=%d) in versionList: %v", maxSdkVersion, versionList)
+	return "", fmt.Errorf("not found a version(<=%s) in versionList: %v", maxSdkVersion, versionList)
 }
 
 func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
@@ -347,18 +358,18 @@
 // mergeApexVariations deduplicates APEX variations that would build identically into a common
 // variation.  It returns the reduced list of variations and a list of aliases from the original
 // variation names to the new variation names.
-func mergeApexVariations(apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
+func mergeApexVariations(ctx EarlyModuleContext, apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
 	sort.Sort(byApexName(apexVariations))
 	seen := make(map[string]int)
 	for _, apexInfo := range apexVariations {
 		apexName := apexInfo.ApexVariationName
-		mergedName := apexInfo.mergedName()
+		mergedName := apexInfo.mergedName(ctx)
 		if index, exists := seen[mergedName]; exists {
 			merged[index].InApexes = append(merged[index].InApexes, apexName)
 			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
 		} else {
 			seen[mergedName] = len(merged)
-			apexInfo.ApexVariationName = apexInfo.mergedName()
+			apexInfo.ApexVariationName = apexInfo.mergedName(ctx)
 			apexInfo.InApexes = CopyOf(apexInfo.InApexes)
 			merged = append(merged, apexInfo)
 		}
@@ -374,7 +385,7 @@
 		var apexVariations []ApexInfo
 		var aliases [][2]string
 		if !mctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps {
-			apexVariations, aliases = mergeApexVariations(m.apexVariations)
+			apexVariations, aliases = mergeApexVariations(mctx, m.apexVariations)
 		} else {
 			apexVariations = m.apexVariations
 		}
@@ -570,15 +581,15 @@
 	var fullContent strings.Builder
 	var flatContent strings.Builder
 
-	fmt.Fprintf(&flatContent, "%s(minSdkVersion:%s):\\n", ctx.ModuleName(), minSdkVersion)
+	fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\\n", ctx.ModuleName(), minSdkVersion)
 	for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) {
 		info := depInfos[key]
 		toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion)
 		if info.IsExternal {
 			toName = toName + " (external)"
 		}
-		fmt.Fprintf(&fullContent, "%s <- %s\\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
-		fmt.Fprintf(&flatContent, "  %s\\n", toName)
+		fmt.Fprintf(&fullContent, "  %s <- %s\\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
+		fmt.Fprintf(&flatContent, "%s\\n", toName)
 	}
 
 	d.fullListPath = PathForModuleOut(ctx, "depsinfo", "fulllist.txt").OutputPath
@@ -603,7 +614,13 @@
 }
 
 // TODO(b/158059172): remove minSdkVersion allowlist
-var minSdkVersionAllowlist = map[string]int{
+var minSdkVersionAllowlist = func(apiMap map[string]int) map[string]ApiLevel {
+	list := make(map[string]ApiLevel, len(apiMap))
+	for name, finalApiInt := range apiMap {
+		list[name] = uncheckedFinalApiLevel(finalApiInt)
+	}
+	return list
+}(map[string]int{
 	"adbd":                  30,
 	"android.net.ipsec.ike": 30,
 	"androidx-constraintlayout_constraintlayout-solver": 30,
@@ -672,7 +689,7 @@
 	"statsd":                                            30,
 	"tensorflow_headers":                                30,
 	"xz-java":                                           29,
-}
+})
 
 // Function called while walking an APEX's payload dependencies.
 //
@@ -686,7 +703,7 @@
 }
 
 // CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly
-func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion int) {
+func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
 	// do not enforce min_sdk_version for host
 	if ctx.Host() {
 		return
@@ -699,7 +716,7 @@
 
 	// do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version or
 	// min_sdk_version is not finalized (e.g. current or codenames)
-	if minSdkVersion == FutureApiLevel {
+	if minSdkVersion.IsCurrent() {
 		return
 	}
 
@@ -714,7 +731,7 @@
 		}
 		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
 			toName := ctx.OtherModuleName(to)
-			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver > minSdkVersion {
+			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
 				ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s",
 					minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false))
 				return false
diff --git a/android/api_levels.go b/android/api_levels.go
index 0872066..9768340 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -24,6 +24,193 @@
 	RegisterSingletonType("api_levels", ApiLevelsSingleton)
 }
 
+// An API level, which may be a finalized (numbered) API, a preview (codenamed)
+// API, or the future API level (10000). Can be parsed from a string with
+// ApiLevelFromUser or ApiLevelOrPanic.
+//
+// The different *types* of API levels are handled separately. Currently only
+// Java has these, and they're managed with the sdkKind enum of the sdkSpec. A
+// future cleanup should be to migrate sdkSpec to using ApiLevel instead of its
+// sdkVersion int, and to move sdkSpec into this package.
+type ApiLevel struct {
+	// The string representation of the API level.
+	value string
+
+	// A number associated with the API level. The exact value depends on
+	// whether this API level is a preview or final API.
+	//
+	// For final API levels, this is the assigned version number.
+	//
+	// For preview API levels, this value has no meaning except to index known
+	// previews to determine ordering.
+	number int
+
+	// Identifies this API level as either a preview or final API level.
+	isPreview bool
+}
+
+func (this ApiLevel) FinalOrFutureInt() int {
+	if this.IsPreview() {
+		return FutureApiLevelInt
+	} else {
+		return this.number
+	}
+}
+
+// Returns the canonical name for this API level. For a finalized API level
+// this will be the API number as a string. For a preview API level this
+// will be the codename, or "current".
+func (this ApiLevel) String() string {
+	return this.value
+}
+
+// Returns true if this is a non-final API level.
+func (this ApiLevel) IsPreview() bool {
+	return this.isPreview
+}
+
+// Returns true if this is the unfinalized "current" API level. This means
+// different things across Java and native. Java APIs do not use explicit
+// codenames, so all non-final codenames are grouped into "current". For native
+// explicit codenames are typically used, and current is the union of all
+// non-final APIs, including those that may not yet be in any codename.
+//
+// Note that in a build where the platform is final, "current" will not be a
+// preview API level but will instead be canonicalized to the final API level.
+func (this ApiLevel) IsCurrent() bool {
+	return this.value == "current"
+}
+
+// Returns -1 if the current API level is less than the argument, 0 if they
+// are equal, and 1 if it is greater than the argument.
+func (this ApiLevel) CompareTo(other ApiLevel) int {
+	if this.IsPreview() && !other.IsPreview() {
+		return 1
+	} else if !this.IsPreview() && other.IsPreview() {
+		return -1
+	}
+
+	if this.number < other.number {
+		return -1
+	} else if this.number == other.number {
+		return 0
+	} else {
+		return 1
+	}
+}
+
+func (this ApiLevel) EqualTo(other ApiLevel) bool {
+	return this.CompareTo(other) == 0
+}
+
+func (this ApiLevel) GreaterThan(other ApiLevel) bool {
+	return this.CompareTo(other) > 0
+}
+
+func (this ApiLevel) GreaterThanOrEqualTo(other ApiLevel) bool {
+	return this.CompareTo(other) >= 0
+}
+
+func (this ApiLevel) LessThan(other ApiLevel) bool {
+	return this.CompareTo(other) < 0
+}
+
+func (this ApiLevel) LessThanOrEqualTo(other ApiLevel) bool {
+	return this.CompareTo(other) <= 0
+}
+
+func uncheckedFinalApiLevel(num int) ApiLevel {
+	return ApiLevel{
+		value:     strconv.Itoa(num),
+		number:    num,
+		isPreview: false,
+	}
+}
+
+var NoneApiLevel = ApiLevel{
+	value: "(no version)",
+	// Not 0 because we don't want this to compare equal with the first preview.
+	number:    -1,
+	isPreview: true,
+}
+
+// The first version that introduced 64-bit ABIs.
+var FirstLp64Version = uncheckedFinalApiLevel(21)
+
+// The first API level that does not require NDK code to link
+// libandroid_support.
+var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21)
+
+// If the `raw` input is the codename of an API level has been finalized, this
+// function returns the API level number associated with that API level. If the
+// input is *not* a finalized codename, the input is returned unmodified.
+//
+// For example, at the time of writing, R has been finalized as API level 30,
+// but S is in development so it has no number assigned. For the following
+// inputs:
+//
+// * "30" -> "30"
+// * "R" -> "30"
+// * "S" -> "S"
+func ReplaceFinalizedCodenames(ctx EarlyModuleContext, raw string) string {
+	num, ok := getFinalCodenamesMap(ctx.Config())[raw]
+	if !ok {
+		return raw
+	}
+
+	return strconv.Itoa(num)
+}
+
+// Converts the given string `raw` to an ApiLevel, possibly returning an error.
+//
+// `raw` must be non-empty. Passing an empty string results in a panic.
+//
+// "current" will return CurrentApiLevel, which is the ApiLevel associated with
+// an arbitrary future release (often referred to as API level 10000).
+//
+// Finalized codenames will be interpreted as their final API levels, not the
+// preview of the associated releases. R is now API 30, not the R preview.
+//
+// Future codenames return a preview API level that has no associated integer.
+//
+// Inputs that are not "current", known previews, or convertible to an integer
+// will return an error.
+func ApiLevelFromUser(ctx EarlyModuleContext, raw string) (ApiLevel, error) {
+	if raw == "" {
+		panic("API level string must be non-empty")
+	}
+
+	if raw == "current" {
+		return FutureApiLevel, nil
+	}
+
+	for _, preview := range ctx.Config().PreviewApiLevels() {
+		if raw == preview.String() {
+			return preview, nil
+		}
+	}
+
+	canonical := ReplaceFinalizedCodenames(ctx, raw)
+	asInt, err := strconv.Atoi(canonical)
+	if err != nil {
+		return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical)
+	}
+
+	apiLevel := uncheckedFinalApiLevel(asInt)
+	return apiLevel, nil
+}
+
+// Converts an API level string `raw` into an ApiLevel in the same method as
+// `ApiLevelFromUser`, but the input is assumed to have no errors and any errors
+// will panic instead of returning an error.
+func ApiLevelOrPanic(ctx EarlyModuleContext, raw string) ApiLevel {
+	value, err := ApiLevelFromUser(ctx, raw)
+	if err != nil {
+		panic(err.Error())
+	}
+	return value
+}
+
 func ApiLevelsSingleton() Singleton {
 	return &apiLevelsSingleton{}
 }
@@ -52,6 +239,48 @@
 	return PathForOutput(ctx, "api_levels.json")
 }
 
+var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap")
+
+func getFinalCodenamesMap(config Config) map[string]int {
+	return config.Once(finalCodenamesMapKey, func() interface{} {
+		apiLevelsMap := map[string]int{
+			"G":     9,
+			"I":     14,
+			"J":     16,
+			"J-MR1": 17,
+			"J-MR2": 18,
+			"K":     19,
+			"L":     21,
+			"L-MR1": 22,
+			"M":     23,
+			"N":     24,
+			"N-MR1": 25,
+			"O":     26,
+			"O-MR1": 27,
+			"P":     28,
+			"Q":     29,
+			"R":     30,
+		}
+
+		// TODO: Differentiate "current" and "future".
+		// The code base calls it FutureApiLevel, but the spelling is "current",
+		// and these are really two different things. When defining APIs it
+		// means the API has not yet been added to a specific release. When
+		// choosing an API level to build for it means that the future API level
+		// should be used, except in the case where the build is finalized in
+		// which case the platform version should be used. This is *weird*,
+		// because in the circumstance where API foo was added in R and bar was
+		// added in S, both of these are usable when building for "current" when
+		// neither R nor S are final, but the S APIs stop being available in a
+		// final R build.
+		if Bool(config.productVariables.Platform_sdk_final) {
+			apiLevelsMap["current"] = config.PlatformSdkVersion().FinalOrFutureInt()
+		}
+
+		return apiLevelsMap
+	}).(map[string]int)
+}
+
 var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
 
 func getApiLevelsMap(config Config) map[string]int {
@@ -83,24 +312,6 @@
 	}).(map[string]int)
 }
 
-// Converts an API level string into its numeric form.
-// * Codenames are decoded.
-// * Numeric API levels are simply converted.
-// * "current" is mapped to FutureApiLevel(10000)
-// * "minimum" is NDK specific and not handled with this. (refer normalizeNdkApiLevel in cc.go)
-func ApiStrToNum(ctx BaseModuleContext, apiLevel string) (int, error) {
-	if apiLevel == "current" {
-		return FutureApiLevel, nil
-	}
-	if num, ok := getApiLevelsMap(ctx.Config())[apiLevel]; ok {
-		return num, nil
-	}
-	if num, err := strconv.Atoi(apiLevel); err == nil {
-		return num, nil
-	}
-	return 0, fmt.Errorf("SDK version should be one of \"current\", <number> or <codename>: %q", apiLevel)
-}
-
 func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	apiLevelsMap := getApiLevelsMap(ctx.Config())
 	apiLevelsJson := GetApiLevelsJson(ctx)
diff --git a/android/arch.go b/android/arch.go
index d055a6f..f4b0d66 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -78,13 +78,22 @@
     },
     target: {
         android: {
-            // Device variants
+            // Device variants (implies Bionic)
         },
         host: {
             // Host variants
         },
+        bionic: {
+            // Bionic (device and host) variants
+        },
+        linux_bionic: {
+            // Bionic host variants
+        },
+        linux: {
+            // Bionic (device and host) and Linux glibc variants
+        },
         linux_glibc: {
-            // Linux host variants
+            // Linux host variants (using non-Bionic libc)
         },
         darwin: {
             // Darwin host variants
@@ -95,6 +104,9 @@
         not_windows: {
             // Non-windows host variants
         },
+        android_arm: {
+            // Any <os>_<arch> combination restricts to that os and arch
+        },
     },
 }
 */
@@ -125,6 +137,7 @@
 	Arm64: {
 		"armv8_a",
 		"armv8_2a",
+		"armv8-2a-dotprod",
 		"cortex-a53",
 		"cortex-a55",
 		"cortex-a72",
@@ -172,6 +185,9 @@
 	Arm: {
 		"neon",
 	},
+	Arm64: {
+		"dotprod",
+	},
 	X86: {
 		"ssse3",
 		"sse4",
@@ -209,6 +225,11 @@
 			"neon",
 		},
 	},
+	Arm64: {
+		"armv8-2a-dotprod": {
+			"dotprod",
+		},
+	},
 	X86: {
 		"amberlake": {
 			"ssse3",
@@ -569,7 +590,7 @@
 	Linux       = NewOsType("linux_glibc", Host, false)
 	Darwin      = NewOsType("darwin", Host, false)
 	LinuxBionic = NewOsType("linux_bionic", Host, false)
-	Windows     = NewOsType("windows", HostCross, true)
+	Windows     = NewOsType("windows", Host, true)
 	Android     = NewOsType("android", Device, false)
 	Fuchsia     = NewOsType("fuchsia", Device, false)
 
@@ -600,7 +621,6 @@
 	Generic OsClass = iota
 	Device
 	Host
-	HostCross
 )
 
 func (class OsClass) String() string {
@@ -611,8 +631,6 @@
 		return "device"
 	case Host:
 		return "host"
-	case HostCross:
-		return "host cross"
 	default:
 		panic(fmt.Errorf("unknown class %d", class))
 	}
@@ -672,6 +690,11 @@
 	NativeBridge             NativeBridgeSupport
 	NativeBridgeHostArchName string
 	NativeBridgeRelativePath string
+
+	// HostCross is true when the target cannot run natively on the current build host.
+	// For example, linux_glibc_x86 returns true on a regular x86/i686/Linux machines, but returns false
+	// on Mac (different OS), or on 64-bit only i686/Linux machines (unsupported arch).
+	HostCross bool
 }
 
 func (target Target) String() string {
@@ -730,26 +753,15 @@
 		return
 	}
 
-	osClasses := base.OsClassSupported()
-
 	var moduleOSList []OsType
 
 	for _, os := range OsTypeList {
-		supportedClass := false
-		for _, osClass := range osClasses {
-			if os.Class == osClass {
-				supportedClass = true
+		for _, t := range mctx.Config().Targets[os] {
+			if base.supportsTarget(t, mctx.Config()) {
+				moduleOSList = append(moduleOSList, os)
+				break
 			}
 		}
-		if !supportedClass {
-			continue
-		}
-
-		if len(mctx.Config().Targets[os]) == 0 {
-			continue
-		}
-
-		moduleOSList = append(moduleOSList, os)
 	}
 
 	if len(moduleOSList) == 0 {
@@ -904,7 +916,7 @@
 
 	prefer32 := false
 	if base.prefer32 != nil {
-		prefer32 = base.prefer32(mctx, base, os.Class)
+		prefer32 = base.prefer32(mctx, base, os)
 	}
 
 	multilib, extraMultilib := decodeMultilib(base, os.Class)
@@ -955,7 +967,7 @@
 	switch class {
 	case Device:
 		multilib = String(base.commonProperties.Target.Android.Compile_multilib)
-	case Host, HostCross:
+	case Host:
 		multilib = String(base.commonProperties.Target.Host.Compile_multilib)
 	}
 	if multilib == "" {
@@ -1231,7 +1243,7 @@
 			//         key: value,
 			//     },
 			// },
-			if os.Class == Host || os.Class == HostCross {
+			if os.Class == Host {
 				field := "Host"
 				prefix := "target.host"
 				m.appendProperties(ctx, genProps, targetProp, field, prefix)
@@ -1271,7 +1283,7 @@
 			prefix := "target." + os.Name
 			m.appendProperties(ctx, genProps, targetProp, field, prefix)
 
-			if (os.Class == Host || os.Class == HostCross) && os != Windows {
+			if os.Class == Host && os != Windows {
 				field := "Not_windows"
 				prefix := "target.not_windows"
 				m.appendProperties(ctx, genProps, targetProp, field, prefix)
@@ -1434,20 +1446,15 @@
 			//         key: value,
 			//     },
 			// },
-			// TODO(ccross): is this still necessary with native bridge?
 			if os.Class == Device {
-				if (arch.ArchType == X86 && (hasArmAbi(arch) ||
-					hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
-					(arch.ArchType == Arm &&
-						hasX86AndroidArch(ctx.Config().Targets[Android])) {
+				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]))) ||
-					(arch.ArchType == Arm &&
-						hasX8664AndroidArch(ctx.Config().Targets[Android])) {
+				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)
@@ -1504,6 +1511,36 @@
 			nativeBridgeRelativePathStr = arch.ArchType.String()
 		}
 
+		// A target is considered as HostCross if it's a host target which can't run natively on
+		// the currently configured build machine (either because the OS is different or because of
+		// the unsupported arch)
+		hostCross := false
+		if os.Class == Host {
+			var osSupported bool
+			if os == BuildOs {
+				osSupported = true
+			} else if BuildOs.Linux() && os.Linux() {
+				// LinuxBionic and Linux are compatible
+				osSupported = true
+			} else {
+				osSupported = false
+			}
+
+			var archSupported bool
+			if arch.ArchType == Common {
+				archSupported = true
+			} else if arch.ArchType.Name == *variables.HostArch {
+				archSupported = true
+			} else if variables.HostSecondaryArch != nil && arch.ArchType.Name == *variables.HostSecondaryArch {
+				archSupported = true
+			} else {
+				archSupported = false
+			}
+			if !osSupported || !archSupported {
+				hostCross = true
+			}
+		}
+
 		targets[os] = append(targets[os],
 			Target{
 				Os:                       os,
@@ -1511,6 +1548,7 @@
 				NativeBridge:             nativeBridgeEnabled,
 				NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
 				NativeBridgeRelativePath: nativeBridgeRelativePathStr,
+				HostCross:                hostCross,
 			})
 	}
 
@@ -1527,6 +1565,9 @@
 	if Bool(config.Host_bionic) {
 		addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
 	}
+	if Bool(config.Host_bionic_arm64) {
+		addTarget(LinuxBionic, "arm64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
+	}
 
 	if String(variables.CrossHost) != "" {
 		crossHostOs := osByName(*variables.CrossHost)
@@ -1594,27 +1635,7 @@
 // hasArmArch returns true if targets has at least non-native_bridge arm Android arch
 func hasArmAndroidArch(targets []Target) bool {
 	for _, target := range targets {
-		if target.Os == Android && target.Arch.ArchType == Arm && target.NativeBridge == NativeBridgeDisabled {
-			return true
-		}
-	}
-	return false
-}
-
-// hasX86Arch returns true if targets has at least x86 Android arch
-func hasX86AndroidArch(targets []Target) bool {
-	for _, target := range targets {
-		if target.Os == Android && target.Arch.ArchType == X86 {
-			return true
-		}
-	}
-	return false
-}
-
-// hasX8664Arch returns true if targets has at least x86_64 Android arch
-func hasX8664AndroidArch(targets []Target) bool {
-	for _, target := range targets {
-		if target.Os == Android && target.Arch.ArchType == X86_64 {
+		if target.Os == Android && target.Arch.ArchType == Arm {
 			return true
 		}
 	}
@@ -1653,9 +1674,10 @@
 		{"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
 		{"arm64", "armv8-a", "exynos-m1", []string{"arm64-v8a"}},
 		{"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
-		{"arm64", "armv8-2a", "cortex-a75", []string{"arm64-v8a"}},
-		{"arm64", "armv8-2a", "cortex-a76", []string{"arm64-v8a"}},
 		{"arm64", "armv8-2a", "kryo385", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a-dotprod", "cortex-a55", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a-dotprod", "cortex-a75", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a-dotprod", "cortex-a76", []string{"arm64-v8a"}},
 		{"x86", "", "", []string{"x86"}},
 		{"x86", "atom", "", []string{"x86"}},
 		{"x86", "haswell", "", []string{"x86"}},
@@ -1786,13 +1808,22 @@
 }
 
 func firstTarget(targets []Target, filters ...string) []Target {
+	// find the first target from each OS
+	var ret []Target
+	hasHost := false
+	set := make(map[OsType]bool)
+
 	for _, filter := range filters {
 		buildTargets := filterMultilibTargets(targets, filter)
-		if len(buildTargets) > 0 {
-			return buildTargets[:1]
+		for _, t := range buildTargets {
+			if _, found := set[t.Os]; !found {
+				hasHost = hasHost || (t.Os.Class == Host)
+				set[t.Os] = true
+				ret = append(ret, t)
+			}
 		}
 	}
-	return nil
+	return ret
 }
 
 // Use the module multilib setting to select one or more targets from a target list
diff --git a/android/config.go b/android/config.go
index dd622e5..8df65f7 100644
--- a/android/config.go
+++ b/android/config.go
@@ -21,7 +21,6 @@
 	"os"
 	"path/filepath"
 	"runtime"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -37,7 +36,13 @@
 var String = proptools.String
 var StringDefault = proptools.StringDefault
 
-const FutureApiLevel = 10000
+const FutureApiLevelInt = 10000
+
+var FutureApiLevel = ApiLevel{
+	value:     "current",
+	number:    FutureApiLevelInt,
+	isPreview: true,
+}
 
 // The configuration file name
 const configFileName = "soong.config"
@@ -46,8 +51,9 @@
 // A FileConfigurableOptions contains options which can be configured by the
 // config file. These will be included in the config struct.
 type FileConfigurableOptions struct {
-	Mega_device *bool `json:",omitempty"`
-	Host_bionic *bool `json:",omitempty"`
+	Mega_device       *bool `json:",omitempty"`
+	Host_bionic       *bool `json:",omitempty"`
+	Host_bionic_arm64 *bool `json:",omitempty"`
 }
 
 func (f *FileConfigurableOptions) SetDefaultConfig() {
@@ -222,15 +228,17 @@
 
 	config := &config{
 		productVariables: productVariables{
-			DeviceName:                  stringPtr("test_device"),
-			Platform_sdk_version:        intPtr(30),
-			DeviceSystemSdkVersions:     []string{"14", "15"},
-			Platform_systemsdk_versions: []string{"29", "30"},
-			AAPTConfig:                  []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
-			AAPTPreferredConfig:         stringPtr("xhdpi"),
-			AAPTCharacteristics:         stringPtr("nosdcard"),
-			AAPTPrebuiltDPI:             []string{"xhdpi", "xxhdpi"},
-			UncompressPrivAppDex:        boolPtr(true),
+			DeviceName:                        stringPtr("test_device"),
+			Platform_sdk_version:              intPtr(30),
+			Platform_sdk_codename:             stringPtr("S"),
+			Platform_version_active_codenames: []string{"S"},
+			DeviceSystemSdkVersions:           []string{"14", "15"},
+			Platform_systemsdk_versions:       []string{"29", "30"},
+			AAPTConfig:                        []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+			AAPTPreferredConfig:               stringPtr("xhdpi"),
+			AAPTCharacteristics:               stringPtr("nosdcard"),
+			AAPTPrebuiltDPI:                   []string{"xhdpi", "xxhdpi"},
+			UncompressPrivAppDex:              boolPtr(true),
 		},
 
 		buildDir:     buildDir,
@@ -260,10 +268,10 @@
 	config := testConfig.config
 
 	config.Targets[Android] = []Target{
-		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
-		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
-		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64"},
-		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm"},
+		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
+		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
+		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64", false},
+		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm", false},
 	}
 
 	return testConfig
@@ -275,10 +283,10 @@
 
 	config.Targets = map[OsType][]Target{
 		Fuchsia: []Target{
-			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
 		},
 		BuildOs: []Target{
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
 		},
 	}
 
@@ -292,12 +300,12 @@
 
 	config.Targets = map[OsType][]Target{
 		Android: []Target{
-			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
-			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
+			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
+			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
 		},
 		BuildOs: []Target{
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
-			{BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", ""},
+			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+			{BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
 		},
 	}
 
@@ -614,12 +622,8 @@
 	return String(c.productVariables.Platform_version_name)
 }
 
-func (c *config) PlatformSdkVersionInt() int {
-	return *c.productVariables.Platform_sdk_version
-}
-
-func (c *config) PlatformSdkVersion() string {
-	return strconv.Itoa(c.PlatformSdkVersionInt())
+func (c *config) PlatformSdkVersion() ApiLevel {
+	return uncheckedFinalApiLevel(*c.productVariables.Platform_sdk_version)
 }
 
 func (c *config) PlatformSdkCodename() string {
@@ -642,23 +646,48 @@
 	return String(c.productVariables.Platform_base_os)
 }
 
-func (c *config) MinSupportedSdkVersion() int {
-	return 16
+func (c *config) MinSupportedSdkVersion() ApiLevel {
+	return uncheckedFinalApiLevel(16)
 }
 
-func (c *config) DefaultAppTargetSdkInt() int {
-	if Bool(c.productVariables.Platform_sdk_final) {
-		return c.PlatformSdkVersionInt()
-	} else {
-		return FutureApiLevel
+func (c *config) FinalApiLevels() []ApiLevel {
+	var levels []ApiLevel
+	for i := 1; i <= c.PlatformSdkVersion().FinalOrFutureInt(); i++ {
+		levels = append(levels, uncheckedFinalApiLevel(i))
 	}
+	return levels
 }
 
-func (c *config) DefaultAppTargetSdk() string {
+func (c *config) PreviewApiLevels() []ApiLevel {
+	var levels []ApiLevel
+	for i, codename := range c.PlatformVersionActiveCodenames() {
+		levels = append(levels, ApiLevel{
+			value:     codename,
+			number:    i,
+			isPreview: true,
+		})
+	}
+	return levels
+}
+
+func (c *config) AllSupportedApiLevels() []ApiLevel {
+	var levels []ApiLevel
+	levels = append(levels, c.FinalApiLevels()...)
+	return append(levels, c.PreviewApiLevels()...)
+}
+
+func (c *config) DefaultAppTargetSdk(ctx EarlyModuleContext) ApiLevel {
 	if Bool(c.productVariables.Platform_sdk_final) {
 		return c.PlatformSdkVersion()
 	} else {
-		return c.PlatformSdkCodename()
+		codename := c.PlatformSdkCodename()
+		if codename == "" {
+			return NoneApiLevel
+		}
+		if codename == "REL" {
+			panic("Platform_sdk_codename should not be REL when Platform_sdk_final is true")
+		}
+		return ApiLevelOrPanic(ctx, codename)
 	}
 }
 
diff --git a/android/makevars.go b/android/makevars.go
index 003a9df..374986e 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -18,7 +18,6 @@
 	"bytes"
 	"fmt"
 	"sort"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -31,7 +30,7 @@
 }
 
 func androidMakeVarsProvider(ctx MakeVarsContext) {
-	ctx.Strict("MIN_SUPPORTED_SDK_VERSION", strconv.Itoa(ctx.Config().MinSupportedSdkVersion()))
+	ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String())
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/android/module.go b/android/module.go
index 337ae40..c4e43c2 100644
--- a/android/module.go
+++ b/android/module.go
@@ -61,18 +61,44 @@
 // EarlyModuleContext provides methods that can be called early, as soon as the properties have
 // been parsed into the module and before any mutators have run.
 type EarlyModuleContext interface {
+	// Module returns the current module as a Module.  It should rarely be necessary, as the module already has a
+	// reference to itself.
 	Module() Module
+
+	// ModuleName returns the name of the module.  This is generally the value that was returned by Module.Name() when
+	// the module was created, but may have been modified by calls to BaseMutatorContext.Rename.
 	ModuleName() string
+
+	// ModuleDir returns the path to the directory that contains the definition of the module.
 	ModuleDir() string
+
+	// ModuleType returns the name of the module type that was used to create the module, as specified in
+	// RegisterModuleType.
 	ModuleType() string
+
+	// BlueprintFile returns the name of the blueprint file that contains the definition of this
+	// module.
 	BlueprintsFile() string
 
+	// ContainsProperty returns true if the specified property name was set in the module definition.
 	ContainsProperty(name string) bool
+
+	// Errorf reports an error at the specified position of the module definition file.
 	Errorf(pos scanner.Position, fmt string, args ...interface{})
+
+	// ModuleErrorf reports an error at the line number of the module type in the module definition.
 	ModuleErrorf(fmt string, args ...interface{})
+
+	// PropertyErrorf reports an error at the line number of a property in the module definition.
 	PropertyErrorf(property, fmt string, args ...interface{})
+
+	// Failed returns true if any errors have been reported.  In most cases the module can continue with generating
+	// build rules after an error, allowing it to report additional errors in a single run, but in cases where the error
+	// has prevented the module from creating necessary data it can return early when Failed returns true.
 	Failed() bool
 
+	// AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest.  The
+	// primary builder will be rerun whenever the specified files are modified.
 	AddNinjaFileDeps(deps ...string)
 
 	DeviceSpecific() bool
@@ -98,6 +124,8 @@
 	IsSymlink(path Path) bool
 	Readlink(path Path) string
 
+	// Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the
+	// default SimpleNameInterface if Context.SetNameInterface was not called.
 	Namespace() *Namespace
 }
 
@@ -110,33 +138,156 @@
 
 	blueprintBaseModuleContext() blueprint.BaseModuleContext
 
+	// OtherModuleName returns the name of another Module.  See BaseModuleContext.ModuleName for more information.
+	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleName(m blueprint.Module) string
+
+	// OtherModuleDir returns the directory of another Module.  See BaseModuleContext.ModuleDir for more information.
+	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleDir(m blueprint.Module) string
+
+	// OtherModuleErrorf reports an error on another Module.  See BaseModuleContext.ModuleErrorf for more information.
+	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
+
+	// OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency
+	// on the module.  When called inside a Visit* method with current module being visited, and there are multiple
+	// dependencies on the module being visited, it returns the dependency tag used for the current dependency.
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
+
+	// OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface
+	// passed to Context.SetNameInterface, or SimpleNameInterface if it was not called.
 	OtherModuleExists(name string) bool
+
+	// OtherModuleDependencyVariantExists returns true if a module with the
+	// specified name and variant exists. The variant must match the given
+	// variations. It must also match all the non-local variations of the current
+	// module. In other words, it checks for the module AddVariationDependencies
+	// would add a dependency on with the same arguments.
 	OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool
+
+	// OtherModuleReverseDependencyVariantExists returns true if a module with the
+	// specified name exists with the same variations as the current module. In
+	// other words, it checks for the module AddReverseDependency would add a
+	// dependency on with the same argument.
 	OtherModuleReverseDependencyVariantExists(name string) bool
+
+	// OtherModuleType returns the type of another Module.  See BaseModuleContext.ModuleType for more information.
+	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleType(m blueprint.Module) string
 
+	// OtherModuleProvider returns the value for a provider for the given module.  If the value is
+	// not set it returns the zero value of the type of the provider, so the return value can always
+	// be type asserted to the type of the provider.  The value returned may be a deep copy of the
+	// value originally passed to SetProvider.
+	OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
+
+	// OtherModuleHasProvider returns true if the provider for the given module has been set.
+	OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
+
+	// Provider returns the value for a provider for the current module.  If the value is
+	// not set it returns the zero value of the type of the provider, so the return value can always
+	// be type asserted to the type of the provider.  It panics if called before the appropriate
+	// mutator or GenerateBuildActions pass for the provider.  The value returned may be a deep
+	// copy of the value originally passed to SetProvider.
+	Provider(provider blueprint.ProviderKey) interface{}
+
+	// HasProvider returns true if the provider for the current module has been set.
+	HasProvider(provider blueprint.ProviderKey) bool
+
+	// SetProvider sets the value for a provider for the current module.  It panics if not called
+	// during the appropriate mutator or GenerateBuildActions pass for the provider, if the value
+	// is not of the appropriate type, or if the value has already been set.  The value should not
+	// be modified after being passed to SetProvider.
+	SetProvider(provider blueprint.ProviderKey, value interface{})
+
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
+
+	// GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if
+	// none exists.  It panics if the dependency does not have the specified tag.  It skips any
+	// dependencies that are not an android.Module.
 	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.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.  It skips any dependencies that are not an android.Module.
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 
+	// VisitDirectDepsBlueprint calls visit for each direct dependency.  If there are multiple
+	// direct dependencies on the same module visit will be called multiple times on that module
+	// and OtherModuleDependencyTag will return a different tag for each.
+	//
+	// The Module passed to the visit function should not be retained outside of the visit
+	// function, it may be invalidated by future mutators.
 	VisitDirectDepsBlueprint(visit func(blueprint.Module))
+
+	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
+	// direct dependencies on the same module visit will be called multiple times on that module
+	// and OtherModuleDependencyTag will return a different tag for each.  It skips any
+	// dependencies that are not an android.Module.
+	//
+	// The Module passed to the visit function should not be retained outside of the visit
+	// function, it may be invalidated by future mutators.
 	VisitDirectDeps(visit func(Module))
+
 	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
+
+	// VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit.  If there are
+	// multiple direct dependencies on the same module pred and visit will be called multiple times on that module and
+	// OtherModuleDependencyTag will return a different tag for each.  It skips any
+	// dependencies that are not an android.Module.
+	//
+	// The Module passed to the visit function should not be retained outside of the visit function, it may be
+	// invalidated by future mutators.
 	VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
 	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
 	VisitDepsDepthFirst(visit func(Module))
 	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
 	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
+
+	// WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order.  visit may
+	// be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the
+	// child and parent with different tags.  OtherModuleDependencyTag will return the tag for the currently visited
+	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down to child.  It skips
+	// any dependencies that are not an android.Module.
+	//
+	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
+	// invalidated by future mutators.
 	WalkDeps(visit func(Module, Module) bool)
+
+	// WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency
+	// tree in top down order.  visit may be called multiple times for the same (child, parent)
+	// pair if there are multiple direct dependencies between the child and parent with different
+	// tags.  OtherModuleDependencyTag will return the tag for the currently visited
+	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down
+	// to child.
+	//
+	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
+	// invalidated by future mutators.
 	WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool)
+
 	// GetWalkPath is supposed to be called in visit function passed in WalkDeps()
 	// and returns a top-down dependency path from a start module to current child module.
 	GetWalkPath() []Module
 
+	// PrimaryModule returns the first variant of the current module.  Variants of a module are always visited in
+	// order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the
+	// Module returned by PrimaryModule without data races.  This can be used to perform singleton actions that are
+	// only done once for all variants of a module.
+	PrimaryModule() Module
+
+	// FinalModule returns the last variant of the current module.  Variants of a module are always visited in
+	// order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all
+	// variants using VisitAllModuleVariants if the current module == FinalModule().  This can be used to perform
+	// singleton actions that are only done once for all variants of a module.
+	FinalModule() Module
+
+	// VisitAllModuleVariants calls visit for each variant of the current module.  Variants of a module are always
+	// visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read
+	// from all variants if the current module == FinalModule().  Otherwise, care must be taken to not access any
+	// data modified by the current mutator.
+	VisitAllModuleVariants(visit func(Module))
+
 	// GetTagPath is supposed to be called in visit function passed in WalkDeps()
 	// and returns a top-down dependency tags path from a start module to current child module.
 	// It has one less entry than GetWalkPath() as it contains the dependency tags that
@@ -216,10 +367,8 @@
 	// additional dependencies.
 	Phony(phony string, deps ...Path)
 
-	PrimaryModule() Module
-	FinalModule() Module
-	VisitAllModuleVariants(visit func(Module))
-
+	// GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods,
+	// but do not exist.
 	GetMissingDependencies() []string
 }
 
@@ -417,7 +566,7 @@
 	// control whether this module compiles for 32-bit, 64-bit, or both.  Possible values
 	// are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
 	// architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
-	// platform
+	// platform).
 	Compile_multilib *string `android:"arch_variant"`
 
 	Target struct {
@@ -476,7 +625,7 @@
 	Native_bridge_supported *bool `android:"arch_variant"`
 
 	// init.rc files to be installed if this module is installed
-	Init_rc []string `android:"path"`
+	Init_rc []string `android:"arch_variant,path"`
 
 	// VINTF manifest fragments to be installed if this module is installed
 	Vintf_fragments []string `android:"path"`
@@ -799,7 +948,7 @@
 	initRcPaths         Paths
 	vintfFragmentsPaths Paths
 
-	prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool
+	prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool
 }
 
 func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
@@ -826,7 +975,7 @@
 	return m.variables
 }
 
-func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool) {
+func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool) {
 	m.prefer32 = prefer32
 }
 
@@ -922,7 +1071,7 @@
 }
 
 func (m *ModuleBase) Host() bool {
-	return m.Os().Class == Host || m.Os().Class == HostCross
+	return m.Os().Class == Host
 }
 
 func (m *ModuleBase) Device() bool {
@@ -942,28 +1091,28 @@
 	return m.commonProperties.CommonOSVariant
 }
 
-func (m *ModuleBase) OsClassSupported() []OsClass {
+func (m *ModuleBase) supportsTarget(target Target, config Config) bool {
 	switch m.commonProperties.HostOrDeviceSupported {
 	case HostSupported:
-		return []OsClass{Host, HostCross}
+		return target.Os.Class == Host
 	case HostSupportedNoCross:
-		return []OsClass{Host}
+		return target.Os.Class == Host && !target.HostCross
 	case DeviceSupported:
-		return []OsClass{Device}
+		return target.Os.Class == Device
 	case HostAndDeviceSupported, HostAndDeviceDefault:
-		var supported []OsClass
+		supported := false
 		if Bool(m.hostAndDeviceProperties.Host_supported) ||
 			(m.commonProperties.HostOrDeviceSupported == HostAndDeviceDefault &&
 				m.hostAndDeviceProperties.Host_supported == nil) {
-			supported = append(supported, Host, HostCross)
+			supported = supported || target.Os.Class == Host
 		}
 		if m.hostAndDeviceProperties.Device_supported == nil ||
 			*m.hostAndDeviceProperties.Device_supported {
-			supported = append(supported, Device)
+			supported = supported || target.Os.Class == Device
 		}
 		return supported
 	default:
-		return nil
+		return false
 	}
 }
 
@@ -1557,6 +1706,21 @@
 func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
 	return b.bp.OtherModuleType(m)
 }
+func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} {
+	return b.bp.OtherModuleProvider(m, provider)
+}
+func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool {
+	return b.bp.OtherModuleHasProvider(m, provider)
+}
+func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} {
+	return b.bp.Provider(provider)
+}
+func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool {
+	return b.bp.HasProvider(provider)
+}
+func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) {
+	b.bp.SetProvider(provider, value)
+}
 
 func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
 	return b.bp.GetDirectDepWithTag(name, tag)
@@ -1877,6 +2041,20 @@
 	return b.tagPath
 }
 
+func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) {
+	b.bp.VisitAllModuleVariants(func(module blueprint.Module) {
+		visit(module.(Module))
+	})
+}
+
+func (b *baseModuleContext) PrimaryModule() Module {
+	return b.bp.PrimaryModule().(Module)
+}
+
+func (b *baseModuleContext) FinalModule() Module {
+	return b.bp.FinalModule().(Module)
+}
+
 // A regexp for removing boilerplate from BaseDependencyTag from the string representation of
 // a dependency tag.
 var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`)
@@ -1912,20 +2090,6 @@
 	return sb.String()
 }
 
-func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
-	m.bp.VisitAllModuleVariants(func(module blueprint.Module) {
-		visit(module.(Module))
-	})
-}
-
-func (m *moduleContext) PrimaryModule() Module {
-	return m.bp.PrimaryModule().(Module)
-}
-
-func (m *moduleContext) FinalModule() Module {
-	return m.bp.FinalModule().(Module)
-}
-
 func (m *moduleContext) ModuleSubDir() string {
 	return m.bp.ModuleSubDir()
 }
@@ -1951,7 +2115,7 @@
 }
 
 func (b *baseModuleContext) Host() bool {
-	return b.os.Class == Host || b.os.Class == HostCross
+	return b.os.Class == Host
 }
 
 func (b *baseModuleContext) Device() bool {
@@ -2411,30 +2575,36 @@
 	}
 
 	// Create (host|host-cross|target)-<OS> phony rules to build a reduced checkbuild.
-	osDeps := map[OsType]Paths{}
+	type osAndCross struct {
+		os        OsType
+		hostCross bool
+	}
+	osDeps := map[osAndCross]Paths{}
 	ctx.VisitAllModules(func(module Module) {
 		if module.Enabled() {
-			os := module.Target().Os
-			osDeps[os] = append(osDeps[os], module.base().checkbuildFiles...)
+			key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross}
+			osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...)
 		}
 	})
 
 	osClass := make(map[string]Paths)
-	for os, deps := range osDeps {
+	for key, deps := range osDeps {
 		var className string
 
-		switch os.Class {
+		switch key.os.Class {
 		case Host:
-			className = "host"
-		case HostCross:
-			className = "host-cross"
+			if key.hostCross {
+				className = "host-cross"
+			} else {
+				className = "host"
+			}
 		case Device:
 			className = "target"
 		default:
 			continue
 		}
 
-		name := className + "-" + os.Name
+		name := className + "-" + key.os.Name
 		osClass[className] = append(osClass[className], PathForPhony(ctx, name))
 
 		ctx.Phony(name, deps...)
diff --git a/android/mutator.go b/android/mutator.go
index 5212553..7a10477 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -179,15 +179,24 @@
 	finalDeps = append(finalDeps, f)
 }
 
+type BaseMutatorContext interface {
+	BaseModuleContext
+
+	// MutatorName returns the name that this mutator was registered with.
+	MutatorName() string
+
+	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
+	// AddDependency or OtherModuleName until after this mutator pass is complete.
+	Rename(name string)
+}
+
 type TopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
-	BaseModuleContext
+	BaseMutatorContext
 
-	MutatorName() string
-
-	Rename(name string)
-
+	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
+	// the specified property structs to it as if the properties were set in a blueprint file.
 	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
@@ -199,25 +208,121 @@
 type BottomUpMutator func(BottomUpMutatorContext)
 
 type BottomUpMutatorContext interface {
-	BaseModuleContext
+	BaseMutatorContext
 
-	MutatorName() string
+	// AddDependency adds a dependency to the given module.  It returns a slice of modules for each
+	// dependency (some entries may be nil).
+	//
+	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
+	// new dependencies have had the current mutator called on them.  If the mutator is not
+	// parallel this method does not affect the ordering of the current mutator pass, but will
+	// be ordered correctly for all future mutator passes.
+	AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module
 
-	Rename(name string)
-
-	AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string)
+	// AddReverseDependency adds a dependency from the destination to the given module.
+	// Does not affect the ordering of the current mutator pass, but will be ordered
+	// correctly for all future mutator passes.  All reverse dependencies for a destination module are
+	// collected until the end of the mutator pass, sorted by name, and then appended to the destination
+	// module's dependency list.
 	AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string)
+
+	// CreateVariations splits  a module into multiple variants, one for each name in the variationNames
+	// parameter.  It returns a list of new modules in the same order as the variationNames
+	// list.
+	//
+	// If any of the dependencies of the module being operated on were already split
+	// by calling CreateVariations with the same name, the dependency will automatically
+	// be updated to point the matching variant.
+	//
+	// If a module is split, and then a module depending on the first module is not split
+	// when the Mutator is later called on it, the dependency of the depending module will
+	// automatically be updated to point to the first variant.
 	CreateVariations(...string) []Module
+
+	// CreateLocationVariations splits a module into multiple variants, one for each name in the variantNames
+	// parameter.  It returns a list of new modules in the same order as the variantNames
+	// list.
+	//
+	// Local variations do not affect automatic dependency resolution - dependencies added
+	// to the split module via deps or DynamicDependerModule must exactly match a variant
+	// that contains all the non-local variations.
 	CreateLocalVariations(...string) []Module
+
+	// SetDependencyVariation sets all dangling dependencies on the current module to point to the variation
+	// with given name. This function ignores the default variation set by SetDefaultDependencyVariation.
 	SetDependencyVariation(string)
+
+	// SetDefaultDependencyVariation sets the default variation when a dangling reference is detected
+	// during the subsequent calls on Create*Variations* functions. To reset, set it to nil.
 	SetDefaultDependencyVariation(*string)
-	AddVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
-	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
+
+	// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
+	// argument to select which variant of the dependency to use.  It returns a slice of modules for
+	// each dependency (some entries may be nil).  A variant of the dependency must exist that matches
+	// the all of the non-local variations of the current module, plus the variations argument.
+	//
+	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
+	// new dependencies have had the current mutator called on them.  If the mutator is not
+	// parallel this method does not affect the ordering of the current mutator pass, but will
+	// be ordered correctly for all future mutator passes.
+	AddVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
+
+	// AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
+	// variations argument to select which variant of the dependency to use.  It returns a slice of
+	// modules for each dependency (some entries may be nil).  A variant of the dependency must
+	// exist that matches the variations argument, but may also have other variations.
+	// For any unspecified variation the first variant will be used.
+	//
+	// Unlike AddVariationDependencies, the variations of the current module are ignored - the
+	// dependency only needs to match the supplied variations.
+	//
+	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
+	// new dependencies have had the current mutator called on them.  If the mutator is not
+	// parallel this method does not affect the ordering of the current mutator pass, but will
+	// be ordered correctly for all future mutator passes.
+	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
+
+	// AddInterVariantDependency adds a dependency between two variants of the same module.  Variants are always
+	// ordered in the same orderas they were listed in CreateVariations, and AddInterVariantDependency does not change
+	// that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps,
+	// WalkDeps, etc.
 	AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
+
+	// ReplaceDependencies replaces all dependencies on the identical variant of the module with the
+	// specified name with the current variant of this module.  Replacements don't take effect until
+	// after the mutator pass is finished.
 	ReplaceDependencies(string)
+
+	// ReplaceDependencies replaces all dependencies on the identical variant of the module with the
+	// specified name with the current variant of this module as long as the supplied predicate returns
+	// true.
+	//
+	// Replacements don't take effect until after the mutator pass is finished.
 	ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate)
+
+	// AliasVariation takes a variationName that was passed to CreateVariations for this module,
+	// and creates an alias from the current variant (before the mutator has run) to the new
+	// variant.  The alias will be valid until the next time a mutator calls CreateVariations or
+	// CreateLocalVariations on this module without also calling AliasVariation.  The alias can
+	// be used to add dependencies on the newly created variant using the variant map from
+	// before CreateVariations was run.
 	AliasVariation(variationName string)
+
+	// CreateAliasVariation takes a toVariationName that was passed to CreateVariations for this
+	// module, and creates an alias from a new fromVariationName variant the toVariationName
+	// variant.  The alias will be valid until the next time a mutator calls CreateVariations or
+	// CreateLocalVariations on this module without also calling AliasVariation.  The alias can
+	// be used to add dependencies on the toVariationName variant using the fromVariationName
+	// variant.
 	CreateAliasVariation(fromVariationName, toVariationName string)
+
+	// SetVariationProvider sets the value for a provider for the given newly created variant of
+	// the current module, i.e. one of the Modules returned by CreateVariations..  It panics if
+	// not called during the appropriate mutator or GenerateBuildActions pass for the provider,
+	// if the value is not of the appropriate type, or if the module is not a newly created
+	// variant of the current module.  The value should not be modified after being passed to
+	// SetVariationProvider.
+	SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{})
 }
 
 type bottomUpMutatorContext struct {
@@ -370,8 +475,8 @@
 	b.Module().base().commonProperties.DebugName = name
 }
 
-func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) {
-	b.bp.AddDependency(module, tag, name...)
+func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
+	return b.bp.AddDependency(module, tag, name...)
 }
 
 func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) {
@@ -423,15 +528,15 @@
 }
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
-	names ...string) {
+	names ...string) []blueprint.Module {
 
-	b.bp.AddVariationDependencies(variations, tag, names...)
+	return b.bp.AddVariationDependencies(variations, tag, names...)
 }
 
 func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
-	tag blueprint.DependencyTag, names ...string) {
+	tag blueprint.DependencyTag, names ...string) []blueprint.Module {
 
-	b.bp.AddFarVariationDependencies(variations, tag, names...)
+	return b.bp.AddFarVariationDependencies(variations, tag, names...)
 }
 
 func (b *bottomUpMutatorContext) AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module) {
@@ -453,3 +558,7 @@
 func (b *bottomUpMutatorContext) CreateAliasVariation(fromVariationName, toVariationName string) {
 	b.bp.CreateAliasVariation(fromVariationName, toVariationName)
 }
+
+func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{}) {
+	b.bp.SetVariationProvider(module, provider, value)
+}
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index 1dcf199..e2555e4 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -14,6 +14,8 @@
 
 package android
 
+import "path/filepath"
+
 func init() {
 	RegisterModuleType("prebuilt_build_tool", prebuiltBuildToolFactory)
 }
@@ -58,13 +60,18 @@
 	installedPath := PathForModuleOut(ctx, t.ModuleBase.Name())
 	deps := PathsForModuleSrc(ctx, t.properties.Deps)
 
+	var fromPath = sourcePath.String()
+	if !filepath.IsAbs(fromPath) {
+		fromPath = "$$PWD/" + fromPath
+	}
+
 	ctx.Build(pctx, BuildParams{
 		Rule:      Symlink,
 		Output:    installedPath,
 		Input:     sourcePath,
 		Implicits: deps,
 		Args: map[string]string{
-			"fromPath": "$$PWD/" + sourcePath.String(),
+			"fromPath": fromPath,
 		},
 	})
 
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 6c3cd9e..854395e 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -285,7 +285,7 @@
 				t.Errorf("windows is assumed to be disabled by default")
 			}
 			config.config.Targets[Windows] = []Target{
-				{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+				{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
 			}
 
 			ctx := NewTestArchContext()
diff --git a/android/sdk.go b/android/sdk.go
index 9ea7ff4..f2cdc88 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -237,9 +237,25 @@
 	// * string
 	// * array of the above
 	// * bool
+	// For these types it is an error if multiple properties with the same name
+	// are added.
+	//
+	// * pointer to a struct
 	// * BpPropertySet
 	//
-	// It is an error if multiple properties with the same name are added.
+	// A pointer to a Blueprint-style property struct is first converted into a
+	// BpPropertySet by traversing the fields and adding their values as
+	// properties in a BpPropertySet. A field with a struct value is itself
+	// converted into a BpPropertySet before adding.
+	//
+	// Adding a BpPropertySet is done as follows:
+	// * If no property with the name exists then the BpPropertySet is added
+	//   directly to this property. Care must be taken to ensure that it does not
+	//   introduce a cycle.
+	// * If a property exists with the name and the current value is a
+	//   BpPropertySet then every property of the new BpPropertySet is added to
+	//   the existing BpPropertySet.
+	// * Otherwise, if a property exists with the name then it is an error.
 	AddProperty(name string, value interface{})
 
 	// Add a property with an associated tag
diff --git a/android/singleton.go b/android/singleton.go
index 2c51c6c..9832378 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -29,6 +29,16 @@
 	ModuleType(module blueprint.Module) string
 	BlueprintFile(module blueprint.Module) string
 
+	// ModuleProvider returns the value, if any, for the provider for a module.  If the value for the
+	// provider was not set it returns the zero value of the type of the provider, which means the
+	// return value can always be type-asserted to the type of the provider.  The return value should
+	// always be considered read-only.  It panics if called before the appropriate mutator or
+	// GenerateBuildActions pass for the provider on the module.
+	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
+
+	// ModuleHasProvider returns true if the provider for the given module has been set.
+	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
+
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
 	Errorf(format string, args ...interface{})
 	Failed() bool
diff --git a/android/variable.go b/android/variable.go
index 53f081e..ce523ed 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -365,12 +365,12 @@
 	*v = productVariables{
 		BuildNumberFile: stringPtr("build_number.txt"),
 
-		Platform_version_name:             stringPtr("Q"),
-		Platform_sdk_version:              intPtr(28),
-		Platform_sdk_codename:             stringPtr("Q"),
+		Platform_version_name:             stringPtr("S"),
+		Platform_sdk_version:              intPtr(30),
+		Platform_sdk_codename:             stringPtr("S"),
 		Platform_sdk_final:                boolPtr(false),
-		Platform_version_active_codenames: []string{"Q"},
-		Platform_vndk_version:             stringPtr("Q"),
+		Platform_version_active_codenames: []string{"S"},
+		Platform_vndk_version:             stringPtr("S"),
 
 		HostArch:                   stringPtr("x86_64"),
 		HostSecondaryArch:          stringPtr("x86"),
diff --git a/android/visibility.go b/android/visibility.go
index 68da1c4..51d5611 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"regexp"
+	"sort"
 	"strings"
 	"sync"
 
@@ -441,12 +442,19 @@
 		}
 
 		rule := effectiveVisibilityRules(ctx.Config(), depQualified)
-		if rule != nil && !rule.matches(qualified) {
+		if !rule.matches(qualified) {
 			ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
 		}
 	})
 }
 
+// Default visibility is public.
+var defaultVisibility = compositeRule{publicRule{}}
+
+// Return the effective visibility rules.
+//
+// If no rules have been specified this will return the default visibility rule
+// which is currently //visibility:public.
 func effectiveVisibilityRules(config Config, qualified qualifiedModuleName) compositeRule {
 	moduleToVisibilityRule := moduleToVisibilityRuleMap(config)
 	value, ok := moduleToVisibilityRule.Load(qualified)
@@ -456,6 +464,12 @@
 	} else {
 		rule = packageDefaultVisibility(config, qualified)
 	}
+
+	// If no rule is specified then return the default visibility rule to avoid
+	// every caller having to treat nil as public.
+	if rule == nil {
+		rule = defaultVisibility
+	}
 	return rule
 }
 
@@ -483,13 +497,63 @@
 	}
 }
 
+type VisibilityRuleSet interface {
+	// Widen the visibility with some extra rules.
+	Widen(extra []string) error
+
+	Strings() []string
+}
+
+type visibilityRuleSet struct {
+	rules []string
+}
+
+var _ VisibilityRuleSet = (*visibilityRuleSet)(nil)
+
+func (v *visibilityRuleSet) Widen(extra []string) error {
+	// Check the extra rules first just in case they are invalid. Otherwise, if
+	// the current visibility is public then the extra rules will just be ignored.
+	if len(extra) == 1 {
+		singularRule := extra[0]
+		switch singularRule {
+		case "//visibility:public":
+			// Public overrides everything so just discard any existing rules.
+			v.rules = extra
+			return nil
+		case "//visibility:private":
+			// Extending rule with private is an error.
+			return fmt.Errorf("%q does not widen the visibility", singularRule)
+		}
+	}
+
+	if len(v.rules) == 1 {
+		switch v.rules[0] {
+		case "//visibility:public":
+			// No point in adding rules to something which is already public.
+			return nil
+		case "//visibility:private":
+			// Adding any rules to private means it is no longer private so the
+			// private can be discarded.
+			v.rules = nil
+		}
+	}
+
+	v.rules = FirstUniqueStrings(append(v.rules, extra...))
+	sort.Strings(v.rules)
+	return nil
+}
+
+func (v *visibilityRuleSet) Strings() []string {
+	return v.rules
+}
+
 // Get the effective visibility rules, i.e. the actual rules that affect the visibility of the
 // property irrespective of where they are defined.
 //
 // Includes visibility rules specified by package default_visibility and/or on defaults.
 // Short hand forms, e.g. //:__subpackages__ are replaced with their full form, e.g.
 // //package/containing/rule:__subpackages__.
-func EffectiveVisibilityRules(ctx BaseModuleContext, module Module) []string {
+func EffectiveVisibilityRules(ctx BaseModuleContext, module Module) VisibilityRuleSet {
 	moduleName := ctx.OtherModuleName(module)
 	dir := ctx.OtherModuleDir(module)
 	qualified := qualifiedModuleName{dir, moduleName}
@@ -499,7 +563,7 @@
 	// Modules are implicitly visible to other modules in the same package,
 	// without checking the visibility rules. Here we need to add that visibility
 	// explicitly.
-	if rule != nil && !rule.matches(qualified) {
+	if !rule.matches(qualified) {
 		if len(rule) == 1 {
 			if _, ok := rule[0].(privateRule); ok {
 				// If the rule is //visibility:private we can't append another
@@ -508,13 +572,13 @@
 				// modules are implicitly visible within the package we get the same
 				// result without any rule at all, so just make it an empty list to be
 				// appended below.
-				rule = compositeRule{}
+				rule = nil
 			}
 		}
 		rule = append(rule, packageRule{dir})
 	}
 
-	return rule.Strings()
+	return &visibilityRuleSet{rule.Strings()}
 }
 
 // Clear the default visibility properties so they can be replaced.
diff --git a/android/visibility_test.go b/android/visibility_test.go
index ca09345..9d9e574 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1270,3 +1270,49 @@
 	})
 	return m
 }
+
+func testVisibilityRuleSet(t *testing.T, rules, extra, expected []string) {
+	t.Helper()
+	set := &visibilityRuleSet{rules}
+	err := set.Widen(extra)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual := set.Strings()
+	if !reflect.DeepEqual(actual, expected) {
+		t.Errorf("mismatching rules after extend: expected %#v, actual %#v", expected, actual)
+	}
+}
+
+func TestVisibilityRuleSet(t *testing.T) {
+	t.Run("extend empty", func(t *testing.T) {
+		testVisibilityRuleSet(t, nil, []string{"//foo"}, []string{"//foo"})
+	})
+	t.Run("extend", func(t *testing.T) {
+		testVisibilityRuleSet(t, []string{"//foo"}, []string{"//bar"}, []string{"//bar", "//foo"})
+	})
+	t.Run("extend duplicate", func(t *testing.T) {
+		testVisibilityRuleSet(t, []string{"//foo"}, []string{"//bar", "//foo"}, []string{"//bar", "//foo"})
+	})
+	t.Run("extend public", func(t *testing.T) {
+		testVisibilityRuleSet(t, []string{"//visibility:public"}, []string{"//foo"}, []string{"//visibility:public"})
+	})
+	t.Run("extend private", func(t *testing.T) {
+		testVisibilityRuleSet(t, []string{"//visibility:private"}, []string{"//foo"}, []string{"//foo"})
+	})
+	t.Run("extend with public", func(t *testing.T) {
+		testVisibilityRuleSet(t, []string{"//foo"}, []string{"//visibility:public"}, []string{"//visibility:public"})
+	})
+	t.Run("extend with private", func(t *testing.T) {
+		t.Helper()
+		set := &visibilityRuleSet{[]string{"//foo"}}
+		err := set.Widen([]string{"//visibility:private"})
+		expectedError := `"//visibility:private" does not widen the visibility`
+		if err == nil {
+			t.Errorf("missing error")
+		} else if err.Error() != expectedError {
+			t.Errorf("expected error %q found error %q", expectedError, err)
+		}
+	})
+}
diff --git a/apex/OWNERS b/apex/OWNERS
index a382ae8..793f3ed 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1 +1,4 @@
-per-file * = jiyong@google.com
\ No newline at end of file
+per-file * = jiyong@google.com
+
+per-file allowed_deps.txt = set noparent
+per-file allowed_deps.txt = dariofreni@google.com,hansson@google.com,harpin@google.com,jiyong@google.com,narayan@google.com,omakoto@google.com,jham@google.com
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
new file mode 100644
index 0000000..87e96ea
--- /dev/null
+++ b/apex/allowed_deps.txt
@@ -0,0 +1,521 @@
+# A list of allowed dependencies for all updatable modules.
+#
+# The list tracks all direct and transitive dependencies that end up within any
+# of the updatable binaries; specifically excluding external dependencies
+# required to compile those binaries. This prevents potential regressions in
+# case a new dependency is not aware of the different functional and
+# non-functional requirements being part of an updatable module, for example
+# setting correct min_sdk_version.
+#
+# To update the list, run:
+# repo-root$ build/soong/scripts/update-apex-allowed-deps.sh
+#
+# See go/apex-allowed-deps-error for more details.
+# TODO(b/157465465): introduce automated quality signals and remove this list.
+
+adbd(minSdkVersion:(no version))
+android.hardware.cas.native@1.0(minSdkVersion:29)
+android.hardware.cas@1.0(minSdkVersion:29)
+android.hardware.common-ndk_platform(minSdkVersion:29)
+android.hardware.graphics.allocator@2.0(minSdkVersion:29)
+android.hardware.graphics.allocator@3.0(minSdkVersion:29)
+android.hardware.graphics.allocator@4.0(minSdkVersion:29)
+android.hardware.graphics.bufferqueue@1.0(minSdkVersion:29)
+android.hardware.graphics.bufferqueue@2.0(minSdkVersion:29)
+android.hardware.graphics.common-ndk_platform(minSdkVersion:29)
+android.hardware.graphics.common@1.0(minSdkVersion:29)
+android.hardware.graphics.common@1.1(minSdkVersion:29)
+android.hardware.graphics.common@1.2(minSdkVersion:29)
+android.hardware.graphics.mapper@2.0(minSdkVersion:29)
+android.hardware.graphics.mapper@2.1(minSdkVersion:29)
+android.hardware.graphics.mapper@3.0(minSdkVersion:29)
+android.hardware.graphics.mapper@4.0(minSdkVersion:29)
+android.hardware.media.bufferpool@2.0(minSdkVersion:29)
+android.hardware.media.c2@1.0(minSdkVersion:29)
+android.hardware.media.c2@1.1(minSdkVersion:29)
+android.hardware.media.omx@1.0(minSdkVersion:29)
+android.hardware.media@1.0(minSdkVersion:29)
+android.hardware.neuralnetworks@1.0(minSdkVersion:30)
+android.hardware.neuralnetworks@1.1(minSdkVersion:30)
+android.hardware.neuralnetworks@1.2(minSdkVersion:30)
+android.hardware.neuralnetworks@1.3(minSdkVersion:30)
+android.hardware.tetheroffload.config-V1.0-java(minSdkVersion:current)
+android.hardware.tetheroffload.control-V1.0-java(minSdkVersion:current)
+android.hidl.allocator@1.0(minSdkVersion:29)
+android.hidl.base-V1.0-java(minSdkVersion:current)
+android.hidl.memory.token@1.0(minSdkVersion:29)
+android.hidl.memory@1.0(minSdkVersion:29)
+android.hidl.safe_union@1.0(minSdkVersion:29)
+android.hidl.token@1.0(minSdkVersion:29)
+android.hidl.token@1.0-utils(minSdkVersion:29)
+android.net.ipsec.ike(minSdkVersion:current)
+android.net.ipsec.ike.xml(minSdkVersion:(no version))
+androidx-constraintlayout_constraintlayout(minSdkVersion:14)
+androidx-constraintlayout_constraintlayout-solver(minSdkVersion:24)
+androidx.activity_activity(minSdkVersion:14)
+androidx.activity_activity-ktx(minSdkVersion:14)
+androidx.annotation_annotation(minSdkVersion:24)
+androidx.annotation_annotation(minSdkVersion:current)
+androidx.appcompat_appcompat(minSdkVersion:14)
+androidx.appcompat_appcompat-resources(minSdkVersion:14)
+androidx.arch.core_core-common(minSdkVersion:24)
+androidx.arch.core_core-common(minSdkVersion:current)
+androidx.arch.core_core-runtime(minSdkVersion:14)
+androidx.asynclayoutinflater_asynclayoutinflater(minSdkVersion:14)
+androidx.autofill_autofill(minSdkVersion:14)
+androidx.cardview_cardview(minSdkVersion:14)
+androidx.collection_collection(minSdkVersion:24)
+androidx.collection_collection(minSdkVersion:current)
+androidx.collection_collection-ktx(minSdkVersion:24)
+androidx.coordinatorlayout_coordinatorlayout(minSdkVersion:14)
+androidx.core_core(minSdkVersion:14)
+androidx.core_core-ktx(minSdkVersion:14)
+androidx.cursoradapter_cursoradapter(minSdkVersion:14)
+androidx.customview_customview(minSdkVersion:14)
+androidx.documentfile_documentfile(minSdkVersion:14)
+androidx.drawerlayout_drawerlayout(minSdkVersion:14)
+androidx.fragment_fragment(minSdkVersion:14)
+androidx.fragment_fragment-ktx(minSdkVersion:14)
+androidx.interpolator_interpolator(minSdkVersion:14)
+androidx.leanback_leanback(minSdkVersion:17)
+androidx.leanback_leanback-preference(minSdkVersion:21)
+androidx.legacy_legacy-preference-v14(minSdkVersion:14)
+androidx.legacy_legacy-support-core-ui(minSdkVersion:14)
+androidx.legacy_legacy-support-core-utils(minSdkVersion:14)
+androidx.legacy_legacy-support-v13(minSdkVersion:14)
+androidx.legacy_legacy-support-v4(minSdkVersion:14)
+androidx.lifecycle_lifecycle-common(minSdkVersion:24)
+androidx.lifecycle_lifecycle-common(minSdkVersion:current)
+androidx.lifecycle_lifecycle-common-java8(minSdkVersion:24)
+androidx.lifecycle_lifecycle-extensions(minSdkVersion:14)
+androidx.lifecycle_lifecycle-livedata(minSdkVersion:14)
+androidx.lifecycle_lifecycle-livedata-core(minSdkVersion:14)
+androidx.lifecycle_lifecycle-livedata-core-ktx(minSdkVersion:14)
+androidx.lifecycle_lifecycle-process(minSdkVersion:14)
+androidx.lifecycle_lifecycle-runtime(minSdkVersion:14)
+androidx.lifecycle_lifecycle-runtime-ktx(minSdkVersion:14)
+androidx.lifecycle_lifecycle-service(minSdkVersion:14)
+androidx.lifecycle_lifecycle-viewmodel(minSdkVersion:14)
+androidx.lifecycle_lifecycle-viewmodel-ktx(minSdkVersion:14)
+androidx.lifecycle_lifecycle-viewmodel-savedstate(minSdkVersion:14)
+androidx.loader_loader(minSdkVersion:14)
+androidx.localbroadcastmanager_localbroadcastmanager(minSdkVersion:14)
+androidx.media_media(minSdkVersion:14)
+androidx.navigation_navigation-common(minSdkVersion:14)
+androidx.navigation_navigation-common-ktx(minSdkVersion:14)
+androidx.navigation_navigation-fragment(minSdkVersion:14)
+androidx.navigation_navigation-fragment-ktx(minSdkVersion:14)
+androidx.navigation_navigation-runtime(minSdkVersion:14)
+androidx.navigation_navigation-runtime-ktx(minSdkVersion:14)
+androidx.navigation_navigation-ui(minSdkVersion:14)
+androidx.navigation_navigation-ui-ktx(minSdkVersion:14)
+androidx.preference_preference(minSdkVersion:14)
+androidx.print_print(minSdkVersion:14)
+androidx.recyclerview_recyclerview(minSdkVersion:14)
+androidx.recyclerview_recyclerview-selection(minSdkVersion:14)
+androidx.savedstate_savedstate(minSdkVersion:14)
+androidx.slidingpanelayout_slidingpanelayout(minSdkVersion:14)
+androidx.swiperefreshlayout_swiperefreshlayout(minSdkVersion:14)
+androidx.transition_transition(minSdkVersion:14)
+androidx.vectordrawable_vectordrawable(minSdkVersion:14)
+androidx.vectordrawable_vectordrawable-animated(minSdkVersion:14)
+androidx.versionedparcelable_versionedparcelable(minSdkVersion:14)
+androidx.viewpager_viewpager(minSdkVersion:14)
+apache-commons-compress(minSdkVersion:current)
+art.module.public.api.stubs(minSdkVersion:(no version))
+bcm_object(minSdkVersion:29)
+boringssl_self_test(minSdkVersion:29)
+bouncycastle_ike_digests(minSdkVersion:current)
+brotli-java(minSdkVersion:current)
+captiveportal-lib(minSdkVersion:29)
+car-ui-lib(minSdkVersion:28)
+CellBroadcastApp(minSdkVersion:29)
+CellBroadcastServiceModule(minSdkVersion:29)
+codecs_g711dec(minSdkVersion:29)
+com.google.android.material_material(minSdkVersion:14)
+conscrypt(minSdkVersion:29)
+conscrypt.module.platform.api.stubs(minSdkVersion:(no version))
+conscrypt.module.public.api.stubs(minSdkVersion:(no version))
+core-lambda-stubs(minSdkVersion:(no version))
+core.current.stubs(minSdkVersion:(no version))
+crtbegin_dynamic(minSdkVersion:apex_inherit)
+crtbegin_dynamic1(minSdkVersion:apex_inherit)
+crtbegin_so(minSdkVersion:apex_inherit)
+crtbegin_so1(minSdkVersion:apex_inherit)
+crtbrand(minSdkVersion:apex_inherit)
+crtend_android(minSdkVersion:apex_inherit)
+crtend_so(minSdkVersion:apex_inherit)
+datastallprotosnano(minSdkVersion:29)
+derive_sdk(minSdkVersion:current)
+derive_sdk_prefer32(minSdkVersion:current)
+dnsresolver_aidl_interface-unstable-ndk_platform(minSdkVersion:29)
+DocumentsUI-res-lib(minSdkVersion:29)
+exoplayer2-extractor(minSdkVersion:16)
+exoplayer2-extractor-annotation-stubs(minSdkVersion:16)
+ExtServices(minSdkVersion:current)
+ExtServices-core(minSdkVersion:current)
+flatbuffer_headers(minSdkVersion:(no version))
+fmtlib(minSdkVersion:29)
+framework-permission(minSdkVersion:current)
+framework-sdkextensions(minSdkVersion:current)
+framework-statsd(minSdkVersion:current)
+framework-tethering(minSdkVersion:current)
+gemmlowp_headers(minSdkVersion:(no version))
+GoogleCellBroadcastApp(minSdkVersion:29)
+GoogleCellBroadcastServiceModule(minSdkVersion:29)
+GoogleExtServices(minSdkVersion:current)
+GooglePermissionController(minSdkVersion:28)
+guava(minSdkVersion:current)
+gwp_asan_headers(minSdkVersion:(no version))
+i18n.module.public.api.stubs(minSdkVersion:(no version))
+iconloader(minSdkVersion:21)
+ike-internals(minSdkVersion:current)
+InProcessTethering(minSdkVersion:current)
+ipmemorystore-aidl-interfaces-java(minSdkVersion:29)
+ipmemorystore-aidl-interfaces-unstable-java(minSdkVersion:29)
+jni_headers(minSdkVersion:29)
+jsr305(minSdkVersion:14)
+kotlinx-coroutines-android(minSdkVersion:current)
+kotlinx-coroutines-core(minSdkVersion:current)
+legacy.art.module.platform.api.stubs(minSdkVersion:(no version))
+legacy.core.platform.api.stubs(minSdkVersion:(no version))
+legacy.i18n.module.platform.api.stubs(minSdkVersion:(no version))
+libaacextractor(minSdkVersion:29)
+libadb_crypto(minSdkVersion:(no version))
+libadb_pairing_auth(minSdkVersion:(no version))
+libadb_pairing_connection(minSdkVersion:(no version))
+libadb_pairing_server(minSdkVersion:(no version))
+libadb_protos(minSdkVersion:(no version))
+libadb_tls_connection(minSdkVersion:(no version))
+libadbconnection_client(minSdkVersion:(no version))
+libadbconnection_server(minSdkVersion:(no version))
+libadbd(minSdkVersion:(no version))
+libadbd_core(minSdkVersion:(no version))
+libadbd_services(minSdkVersion:(no version))
+libamrextractor(minSdkVersion:29)
+libapp_processes_protos_lite(minSdkVersion:(no version))
+libarect(minSdkVersion:29)
+libasyncio(minSdkVersion:(no version))
+libatomic(minSdkVersion:(no version))
+libaudio_system_headers(minSdkVersion:29)
+libaudioclient_headers(minSdkVersion:29)
+libaudiofoundation_headers(minSdkVersion:29)
+libaudioutils(minSdkVersion:29)
+libaudioutils_fixedfft(minSdkVersion:29)
+libavcdec(minSdkVersion:29)
+libavcenc(minSdkVersion:29)
+libavservices_minijail(minSdkVersion:29)
+libbacktrace_headers(minSdkVersion:apex_inherit)
+libbase(minSdkVersion:29)
+libbase_headers(minSdkVersion:29)
+libbinder_headers(minSdkVersion:29)
+libbinderthreadstateutils(minSdkVersion:29)
+libbluetooth-types-header(minSdkVersion:29)
+libbrotli(minSdkVersion:(no version))
+libbuildversion(minSdkVersion:(no version))
+libc(minSdkVersion:(no version))
+libc++(minSdkVersion:apex_inherit)
+libc++_static(minSdkVersion:apex_inherit)
+libc++abi(minSdkVersion:apex_inherit)
+libc++demangle(minSdkVersion:apex_inherit)
+libc_headers(minSdkVersion:apex_inherit)
+libc_headers_arch(minSdkVersion:apex_inherit)
+libcap(minSdkVersion:29)
+libcodec2(minSdkVersion:29)
+libcodec2_headers(minSdkVersion:29)
+libcodec2_hidl@1.0(minSdkVersion:29)
+libcodec2_hidl@1.1(minSdkVersion:29)
+libcodec2_internal(minSdkVersion:29)
+libcodec2_soft_aacdec(minSdkVersion:29)
+libcodec2_soft_aacenc(minSdkVersion:29)
+libcodec2_soft_amrnbdec(minSdkVersion:29)
+libcodec2_soft_amrnbenc(minSdkVersion:29)
+libcodec2_soft_amrwbdec(minSdkVersion:29)
+libcodec2_soft_amrwbenc(minSdkVersion:29)
+libcodec2_soft_av1dec_gav1(minSdkVersion:29)
+libcodec2_soft_avcdec(minSdkVersion:29)
+libcodec2_soft_avcenc(minSdkVersion:29)
+libcodec2_soft_common(minSdkVersion:29)
+libcodec2_soft_flacdec(minSdkVersion:29)
+libcodec2_soft_flacenc(minSdkVersion:29)
+libcodec2_soft_g711alawdec(minSdkVersion:29)
+libcodec2_soft_g711mlawdec(minSdkVersion:29)
+libcodec2_soft_gsmdec(minSdkVersion:29)
+libcodec2_soft_h263dec(minSdkVersion:29)
+libcodec2_soft_h263enc(minSdkVersion:29)
+libcodec2_soft_hevcdec(minSdkVersion:29)
+libcodec2_soft_hevcenc(minSdkVersion:29)
+libcodec2_soft_mp3dec(minSdkVersion:29)
+libcodec2_soft_mpeg2dec(minSdkVersion:29)
+libcodec2_soft_mpeg4dec(minSdkVersion:29)
+libcodec2_soft_mpeg4enc(minSdkVersion:29)
+libcodec2_soft_opusdec(minSdkVersion:29)
+libcodec2_soft_opusenc(minSdkVersion:29)
+libcodec2_soft_rawdec(minSdkVersion:29)
+libcodec2_soft_vorbisdec(minSdkVersion:29)
+libcodec2_soft_vp8dec(minSdkVersion:29)
+libcodec2_soft_vp8enc(minSdkVersion:29)
+libcodec2_soft_vp9dec(minSdkVersion:29)
+libcodec2_soft_vp9enc(minSdkVersion:29)
+libcodec2_vndk(minSdkVersion:29)
+libcrypto(minSdkVersion:29)
+libcrypto_static(minSdkVersion:(no version))
+libcrypto_utils(minSdkVersion:(no version))
+libcutils(minSdkVersion:29)
+libcutils_headers(minSdkVersion:29)
+libcutils_sockets(minSdkVersion:29)
+libdiagnose_usb(minSdkVersion:(no version))
+libdl(minSdkVersion:(no version))
+libeigen(minSdkVersion:(no version))
+libfifo(minSdkVersion:29)
+libFLAC(minSdkVersion:29)
+libFLAC-config(minSdkVersion:29)
+libFLAC-headers(minSdkVersion:29)
+libflacextractor(minSdkVersion:29)
+libfmq(minSdkVersion:29)
+libFraunhoferAAC(minSdkVersion:29)
+libgav1(minSdkVersion:29)
+libgcc_stripped(minSdkVersion:(no version))
+libgralloctypes(minSdkVersion:29)
+libgrallocusage(minSdkVersion:29)
+libgsm(minSdkVersion:apex_inherit)
+libgtest_prod(minSdkVersion:apex_inherit)
+libgui_bufferqueue_static(minSdkVersion:29)
+libgui_headers(minSdkVersion:29)
+libhardware(minSdkVersion:29)
+libhardware_headers(minSdkVersion:29)
+libhevcdec(minSdkVersion:29)
+libhevcenc(minSdkVersion:29)
+libhidlbase(minSdkVersion:29)
+libhidlmemory(minSdkVersion:29)
+libhwbinder-impl-internal(minSdkVersion:29)
+libion(minSdkVersion:29)
+libjavacrypto(minSdkVersion:29)
+libjsoncpp(minSdkVersion:29)
+libLibGuiProperties(minSdkVersion:29)
+liblog(minSdkVersion:(no version))
+liblog_headers(minSdkVersion:29)
+liblua(minSdkVersion:(no version))
+liblz4(minSdkVersion:(no version))
+libm(minSdkVersion:(no version))
+libmath(minSdkVersion:29)
+libmdnssd(minSdkVersion:(no version))
+libmedia_codecserviceregistrant(minSdkVersion:29)
+libmedia_datasource_headers(minSdkVersion:29)
+libmedia_headers(minSdkVersion:29)
+libmedia_helper_headers(minSdkVersion:29)
+libmedia_midiiowrapper(minSdkVersion:29)
+libmidiextractor(minSdkVersion:29)
+libminijail(minSdkVersion:29)
+libminijail_gen_constants(minSdkVersion:(no version))
+libminijail_gen_constants_obj(minSdkVersion:29)
+libminijail_gen_syscall(minSdkVersion:(no version))
+libminijail_gen_syscall_obj(minSdkVersion:29)
+libminijail_generated(minSdkVersion:29)
+libmkvextractor(minSdkVersion:29)
+libmp3extractor(minSdkVersion:29)
+libmp4extractor(minSdkVersion:29)
+libmpeg2dec(minSdkVersion:29)
+libmpeg2extractor(minSdkVersion:29)
+libnativebase_headers(minSdkVersion:29)
+libnativehelper_compat_libc++(minSdkVersion:(no version))
+libnativehelper_header_only(minSdkVersion:29)
+libnativewindow_headers(minSdkVersion:29)
+libnetd_resolv(minSdkVersion:29)
+libnetdbinder_utils_headers(minSdkVersion:29)
+libnetdutils(minSdkVersion:29)
+libnetworkstackutilsjni(minSdkVersion:29)
+libneuralnetworks(minSdkVersion:(no version))
+libneuralnetworks_common(minSdkVersion:(no version))
+libneuralnetworks_headers(minSdkVersion:(no version))
+liboggextractor(minSdkVersion:29)
+libopus(minSdkVersion:29)
+libprocessgroup(minSdkVersion:29)
+libprocessgroup_headers(minSdkVersion:29)
+libprocpartition(minSdkVersion:(no version))
+libprotobuf-cpp-lite(minSdkVersion:29)
+libprotobuf-java-lite(minSdkVersion:current)
+libprotobuf-java-nano(minSdkVersion:9)
+libprotoutil(minSdkVersion:(no version))
+libqemu_pipe(minSdkVersion:(no version))
+libsfplugin_ccodec_utils(minSdkVersion:29)
+libsonivoxwithoutjet(minSdkVersion:29)
+libspeexresampler(minSdkVersion:29)
+libssl(minSdkVersion:29)
+libstagefright_amrnb_common(minSdkVersion:29)
+libstagefright_amrnbdec(minSdkVersion:29)
+libstagefright_amrnbenc(minSdkVersion:29)
+libstagefright_amrwbdec(minSdkVersion:29)
+libstagefright_amrwbenc(minSdkVersion:29)
+libstagefright_bufferpool@2.0.1(minSdkVersion:29)
+libstagefright_bufferqueue_helper(minSdkVersion:29)
+libstagefright_enc_common(minSdkVersion:29)
+libstagefright_esds(minSdkVersion:29)
+libstagefright_flacdec(minSdkVersion:29)
+libstagefright_foundation(minSdkVersion:29)
+libstagefright_foundation_headers(minSdkVersion:29)
+libstagefright_foundation_without_imemory(minSdkVersion:29)
+libstagefright_headers(minSdkVersion:29)
+libstagefright_id3(minSdkVersion:29)
+libstagefright_m4vh263dec(minSdkVersion:29)
+libstagefright_m4vh263enc(minSdkVersion:29)
+libstagefright_metadatautils(minSdkVersion:29)
+libstagefright_mp3dec(minSdkVersion:29)
+libstagefright_mpeg2extractor(minSdkVersion:29)
+libstagefright_mpeg2support_nocrypto(minSdkVersion:29)
+libstats_jni(minSdkVersion:(no version))
+libstatslog_resolv(minSdkVersion:29)
+libstatslog_statsd(minSdkVersion:(no version))
+libstatspull(minSdkVersion:(no version))
+libstatspush_compat(minSdkVersion:29)
+libstatssocket(minSdkVersion:(no version))
+libstatssocket_headers(minSdkVersion:29)
+libsystem_headers(minSdkVersion:apex_inherit)
+libsysutils(minSdkVersion:apex_inherit)
+libtetherutilsjni(minSdkVersion:current)
+libtextclassifier(minSdkVersion:(no version))
+libtextclassifier-java(minSdkVersion:current)
+libtextclassifier_hash_headers(minSdkVersion:(no version))
+libtextclassifier_hash_static(minSdkVersion:(no version))
+libtflite_kernel_utils(minSdkVersion:(no version))
+libtflite_static(minSdkVersion:(no version))
+libui(minSdkVersion:29)
+libui_headers(minSdkVersion:29)
+libunwind_llvm(minSdkVersion:apex_inherit)
+libutf(minSdkVersion:(no version))
+libutils(minSdkVersion:apex_inherit)
+libutils_headers(minSdkVersion:apex_inherit)
+libvorbisidec(minSdkVersion:29)
+libvpx(minSdkVersion:29)
+libwatchdog(minSdkVersion:29)
+libwavextractor(minSdkVersion:29)
+libwebm(minSdkVersion:29)
+libyuv(minSdkVersion:29)
+libyuv_static(minSdkVersion:29)
+libzstd(minSdkVersion:(no version))
+media_ndk_headers(minSdkVersion:29)
+media_plugin_headers(minSdkVersion:29)
+mediaswcodec(minSdkVersion:29)
+metrics-constants-protos(minSdkVersion:29)
+ndk_crtbegin_so.19(minSdkVersion:(no version))
+ndk_crtbegin_so.21(minSdkVersion:(no version))
+ndk_crtbegin_so.27(minSdkVersion:(no version))
+ndk_crtend_so.19(minSdkVersion:(no version))
+ndk_crtend_so.21(minSdkVersion:(no version))
+ndk_crtend_so.27(minSdkVersion:(no version))
+ndk_libc++_static(minSdkVersion:(no version))
+ndk_libc++abi(minSdkVersion:(no version))
+net-utils-framework-common(minSdkVersion:current)
+netd_aidl_interface-unstable-java(minSdkVersion:29)
+netd_event_listener_interface-ndk_platform(minSdkVersion:29)
+netlink-client(minSdkVersion:29)
+networkstack-aidl-interfaces-unstable-java(minSdkVersion:29)
+networkstack-client(minSdkVersion:29)
+NetworkStackApiStableDependencies(minSdkVersion:29)
+NetworkStackApiStableLib(minSdkVersion:29)
+networkstackprotos(minSdkVersion:29)
+PermissionController(minSdkVersion:28)
+permissioncontroller-statsd(minSdkVersion:current)
+philox_random(minSdkVersion:(no version))
+philox_random_headers(minSdkVersion:(no version))
+prebuilt_androidx-constraintlayout_constraintlayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx-constraintlayout_constraintlayout-solver-nodeps(minSdkVersion:current)
+prebuilt_androidx.activity_activity-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.activity_activity-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.annotation_annotation-nodeps(minSdkVersion:current)
+prebuilt_androidx.appcompat_appcompat-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.appcompat_appcompat-resources-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.arch.core_core-common-nodeps(minSdkVersion:current)
+prebuilt_androidx.arch.core_core-runtime-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.asynclayoutinflater_asynclayoutinflater-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.autofill_autofill-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.cardview_cardview-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.collection_collection-ktx-nodeps(minSdkVersion:current)
+prebuilt_androidx.collection_collection-nodeps(minSdkVersion:current)
+prebuilt_androidx.coordinatorlayout_coordinatorlayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.core_core-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.core_core-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.cursoradapter_cursoradapter-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.customview_customview-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.documentfile_documentfile-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.drawerlayout_drawerlayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.fragment_fragment-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.fragment_fragment-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.interpolator_interpolator-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.leanback_leanback-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.leanback_leanback-preference-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.legacy_legacy-support-core-ui-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.legacy_legacy-support-core-utils-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.legacy_legacy-support-v13-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-common-java8-nodeps(minSdkVersion:current)
+prebuilt_androidx.lifecycle_lifecycle-common-nodeps(minSdkVersion:current)
+prebuilt_androidx.lifecycle_lifecycle-extensions-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-livedata-core-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-livedata-core-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-livedata-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-process-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-runtime-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-runtime-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-service-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-viewmodel-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-viewmodel-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.lifecycle_lifecycle-viewmodel-savedstate-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.loader_loader-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.localbroadcastmanager_localbroadcastmanager-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.media_media-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-common-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-common-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-fragment-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-fragment-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-runtime-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-runtime-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-ui-ktx-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.navigation_navigation-ui-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.preference_preference-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.print_print-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.recyclerview_recyclerview-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.recyclerview_recyclerview-selection-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.savedstate_savedstate-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.slidingpanelayout_slidingpanelayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.swiperefreshlayout_swiperefreshlayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.transition_transition-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.vectordrawable_vectordrawable-animated-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.vectordrawable_vectordrawable-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.versionedparcelable_versionedparcelable-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.viewpager_viewpager-nodeps(minSdkVersion:(no version))
+prebuilt_com.google.android.material_material-nodeps(minSdkVersion:(no version))
+prebuilt_error_prone_annotations(minSdkVersion:(no version))
+prebuilt_kotlin-stdlib(minSdkVersion:current)
+prebuilt_kotlinx-coroutines-android-nodeps(minSdkVersion:(no version))
+prebuilt_kotlinx-coroutines-core-nodeps(minSdkVersion:(no version))
+prebuilt_libclang_rt.builtins-aarch64-android(minSdkVersion:(no version))
+prebuilt_libclang_rt.builtins-arm-android(minSdkVersion:(no version))
+prebuilt_libclang_rt.builtins-i686-android(minSdkVersion:(no version))
+prebuilt_libclang_rt.builtins-x86_64-android(minSdkVersion:(no version))
+prebuilt_test_framework-sdkextensions(minSdkVersion:(no version))
+server_configurable_flags(minSdkVersion:29)
+service-permission(minSdkVersion:current)
+service-statsd(minSdkVersion:current)
+SettingsLibActionBarShadow(minSdkVersion:21)
+SettingsLibAppPreference(minSdkVersion:21)
+SettingsLibBarChartPreference(minSdkVersion:21)
+SettingsLibHelpUtils(minSdkVersion:21)
+SettingsLibLayoutPreference(minSdkVersion:21)
+SettingsLibProgressBar(minSdkVersion:21)
+SettingsLibRestrictedLockUtils(minSdkVersion:21)
+SettingsLibSearchWidget(minSdkVersion:21)
+SettingsLibSettingsTheme(minSdkVersion:21)
+SettingsLibUtils(minSdkVersion:21)
+stats_proto(minSdkVersion:29)
+statsd(minSdkVersion:(no version))
+statsd-aidl-ndk_platform(minSdkVersion:(no version))
+statsprotos(minSdkVersion:29)
+tensorflow_headers(minSdkVersion:(no version))
+Tethering(minSdkVersion:current)
+TetheringApiCurrentLib(minSdkVersion:current)
+TetheringGoogle(minSdkVersion:current)
+textclassifier-statsd(minSdkVersion:current)
+TextClassifierNotificationLibNoManifest(minSdkVersion:29)
+TextClassifierServiceLibNoManifest(minSdkVersion:28)
+updatable-media(minSdkVersion:29)
+xz-java(minSdkVersion:current)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 1b53a67..f76181d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -135,7 +135,7 @@
 				}
 			}
 			if len(newDataPaths) > 0 {
-				fmt.Fprintln(w, "LOCAL_TEST_DATA :=", strings.Join(cc.AndroidMkDataPaths(newDataPaths), " "))
+				fmt.Fprintln(w, "LOCAL_TEST_DATA :=", strings.Join(android.AndroidMkDataPaths(newDataPaths), " "))
 			}
 
 			if fi.module != nil && len(fi.module.NoticeFiles()) > 0 {
@@ -157,13 +157,14 @@
 			host := false
 			switch fi.module.Target().Os.Class {
 			case android.Host:
-				if fi.module.Target().Arch.ArchType != android.Common {
-					fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
-				}
-				host = true
-			case android.HostCross:
-				if fi.module.Target().Arch.ArchType != android.Common {
-					fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+				if fi.module.Target().HostCross {
+					if fi.module.Target().Arch.ArchType != android.Common {
+						fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+					}
+				} else {
+					if fi.module.Target().Arch.ArchType != android.Common {
+						fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
+					}
 				}
 				host = true
 			case android.Device:
diff --git a/apex/apex.go b/apex/apex.go
index 8e35e07..0108c0f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -565,7 +565,6 @@
 		"libdebuggerd_handler_fallback",
 		"libdexfile_external_headers",
 		"libdexfile_support",
-		"libdexfile_support_static",
 		"libdl_static",
 		"libjemalloc5",
 		"liblinker_main",
@@ -795,7 +794,7 @@
 	}
 	apexInfo := android.ApexInfo{
 		ApexVariationName: mctx.ModuleName(),
-		MinSdkVersion:     a.minSdkVersion(mctx),
+		MinSdkVersionStr:  a.minSdkVersion(mctx).String(),
 		RequiredSdks:      a.RequiredSdks(),
 		Updatable:         a.Updatable(),
 		InApexes:          []string{mctx.ModuleName()},
@@ -1499,6 +1498,12 @@
 		}
 	}
 	for i, target := range targets {
+		if target.HostCross {
+			// Don't include artifats for the host cross targets because there is no way
+			// for us to run those artifacts natively on host
+			continue
+		}
+
 		// When multilib.* is omitted for native_shared_libs/jni_libs/tests, it implies
 		// multilib.both
 		addDependenciesForNativeModules(ctx,
@@ -1952,27 +1957,21 @@
 	})
 }
 
-func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) int {
+func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
 	ver := proptools.String(a.properties.Min_sdk_version)
 	if ver == "" {
 		return android.FutureApiLevel
 	}
-	// Treat the current codenames as "current", which means future API version (10000)
-	// Otherwise, ApiStrToNum converts codename(non-finalized) to a value from [9000...]
-	// and would fail to build against "current".
-	if android.InList(ver, ctx.Config().PlatformVersionActiveCodenames()) {
-		return android.FutureApiLevel
-	}
-	// In "REL" branch, "current" is mapped to finalized sdk version
-	if ctx.Config().PlatformSdkCodename() == "REL" && ver == "current" {
-		return ctx.Config().PlatformSdkVersionInt()
-	}
-	// Finalized codenames are OKAY and will be converted to int
-	intVer, err := android.ApiStrToNum(ctx, ver)
+	apiLevel, err := android.ApiLevelFromUser(ctx, ver)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
+		return android.NoneApiLevel
 	}
-	return intVer
+	if apiLevel.IsPreview() {
+		// All codenames should build against "current".
+		return android.FutureApiLevel
+	}
+	return apiLevel
 }
 
 func (a *apexBundle) Updatable() bool {
@@ -2046,7 +2045,9 @@
 	if proptools.Bool(a.properties.Use_vendor) && ctx.DeviceConfig().VndkVersion() == "" {
 		return
 	}
-	android.CheckMinSdkVersion(a, ctx, a.minSdkVersion(ctx))
+	// apexBundle::minSdkVersion reports its own errors.
+	minSdkVersion := a.minSdkVersion(ctx)
+	android.CheckMinSdkVersion(a, ctx, minSdkVersion)
 }
 
 // Ensures that a lib providing stub isn't statically linked
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 83a56a2..afb739c 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -17,9 +17,9 @@
 package apex
 
 import (
-	"github.com/google/blueprint"
-
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 func init() {
@@ -27,39 +27,79 @@
 }
 
 type apexDepsInfoSingleton struct {
-	// Output file with all flatlists from updatable modules' deps-info combined
-	updatableFlatListsPath android.OutputPath
+	allowedApexDepsInfoCheckResult android.OutputPath
 }
 
 func apexDepsInfoSingletonFactory() android.Singleton {
 	return &apexDepsInfoSingleton{}
 }
 
-var combineFilesRule = pctx.AndroidStaticRule("combineFilesRule",
-	blueprint.RuleParams{
-		Command:        "cat $out.rsp | xargs cat > $out",
+var (
+	// Generate new apex allowed_deps.txt by merging all internal dependencies.
+	generateApexDepsInfoFilesRule = pctx.AndroidStaticRule("generateApexDepsInfoFilesRule", blueprint.RuleParams{
+		Command: "cat $out.rsp | xargs cat" +
+			// Only track non-external dependencies, i.e. those that end up in the binary
+			" | grep -v '(external)'" +
+			// Ignore comments in any of the files
+			" | grep -v '^#'" +
+			" | sort -u -f >$out",
 		Rspfile:        "$out.rsp",
 		RspfileContent: "$in",
-	},
+	})
+
+	// Diff two given lists while ignoring comments in the allowed deps file.
+	diffAllowedApexDepsInfoRule = pctx.AndroidStaticRule("diffAllowedApexDepsInfoRule", blueprint.RuleParams{
+		Description: "Diff ${allowed_deps} and ${new_allowed_deps}",
+		Command: `
+			if grep -v '^#' ${allowed_deps} | diff -B - ${new_allowed_deps}; then
+			   touch ${out};
+			else
+				echo -e "\n******************************";
+				echo "ERROR: go/apex-allowed-deps-error";
+				echo "******************************";
+				echo "Detected changes to allowed dependencies in updatable modules.";
+				echo "To fix and update build/soong/apex/allowed_deps.txt, please run:";
+				echo "$$ (croot && build/soong/scripts/update-apex-allowed-deps.sh)";
+				echo "Members of mainline-modularization@google.com will review the changes.";
+				echo -e "******************************\n";
+				exit 1;
+			fi;
+		`,
+	}, "allowed_deps", "new_allowed_deps")
 )
 
 func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
-			if path := binaryInfo.FlatListPath(); path != nil {
-				if binaryInfo.Updatable() {
-					updatableFlatLists = append(updatableFlatLists, path)
-				}
+			if path := binaryInfo.FlatListPath(); path != nil && binaryInfo.Updatable() {
+				updatableFlatLists = append(updatableFlatLists, path)
 			}
 		}
 	})
 
-	s.updatableFlatListsPath = android.PathForOutput(ctx, "apex", "depsinfo", "updatable-flatlists.txt")
+	allowedDeps := android.ExistentPathForSource(ctx, "build/soong/apex/allowed_deps.txt").Path()
+
+	newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        combineFilesRule,
-		Description: "Generate " + s.updatableFlatListsPath.String(),
-		Inputs:      updatableFlatLists,
-		Output:      s.updatableFlatListsPath,
+		Rule:   generateApexDepsInfoFilesRule,
+		Inputs: append(updatableFlatLists, allowedDeps),
+		Output: newAllowedDeps,
 	})
+
+	s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   diffAllowedApexDepsInfoRule,
+		Input:  newAllowedDeps,
+		Output: s.allowedApexDepsInfoCheckResult,
+		Args: map[string]string{
+			"allowed_deps":     allowedDeps.String(),
+			"new_allowed_deps": newAllowedDeps.String(),
+		},
+	})
+}
+
+func (s *apexDepsInfoSingleton) MakeVars(ctx android.MakeVarsContext) {
+	// Export check result to Make. The path is added to droidcore.
+	ctx.Strict("APEX_ALLOWED_DEPS_CHECK", s.allowedApexDepsInfoCheckResult.String())
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 610f667..71c4aa2 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -207,7 +207,7 @@
 	config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
 	config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
 	config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
-	config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
+	config.TestProductVariables.Platform_version_active_codenames = []string{"Q"}
 	config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
 
 	for _, handler := range handlers {
@@ -584,18 +584,18 @@
 	ensureListContains(t, noticeInputs, "custom_notice_for_static_lib")
 
 	fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
-	ensureListContains(t, fullDepsInfo, "myjar(minSdkVersion:(no version)) <- myapex")
-	ensureListContains(t, fullDepsInfo, "mylib(minSdkVersion:(no version)) <- myapex")
-	ensureListContains(t, fullDepsInfo, "mylib2(minSdkVersion:(no version)) <- mylib")
-	ensureListContains(t, fullDepsInfo, "myotherjar(minSdkVersion:(no version)) <- myjar")
-	ensureListContains(t, fullDepsInfo, "mysharedjar(minSdkVersion:(no version)) (external) <- myjar")
+	ensureListContains(t, fullDepsInfo, "  myjar(minSdkVersion:(no version)) <- myapex")
+	ensureListContains(t, fullDepsInfo, "  mylib(minSdkVersion:(no version)) <- myapex")
+	ensureListContains(t, fullDepsInfo, "  mylib2(minSdkVersion:(no version)) <- mylib")
+	ensureListContains(t, fullDepsInfo, "  myotherjar(minSdkVersion:(no version)) <- myjar")
+	ensureListContains(t, fullDepsInfo, "  mysharedjar(minSdkVersion:(no version)) (external) <- myjar")
 
 	flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n")
-	ensureListContains(t, flatDepsInfo, "  myjar(minSdkVersion:(no version))")
-	ensureListContains(t, flatDepsInfo, "  mylib(minSdkVersion:(no version))")
-	ensureListContains(t, flatDepsInfo, "  mylib2(minSdkVersion:(no version))")
-	ensureListContains(t, flatDepsInfo, "  myotherjar(minSdkVersion:(no version))")
-	ensureListContains(t, flatDepsInfo, "  mysharedjar(minSdkVersion:(no version)) (external)")
+	ensureListContains(t, flatDepsInfo, "myjar(minSdkVersion:(no version))")
+	ensureListContains(t, flatDepsInfo, "mylib(minSdkVersion:(no version))")
+	ensureListContains(t, flatDepsInfo, "mylib2(minSdkVersion:(no version))")
+	ensureListContains(t, flatDepsInfo, "myotherjar(minSdkVersion:(no version))")
+	ensureListContains(t, flatDepsInfo, "mysharedjar(minSdkVersion:(no version)) (external)")
 }
 
 func TestDefaults(t *testing.T) {
@@ -836,6 +836,105 @@
 	})
 }
 
+func TestApexWithStubsWithMinSdkVersion(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib", "mylib3"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["mylib2", "mylib3"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			min_sdk_version: "28",
+		}
+
+		cc_library {
+			name: "mylib2",
+			srcs: ["mylib.cpp"],
+			cflags: ["-include mylib.h"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["28", "29", "30", "current"],
+			},
+			min_sdk_version: "28",
+		}
+
+		cc_library {
+			name: "mylib3",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["mylib4"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["28", "29", "30", "current"],
+			},
+			apex_available: [ "myapex" ],
+			min_sdk_version: "28",
+		}
+
+		cc_library {
+			name: "mylib4",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			min_sdk_version: "28",
+		}
+	`)
+
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	// Ensure that direct non-stubs dep is always included
+	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+	// Ensure that indirect stubs dep is not included
+	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+
+	// Ensure that direct stubs dep is included
+	ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
+
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex29").Rule("ld").Args["libFlags"]
+
+	// Ensure that mylib is linking with the version 29 stubs for mylib2
+	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_29/mylib2.so")
+	// ... and not linking to the non-stub (impl) variant of mylib2
+	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
+
+	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
+	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex29/mylib3.so")
+	// .. and not linking to the stubs variant of mylib3
+	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_29/mylib3.so")
+
+	// Ensure that stubs libs are built without -include flags
+	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	ensureNotContains(t, mylib2Cflags, "-include ")
+
+	// Ensure that genstub is invoked with --apex
+	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_29").Rule("genStubSrc").Args["flags"])
+
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"lib64/mylib.so",
+		"lib64/mylib3.so",
+		"lib64/mylib4.so",
+	})
+}
+
 func TestApexWithExplicitStubsDependency(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -913,14 +1012,14 @@
 	ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
 
 	fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
-	ensureListContains(t, fullDepsInfo, "mylib(minSdkVersion:(no version)) <- myapex2")
-	ensureListContains(t, fullDepsInfo, "libbaz(minSdkVersion:(no version)) <- mylib")
-	ensureListContains(t, fullDepsInfo, "libfoo(minSdkVersion:(no version)) (external) <- mylib")
+	ensureListContains(t, fullDepsInfo, "  mylib(minSdkVersion:(no version)) <- myapex2")
+	ensureListContains(t, fullDepsInfo, "  libbaz(minSdkVersion:(no version)) <- mylib")
+	ensureListContains(t, fullDepsInfo, "  libfoo(minSdkVersion:(no version)) (external) <- mylib")
 
 	flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n")
-	ensureListContains(t, flatDepsInfo, "  mylib(minSdkVersion:(no version))")
-	ensureListContains(t, flatDepsInfo, "  libbaz(minSdkVersion:(no version))")
-	ensureListContains(t, flatDepsInfo, "  libfoo(minSdkVersion:(no version)) (external)")
+	ensureListContains(t, flatDepsInfo, "mylib(minSdkVersion:(no version))")
+	ensureListContains(t, flatDepsInfo, "libbaz(minSdkVersion:(no version))")
+	ensureListContains(t, flatDepsInfo, "libfoo(minSdkVersion:(no version)) (external)")
 }
 
 func TestApexWithRuntimeLibsDependency(t *testing.T) {
@@ -1425,13 +1524,7 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	// 9000 is quite a magic number.
-	// Finalized SDK codenames are mapped as P(28), Q(29), ...
-	// And, codenames which are not finalized yet(active_codenames + future_codenames) are numbered from 9000, 9001, ...
-	// to distinguish them from finalized and future_api(10000)
-	// In this test, "R" is assumed not finalized yet( listed in Platform_version_active_codenames) and translated into 9000
-	// (refer android/api_levels.go)
-	expectLink("libx", "shared_apex10000", "libz", "shared_9000")
+	expectLink("libx", "shared_apex10000", "libz", "shared_R")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared_29")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
@@ -2282,30 +2375,40 @@
 	ensureListContains(t, requireNativeLibs, ":vndk")
 }
 
-func TestVendorApex_withPrebuiltFirmware(t *testing.T) {
-	ctx, _ := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			prebuilts: ["myfirmware"],
-			vendor: true,
-		}
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-		prebuilt_firmware {
-			name: "myfirmware",
-			src: "myfirmware.bin",
-			filename_from_src: true,
-			vendor: true,
-		}
-	`)
-
-	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
-		"firmware/myfirmware.bin",
-	})
+func TestApex_withPrebuiltFirmware(t *testing.T) {
+	testCases := []struct {
+		name           string
+		additionalProp string
+	}{
+		{"system apex with prebuilt_firmware", ""},
+		{"vendor apex with prebuilt_firmware", "vendor: true,"},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx, _ := testApex(t, `
+				apex {
+					name: "myapex",
+					key: "myapex.key",
+					prebuilts: ["myfirmware"],
+					`+tc.additionalProp+`
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				prebuilt_firmware {
+					name: "myfirmware",
+					src: "myfirmware.bin",
+					filename_from_src: true,
+					`+tc.additionalProp+`
+				}
+			`)
+			ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+				"etc/firmware/myfirmware.bin",
+			})
+		})
+	}
 }
 
 func TestAndroidMk_UseVendorRequired(t *testing.T) {
@@ -6120,7 +6223,7 @@
 			name: "mylib",
 			srcs: ["mylib.cpp"],
 			stubs: {
-				versions: ["10000"],
+				versions: ["current"],
 			},
 			apex_available: ["myapex"],
 		}
@@ -6130,7 +6233,7 @@
 			prefer: false,
 			srcs: ["prebuilt.so"],
 			stubs: {
-				versions: ["10000"],
+				versions: ["current"],
 			},
 			apex_available: ["myapex"],
 		}
diff --git a/apex/builder.go b/apex/builder.go
index c5680ad..b0f0c82 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -21,7 +21,6 @@
 	"path/filepath"
 	"runtime"
 	"sort"
-	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -214,7 +213,8 @@
 		},
 	})
 
-	if a.minSdkVersion(ctx) == android.SdkVersion_Android10 {
+	minSdkVersion := a.minSdkVersion(ctx)
+	if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
 		// b/143654022 Q apexd can't understand newly added keys in apex_manifest.json
 		// prepare stripped-down version so that APEX modules built from R+ can be installed to Q
 		a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json")
@@ -426,7 +426,8 @@
 	var emitCommands []string
 	imageContentFile := android.PathForModuleOut(ctx, "content.txt")
 	emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
-	if a.minSdkVersion(ctx) == android.SdkVersion_Android10 {
+	minSdkVersion := a.minSdkVersion(ctx)
+	if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
 		emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
 	}
 	for _, fi := range a.filesInfo {
@@ -532,12 +533,13 @@
 			optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
 		}
 
-		targetSdkVersion := ctx.Config().DefaultAppTargetSdk()
+		targetSdkVersion := ctx.Config().DefaultAppTargetSdk(ctx).String()
 		// TODO(b/157078772): propagate min_sdk_version to apexer.
-		minSdkVersion := ctx.Config().DefaultAppTargetSdk()
+		minSdkVersion := ctx.Config().DefaultAppTargetSdk(ctx).String()
 
-		if a.minSdkVersion(ctx) == android.SdkVersion_Android10 {
-			minSdkVersion = strconv.Itoa(a.minSdkVersion(ctx))
+		moduleMinSdkVersion := a.minSdkVersion(ctx)
+		if moduleMinSdkVersion.EqualTo(android.SdkVersion_Android10) {
+			minSdkVersion = moduleMinSdkVersion.String()
 		}
 
 		if java.UseApiFingerprint(ctx) {
@@ -566,7 +568,7 @@
 			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
 			return
 		}
-		if a.minSdkVersion(ctx) > android.SdkVersion_Android10 || a.testOnlyShouldSkipHashtreeGeneration() {
+		if moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration() {
 			// Apexes which are supposed to be installed in builtin dirs(/system, etc)
 			// don't need hashtree for activation. Therefore, by removing hashtree from
 			// apex bundle (filesystem image in it, to be specific), we can save storage.
@@ -583,7 +585,7 @@
 			optFlags = append(optFlags, "--do_not_check_keyname")
 		}
 
-		if a.minSdkVersion(ctx) == android.SdkVersion_Android10 {
+		if moduleMinSdkVersion == android.SdkVersion_Android10 {
 			implicitInputs = append(implicitInputs, a.manifestJsonOut)
 			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
 		}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 37457e9..9f6c8ad 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -326,7 +326,7 @@
 			Args: map[string]string{
 				"abis":              strings.Join(java.SupportedAbis(ctx), ","),
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
-				"sdk-version":       ctx.Config().PlatformSdkVersion(),
+				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
 			},
 		})
 
diff --git a/apex/vndk.go b/apex/vndk.go
index 5cc0e2a..93265c4 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -16,7 +16,6 @@
 
 import (
 	"path/filepath"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -124,10 +123,10 @@
 	// Since prebuilt vndk libs still depend on system/lib/vndk path
 	if strings.HasPrefix(name, vndkApexNamePrefix) {
 		vndkVersion := strings.TrimPrefix(name, vndkApexNamePrefix)
-		if numVer, err := strconv.Atoi(vndkVersion); err != nil {
+		if ver, err := android.ApiLevelFromUser(ctx, vndkVersion); err != nil {
 			ctx.ModuleErrorf("apex_vndk should be named as %v<ver:number>: %s", vndkApexNamePrefix, name)
 			return
-		} else if numVer > android.SdkVersion_Android10 {
+		} else if ver.GreaterThan(android.SdkVersion_Android10) {
 			return
 		}
 		// the name of vndk apex is formatted "com.android.vndk.v" + version
diff --git a/cc/Android.bp b/cc/Android.bp
index 831911e..ff2cdf3 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -13,6 +13,7 @@
     ],
     srcs: [
         "androidmk.go",
+        "api_level.go",
         "builder.go",
         "cc.go",
         "ccdeps.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 380b4e9..d92fabc 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -33,7 +33,7 @@
 )
 
 type AndroidMkContext interface {
-	Name() string
+	BaseModuleName() string
 	Target() android.Target
 	subAndroidMk(*android.AndroidMkEntries, interface{})
 	Arch() android.Arch
@@ -83,6 +83,13 @@
 				if len(c.Properties.Logtags) > 0 {
 					entries.AddStrings("LOCAL_LOGTAGS_FILES", c.Properties.Logtags...)
 				}
+				// Note: Pass the exact value of AndroidMkSystemSharedLibs to the Make
+				// world, even if it is an empty list. In the Make world,
+				// LOCAL_SYSTEM_SHARED_LIBRARIES defaults to "none", which is expanded
+				// to the default list of system shared libs by the build system.
+				// Soong computes the exact list of system shared libs, so we have to
+				// override the default value when the list of libs is actually empty.
+				entries.SetString("LOCAL_SYSTEM_SHARED_LIBRARIES", strings.Join(c.Properties.AndroidMkSystemSharedLibs, " "))
 				if len(c.Properties.AndroidMkSharedLibs) > 0 {
 					entries.AddStrings("LOCAL_SHARED_LIBRARIES", c.Properties.AndroidMkSharedLibs...)
 				}
@@ -149,24 +156,6 @@
 	return []android.AndroidMkEntries{entries}
 }
 
-func AndroidMkDataPaths(data []android.DataPath) []string {
-	var testFiles []string
-	for _, d := range data {
-		rel := d.SrcPath.Rel()
-		path := d.SrcPath.String()
-		if !strings.HasSuffix(path, rel) {
-			panic(fmt.Errorf("path %q does not end with %q", path, rel))
-		}
-		path = strings.TrimSuffix(path, rel)
-		testFileString := path + ":" + rel
-		if len(d.RelativeInstallPath) > 0 {
-			testFileString += ":" + d.RelativeInstallPath
-		}
-		testFiles = append(testFiles, testFileString)
-	}
-	return testFiles
-}
-
 func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *android.AndroidMkEntries) {
 	if len(extraTestConfigs) > 0 {
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
@@ -176,7 +165,7 @@
 }
 
 func androidMkWriteTestData(data []android.DataPath, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	testFiles := AndroidMkDataPaths(data)
+	testFiles := android.AndroidMkDataPaths(data)
 	if len(testFiles) > 0 {
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 			entries.AddStrings("LOCAL_TEST_DATA", testFiles...)
@@ -289,7 +278,7 @@
 		})
 	}
 	if len(library.Properties.Stubs.Versions) > 0 &&
-		android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.InRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() &&
+		android.DirectlyInAnyApex(ctx, ctx.BaseModuleName()) && !ctx.InRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() &&
 		!ctx.static() {
 		if library.buildStubs() && library.isLatestStubVersion() {
 			// reference the latest version via its name without suffix when it is provided by apex
@@ -451,7 +440,7 @@
 }
 
 func (c *stubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.SubName = ndkLibrarySuffix + "." + c.properties.ApiLevel
+	entries.SubName = ndkLibrarySuffix + "." + c.apiLevel.String()
 	entries.Class = "SHARED_LIBRARIES"
 
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
diff --git a/cc/api_level.go b/cc/api_level.go
new file mode 100644
index 0000000..c93d6ed
--- /dev/null
+++ b/cc/api_level.go
@@ -0,0 +1,71 @@
+// Copyright 2020 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 cc
+
+import (
+	"fmt"
+
+	"android/soong/android"
+)
+
+func minApiForArch(ctx android.BaseModuleContext,
+	arch android.ArchType) android.ApiLevel {
+
+	switch arch {
+	case android.Arm, android.X86:
+		return ctx.Config().MinSupportedSdkVersion()
+	case android.Arm64, android.X86_64:
+		return android.FirstLp64Version
+	default:
+		panic(fmt.Errorf("Unknown arch %q", arch))
+	}
+}
+
+func nativeApiLevelFromUser(ctx android.BaseModuleContext,
+	raw string) (android.ApiLevel, error) {
+
+	min := minApiForArch(ctx, ctx.Arch().ArchType)
+	if raw == "minimum" {
+		return min, nil
+	}
+
+	value, err := android.ApiLevelFromUser(ctx, raw)
+	if err != nil {
+		return android.NoneApiLevel, err
+	}
+
+	if value.LessThan(min) {
+		return min, nil
+	}
+
+	return value, nil
+}
+
+func nativeApiLevelFromUserWithDefault(ctx android.BaseModuleContext,
+	raw string, defaultValue string) (android.ApiLevel, error) {
+	if raw == "" {
+		raw = defaultValue
+	}
+	return nativeApiLevelFromUser(ctx, raw)
+}
+
+func nativeApiLevelOrPanic(ctx android.BaseModuleContext,
+	raw string) android.ApiLevel {
+	value, err := nativeApiLevelFromUser(ctx, raw)
+	if err != nil {
+		panic(err.Error())
+	}
+	return value
+}
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index a1abc72..55e400e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -44,7 +44,7 @@
 		for _, target := range targets {
 			name, version := StubsLibNameAndVersion(lib)
 			if version == "" {
-				version = LatestStubsVersionFor(mctx.Config(), name)
+				version = "latest"
 			}
 			variations := target.Variations()
 			if mctx.Device() {
diff --git a/cc/builder.go b/cc/builder.go
index ef65348..81c09b1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -221,7 +221,6 @@
 			ExecStrategy: "${config.REAbiDumperExecStrategy}",
 			Platform: map[string]string{
 				remoteexec.PoolKey:      "${config.RECXXPool}",
-				"InputRootAbsolutePath": android.AbsSrcDirForExistingUseCases(),
 			},
 		}, []string{"cFlags", "exportDirs"}, nil)
 
diff --git a/cc/cc.go b/cc/cc.go
index 70229be..e18349c 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -47,7 +47,8 @@
 		ctx.BottomUp("link", LinkageMutator).Parallel()
 		ctx.BottomUp("ndk_api", NdkApiMutator).Parallel()
 		ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
-		ctx.BottomUp("version", VersionMutator).Parallel()
+		ctx.BottomUp("version_selector", versionSelectorMutator).Parallel()
+		ctx.BottomUp("version", versionMutator).Parallel()
 		ctx.BottomUp("begin", BeginMutator).Parallel()
 		ctx.BottomUp("sysprop_cc", SyspropMutator).Parallel()
 		ctx.BottomUp("vendor_snapshot", VendorSnapshotMutator).Parallel()
@@ -99,6 +100,9 @@
 	// Used for data dependencies adjacent to tests
 	DataLibs []string
 
+	// Used by DepsMutator to pass system_shared_libs information to check_elf_file.py.
+	SystemSharedLibs []string
+
 	StaticUnwinderIfLegacy bool
 
 	ReexportSharedLibHeaders, ReexportStaticLibHeaders, ReexportHeaderLibHeaders []string
@@ -237,6 +241,9 @@
 	PreventInstall            bool     `blueprint:"mutated"`
 	ApexesProvidingSharedLibs []string `blueprint:"mutated"`
 
+	// Set by DepsMutator.
+	AndroidMkSystemSharedLibs []string `blueprint:"mutated"`
+
 	ImageVariationPrefix string `blueprint:"mutated"`
 	VndkVersion          string `blueprint:"mutated"`
 	SubName              string `blueprint:"mutated"`
@@ -353,7 +360,7 @@
 	useClangLld(actx ModuleContext) bool
 	isForPlatform() bool
 	apexVariationName() string
-	apexSdkVersion() int
+	apexSdkVersion() android.ApiLevel
 	hasStubsVariants() bool
 	isStubs() bool
 	bootstrap() bool
@@ -614,7 +621,7 @@
 	kytheFiles android.Paths
 
 	// For apex variants, this is set as apex.min_sdk_version
-	apexSdkVersion int
+	apexSdkVersion android.ApiLevel
 }
 
 func (c *Module) Toc() android.OptionalPath {
@@ -629,7 +636,7 @@
 func (c *Module) ApiLevel() string {
 	if c.linker != nil {
 		if stub, ok := c.linker.(*stubDecorator); ok {
-			return stub.properties.ApiLevel
+			return stub.apiLevel.String()
 		}
 	}
 	panic(fmt.Errorf("ApiLevel() called on non-stub library module: %q", c.BaseModuleName()))
@@ -733,6 +740,9 @@
 		if library, ok := c.linker.(*libraryDecorator); ok {
 			return library.Properties.Stubs.Versions
 		}
+		if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			return library.Properties.Stubs.Versions
+		}
 	}
 	panic(fmt.Errorf("StubsVersions called on non-library module: %q", c.BaseModuleName()))
 }
@@ -742,6 +752,9 @@
 		if _, ok := c.linker.(*libraryDecorator); ok {
 			return true
 		}
+		if _, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			return true
+		}
 	}
 	return false
 }
@@ -767,6 +780,14 @@
 			c.Properties.PreventInstall = true
 			return
 		}
+		if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			library.MutatedProperties.BuildStubs = true
+			c.Properties.HideFromMake = true
+			c.sanitize = nil
+			c.stl = nil
+			c.Properties.PreventInstall = true
+			return
+		}
 		if _, ok := c.linker.(*llndkStubDecorator); ok {
 			c.Properties.HideFromMake = true
 			return
@@ -780,22 +801,57 @@
 		if library, ok := c.linker.(*libraryDecorator); ok {
 			return library.buildStubs()
 		}
+		if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			return library.buildStubs()
+		}
 	}
 	panic(fmt.Errorf("BuildStubs called on non-library module: %q", c.BaseModuleName()))
 }
 
-func (c *Module) SetStubsVersions(version string) {
+func (c *Module) SetAllStubsVersions(versions []string) {
+	if library, ok := c.linker.(*libraryDecorator); ok {
+		library.MutatedProperties.AllStubsVersions = versions
+		return
+	}
+	if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+		library.MutatedProperties.AllStubsVersions = versions
+		return
+	}
+	if llndk, ok := c.linker.(*llndkStubDecorator); ok {
+		llndk.libraryDecorator.MutatedProperties.AllStubsVersions = versions
+		return
+	}
+}
+
+func (c *Module) AllStubsVersions() []string {
+	if library, ok := c.linker.(*libraryDecorator); ok {
+		return library.MutatedProperties.AllStubsVersions
+	}
+	if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+		return library.MutatedProperties.AllStubsVersions
+	}
+	if llndk, ok := c.linker.(*llndkStubDecorator); ok {
+		return llndk.libraryDecorator.MutatedProperties.AllStubsVersions
+	}
+	return nil
+}
+
+func (c *Module) SetStubsVersion(version string) {
 	if c.linker != nil {
 		if library, ok := c.linker.(*libraryDecorator); ok {
 			library.MutatedProperties.StubsVersion = version
 			return
 		}
+		if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			library.MutatedProperties.StubsVersion = version
+			return
+		}
 		if llndk, ok := c.linker.(*llndkStubDecorator); ok {
 			llndk.libraryDecorator.MutatedProperties.StubsVersion = version
 			return
 		}
 	}
-	panic(fmt.Errorf("SetStubsVersions called on non-library module: %q", c.BaseModuleName()))
+	panic(fmt.Errorf("SetStubsVersion called on non-library module: %q", c.BaseModuleName()))
 }
 
 func (c *Module) StubsVersion() string {
@@ -803,6 +859,9 @@
 		if library, ok := c.linker.(*libraryDecorator); ok {
 			return library.MutatedProperties.StubsVersion
 		}
+		if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			return library.MutatedProperties.StubsVersion
+		}
 		if llndk, ok := c.linker.(*llndkStubDecorator); ok {
 			return llndk.libraryDecorator.MutatedProperties.StubsVersion
 		}
@@ -928,9 +987,9 @@
 		c.AddProperties(feature.props()...)
 	}
 
-	c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
+	c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, os android.OsType) bool {
 		// Windows builds always prefer 32-bit
-		return class == android.HostCross
+		return os == android.Windows
 	})
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
 	android.InitApexModule(c)
@@ -1045,6 +1104,8 @@
 func (c *Module) IsStubs() bool {
 	if library, ok := c.linker.(*libraryDecorator); ok {
 		return library.buildStubs()
+	} else if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+		return library.buildStubs()
 	} else if _, ok := c.linker.(*llndkStubDecorator); ok {
 		return true
 	}
@@ -1306,7 +1367,7 @@
 	return ctx.mod.ApexVariationName()
 }
 
-func (ctx *moduleContextImpl) apexSdkVersion() int {
+func (ctx *moduleContextImpl) apexSdkVersion() android.ApiLevel {
 	return ctx.mod.apexSdkVersion
 }
 
@@ -1682,11 +1743,13 @@
 		feature.begin(ctx)
 	}
 	if ctx.useSdk() && c.IsSdkVariant() {
-		version, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch())
+		version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion())
 		if err != nil {
 			ctx.PropertyErrorf("sdk_version", err.Error())
+			c.Properties.Sdk_version = nil
+		} else {
+			c.Properties.Sdk_version = StringPtr(version.String())
 		}
-		c.Properties.Sdk_version = StringPtr(version)
 	}
 }
 
@@ -1800,6 +1863,42 @@
 	}
 }
 
+func (c *Module) addSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext,
+	variations []blueprint.Variation, depTag libraryDependencyTag, name, version string, far bool) {
+
+	variations = append([]blueprint.Variation(nil), variations...)
+
+	if version != "" && VersionVariantAvailable(c) {
+		// Version is explicitly specified. i.e. libFoo#30
+		variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
+		depTag.explicitlyVersioned = true
+	}
+	var deps []blueprint.Module
+	if far {
+		deps = ctx.AddFarVariationDependencies(variations, depTag, name)
+	} else {
+		deps = ctx.AddVariationDependencies(variations, depTag, name)
+	}
+
+	// If the version is not specified, add dependency to all stubs libraries.
+	// The stubs library will be used when the depending module is built for APEX and
+	// the dependent module is not in the same APEX.
+	if version == "" && VersionVariantAvailable(c) {
+		if dep, ok := deps[0].(*Module); ok {
+			for _, ver := range dep.AllStubsVersions() {
+				// Note that depTag.ExplicitlyVersioned is false in this case.
+				versionVariations := append(variations,
+					blueprint.Variation{Mutator: "version", Variation: ver})
+				if far {
+					ctx.AddFarVariationDependencies(versionVariations, depTag, name)
+				} else {
+					ctx.AddVariationDependencies(versionVariations, depTag, name)
+				}
+			}
+		}
+	}
+}
+
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	if !c.Enabled() {
 		return
@@ -1815,6 +1914,8 @@
 
 	deps := c.deps(ctx)
 
+	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
+
 	variantNdkLibs := []string{}
 	variantLateNdkLibs := []string{}
 	if ctx.Os() == android.Android {
@@ -1898,6 +1999,11 @@
 				buildStubs = true
 			}
 		}
+		if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			if library.buildStubs() {
+				buildStubs = true
+			}
+		}
 	}
 
 	rewriteSnapshotLibs := func(lib string, snapshotMap *snapshotMap) string {
@@ -1986,30 +2092,6 @@
 		}, depTag, rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs))
 	}
 
-	addSharedLibDependencies := func(depTag libraryDependencyTag, name string, version string) {
-		var variations []blueprint.Variation
-		variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
-		if version != "" && VersionVariantAvailable(c) {
-			// Version is explicitly specified. i.e. libFoo#30
-			variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
-			depTag.explicitlyVersioned = true
-		}
-		actx.AddVariationDependencies(variations, depTag, name)
-
-		// If the version is not specified, add dependency to all stubs libraries.
-		// The stubs library will be used when the depending module is built for APEX and
-		// the dependent module is not in the same APEX.
-		if version == "" && VersionVariantAvailable(c) {
-			for _, ver := range stubsVersionsFor(actx.Config())[name] {
-				// Note that depTag.ExplicitlyVersioned is false in this case.
-				actx.AddVariationDependencies([]blueprint.Variation{
-					{Mutator: "link", Variation: "shared"},
-					{Mutator: "version", Variation: ver},
-				}, depTag, name)
-			}
-		}
-	}
-
 	// shared lib names without the #version suffix
 	var sharedLibNames []string
 
@@ -2026,7 +2108,10 @@
 		name, version := StubsLibNameAndVersion(lib)
 		sharedLibNames = append(sharedLibNames, name)
 
-		addSharedLibDependencies(depTag, name, version)
+		variations := []blueprint.Variation{
+			{Mutator: "link", Variation: "shared"},
+		}
+		c.addSharedLibDependenciesWithVersions(ctx, variations, depTag, name, version, false)
 	}
 
 	for _, lib := range deps.LateSharedLibs {
@@ -2037,7 +2122,10 @@
 			continue
 		}
 		depTag := libraryDependencyTag{Kind: sharedLibraryDependency, Order: lateLibraryDependency}
-		addSharedLibDependencies(depTag, lib, "")
+		variations := []blueprint.Variation{
+			{Mutator: "link", Variation: "shared"},
+		}
+		c.addSharedLibDependenciesWithVersions(ctx, variations, depTag, lib, "", false)
 	}
 
 	actx.AddVariationDependencies([]blueprint.Variation{
@@ -2291,7 +2379,7 @@
 	// For the dependency from platform to apex, use the latest stubs
 	c.apexSdkVersion = android.FutureApiLevel
 	if !c.IsForPlatform() {
-		c.apexSdkVersion = c.ApexProperties.Info.MinSdkVersion
+		c.apexSdkVersion = c.ApexProperties.Info.MinSdkVersion(ctx)
 	}
 
 	if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
@@ -2395,7 +2483,7 @@
 
 		if libDepTag, ok := depTag.(libraryDependencyTag); ok {
 			// Only use static unwinder for legacy (min_sdk_version = 29) apexes (b/144430859)
-			if libDepTag.staticUnwinder && c.apexSdkVersion > android.SdkVersion_Android10 {
+			if libDepTag.staticUnwinder && c.apexSdkVersion.GreaterThan(android.SdkVersion_Android10) {
 				return
 			}
 
@@ -2439,7 +2527,7 @@
 
 				// when to use (unspecified) stubs, check min_sdk_version and choose the right one
 				if useThisDep && depIsStubs && !libDepTag.explicitlyVersioned {
-					versionToUse, err := c.ChooseSdkVersion(ccDep.StubsVersions(), c.apexSdkVersion)
+					versionToUse, err := c.ChooseSdkVersion(ctx, ccDep.StubsVersions(), c.apexSdkVersion)
 					if err != nil {
 						ctx.OtherModuleErrorf(dep, err.Error())
 						return
@@ -2457,12 +2545,12 @@
 				if m, ok := ccDep.(*Module); ok && m.IsStubs() { // LLNDK
 					// by default, use current version of LLNDK
 					versionToUse := ""
-					versions := stubsVersionsFor(ctx.Config())[depName]
+					versions := m.AllStubsVersions()
 					if c.ApexVariationName() != "" && len(versions) > 0 {
 						// if this is for use_vendor apex && dep has stubsVersions
 						// apply the same rule of apex sdk enforcement to choose right version
 						var err error
-						versionToUse, err = c.ChooseSdkVersion(versions, c.apexSdkVersion)
+						versionToUse, err = c.ChooseSdkVersion(ctx, versions, c.apexSdkVersion)
 						if err != nil {
 							ctx.OtherModuleErrorf(dep, err.Error())
 							return
@@ -2533,17 +2621,20 @@
 					// in the context of proper cc.Modules.
 					if ccWholeStaticLib, ok := ccDep.(*Module); ok {
 						staticLib := ccWholeStaticLib.linker.(libraryInterface)
-						if missingDeps := staticLib.getWholeStaticMissingDeps(); missingDeps != nil {
-							postfix := " (required by " + ctx.OtherModuleName(dep) + ")"
-							for i := range missingDeps {
-								missingDeps[i] += postfix
-							}
-							ctx.AddMissingDependencies(missingDeps)
-						}
-						if _, ok := ccWholeStaticLib.linker.(prebuiltLinkerInterface); ok {
-							depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
+						if objs := staticLib.objs(); len(objs.objFiles) > 0 {
+							depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(objs)
 						} else {
-							depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
+							// This case normally catches prebuilt static
+							// libraries, but it can also occur when
+							// AllowMissingDependencies is on and the
+							// dependencies has no sources of its own
+							// but has a whole_static_libs dependency
+							// on a missing library.  We want to depend
+							// on the .a file so that there is something
+							// in the dependency tree that contains the
+							// error rule for the missing transitive
+							// dependency.
+							depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
 						}
 					} else {
 						ctx.ModuleErrorf(
@@ -2986,21 +3077,8 @@
 	return true
 }
 
-// b/154667674: refactor this to handle "current" in a consistent way
-func decodeSdkVersionString(ctx android.BaseModuleContext, versionString string) (int, error) {
-	if versionString == "" {
-		return 0, fmt.Errorf("not specified")
-	}
-	if versionString == "current" {
-		if ctx.Config().PlatformSdkCodename() == "REL" {
-			return ctx.Config().PlatformSdkVersionInt(), nil
-		}
-		return android.FutureApiLevel, nil
-	}
-	return android.ApiStrToNum(ctx, versionString)
-}
-
-func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	// We ignore libclang_rt.* prebuilt libs since they declare sdk_version: 14(b/121358700)
 	if strings.HasPrefix(ctx.OtherModuleName(c), "libclang_rt") {
 		return nil
@@ -3024,11 +3102,17 @@
 		// non-SDK variant resets sdk_version, which works too.
 		minSdkVersion = c.SdkVersion()
 	}
-	ver, err := decodeSdkVersionString(ctx, minSdkVersion)
+	if minSdkVersion == "" {
+		return fmt.Errorf("neither min_sdk_version nor sdk_version specificed")
+	}
+	// Not using nativeApiLevelFromUser because the context here is not
+	// necessarily a native context.
+	ver, err := android.ApiLevelFromUser(ctx, minSdkVersion)
 	if err != nil {
 		return err
 	}
-	if ver > sdkVersion {
+
+	if ver.GreaterThan(sdkVersion) {
 		return fmt.Errorf("newer SDK(%v)", ver)
 	}
 	return nil
@@ -3082,6 +3166,8 @@
 		&LTOProperties{},
 		&PgoProperties{},
 		&android.ProtoProperties{},
+		// RustBindgenProperties is included here so that cc_defaults can be used for rust_bindgen modules.
+		&RustBindgenClangProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -3119,13 +3205,6 @@
 	return c.Properties.IsSdkVariant || c.AlwaysSdk()
 }
 
-func getCurrentNdkPrebuiltVersion(ctx DepsContext) string {
-	if ctx.Config().PlatformSdkVersionInt() > config.NdkMaxPrebuiltVersionInt {
-		return strconv.Itoa(config.NdkMaxPrebuiltVersionInt)
-	}
-	return ctx.Config().PlatformSdkVersion()
-}
-
 func kytheExtractAllFactory() android.Singleton {
 	return &kytheExtractAllSingleton{}
 }
diff --git a/cc/cc_test.go b/cc/cc_test.go
index a4c0677..e0d4640 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3025,6 +3025,7 @@
 }
 
 func checkEquals(t *testing.T, message string, expected, actual interface{}) {
+	t.Helper()
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf(message+
 			"\nactual:   %v"+
@@ -3779,3 +3780,46 @@
 		t.Errorf("expected -DBAR in cppflags, got %q", libfoo.flags.Local.CppFlags)
 	}
 }
+
+func TestEmptyWholeStaticLibsAllowMissingDependencies(t *testing.T) {
+	t.Parallel()
+	bp := `
+		cc_library_static {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			whole_static_libs: ["libbar"],
+		}
+
+		cc_library_static {
+			name: "libbar",
+			whole_static_libs: ["libmissing"],
+		}
+	`
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.Allow_missing_dependencies = BoolPtr(true)
+
+	ctx := CreateTestContext()
+	ctx.SetAllowMissingDependencies(true)
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Output("libbar.a")
+	if g, w := libbar.Rule, android.ErrorRule; g != w {
+		t.Fatalf("Expected libbar rule to be %q, got %q", w, g)
+	}
+
+	if g, w := libbar.Args["error"], "missing dependencies: libmissing"; !strings.Contains(g, w) {
+		t.Errorf("Expected libbar error to contain %q, was %q", w, g)
+	}
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Output("libfoo.a")
+	if g, w := libfoo.Inputs.Strings(), libbar.Output.String(); !android.InList(w, g) {
+		t.Errorf("Expected libfoo.a to depend on %q, got %q", w, g)
+	}
+
+}
diff --git a/cc/check.go b/cc/check.go
index 46328e9..0058b8c 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -38,6 +38,8 @@
 			ctx.PropertyErrorf(prop, "Illegal flag `%s`", flag)
 		} else if flag == "--coverage" {
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag)
+		} else if flag == "-fwhole-program-vtables" {
+			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use whole_program_vtables instead", flag)
 		} else if flag == "-Weverything" {
 			if !ctx.Config().IsEnvTrue("ANDROID_TEMPORARILY_ALLOW_WEVERYTHING") {
 				ctx.PropertyErrorf(prop, "-Weverything is not allowed in Android.bp files.  "+
diff --git a/cc/compiler.go b/cc/compiler.go
index c268dec..21da2fc 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -354,10 +354,16 @@
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_NAME__='\""+ctx.apexVariationName()+"\"'")
 		}
 		if ctx.Device() {
-			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_SDK_VERSION__="+strconv.Itoa(ctx.apexSdkVersion()))
+			flags.Global.CommonFlags = append(flags.Global.CommonFlags,
+				fmt.Sprintf("-D__ANDROID_SDK_VERSION__=%d",
+					ctx.apexSdkVersion().FinalOrFutureInt()))
 		}
 	}
 
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_NATIVE_BRIDGE__")
+	}
+
 	instructionSet := String(compiler.Properties.Instruction_set)
 	if flags.RequiredInstructionSet != "" {
 		instructionSet = flags.RequiredInstructionSet
@@ -386,7 +392,7 @@
 	if ctx.Os().Class == android.Device {
 		version := ctx.sdkVersion()
 		if version == "" || version == "current" {
-			target += strconv.Itoa(android.FutureApiLevel)
+			target += strconv.Itoa(android.FutureApiLevelInt)
 		} else {
 			target += version
 		}
@@ -646,3 +652,43 @@
 	}
 	return true
 }
+
+// Properties for rust_bindgen related to generating rust bindings.
+// This exists here so these properties can be included in a cc_default
+// which can be used in both cc and rust modules.
+type RustBindgenClangProperties struct {
+	// list of directories relative to the Blueprints file that will
+	// be added to the include path using -I
+	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+
+	// list of static libraries that provide headers for this binding.
+	Static_libs []string `android:"arch_variant,variant_prepend"`
+
+	// list of shared libraries that provide headers for this binding.
+	Shared_libs []string `android:"arch_variant"`
+
+	// list of clang flags required to correctly interpret the headers.
+	Cflags []string `android:"arch_variant"`
+
+	// list of c++ specific clang flags required to correctly interpret the headers.
+	// This is provided primarily to make sure cppflags defined in cc_defaults are pulled in.
+	Cppflags []string `android:"arch_variant"`
+
+	// C standard version to use. Can be a specific version (such as "gnu11"),
+	// "experimental" (which will use draft versions like C1x when available),
+	// or the empty string (which will use the default).
+	//
+	// If this is set, the file extension will be ignored and this will be used as the std version value. Setting this
+	// to "default" will use the build system default version. This cannot be set at the same time as cpp_std.
+	C_std *string
+
+	// C++ standard version to use. Can be a specific version (such as
+	// "gnu++11"), "experimental" (which will use draft versions like C++1z when
+	// available), or the empty string (which will use the default).
+	//
+	// If this is set, the file extension will be ignored and this will be used as the std version value. Setting this
+	// to "default" will use the build system default version. This cannot be set at the same time as c_std.
+	Cpp_std *string
+
+	//TODO(b/161141999) Add support for headers from cc_library_header modules.
+}
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 62d8cc8..e6024aa 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -34,6 +34,9 @@
 		"armv8-2a": []string{
 			"-march=armv8.2-a",
 		},
+		"armv8-2a-dotprod": []string{
+			"-march=armv8.2-a+dotprod",
+		},
 	}
 
 	arm64Ldflags = []string{
@@ -100,6 +103,7 @@
 
 	pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
 	pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
+	pctx.StaticVariable("Arm64ClangArmv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
 
 	pctx.StaticVariable("Arm64ClangCortexA53Cflags",
 		strings.Join(arm64ClangCpuVariantCflags["cortex-a53"], " "))
@@ -121,6 +125,7 @@
 	arm64ClangArchVariantCflagsVar = map[string]string{
 		"armv8-a":  "${config.Arm64ClangArmv8ACflags}",
 		"armv8-2a": "${config.Arm64ClangArmv82ACflags}",
+		"armv8-2a-dotprod": "${config.Arm64ClangArmv82ADotprodCflags}",
 	}
 
 	arm64ClangCpuVariantCflagsVar = map[string]string{
@@ -198,6 +203,7 @@
 	switch arch.ArchVariant {
 	case "armv8-a":
 	case "armv8-2a":
+	case "armv8-2a-dotprod":
 		// Nothing extra for armv8-a/armv8-2a
 	default:
 		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 7db405c..441bff2 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -42,7 +42,6 @@
 	"-Wno-literal-suffix",
 	"-Wno-maybe-uninitialized",
 	"-Wno-old-style-declaration",
-	"-Wno-psabi",
 	"-Wno-unused-but-set-parameter",
 	"-Wno-unused-but-set-variable",
 	"-Wno-unused-local-typedefs",
@@ -93,7 +92,9 @@
 // updated, some checks enabled by this module may be disabled if they have
 // become more strict, or if they are a new match for a wildcard group like
 // `modernize-*`.
-var ClangTidyDisableChecks = []string{}
+var ClangTidyDisableChecks = []string{
+	"misc-no-recursion",
+}
 
 func init() {
 	pctx.StaticVariable("ClangExtraCflags", strings.Join([]string{
@@ -103,6 +104,10 @@
 		// not emit the table by default on Android since NDK still uses GNU binutils.
 		"-faddrsig",
 
+		// Turn on -fcommon explicitly, since Clang now defaults to -fno-common. The cleanup bug
+		// tracking this is http://b/151457797.
+		"-fcommon",
+
 		// Help catch common 32/64-bit errors.
 		"-Werror=int-conversion",
 
@@ -183,6 +188,8 @@
 		"-Wno-enum-enum-conversion",                 // http://b/154138986
 		"-Wno-enum-float-conversion",                // http://b/154255917
 		"-Wno-pessimizing-move",                     // http://b/154270751
+		// New warnings to be fixed after clang-r399163
+		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
 	}, " "))
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -205,6 +212,9 @@
 		"-Wno-xor-used-as-pow",
 		// http://b/145211022
 		"-Wno-final-dtor-non-final-class",
+
+		// http://b/165945989
+		"-Wno-psabi",
 	}, " "))
 }
 
diff --git a/cc/config/global.go b/cc/config/global.go
index 32f163d..d4c6266 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -114,6 +114,12 @@
 	noOverrideGlobalCflags = []string{
 		"-Werror=int-to-pointer-cast",
 		"-Werror=pointer-to-int-cast",
+		// http://b/161386391 for -Wno-void-pointer-to-enum-cast
+		"-Wno-void-pointer-to-enum-cast",
+		// http://b/161386391 for -Wno-void-pointer-to-int-cast
+		"-Wno-void-pointer-to-int-cast",
+		// http://b/161386391 for -Wno-pointer-to-int-cast
+		"-Wno-pointer-to-int-cast",
 		"-Werror=fortify-source",
 	}
 
@@ -126,12 +132,10 @@
 	ExperimentalCStdVersion   = "gnu11"
 	ExperimentalCppStdVersion = "gnu++2a"
 
-	NdkMaxPrebuiltVersionInt = 27
-
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r383902b"
-	ClangDefaultShortVersion = "11.0.2"
+	ClangDefaultVersion      = "clang-r399163b"
+	ClangDefaultShortVersion = "11.0.5"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index b5b5553..b77df79 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -39,6 +39,9 @@
 		// Get 64-bit off_t and related functions.
 		"-D_FILE_OFFSET_BITS=64",
 
+		// Don't adjust the layout of bitfields like msvc does.
+		"-mno-ms-bitfields",
+
 		"--sysroot ${WindowsGccRoot}/${WindowsGccTriple}",
 	}
 	windowsClangCflags = append(ClangFilterUnknownCflags(windowsCflags), []string{}...)
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 5295418..0453847 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -38,6 +38,9 @@
 	// Specify whether this fuzz target was submitted by a researcher. Defaults
 	// to false.
 	Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
+	// Specify who should be acknowledged for CVEs in the Android Security
+	// Bulletin.
+	Acknowledgement []string `json:"acknowledgement,omitempty"`
 }
 
 func (f *FuzzConfig) String() string {
diff --git a/cc/library.go b/cc/library.go
index 92853b5..bf7868f 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -19,8 +19,6 @@
 	"io"
 	"path/filepath"
 	"regexp"
-	"sort"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -152,6 +150,8 @@
 	BuildStubs bool `blueprint:"mutated"`
 	// Version of the stubs lib
 	StubsVersion string `blueprint:"mutated"`
+	// List of all stubs versions associated with an implementation lib
+	AllStubsVersions []string `blueprint:"mutated"`
 }
 
 type FlagExporterProperties struct {
@@ -338,10 +338,6 @@
 	flagExporter
 	stripper Stripper
 
-	// If we're used as a whole_static_lib, our missing dependencies need
-	// to be given
-	wholeStaticMissingDeps []string
-
 	// For whole_static_libs
 	objects Objects
 
@@ -682,7 +678,6 @@
 }
 
 type libraryInterface interface {
-	getWholeStaticMissingDeps() []string
 	static() bool
 	shared() bool
 	objs() Objects
@@ -889,8 +884,6 @@
 
 	library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
-	library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
-
 	ctx.CheckbuildFile(outputFile)
 
 	return outputFile
@@ -1182,10 +1175,6 @@
 		BoolDefault(library.SharedProperties.Shared.Enabled, true)
 }
 
-func (library *libraryDecorator) getWholeStaticMissingDeps() []string {
-	return append([]string(nil), library.wholeStaticMissingDeps...)
-}
-
 func (library *libraryDecorator) objs() Objects {
 	return library.objects
 }
@@ -1517,56 +1506,39 @@
 	}
 }
 
-var stubVersionsKey = android.NewOnceKey("stubVersions")
-
-// maps a module name to the list of stubs versions available for the module
-func stubsVersionsFor(config android.Config) map[string][]string {
-	return config.Once(stubVersionsKey, func() interface{} {
-		return make(map[string][]string)
-	}).(map[string][]string)
-}
-
-var stubsVersionsLock sync.Mutex
-
-func LatestStubsVersionFor(config android.Config, name string) string {
-	versions, ok := stubsVersionsFor(config)[name]
-	if ok && len(versions) > 0 {
-		// the versions are alreay sorted in ascending order
-		return versions[len(versions)-1]
-	}
-	return ""
-}
-
 func normalizeVersions(ctx android.BaseModuleContext, versions []string) {
-	numVersions := make([]int, len(versions))
+	var previous android.ApiLevel
 	for i, v := range versions {
-		numVer, err := android.ApiStrToNum(ctx, v)
+		ver, err := android.ApiLevelFromUser(ctx, v)
 		if err != nil {
 			ctx.PropertyErrorf("versions", "%s", err.Error())
 			return
 		}
-		numVersions[i] = numVer
-	}
-	if !sort.IsSorted(sort.IntSlice(numVersions)) {
-		ctx.PropertyErrorf("versions", "not sorted: %v", versions)
-	}
-	for i, v := range numVersions {
-		versions[i] = strconv.Itoa(v)
+		if i > 0 && ver.LessThanOrEqualTo(previous) {
+			ctx.PropertyErrorf("versions", "not sorted: %v", versions)
+		}
+		versions[i] = ver.String()
+		previous = ver
 	}
 }
 
 func createVersionVariations(mctx android.BottomUpMutatorContext, versions []string) {
-	// "" is for the non-stubs variant
-	versions = append([]string{""}, versions...)
+	// "" is for the non-stubs (implementation) variant.
+	variants := append([]string{""}, versions...)
 
-	modules := mctx.CreateLocalVariations(versions...)
+	modules := mctx.CreateLocalVariations(variants...)
 	for i, m := range modules {
-		if versions[i] != "" {
+		if variants[i] != "" {
 			m.(LinkableInterface).SetBuildStubs()
-			m.(LinkableInterface).SetStubsVersions(versions[i])
+			m.(LinkableInterface).SetStubsVersion(variants[i])
 		}
 	}
 	mctx.AliasVariation("")
+	latestVersion := ""
+	if len(versions) > 0 {
+		latestVersion = versions[len(versions)-1]
+	}
+	mctx.CreateAliasVariation("latest", latestVersion)
 }
 
 func VersionVariantAvailable(module interface {
@@ -1577,44 +1549,41 @@
 	return !module.Host() && !module.InRamdisk() && !module.InRecovery()
 }
 
-// VersionMutator splits a module into the mandatory non-stubs variant
-// (which is unnamed) and zero or more stubs variants.
-func VersionMutator(mctx android.BottomUpMutatorContext) {
+// versionSelector normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions,
+// and propagates the value from implementation libraries to llndk libraries with the same name.
+func versionSelectorMutator(mctx android.BottomUpMutatorContext) {
 	if library, ok := mctx.Module().(LinkableInterface); ok && VersionVariantAvailable(library) {
 		if library.CcLibrary() && library.BuildSharedVariant() && len(library.StubsVersions()) > 0 &&
 			!library.IsSdkVariant() {
+
 			versions := library.StubsVersions()
 			normalizeVersions(mctx, versions)
 			if mctx.Failed() {
 				return
 			}
-
-			stubsVersionsLock.Lock()
-			defer stubsVersionsLock.Unlock()
-			// save the list of versions for later use
-			stubsVersionsFor(mctx.Config())[mctx.ModuleName()] = versions
-
-			createVersionVariations(mctx, versions)
+			// Set the versions on the pre-mutated module so they can be read by any llndk modules that
+			// depend on the implementation library and haven't been mutated yet.
+			library.SetAllStubsVersions(versions)
 			return
 		}
 
 		if c, ok := library.(*Module); ok && c.IsStubs() {
-			stubsVersionsLock.Lock()
-			defer stubsVersionsLock.Unlock()
-			// For LLNDK llndk_library, we borrow stubs.versions from its implementation library.
-			// Since llndk_library has dependency to its implementation library,
-			// we can safely access stubsVersionsFor() with its baseModuleName.
-			versions := stubsVersionsFor(mctx.Config())[c.BaseModuleName()]
-			// save the list of versions for later use
-			stubsVersionsFor(mctx.Config())[mctx.ModuleName()] = versions
-
-			createVersionVariations(mctx, versions)
-			return
+			// Get the versions from the implementation module.
+			impls := mctx.GetDirectDepsWithTag(llndkImplDep)
+			if len(impls) > 1 {
+				panic(fmt.Errorf("Expected single implmenetation library, got %d", len(impls)))
+			} else if len(impls) == 1 {
+				c.SetAllStubsVersions(impls[0].(*Module).AllStubsVersions())
+			}
 		}
+	}
+}
 
-		mctx.CreateLocalVariations("")
-		mctx.AliasVariation("")
-		return
+// versionMutator splits a module into the mandatory non-stubs variant
+// (which is unnamed) and zero or more stubs variants.
+func versionMutator(mctx android.BottomUpMutatorContext) {
+	if library, ok := mctx.Module().(LinkableInterface); ok && VersionVariantAvailable(library) {
+		createVersionVariations(mctx, library.AllStubsVersions())
 	}
 }
 
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index ecfdc99..41ce294 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -80,7 +80,7 @@
 		for _, target := range targets {
 			name, version := StubsLibNameAndVersion(lib)
 			if version == "" {
-				version = LatestStubsVersionFor(mctx.Config(), name)
+				version = "latest"
 			}
 			variations := target.Variations()
 			if mctx.Device() {
@@ -221,10 +221,7 @@
 // Add properties that may, or may not, be arch specific.
 func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, libInfo *nativeLibInfoProperties, outputProperties android.BpPropertySet) {
 
-	if libInfo.SanitizeNever {
-		sanitizeSet := outputProperties.AddPropertySet("sanitize")
-		sanitizeSet.AddProperty("never", true)
-	}
+	outputProperties.AddProperty("sanitize", &libInfo.Sanitize)
 
 	// Copy the generated library to the snapshot and add a reference to it in the .bp module.
 	if libInfo.outputFile != nil {
@@ -295,9 +292,9 @@
 		outputProperties.AddProperty(property, includeDirs[property])
 	}
 
-	if len(libInfo.StubsVersion) > 0 {
+	if len(libInfo.StubsVersions) > 0 {
 		stubsSet := outputProperties.AddPropertySet("stubs")
-		stubsSet.AddProperty("versions", []string{libInfo.StubsVersion})
+		stubsSet.AddProperty("versions", libInfo.StubsVersions)
 	}
 }
 
@@ -368,13 +365,15 @@
 	// The specific stubs version for the lib variant, or empty string if stubs
 	// are not in use.
 	//
-	// Marked 'ignored-on-host' as the StubsVersion() from which this is initialized is
-	// not set on host and the stubs.versions property which this is written to is does
-	// not vary by arch so cannot be android specific.
-	StubsVersion string `sdk:"ignored-on-host"`
+	// Marked 'ignored-on-host' as the AllStubsVersions() from which this is
+	// initialized is not set on host and the stubs.versions property which this
+	// is written to does not vary by arch so cannot be android specific.
+	StubsVersions []string `sdk:"ignored-on-host"`
 
-	// Value of SanitizeProperties.Sanitize.Never. Needs to be propagated for CRT objects.
-	SanitizeNever bool `android:"arch_variant"`
+	// Value of SanitizeProperties.Sanitize. Several - but not all - of these
+	// affect the expanded variants. All are propagated to avoid entangling the
+	// sanitizer logic with the snapshot generation.
+	Sanitize SanitizeUserProps `android:"arch_variant"`
 
 	// outputFile is not exported as it is always arch specific.
 	outputFile android.Path
@@ -420,11 +419,15 @@
 	p.exportedGeneratedHeaders = ccModule.ExportedGeneratedHeaders()
 
 	if ccModule.HasStubsVariants() {
-		p.StubsVersion = ccModule.StubsVersion()
+		// TODO(b/169373910): 1. Only output the specific version (from
+		// ccModule.StubsVersion()) if the module is versioned. 2. Ensure that all
+		// the versioned stub libs are retained in the prebuilt tree; currently only
+		// the stub corresponding to ccModule.StubsVersion() is.
+		p.StubsVersions = ccModule.AllStubsVersions()
 	}
 
-	if ccModule.sanitize != nil && proptools.Bool(ccModule.sanitize.Properties.Sanitize.Never) {
-		p.SanitizeNever = true
+	if ccModule.sanitize != nil {
+		p.Sanitize = ccModule.sanitize.Properties.Sanitize
 	}
 }
 
diff --git a/cc/library_test.go b/cc/library_test.go
index cb16725..49838b4 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -195,7 +195,7 @@
 			name: "libfoo",
 			srcs: ["foo.c"],
 			stubs: {
-				versions: ["29", "R", "10000"],
+				versions: ["29", "R", "current"],
 			},
 		}
 	`
@@ -204,7 +204,7 @@
 	ctx := testCcWithConfig(t, config)
 
 	variants := ctx.ModuleVariantsForTests("libfoo")
-	for _, expectedVer := range []string{"29", "9000", "10000"} {
+	for _, expectedVer := range []string{"29", "R", "current"} {
 		expectedVariant := "android_arm_armv7-a-neon_shared_" + expectedVer
 		if !inList(expectedVariant, variants) {
 			t.Errorf("missing expected variant: %q", expectedVariant)
@@ -218,7 +218,7 @@
 			name: "libfoo",
 			srcs: ["foo.c"],
 			stubs: {
-				versions: ["29", "10000", "R"],
+				versions: ["29", "current", "R"],
 			},
 		}
 	`
@@ -233,10 +233,10 @@
 			name: "libfoo",
 			srcs: ["foo.c"],
 			stubs: {
-				versions: ["29", "10000", "X"],
+				versions: ["29", "current", "X"],
 			},
 		}
 	`
 
-	testCcError(t, `"libfoo" .*: versions: SDK version should be`, bp)
+	testCcError(t, `"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`, bp)
 }
diff --git a/cc/linkable.go b/cc/linkable.go
index 4c84163..6d8a4b7 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -26,8 +26,10 @@
 	StubsVersions() []string
 	BuildStubs() bool
 	SetBuildStubs()
-	SetStubsVersions(string)
+	SetStubsVersion(string)
 	StubsVersion() string
+	SetAllStubsVersions([]string)
+	AllStubsVersions() []string
 	HasStubsVariants() bool
 	SelectedStl() string
 	ApiLevel() string
diff --git a/cc/linker.go b/cc/linker.go
index 58f8a29..12c8b2c 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -278,19 +278,19 @@
 			deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
 		}
 
-		systemSharedLibs := linker.Properties.System_shared_libs
-		if systemSharedLibs == nil {
+		deps.SystemSharedLibs = linker.Properties.System_shared_libs
+		if deps.SystemSharedLibs == nil {
 			// Provide a default system_shared_libs if it is unspecified. Note: If an
 			// empty list [] is specified, it implies that the module declines the
 			// default system_shared_libs.
-			systemSharedLibs = []string{"libc", "libm", "libdl"}
+			deps.SystemSharedLibs = []string{"libc", "libm", "libdl"}
 		}
 
 		if inList("libdl", deps.SharedLibs) {
 			// If system_shared_libs has libc but not libdl, make sure shared_libs does not
 			// have libdl to avoid loading libdl before libc.
-			if inList("libc", systemSharedLibs) {
-				if !inList("libdl", systemSharedLibs) {
+			if inList("libc", deps.SystemSharedLibs) {
+				if !inList("libdl", deps.SystemSharedLibs) {
 					ctx.PropertyErrorf("shared_libs",
 						"libdl must be in system_shared_libs, not shared_libs")
 				}
@@ -300,12 +300,12 @@
 
 		// If libc and libdl are both in system_shared_libs make sure libdl comes after libc
 		// to avoid loading libdl before libc.
-		if inList("libdl", systemSharedLibs) && inList("libc", systemSharedLibs) &&
-			indexList("libdl", systemSharedLibs) < indexList("libc", systemSharedLibs) {
+		if inList("libdl", deps.SystemSharedLibs) && inList("libc", deps.SystemSharedLibs) &&
+			indexList("libdl", deps.SystemSharedLibs) < indexList("libc", deps.SystemSharedLibs) {
 			ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
 		}
 
-		deps.LateSharedLibs = append(deps.LateSharedLibs, systemSharedLibs...)
+		deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
 	}
 
 	if ctx.Fuchsia() {
diff --git a/cc/lto.go b/cc/lto.go
index 9868cdf..abd8dfb 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -52,6 +52,9 @@
 
 	// Use clang lld instead of gnu ld.
 	Use_clang_lld *bool
+
+	// Use -fwhole-program-vtables cflag.
+	Whole_program_vtables *bool
 }
 
 type lto struct {
@@ -65,6 +68,14 @@
 func (lto *lto) begin(ctx BaseModuleContext) {
 	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
 		lto.Properties.Lto.Never = boolPtr(true)
+	} else if ctx.Config().IsEnvTrue("GLOBAL_THINLTO") {
+		staticLib := ctx.static() && !ctx.staticBinary()
+		hostBin := ctx.Host()
+		if !staticLib && !hostBin {
+			if !lto.Never() && !lto.FullLTO() {
+				lto.Properties.Lto.Thin = boolPtr(true)
+			}
+		}
 	}
 }
 
@@ -88,7 +99,7 @@
 
 	if lto.LTO() {
 		var ltoFlag string
-		if Bool(lto.Properties.Lto.Thin) {
+		if lto.ThinLTO() {
 			ltoFlag = "-flto=thin -fsplit-lto-unit"
 		} else {
 			ltoFlag = "-flto"
@@ -97,7 +108,11 @@
 		flags.Local.CFlags = append(flags.Local.CFlags, ltoFlag)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoFlag)
 
-		if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && lto.useClangLld(ctx) {
+		if Bool(lto.Properties.Whole_program_vtables) {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables")
+		}
+
+		if lto.ThinLTO() && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && lto.useClangLld(ctx) {
 			// Set appropriate ThinLTO cache policy
 			cacheDirFormat := "-Wl,--thinlto-cache-dir="
 			cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
@@ -110,12 +125,11 @@
 			flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy)
 		}
 
-		// If the module does not have a profile, be conservative and do not inline
-		// or unroll loops during LTO, in order to prevent significant size bloat.
+		// If the module does not have a profile, be conservative and limit cross TU inline
+		// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
 		if !ctx.isPgoCompile() {
 			flags.Local.LdFlags = append(flags.Local.LdFlags,
-				"-Wl,-plugin-opt,-inline-threshold=0",
-				"-Wl,-plugin-opt,-unroll-threshold=0")
+				"-Wl,-plugin-opt,-import-instr-limit=5")
 		}
 	}
 	return flags
@@ -123,25 +137,31 @@
 
 // Can be called with a null receiver
 func (lto *lto) LTO() bool {
-	if lto == nil || lto.Disabled() {
+	if lto == nil || lto.Never() {
 		return false
 	}
 
-	full := Bool(lto.Properties.Lto.Full)
-	thin := Bool(lto.Properties.Lto.Thin)
-	return full || thin
+	return lto.FullLTO() || lto.ThinLTO()
+}
+
+func (lto *lto) FullLTO() bool {
+	return Bool(lto.Properties.Lto.Full)
+}
+
+func (lto *lto) ThinLTO() bool {
+	return Bool(lto.Properties.Lto.Thin)
 }
 
 // Is lto.never explicitly set to true?
-func (lto *lto) Disabled() bool {
-	return lto.Properties.Lto.Never != nil && *lto.Properties.Lto.Never
+func (lto *lto) Never() bool {
+	return Bool(lto.Properties.Lto.Never)
 }
 
 // Propagate lto requirements down from binaries
 func ltoDepsMutator(mctx android.TopDownMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok && m.lto.LTO() {
-		full := Bool(m.lto.Properties.Lto.Full)
-		thin := Bool(m.lto.Properties.Lto.Thin)
+		full := m.lto.FullLTO()
+		thin := m.lto.ThinLTO()
 		if full && thin {
 			mctx.PropertyErrorf("LTO", "FullLTO and ThinLTO are mutually exclusive")
 		}
@@ -162,11 +182,11 @@
 			}
 
 			if dep, ok := dep.(*Module); ok && dep.lto != nil &&
-				!dep.lto.Disabled() {
-				if full && !Bool(dep.lto.Properties.Lto.Full) {
+				!dep.lto.Never() {
+				if full && !dep.lto.FullLTO() {
 					dep.lto.Properties.FullDep = true
 				}
-				if thin && !Bool(dep.lto.Properties.Lto.Thin) {
+				if thin && !dep.lto.ThinLTO() {
 					dep.lto.Properties.ThinDep = true
 				}
 			}
@@ -183,19 +203,19 @@
 		// Create variations for LTO types required as static
 		// dependencies
 		variationNames := []string{""}
-		if m.lto.Properties.FullDep && !Bool(m.lto.Properties.Lto.Full) {
+		if m.lto.Properties.FullDep && !m.lto.FullLTO() {
 			variationNames = append(variationNames, "lto-full")
 		}
-		if m.lto.Properties.ThinDep && !Bool(m.lto.Properties.Lto.Thin) {
+		if m.lto.Properties.ThinDep && !m.lto.ThinLTO() {
 			variationNames = append(variationNames, "lto-thin")
 		}
 
 		// Use correct dependencies if LTO property is explicitly set
 		// (mutually exclusive)
-		if Bool(m.lto.Properties.Lto.Full) {
+		if m.lto.FullLTO() {
 			mctx.SetDependencyVariation("lto-full")
 		}
-		if Bool(m.lto.Properties.Lto.Thin) {
+		if m.lto.ThinLTO() {
 			mctx.SetDependencyVariation("lto-thin")
 		}
 
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index fe3efc0..5682d1c 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -52,6 +51,10 @@
 	ndkKnownLibsLock sync.Mutex
 )
 
+// The First_version and Unversioned_until properties of this struct should not
+// be used directly, but rather through the ApiLevel returning methods
+// firstVersion() and unversionedUntil().
+
 // Creates a stub shared library based on the provided version file.
 //
 // Example:
@@ -77,9 +80,7 @@
 	// https://github.com/android-ndk/ndk/issues/265.
 	Unversioned_until *string
 
-	// Private property for use by the mutator that splits per-API level. Can be
-	// one of <number:sdk_version> or <codename> or "current" passed to
-	// "ndkstubgen.py" as it is
+	// Use via apiLevel on the stubDecorator.
 	ApiLevel string `blueprint:"mutated"`
 
 	// True if this API is not yet ready to be shipped in the NDK. It will be
@@ -96,125 +97,33 @@
 	versionScriptPath     android.ModuleGenPath
 	parsedCoverageXmlPath android.ModuleOutPath
 	installPath           android.Path
+
+	apiLevel         android.ApiLevel
+	firstVersion     android.ApiLevel
+	unversionedUntil android.ApiLevel
 }
 
-// OMG GO
-func intMax(a int, b int) int {
-	if a > b {
-		return a
-	} else {
-		return b
-	}
-}
-
-func normalizeNdkApiLevel(ctx android.BaseModuleContext, apiLevel string,
-	arch android.Arch) (string, error) {
-
-	if apiLevel == "" {
-		panic("empty apiLevel not allowed")
-	}
-
-	if apiLevel == "current" {
-		return apiLevel, nil
-	}
-
-	minVersion := ctx.Config().MinSupportedSdkVersion()
-	firstArchVersions := map[android.ArchType]int{
-		android.Arm:    minVersion,
-		android.Arm64:  21,
-		android.X86:    minVersion,
-		android.X86_64: 21,
-	}
-
-	firstArchVersion, ok := firstArchVersions[arch.ArchType]
-	if !ok {
-		panic(fmt.Errorf("Arch %q not found in firstArchVersions", arch.ArchType))
-	}
-
-	if apiLevel == "minimum" {
-		return strconv.Itoa(firstArchVersion), nil
-	}
-
-	// If the NDK drops support for a platform version, we don't want to have to
-	// fix up every module that was using it as its SDK version. Clip to the
-	// supported version here instead.
-	version, err := strconv.Atoi(apiLevel)
-	if err != nil {
-		// Non-integer API levels are codenames.
-		return apiLevel, nil
-	}
-	version = intMax(version, minVersion)
-
-	return strconv.Itoa(intMax(version, firstArchVersion)), nil
-}
-
-func getFirstGeneratedVersion(firstSupportedVersion string, platformVersion int) (int, error) {
-	if firstSupportedVersion == "current" {
-		return platformVersion + 1, nil
-	}
-
-	return strconv.Atoi(firstSupportedVersion)
-}
-
-func shouldUseVersionScript(ctx android.BaseModuleContext, stub *stubDecorator) (bool, error) {
-	// unversioned_until is normally empty, in which case we should use the version script.
-	if String(stub.properties.Unversioned_until) == "" {
-		return true, nil
-	}
-
-	if String(stub.properties.Unversioned_until) == "current" {
-		if stub.properties.ApiLevel == "current" {
-			return true, nil
-		} else {
-			return false, nil
-		}
-	}
-
-	if stub.properties.ApiLevel == "current" {
-		return true, nil
-	}
-
-	unversionedUntil, err := android.ApiStrToNum(ctx, String(stub.properties.Unversioned_until))
-	if err != nil {
-		return true, err
-	}
-
-	version, err := android.ApiStrToNum(ctx, stub.properties.ApiLevel)
-	if err != nil {
-		return true, err
-	}
-
-	return version >= unversionedUntil, nil
+func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool {
+	return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil)
 }
 
 func generatePerApiVariants(ctx android.BottomUpMutatorContext, m *Module,
-	propName string, propValue string, perSplit func(*Module, string)) {
-	platformVersion := ctx.Config().PlatformSdkVersionInt()
+	from android.ApiLevel, perSplit func(*Module, android.ApiLevel)) {
 
-	firstSupportedVersion, err := normalizeNdkApiLevel(ctx, propValue,
-		ctx.Arch())
-	if err != nil {
-		ctx.PropertyErrorf(propName, err.Error())
+	var versions []android.ApiLevel
+	versionStrs := []string{}
+	for _, version := range ctx.Config().AllSupportedApiLevels() {
+		if version.GreaterThanOrEqualTo(from) {
+			versions = append(versions, version)
+			versionStrs = append(versionStrs, version.String())
+		}
 	}
-
-	firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion,
-		platformVersion)
-	if err != nil {
-		// In theory this is impossible because we've already run this through
-		// normalizeNdkApiLevel above.
-		ctx.PropertyErrorf(propName, err.Error())
-	}
-
-	var versionStrs []string
-	for version := firstGenVersion; version <= platformVersion; version++ {
-		versionStrs = append(versionStrs, strconv.Itoa(version))
-	}
-	versionStrs = append(versionStrs, ctx.Config().PlatformVersionActiveCodenames()...)
-	versionStrs = append(versionStrs, "current")
+	versions = append(versions, android.FutureApiLevel)
+	versionStrs = append(versionStrs, android.FutureApiLevel.String())
 
 	modules := ctx.CreateVariations(versionStrs...)
 	for i, module := range modules {
-		perSplit(module.(*Module), versionStrs[i])
+		perSplit(module.(*Module), versions[i])
 	}
 }
 
@@ -228,25 +137,56 @@
 					ctx.Module().Disable()
 					return
 				}
-				generatePerApiVariants(ctx, m, "first_version",
-					String(compiler.properties.First_version),
-					func(m *Module, version string) {
+				firstVersion, err := nativeApiLevelFromUser(ctx,
+					String(compiler.properties.First_version))
+				if err != nil {
+					ctx.PropertyErrorf("first_version", err.Error())
+					return
+				}
+				generatePerApiVariants(ctx, m, firstVersion,
+					func(m *Module, version android.ApiLevel) {
 						m.compiler.(*stubDecorator).properties.ApiLevel =
-							version
+							version.String()
 					})
 			} else if m.SplitPerApiLevel() && m.IsSdkVariant() {
 				if ctx.Os() != android.Android {
 					return
 				}
-				generatePerApiVariants(ctx, m, "min_sdk_version",
-					m.MinSdkVersion(), func(m *Module, version string) {
-						m.Properties.Sdk_version = &version
+				from, err := nativeApiLevelFromUser(ctx, m.MinSdkVersion())
+				if err != nil {
+					ctx.PropertyErrorf("min_sdk_version", err.Error())
+					return
+				}
+				generatePerApiVariants(ctx, m, from,
+					func(m *Module, version android.ApiLevel) {
+						m.Properties.Sdk_version = StringPtr(version.String())
 					})
 			}
 		}
 	}
 }
 
+func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool {
+	this.apiLevel = nativeApiLevelOrPanic(ctx, this.properties.ApiLevel)
+
+	var err error
+	this.firstVersion, err = nativeApiLevelFromUser(ctx,
+		String(this.properties.First_version))
+	if err != nil {
+		ctx.PropertyErrorf("first_version", err.Error())
+		return false
+	}
+
+	this.unversionedUntil, err = nativeApiLevelFromUserWithDefault(ctx,
+		String(this.properties.Unversioned_until), "minimum")
+	if err != nil {
+		ctx.PropertyErrorf("unversioned_until", err.Error())
+		return false
+	}
+
+	return true
+}
+
 func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
 	c.baseCompiler.compilerInit(ctx)
 
@@ -340,11 +280,16 @@
 		ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
 	}
 
+	if !c.initializeProperties(ctx) {
+		// Emits its own errors, so we don't need to.
+		return Objects{}
+	}
+
 	symbolFile := String(c.properties.Symbol_file)
 	objs, versionScript := compileStubLibrary(ctx, flags, symbolFile,
-		c.properties.ApiLevel, "")
+		c.apiLevel.String(), "")
 	c.versionScriptPath = versionScript
-	if c.properties.ApiLevel == "current" && ctx.PrimaryArch() {
+	if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
 		c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile)
 	}
 	return objs
@@ -366,12 +311,7 @@
 func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
 	objs Objects) android.Path {
 
-	useVersionScript, err := shouldUseVersionScript(ctx, stub)
-	if err != nil {
-		ctx.ModuleErrorf(err.Error())
-	}
-
-	if useVersionScript {
+	if shouldUseVersionScript(ctx, stub) {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
 		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
@@ -386,8 +326,6 @@
 
 func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
 	arch := ctx.Target().Arch.ArchType.Name
-	apiLevel := stub.properties.ApiLevel
-
 	// arm64 isn't actually a multilib toolchain, so unlike the other LP64
 	// architectures it's just installed to lib.
 	libDir := "lib"
@@ -396,7 +334,7 @@
 	}
 
 	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
-		"platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir))
+		"platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir))
 	stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
 }
 
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 2f4326a..86bf6ff 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -20,13 +20,16 @@
 import logging
 import os
 import sys
+from typing import Iterable, TextIO
 
 import symbolfile
+from symbolfile import Arch, Version
 
 
 class Generator:
     """Output generator that writes stub source files and version scripts."""
-    def __init__(self, src_file, version_script, arch, api, llndk, apex):
+    def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch,
+                 api: int, llndk: bool, apex: bool) -> None:
         self.src_file = src_file
         self.version_script = version_script
         self.arch = arch
@@ -34,12 +37,12 @@
         self.llndk = llndk
         self.apex = apex
 
-    def write(self, versions):
+    def write(self, versions: Iterable[Version]) -> None:
         """Writes all symbol data to the output files."""
         for version in versions:
             self.write_version(version)
 
-    def write_version(self, version):
+    def write_version(self, version: Version) -> None:
         """Writes a single version block's data to the output files."""
         if symbolfile.should_omit_version(version, self.arch, self.api,
                                           self.llndk, self.apex):
@@ -84,7 +87,7 @@
                 self.version_script.write('}' + base + ';\n')
 
 
-def parse_args():
+def parse_args() -> argparse.Namespace:
     """Parses and returns command line arguments."""
     parser = argparse.ArgumentParser()
 
@@ -100,23 +103,31 @@
     parser.add_argument(
         '--apex', action='store_true', help='Use the APEX variant.')
 
+    # https://github.com/python/mypy/issues/1317
+    # mypy has issues with using os.path.realpath as an argument here.
     parser.add_argument(
-        '--api-map', type=os.path.realpath, required=True,
+        '--api-map',
+        type=os.path.realpath,  # type: ignore
+        required=True,
         help='Path to the API level map JSON file.')
 
     parser.add_argument(
-        'symbol_file', type=os.path.realpath, help='Path to symbol file.')
+        'symbol_file',
+        type=os.path.realpath,  # type: ignore
+        help='Path to symbol file.')
     parser.add_argument(
-        'stub_src', type=os.path.realpath,
+        'stub_src',
+        type=os.path.realpath,  # type: ignore
         help='Path to output stub source file.')
     parser.add_argument(
-        'version_script', type=os.path.realpath,
+        'version_script',
+        type=os.path.realpath,  # type: ignore
         help='Path to output version script.')
 
     return parser.parse_args()
 
 
-def main():
+def main() -> None:
     """Program entry point."""
     args = parse_args()
 
diff --git a/cc/ndkstubgen/mypy.ini b/cc/ndkstubgen/mypy.ini
new file mode 100644
index 0000000..82aa7eb
--- /dev/null
+++ b/cc/ndkstubgen/mypy.ini
@@ -0,0 +1,2 @@
+[mypy]
+disallow_untyped_defs = True
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 70bcf78..6d2c9d6 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -21,19 +21,20 @@
 
 import ndkstubgen
 import symbolfile
+from symbolfile import Arch, Tag
 
 
 # pylint: disable=missing-docstring
 
 
 class GeneratorTest(unittest.TestCase):
-    def test_omit_version(self):
+    def test_omit_version(self) -> None:
         # Thorough testing of the cases involved here is handled by
         # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
 
         version = symbolfile.Version('VERSION_PRIVATE', None, [], [
             symbolfile.Symbol('foo', []),
@@ -42,74 +43,75 @@
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION', None, ['x86'], [
+        version = symbolfile.Version('VERSION', None, [Tag('x86')], [
             symbolfile.Symbol('foo', []),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION', None, ['introduced=14'], [
+        version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [
             symbolfile.Symbol('foo', []),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-    def test_omit_symbol(self):
+    def test_omit_symbol(self) -> None:
         # Thorough testing of the cases involved here is handled by
         # SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['x86']),
+            symbolfile.Symbol('foo', [Tag('x86')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['introduced=14']),
+            symbolfile.Symbol('foo', [Tag('introduced=14')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['llndk']),
+            symbolfile.Symbol('foo', [Tag('llndk')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['apex']),
+            symbolfile.Symbol('foo', [Tag('apex')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-    def test_write(self):
+    def test_write(self) -> None:
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
 
         versions = [
             symbolfile.Version('VERSION_1', None, [], [
                 symbolfile.Symbol('foo', []),
-                symbolfile.Symbol('bar', ['var']),
-                symbolfile.Symbol('woodly', ['weak']),
-                symbolfile.Symbol('doodly', ['weak', 'var']),
+                symbolfile.Symbol('bar', [Tag('var')]),
+                symbolfile.Symbol('woodly', [Tag('weak')]),
+                symbolfile.Symbol('doodly',
+                                  [Tag('weak'), Tag('var')]),
             ]),
             symbolfile.Version('VERSION_2', 'VERSION_1', [], [
                 symbolfile.Symbol('baz', []),
             ]),
             symbolfile.Version('VERSION_3', 'VERSION_1', [], [
-                symbolfile.Symbol('qux', ['versioned=14']),
+                symbolfile.Symbol('qux', [Tag('versioned=14')]),
             ]),
         ]
 
@@ -141,7 +143,7 @@
 
 
 class IntegrationTest(unittest.TestCase):
-    def test_integration(self):
+    def test_integration(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
@@ -178,14 +180,14 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
+                                             9, False, False)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -213,7 +215,7 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
-    def test_integration_future_api(self):
+    def test_integration_future_api(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
@@ -230,14 +232,14 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9001,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
+                                             9001, False, False)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9001,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9001, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -255,7 +257,7 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
-    def test_multiple_definition(self):
+    def test_multiple_definition(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 global:
@@ -280,8 +282,8 @@
             } VERSION_2;
 
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False,
-                                             False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
 
         with self.assertRaises(
                 symbolfile.MultiplyDefinedSymbolError) as ex_context:
@@ -289,7 +291,7 @@
         self.assertEqual(['bar', 'foo'],
                          ex_context.exception.multiply_defined_symbols)
 
-    def test_integration_with_apex(self):
+    def test_integration_with_apex(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
@@ -328,14 +330,14 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
-                                             False, True)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
+                                             9, False, True)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, True)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, True)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -369,7 +371,8 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
-def main():
+
+def main() -> None:
     suite = unittest.TestLoader().loadTestsFromName(__name__)
     unittest.TextTestRunner(verbosity=3).run(suite)
 
diff --git a/cc/pgo.go b/cc/pgo.go
index 6bf0ad0..439d2f7 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -70,6 +70,7 @@
 	PgoPresent          bool `blueprint:"mutated"`
 	ShouldProfileModule bool `blueprint:"mutated"`
 	PgoCompile          bool `blueprint:"mutated"`
+	PgoInstrLink        bool `blueprint:"mutated"`
 }
 
 type pgo struct {
@@ -89,13 +90,12 @@
 }
 
 func (props *PgoProperties) addInstrumentationProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
-
-	flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag)
-	// The profile runtime is added below in deps().  Add the below
-	// flag, which is the only other link-time action performed by
-	// the Clang driver during link.
-	flags.Local.LdFlags = append(flags.Local.LdFlags, "-u__llvm_profile_runtime")
+	// Add to C flags iff PGO is explicitly enabled for this module.
+	if props.ShouldProfileModule {
+		flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag)
+	}
+	flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrumentFlag)
 	return flags
 }
 func (props *PgoProperties) addSamplingProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
@@ -250,10 +250,12 @@
 
 	if pgoBenchmarksMap["all"] == true || pgoBenchmarksMap["ALL"] == true {
 		pgo.Properties.ShouldProfileModule = true
+		pgo.Properties.PgoInstrLink = pgo.Properties.isInstrumentation()
 	} else {
 		for _, b := range pgo.Properties.Pgo.Benchmarks {
 			if pgoBenchmarksMap[b] == true {
 				pgo.Properties.ShouldProfileModule = true
+				pgo.Properties.PgoInstrLink = pgo.Properties.isInstrumentation()
 				break
 			}
 		}
@@ -286,10 +288,42 @@
 		return flags
 	}
 
-	props := pgo.Properties
+	// Deduce PgoInstrLink property i.e. whether this module needs to be
+	// linked with profile-generation flags.  Here, we're setting it if any
+	// dependency needs PGO instrumentation.  It is initially set in
+	// begin() if PGO is directly enabled for this module.
+	if ctx.static() && !ctx.staticBinary() {
+		// For static libraries, check if any whole_static_libs are
+		// linked with profile generation
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
+				if depTag.static() && depTag.wholeStatic {
+					if cc, ok := m.(*Module); ok {
+						if cc.pgo.Properties.PgoInstrLink {
+							pgo.Properties.PgoInstrLink = true
+						}
+					}
+				}
+			}
+		})
+	} else {
+		// For executables and shared libraries, check all static dependencies.
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
+				if depTag.static() {
+					if cc, ok := m.(*Module); ok {
+						if cc.pgo.Properties.PgoInstrLink {
+							pgo.Properties.PgoInstrLink = true
+						}
+					}
+				}
+			}
+		})
+	}
 
+	props := pgo.Properties
 	// Add flags to profile this module based on its profile_kind
-	if props.ShouldProfileModule && props.isInstrumentation() {
+	if (props.ShouldProfileModule && props.isInstrumentation()) || props.PgoInstrLink {
 		// Instrumentation PGO use and gather flags cannot coexist.
 		return props.addInstrumentationProfileGatherFlags(ctx, flags)
 	} else if props.ShouldProfileModule && props.isSampling() {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 1ee096e..9d1b016 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"path/filepath"
 )
 
 func init() {
@@ -360,13 +361,18 @@
 			sharedLibPaths = append(sharedLibPaths, deps.SharedLibs...)
 			sharedLibPaths = append(sharedLibPaths, deps.LateSharedLibs...)
 
+			var fromPath = in.String()
+			if !filepath.IsAbs(fromPath) {
+				fromPath = "$$PWD/" + fromPath
+			}
+
 			ctx.Build(pctx, android.BuildParams{
 				Rule:      android.Symlink,
 				Output:    outputFile,
 				Input:     in,
 				Implicits: sharedLibPaths,
 				Args: map[string]string{
-					"fromPath": "$$PWD/" + in.String(),
+					"fromPath": fromPath,
 				},
 			})
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 174dcfe..43198c1 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -139,56 +139,59 @@
 	return t == asan || t == fuzzer || t == hwasan
 }
 
-type SanitizeProperties struct {
-	// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
-	Sanitize struct {
-		Never *bool `android:"arch_variant"`
+type SanitizeUserProps struct {
+	Never *bool `android:"arch_variant"`
 
-		// main sanitizers
-		Address   *bool `android:"arch_variant"`
-		Thread    *bool `android:"arch_variant"`
-		Hwaddress *bool `android:"arch_variant"`
+	// main sanitizers
+	Address   *bool `android:"arch_variant"`
+	Thread    *bool `android:"arch_variant"`
+	Hwaddress *bool `android:"arch_variant"`
 
-		// local sanitizers
+	// local sanitizers
+	Undefined        *bool    `android:"arch_variant"`
+	All_undefined    *bool    `android:"arch_variant"`
+	Misc_undefined   []string `android:"arch_variant"`
+	Fuzzer           *bool    `android:"arch_variant"`
+	Safestack        *bool    `android:"arch_variant"`
+	Cfi              *bool    `android:"arch_variant"`
+	Integer_overflow *bool    `android:"arch_variant"`
+	Scudo            *bool    `android:"arch_variant"`
+	Scs              *bool    `android:"arch_variant"`
+
+	// A modifier for ASAN and HWASAN for write only instrumentation
+	Writeonly *bool `android:"arch_variant"`
+
+	// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
+	// Replaces abort() on error with a human-readable error message.
+	// Address and Thread sanitizers always run in diagnostic mode.
+	Diag struct {
 		Undefined        *bool    `android:"arch_variant"`
-		All_undefined    *bool    `android:"arch_variant"`
-		Misc_undefined   []string `android:"arch_variant"`
-		Fuzzer           *bool    `android:"arch_variant"`
-		Safestack        *bool    `android:"arch_variant"`
 		Cfi              *bool    `android:"arch_variant"`
 		Integer_overflow *bool    `android:"arch_variant"`
-		Scudo            *bool    `android:"arch_variant"`
-		Scs              *bool    `android:"arch_variant"`
+		Misc_undefined   []string `android:"arch_variant"`
+		No_recover       []string
+	}
 
-		// A modifier for ASAN and HWASAN for write only instrumentation
-		Writeonly *bool `android:"arch_variant"`
+	// value to pass to -fsanitize-recover=
+	Recover []string
 
-		// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
-		// Replaces abort() on error with a human-readable error message.
-		// Address and Thread sanitizers always run in diagnostic mode.
-		Diag struct {
-			Undefined        *bool    `android:"arch_variant"`
-			Cfi              *bool    `android:"arch_variant"`
-			Integer_overflow *bool    `android:"arch_variant"`
-			Misc_undefined   []string `android:"arch_variant"`
-			No_recover       []string
-		}
+	// value to pass to -fsanitize-blacklist
+	Blocklist *string
+}
 
-		// value to pass to -fsanitize-recover=
-		Recover []string
-
-		// value to pass to -fsanitize-blacklist
-		Blocklist *string
-	} `android:"arch_variant"`
-
-	SanitizerEnabled  bool     `blueprint:"mutated"`
-	SanitizeDep       bool     `blueprint:"mutated"`
-	MinimalRuntimeDep bool     `blueprint:"mutated"`
-	BuiltinsDep       bool     `blueprint:"mutated"`
-	UbsanRuntimeDep   bool     `blueprint:"mutated"`
-	InSanitizerDir    bool     `blueprint:"mutated"`
-	Sanitizers        []string `blueprint:"mutated"`
-	DiagSanitizers    []string `blueprint:"mutated"`
+type SanitizeProperties struct {
+	// Enable AddressSanitizer, ThreadSanitizer, UndefinedBehaviorSanitizer, and
+	// others. Please see SanitizerUserProps in build/soong/cc/sanitize.go for
+	// details.
+	Sanitize          SanitizeUserProps `android:"arch_variant"`
+	SanitizerEnabled  bool              `blueprint:"mutated"`
+	SanitizeDep       bool              `blueprint:"mutated"`
+	MinimalRuntimeDep bool              `blueprint:"mutated"`
+	BuiltinsDep       bool              `blueprint:"mutated"`
+	UbsanRuntimeDep   bool              `blueprint:"mutated"`
+	InSanitizerDir    bool              `blueprint:"mutated"`
+	Sanitizers        []string          `blueprint:"mutated"`
+	DiagSanitizers    []string          `blueprint:"mutated"`
 }
 
 type sanitize struct {
@@ -1037,7 +1040,7 @@
 				if c.Device() {
 					variations = append(variations, c.ImageVariation())
 				}
-				mctx.AddFarVariationDependencies(variations, depTag, runtimeLibrary)
+				c.addSharedLibDependenciesWithVersions(mctx, variations, depTag, runtimeLibrary, "", true)
 			}
 			// static lib does not have dependency to the runtime library. The
 			// dependency will be added to the executables or shared libs using
diff --git a/cc/stl.go b/cc/stl.go
index e18fe95..406fa3a 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -17,7 +17,6 @@
 import (
 	"android/soong/android"
 	"fmt"
-	"strconv"
 )
 
 func getNdkStlFamily(m LinkableInterface) string {
@@ -136,23 +135,8 @@
 }
 
 func needsLibAndroidSupport(ctx BaseModuleContext) bool {
-	versionStr, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch())
-	if err != nil {
-		ctx.PropertyErrorf("sdk_version", err.Error())
-	}
-
-	if versionStr == "current" {
-		return false
-	}
-
-	version, err := strconv.Atoi(versionStr)
-	if err != nil {
-		panic(fmt.Sprintf(
-			"invalid API level returned from normalizeNdkApiLevel: %q",
-			versionStr))
-	}
-
-	return version < 21
+	version := nativeApiLevelOrPanic(ctx, ctx.sdkVersion())
+	return version.LessThan(android.FirstNonLibAndroidSupportVersion)
 }
 
 func staticUnwinder(ctx android.BaseModuleContext) string {
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index faa3823..5678e7d 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -14,15 +14,31 @@
 # limitations under the License.
 #
 """Parser for Android's version script information."""
+from dataclasses import dataclass
 import logging
 import re
+from typing import (
+    Dict,
+    Iterable,
+    List,
+    Mapping,
+    NewType,
+    Optional,
+    TextIO,
+    Tuple,
+)
+
+
+ApiMap = Mapping[str, int]
+Arch = NewType('Arch', str)
+Tag = NewType('Tag', str)
 
 
 ALL_ARCHITECTURES = (
-    'arm',
-    'arm64',
-    'x86',
-    'x86_64',
+    Arch('arm'),
+    Arch('arm64'),
+    Arch('x86'),
+    Arch('x86_64'),
 )
 
 
@@ -30,18 +46,36 @@
 FUTURE_API_LEVEL = 10000
 
 
-def logger():
+def logger() -> logging.Logger:
     """Return the main logger for this module."""
     return logging.getLogger(__name__)
 
 
-def get_tags(line):
+@dataclass
+class Symbol:
+    """A symbol definition from a symbol file."""
+
+    name: str
+    tags: List[Tag]
+
+
+@dataclass
+class Version:
+    """A version block of a symbol file."""
+
+    name: str
+    base: Optional[str]
+    tags: List[Tag]
+    symbols: List[Symbol]
+
+
+def get_tags(line: str) -> List[Tag]:
     """Returns a list of all tags on this line."""
     _, _, all_tags = line.strip().partition('#')
-    return [e for e in re.split(r'\s+', all_tags) if e.strip()]
+    return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()]
 
 
-def is_api_level_tag(tag):
+def is_api_level_tag(tag: Tag) -> bool:
     """Returns true if this tag has an API level that may need decoding."""
     if tag.startswith('introduced='):
         return True
@@ -52,7 +86,7 @@
     return False
 
 
-def decode_api_level(api, api_map):
+def decode_api_level(api: str, api_map: ApiMap) -> int:
     """Decodes the API level argument into the API level number.
 
     For the average case, this just decodes the integer value from the string,
@@ -70,12 +104,13 @@
     return api_map[api]
 
 
-def decode_api_level_tags(tags, api_map):
+def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]:
     """Decodes API level code names in a list of tags.
 
     Raises:
         ParseError: An unknown version name was found in a tag.
     """
+    decoded_tags = list(tags)
     for idx, tag in enumerate(tags):
         if not is_api_level_tag(tag):
             continue
@@ -83,13 +118,13 @@
 
         try:
             decoded = str(decode_api_level(value, api_map))
-            tags[idx] = '='.join([name, decoded])
+            decoded_tags[idx] = Tag('='.join([name, decoded]))
         except KeyError:
-            raise ParseError('Unknown version name in tag: {}'.format(tag))
-    return tags
+            raise ParseError(f'Unknown version name in tag: {tag}')
+    return decoded_tags
 
 
-def split_tag(tag):
+def split_tag(tag: Tag) -> Tuple[str, str]:
     """Returns a key/value tuple of the tag.
 
     Raises:
@@ -103,7 +138,7 @@
     return key, value
 
 
-def get_tag_value(tag):
+def get_tag_value(tag: Tag) -> str:
     """Returns the value of a key/value tag.
 
     Raises:
@@ -114,12 +149,13 @@
     return split_tag(tag)[1]
 
 
-def version_is_private(version):
+def version_is_private(version: str) -> bool:
     """Returns True if the version name should be treated as private."""
     return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
 
 
-def should_omit_version(version, arch, api, llndk, apex):
+def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
+                        apex: bool) -> bool:
     """Returns True if the version section should be ommitted.
 
     We want to omit any sections that do not have any symbols we'll have in the
@@ -145,7 +181,8 @@
     return False
 
 
-def should_omit_symbol(symbol, arch, api, llndk, apex):
+def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
+                       apex: bool) -> bool:
     """Returns True if the symbol should be omitted."""
     no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags
     keep = no_llndk_no_apex or \
@@ -160,7 +197,7 @@
     return False
 
 
-def symbol_in_arch(tags, arch):
+def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool:
     """Returns true if the symbol is present for the given architecture."""
     has_arch_tags = False
     for tag in tags:
@@ -175,7 +212,7 @@
     return not has_arch_tags
 
 
-def symbol_in_api(tags, arch, api):
+def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool:
     """Returns true if the symbol is present for the given API level."""
     introduced_tag = None
     arch_specific = False
@@ -197,7 +234,7 @@
     return api >= int(get_tag_value(introduced_tag))
 
 
-def symbol_versioned_in_api(tags, api):
+def symbol_versioned_in_api(tags: Iterable[Tag], api: int) -> bool:
     """Returns true if the symbol should be versioned for the given API.
 
     This models the `versioned=API` tag. This should be a very uncommonly
@@ -223,68 +260,40 @@
 
 class MultiplyDefinedSymbolError(RuntimeError):
     """A symbol name was multiply defined."""
-    def __init__(self, multiply_defined_symbols):
-        super(MultiplyDefinedSymbolError, self).__init__(
+    def __init__(self, multiply_defined_symbols: Iterable[str]) -> None:
+        super().__init__(
             'Version script contains multiple definitions for: {}'.format(
                 ', '.join(multiply_defined_symbols)))
         self.multiply_defined_symbols = multiply_defined_symbols
 
 
-class Version:
-    """A version block of a symbol file."""
-    def __init__(self, name, base, tags, symbols):
-        self.name = name
-        self.base = base
-        self.tags = tags
-        self.symbols = symbols
-
-    def __eq__(self, other):
-        if self.name != other.name:
-            return False
-        if self.base != other.base:
-            return False
-        if self.tags != other.tags:
-            return False
-        if self.symbols != other.symbols:
-            return False
-        return True
-
-
-class Symbol:
-    """A symbol definition from a symbol file."""
-    def __init__(self, name, tags):
-        self.name = name
-        self.tags = tags
-
-    def __eq__(self, other):
-        return self.name == other.name and set(self.tags) == set(other.tags)
-
-
 class SymbolFileParser:
     """Parses NDK symbol files."""
-    def __init__(self, input_file, api_map, arch, api, llndk, apex):
+    def __init__(self, input_file: TextIO, api_map: ApiMap, arch: Arch,
+                 api: int, llndk: bool, apex: bool) -> None:
         self.input_file = input_file
         self.api_map = api_map
         self.arch = arch
         self.api = api
         self.llndk = llndk
         self.apex = apex
-        self.current_line = None
+        self.current_line: Optional[str] = None
 
-    def parse(self):
+    def parse(self) -> List[Version]:
         """Parses the symbol file and returns a list of Version objects."""
         versions = []
         while self.next_line() != '':
+            assert self.current_line is not None
             if '{' in self.current_line:
                 versions.append(self.parse_version())
             else:
                 raise ParseError(
-                    'Unexpected contents at top level: ' + self.current_line)
+                    f'Unexpected contents at top level: {self.current_line}')
 
         self.check_no_duplicate_symbols(versions)
         return versions
 
-    def check_no_duplicate_symbols(self, versions):
+    def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None:
         """Raises errors for multiply defined symbols.
 
         This situation is the normal case when symbol versioning is actually
@@ -312,12 +321,13 @@
             raise MultiplyDefinedSymbolError(
                 sorted(list(multiply_defined_symbols)))
 
-    def parse_version(self):
+    def parse_version(self) -> Version:
         """Parses a single version section and returns a Version object."""
+        assert self.current_line is not None
         name = self.current_line.split('{')[0].strip()
         tags = get_tags(self.current_line)
         tags = decode_api_level_tags(tags, self.api_map)
-        symbols = []
+        symbols: List[Symbol] = []
         global_scope = True
         cpp_symbols = False
         while self.next_line() != '':
@@ -333,9 +343,7 @@
                     cpp_symbols = False
                 else:
                     base = base.rstrip(';').rstrip()
-                    if base == '':
-                        base = None
-                    return Version(name, base, tags, symbols)
+                    return Version(name, base or None, tags, symbols)
             elif 'extern "C++" {' in self.current_line:
                 cpp_symbols = True
             elif not cpp_symbols and ':' in self.current_line:
@@ -354,8 +362,9 @@
                 pass
         raise ParseError('Unexpected EOF in version block.')
 
-    def parse_symbol(self):
+    def parse_symbol(self) -> Symbol:
         """Parses a single symbol line and returns a Symbol object."""
+        assert self.current_line is not None
         if ';' not in self.current_line:
             raise ParseError(
                 'Expected ; to terminate symbol: ' + self.current_line)
@@ -368,7 +377,7 @@
         tags = decode_api_level_tags(tags, self.api_map)
         return Symbol(name, tags)
 
-    def next_line(self):
+    def next_line(self) -> str:
         """Returns the next non-empty non-comment line.
 
         A return value of '' indicates EOF.
diff --git a/cc/symbolfile/mypy.ini b/cc/symbolfile/mypy.ini
new file mode 100644
index 0000000..82aa7eb
--- /dev/null
+++ b/cc/symbolfile/mypy.ini
@@ -0,0 +1,2 @@
+[mypy]
+disallow_untyped_defs = True
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index c91131f..92b1399 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,12 +19,13 @@
 import unittest
 
 import symbolfile
+from symbolfile import Arch, Tag
 
 # pylint: disable=missing-docstring
 
 
 class DecodeApiLevelTest(unittest.TestCase):
-    def test_decode_api_level(self):
+    def test_decode_api_level(self) -> None:
         self.assertEqual(9, symbolfile.decode_api_level('9', {}))
         self.assertEqual(9000, symbolfile.decode_api_level('O', {'O': 9000}))
 
@@ -33,70 +34,73 @@
 
 
 class TagsTest(unittest.TestCase):
-    def test_get_tags_no_tags(self):
+    def test_get_tags_no_tags(self) -> None:
         self.assertEqual([], symbolfile.get_tags(''))
         self.assertEqual([], symbolfile.get_tags('foo bar baz'))
 
-    def test_get_tags(self):
+    def test_get_tags(self) -> None:
         self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
         self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
 
-    def test_split_tag(self):
-        self.assertTupleEqual(('foo', 'bar'), symbolfile.split_tag('foo=bar'))
-        self.assertTupleEqual(('foo', 'bar=baz'), symbolfile.split_tag('foo=bar=baz'))
+    def test_split_tag(self) -> None:
+        self.assertTupleEqual(('foo', 'bar'),
+                              symbolfile.split_tag(Tag('foo=bar')))
+        self.assertTupleEqual(('foo', 'bar=baz'),
+                              symbolfile.split_tag(Tag('foo=bar=baz')))
         with self.assertRaises(ValueError):
-            symbolfile.split_tag('foo')
+            symbolfile.split_tag(Tag('foo'))
 
-    def test_get_tag_value(self):
-        self.assertEqual('bar', symbolfile.get_tag_value('foo=bar'))
-        self.assertEqual('bar=baz', symbolfile.get_tag_value('foo=bar=baz'))
+    def test_get_tag_value(self) -> None:
+        self.assertEqual('bar', symbolfile.get_tag_value(Tag('foo=bar')))
+        self.assertEqual('bar=baz',
+                         symbolfile.get_tag_value(Tag('foo=bar=baz')))
         with self.assertRaises(ValueError):
-            symbolfile.get_tag_value('foo')
+            symbolfile.get_tag_value(Tag('foo'))
 
-    def test_is_api_level_tag(self):
-        self.assertTrue(symbolfile.is_api_level_tag('introduced=24'))
-        self.assertTrue(symbolfile.is_api_level_tag('introduced-arm=24'))
-        self.assertTrue(symbolfile.is_api_level_tag('versioned=24'))
+    def test_is_api_level_tag(self) -> None:
+        self.assertTrue(symbolfile.is_api_level_tag(Tag('introduced=24')))
+        self.assertTrue(symbolfile.is_api_level_tag(Tag('introduced-arm=24')))
+        self.assertTrue(symbolfile.is_api_level_tag(Tag('versioned=24')))
 
         # Shouldn't try to process things that aren't a key/value tag.
-        self.assertFalse(symbolfile.is_api_level_tag('arm'))
-        self.assertFalse(symbolfile.is_api_level_tag('introduced'))
-        self.assertFalse(symbolfile.is_api_level_tag('versioned'))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('arm')))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('introduced')))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('versioned')))
 
         # We don't support arch specific `versioned` tags.
-        self.assertFalse(symbolfile.is_api_level_tag('versioned-arm=24'))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('versioned-arm=24')))
 
-    def test_decode_api_level_tags(self):
+    def test_decode_api_level_tags(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
         }
 
         tags = [
-            'introduced=9',
-            'introduced-arm=14',
-            'versioned=16',
-            'arm',
-            'introduced=O',
-            'introduced=P',
+            Tag('introduced=9'),
+            Tag('introduced-arm=14'),
+            Tag('versioned=16'),
+            Tag('arm'),
+            Tag('introduced=O'),
+            Tag('introduced=P'),
         ]
         expected_tags = [
-            'introduced=9',
-            'introduced-arm=14',
-            'versioned=16',
-            'arm',
-            'introduced=9000',
-            'introduced=9001',
+            Tag('introduced=9'),
+            Tag('introduced-arm=14'),
+            Tag('versioned=16'),
+            Tag('arm'),
+            Tag('introduced=9000'),
+            Tag('introduced=9001'),
         ]
         self.assertListEqual(
             expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
 
         with self.assertRaises(symbolfile.ParseError):
-            symbolfile.decode_api_level_tags(['introduced=O'], {})
+            symbolfile.decode_api_level_tags([Tag('introduced=O')], {})
 
 
 class PrivateVersionTest(unittest.TestCase):
-    def test_version_is_private(self):
+    def test_version_is_private(self) -> None:
         self.assertFalse(symbolfile.version_is_private('foo'))
         self.assertFalse(symbolfile.version_is_private('PRIVATE'))
         self.assertFalse(symbolfile.version_is_private('PLATFORM'))
@@ -110,191 +114,227 @@
 
 
 class SymbolPresenceTest(unittest.TestCase):
-    def test_symbol_in_arch(self):
-        self.assertTrue(symbolfile.symbol_in_arch([], 'arm'))
-        self.assertTrue(symbolfile.symbol_in_arch(['arm'], 'arm'))
+    def test_symbol_in_arch(self) -> None:
+        self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm')))
+        self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm')))
 
-        self.assertFalse(symbolfile.symbol_in_arch(['x86'], 'arm'))
+        self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm')))
 
-    def test_symbol_in_api(self):
-        self.assertTrue(symbolfile.symbol_in_api([], 'arm', 9))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 9))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced-x86=14'], 'arm', 9))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['introduced-arm=9', 'introduced-x86=21'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['introduced=9', 'introduced-x86=21'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['introduced=21', 'introduced-arm=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['future'], 'arm', symbolfile.FUTURE_API_LEVEL))
+    def test_symbol_in_api(self) -> None:
+        self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced=9')], Arch('arm'), 9))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced=9')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced-arm=9')], Arch('arm'),
+                                     14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced-arm=9')], Arch('arm'),
+                                     14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced-x86=14')], Arch('arm'),
+                                     9))
+        self.assertTrue(
+            symbolfile.symbol_in_api(
+                [Tag('introduced-arm=9'),
+                 Tag('introduced-x86=21')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=9'),
+                 Tag('introduced-x86=21')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=21'),
+                 Tag('introduced-arm=9')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('future')], Arch('arm'),
+                                     symbolfile.FUTURE_API_LEVEL))
 
-        self.assertFalse(symbolfile.symbol_in_api(['introduced=14'], 'arm', 9))
-        self.assertFalse(symbolfile.symbol_in_api(['introduced-arm=14'], 'arm', 9))
-        self.assertFalse(symbolfile.symbol_in_api(['future'], 'arm', 9))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced=9', 'future'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced-arm=9', 'future'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced-arm=21', 'introduced-x86=9'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced=9', 'introduced-arm=21'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced=21', 'introduced-x86=9'], 'arm', 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('introduced=14')], Arch('arm'), 9))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('introduced-arm=14')], Arch('arm'),
+                                     9))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('future')], Arch('arm'), 9))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=9'), Tag('future')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('introduced-arm=9'),
+                                      Tag('future')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced-arm=21'),
+                 Tag('introduced-x86=9')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=9'),
+                 Tag('introduced-arm=21')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=21'),
+                 Tag('introduced-x86=9')], Arch('arm'), 14))
 
         # Interesting edge case: this symbol should be omitted from the
         # library, but this call should still return true because none of the
         # tags indiciate that it's not present in this API level.
-        self.assertTrue(symbolfile.symbol_in_api(['x86'], 'arm', 9))
+        self.assertTrue(symbolfile.symbol_in_api([Tag('x86')], Arch('arm'), 9))
 
-    def test_verioned_in_api(self):
+    def test_verioned_in_api(self) -> None:
         self.assertTrue(symbolfile.symbol_versioned_in_api([], 9))
-        self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 9))
-        self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 14))
+        self.assertTrue(
+            symbolfile.symbol_versioned_in_api([Tag('versioned=9')], 9))
+        self.assertTrue(
+            symbolfile.symbol_versioned_in_api([Tag('versioned=9')], 14))
 
-        self.assertFalse(symbolfile.symbol_versioned_in_api(['versioned=14'], 9))
+        self.assertFalse(
+            symbolfile.symbol_versioned_in_api([Tag('versioned=14')], 9))
 
 
 class OmitVersionTest(unittest.TestCase):
-    def test_omit_private(self):
+    def test_omit_private(self) -> None:
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
                 False))
 
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo_PRIVATE', None, [], []), 'arm', 9,
-                False, False))
+                symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'),
+                9, False, False))
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo_PLATFORM', None, [], []), 'arm', 9,
-                False, False))
-
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['platform-only'], []), 'arm',
+                symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'),
                 9, False, False))
 
-    def test_omit_llndk(self):
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9,
-                False, False))
+                symbolfile.Version('foo', None, [Tag('platform-only')], []),
+                Arch('arm'), 9, False, False))
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, True,
-                False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9, True,
-                False))
-
-    def test_omit_apex(self):
+    def test_omit_llndk(self) -> None:
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
-                False))
+                symbolfile.Version('foo', None, [Tag('llndk')], []),
+                Arch('arm'), 9, False, False))
 
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True,
+                False))
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('llndk')], []),
+                Arch('arm'), 9, True, False))
+
+    def test_omit_apex(self) -> None:
+        self.assertTrue(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('apex')], []),
+                Arch('arm'), 9, False, False))
+
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
                 True))
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
-                True))
+                symbolfile.Version('foo', None, [Tag('apex')], []),
+                Arch('arm'), 9, False, True))
 
-    def test_omit_arch(self):
+    def test_omit_arch(self) -> None:
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
                 False))
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['arm'], []), 'arm', 9, False,
-                False))
-
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['x86'], []), 'arm', 9, False,
-                False))
-
-    def test_omit_api(self):
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
-                False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['introduced=9'], []), 'arm',
+                symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'),
                 9, False, False))
 
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['introduced=14'], []), 'arm',
+                symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'),
                 9, False, False))
 
+    def test_omit_api(self) -> None:
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
+                False))
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('introduced=9')], []),
+                Arch('arm'), 9, False, False))
+
+        self.assertTrue(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('introduced=14')], []),
+                Arch('arm'), 9, False, False))
+
 
 class OmitSymbolTest(unittest.TestCase):
-    def test_omit_llndk(self):
+    def test_omit_llndk(self) -> None:
         self.assertTrue(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
-                                          'arm', 9, False, False))
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9,
+                False, False))
 
         self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, True, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
-                                          'arm', 9, True, False))
-
-    def test_omit_apex(self):
-        self.assertTrue(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
-                                          'arm', 9, False, False))
-
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, False, True))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
-                                          'arm', 9, False, True))
-
-    def test_omit_arch(self):
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, False, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['arm']),
-                                          'arm', 9, False, False))
-
-        self.assertTrue(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['x86']),
-                                          'arm', 9, False, False))
-
-    def test_omit_api(self):
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, False, False))
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, True, False))
         self.assertFalse(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', ['introduced=9']), 'arm', 9, False,
+                symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True,
+                False))
+
+    def test_omit_apex(self) -> None:
+        self.assertTrue(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
+                False))
+
+        self.assertFalse(
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, False, True))
+        self.assertFalse(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
+                True))
+
+    def test_omit_arch(self) -> None:
+        self.assertFalse(
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, False, False))
+        self.assertFalse(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False,
                 False))
 
         self.assertTrue(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', ['introduced=14']), 'arm', 9, False,
+                symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False,
                 False))
 
+    def test_omit_api(self) -> None:
+        self.assertFalse(
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, False, False))
+        self.assertFalse(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'),
+                9, False, False))
+
+        self.assertTrue(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'),
+                9, False, False))
+
 
 class SymbolFileParseTest(unittest.TestCase):
-    def test_next_line(self):
+    def test_next_line(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo
 
@@ -302,10 +342,12 @@
             # baz
             qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         self.assertIsNone(parser.current_line)
 
         self.assertEqual('foo', parser.next_line().strip())
+        assert parser.current_line is not None
         self.assertEqual('foo', parser.current_line.strip())
 
         self.assertEqual('bar', parser.next_line().strip())
@@ -317,7 +359,7 @@
         self.assertEqual('', parser.next_line())
         self.assertEqual('', parser.current_line)
 
-    def test_parse_version(self):
+    def test_parse_version(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 { # foo bar
                 baz;
@@ -327,7 +369,8 @@
             VERSION_2 {
             } VERSION_1; # asdf
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
 
         parser.next_line()
         version = parser.parse_version()
@@ -337,7 +380,7 @@
 
         expected_symbols = [
             symbolfile.Symbol('baz', []),
-            symbolfile.Symbol('qux', ['woodly', 'doodly']),
+            symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -347,32 +390,35 @@
         self.assertEqual('VERSION_1', version.base)
         self.assertEqual([], version.tags)
 
-    def test_parse_version_eof(self):
+    def test_parse_version_eof(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_unknown_scope_label(self):
+    def test_unknown_scope_label(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 foo:
             }
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_parse_symbol(self):
+    def test_parse_symbol(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo;
             bar; # baz qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
 
         parser.next_line()
         symbol = parser.parse_symbol()
@@ -384,48 +430,51 @@
         self.assertEqual('bar', symbol.name)
         self.assertEqual(['baz', 'qux'], symbol.tags)
 
-    def test_wildcard_symbol_global(self):
+    def test_wildcard_symbol_global(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_wildcard_symbol_local(self):
+    def test_wildcard_symbol_local(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 local:
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         version = parser.parse_version()
         self.assertEqual([], version.symbols)
 
-    def test_missing_semicolon(self):
+    def test_missing_semicolon(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 foo
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_parse_fails_invalid_input(self):
+    def test_parse_fails_invalid_input(self) -> None:
         with self.assertRaises(symbolfile.ParseError):
             input_file = io.StringIO('foo')
-            parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16,
-                                                 False, False)
+            parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
+                                                 16, False, False)
             parser.parse()
 
-    def test_parse(self):
+    def test_parse(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 local:
@@ -443,23 +492,24 @@
                     qwerty;
             } VERSION_1;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         versions = parser.parse()
 
         expected = [
             symbolfile.Version('VERSION_1', None, [], [
                 symbolfile.Symbol('foo', []),
-                symbolfile.Symbol('bar', ['baz']),
+                symbolfile.Symbol('bar', [Tag('baz')]),
             ]),
-            symbolfile.Version('VERSION_2', 'VERSION_1', ['wasd'], [
+            symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [
                 symbolfile.Symbol('woodly', []),
-                symbolfile.Symbol('doodly', ['asdf']),
+                symbolfile.Symbol('doodly', [Tag('asdf')]),
             ]),
         ]
 
         self.assertEqual(expected, versions)
 
-    def test_parse_llndk_apex_symbol(self):
+    def test_parse_llndk_apex_symbol(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 foo;
@@ -468,7 +518,8 @@
                 qux; # apex
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, True)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, True)
 
         parser.next_line()
         version = parser.parse_version()
@@ -477,14 +528,14 @@
 
         expected_symbols = [
             symbolfile.Symbol('foo', []),
-            symbolfile.Symbol('bar', ['llndk']),
-            symbolfile.Symbol('baz', ['llndk', 'apex']),
-            symbolfile.Symbol('qux', ['apex']),
+            symbolfile.Symbol('bar', [Tag('llndk')]),
+            symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]),
+            symbolfile.Symbol('qux', [Tag('apex')]),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
 
-def main():
+def main() -> None:
     suite = unittest.TestLoader().loadTestsFromName(__name__)
     unittest.TextTestRunner(verbosity=3).run(suite)
 
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 4aa62be..69e4f69 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -173,7 +173,6 @@
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
 	defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
-	defer build.PrintGomaDeprecation(buildCtx, config)
 
 	os.MkdirAll(logsDir, 0777)
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 5dd2a86..8e35679 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -172,13 +172,7 @@
 }
 
 func (p *PrebuiltEtc) BaseDir() string {
-	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
-	// socInstallDirBase.
-	installBaseDir := p.installDirBase
-	if p.SocSpecific() && p.socInstallDirBase != "" {
-		installBaseDir = p.socInstallDirBase
-	}
-	return installBaseDir
+	return p.installDirBase
 }
 
 func (p *PrebuiltEtc) Installable() bool {
@@ -205,7 +199,13 @@
 		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
 	}
 
-	p.installDirPath = android.PathForModuleInstall(ctx, p.BaseDir(), p.SubDir())
+	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
+	// socInstallDirBase.
+	installBaseDir := p.installDirBase
+	if p.SocSpecific() && p.socInstallDirBase != "" {
+		installBaseDir = p.socInstallDirBase
+	}
+	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir())
 
 	// This ensures that outputFilePath has the correct name for others to
 	// use, as the source file may have a different name.
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 1cec289..4a2f810 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -555,7 +555,8 @@
 	}
 }
 
-func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	// Because generated outputs are checked by client modules(e.g. cc_library, ...)
 	// we can safely ignore the check here.
 	return nil
diff --git a/java/aar.go b/java/aar.go
index 667dd9d..9cab0bd 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -189,7 +189,7 @@
 
 	// Version code
 	if !hasVersionCode {
-		linkFlags = append(linkFlags, "--version-code", ctx.Config().PlatformSdkVersion())
+		linkFlags = append(linkFlags, "--version-code", ctx.Config().PlatformSdkVersion().String())
 	}
 
 	if !hasVersionName {
@@ -774,7 +774,8 @@
 	return a.depIsInSameApex(ctx, dep)
 }
 
-func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	return nil
 }
 
diff --git a/java/app.go b/java/app.go
index ae7373f..13d08b9 100755
--- a/java/app.go
+++ b/java/app.go
@@ -157,7 +157,7 @@
 				"abis":              strings.Join(SupportedAbis(ctx), ","),
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
 				"screen-densities":  screenDensities,
-				"sdk-version":       ctx.Config().PlatformSdkVersion(),
+				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
 				"stem":              as.BaseModuleName(),
 				"apkcerts":          as.apkcertsFile.String(),
 				"partition":         as.PartitionTag(ctx.DeviceConfig()),
@@ -436,7 +436,7 @@
 
 		if minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx); err == nil {
 			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
-			android.CheckMinSdkVersion(a, ctx, int(minSdkVersion))
+			android.CheckMinSdkVersion(a, ctx, minSdkVersion.ApiLevel(ctx))
 		} else {
 			ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
 		}
@@ -678,7 +678,7 @@
 		seenModules[child] = true
 
 		// Skip host modules.
-		if child.Target().Os.Class == android.Host || child.Target().Os.Class == android.HostCross {
+		if child.Target().Os.Class == android.Host {
 			return false
 		}
 
@@ -1637,7 +1637,8 @@
 	return sdkSpecFrom("")
 }
 
-func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
 	return nil
 }
diff --git a/java/app_test.go b/java/app_test.go
index 5367971..4347db8 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1078,6 +1078,7 @@
 		platformSdkFinal      bool
 		expectedMinSdkVersion string
 		platformApis          bool
+		activeCodenames       []string
 	}{
 		{
 			name:                  "current final SDK",
@@ -1094,6 +1095,7 @@
 			platformSdkCodename:   "OMR1",
 			platformSdkFinal:      false,
 			expectedMinSdkVersion: "OMR1",
+			activeCodenames:       []string{"OMR1"},
 		},
 		{
 			name:                  "default final SDK",
@@ -1112,11 +1114,14 @@
 			platformSdkCodename:   "OMR1",
 			platformSdkFinal:      false,
 			expectedMinSdkVersion: "OMR1",
+			activeCodenames:       []string{"OMR1"},
 		},
 		{
 			name:                  "14",
 			sdkVersion:            "14",
 			expectedMinSdkVersion: "14",
+			platformSdkCodename:   "S",
+			activeCodenames:       []string{"S"},
 		},
 	}
 
@@ -1137,6 +1142,7 @@
 				config := testAppConfig(nil, bp, nil)
 				config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
 				config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
+				config.TestProductVariables.Platform_version_active_codenames = test.activeCodenames
 				config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
 				checkSdkVersion(t, config, test.expectedMinSdkVersion)
 
@@ -1179,15 +1185,6 @@
 			platformSdkInt:                        29,
 			platformSdkCodename:                   "Q",
 			platformSdkFinal:                      false,
-			deviceCurrentApiLevelForVendorModules: "current",
-			expectedMinSdkVersion:                 "Q",
-		},
-		{
-			name:                                  "current final SDK",
-			sdkVersion:                            "current",
-			platformSdkInt:                        29,
-			platformSdkCodename:                   "Q",
-			platformSdkFinal:                      false,
 			deviceCurrentApiLevelForVendorModules: "28",
 			expectedMinSdkVersion:                 "28",
 		},
diff --git a/java/dex.go b/java/dex.go
index 21a5926..055d479 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -135,6 +135,7 @@
 		"$r8Template": &remoteexec.REParams{
 			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
 			Inputs:          []string{"$implicits", "${config.R8Jar}"},
+			OutputFiles:     []string{"${outUsage}"},
 			ExecStrategy:    "${config.RER8ExecStrategy}",
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
diff --git a/java/droiddoc.go b/java/droiddoc.go
index e39a556..33f422d 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -253,6 +253,10 @@
 	// if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
 	Create_doc_stubs *bool
 
+	// if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
+	// Has no effect if create_doc_stubs: true.
+	Output_javadoc_comments *bool
+
 	// if set to false then do not write out stubs. Defaults to true.
 	//
 	// TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
@@ -1150,7 +1154,9 @@
 			cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
 		} else {
 			cmd.FlagWithArg("--stubs ", stubsDir.String())
-			cmd.Flag("--exclude-documentation-from-stubs")
+			if !Bool(d.properties.Output_javadoc_comments) {
+				cmd.Flag("--exclude-documentation-from-stubs")
+			}
 		}
 	}
 }
@@ -1228,7 +1234,7 @@
 
 	cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
 	cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
-	cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion())
+	cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
 	cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
 
 	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
@@ -1298,6 +1304,7 @@
 
 	cmd.BuiltTool(ctx, "metalava").
 		Flag(config.JavacVmFlags).
+		Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithArg("-source ", javaVersion.String()).
 		FlagWithRspFileInputList("@", srcs).
@@ -1411,9 +1418,7 @@
 
 		// TODO(b/154317059): Clean up this whitelist by baselining and/or checking in last-released.
 		if d.Name() != "android.car-system-stubs-docs" &&
-			d.Name() != "android.car-stubs-docs" &&
-			d.Name() != "system-api-stubs-docs" &&
-			d.Name() != "test-api-stubs-docs" {
+			d.Name() != "android.car-stubs-docs" {
 			cmd.Flag("--lints-as-errors")
 			cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
 		}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index b6af3bf..61a9b97 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -92,31 +92,34 @@
 // stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
 // modules.
 func stubFlagsRule(ctx android.SingletonContext) {
-	// Public API stubs
-	publicStubModules := []string{
-		"android_stubs_current",
+	var publicStubModules []string
+	var systemStubModules []string
+	var testStubModules []string
+	var corePlatformStubModules []string
+
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
+		// Build configuration mandates using prebuilt stub modules
+		publicStubModules = append(publicStubModules, "sdk_public_current_android")
+		systemStubModules = append(systemStubModules, "sdk_system_current_android")
+		testStubModules = append(testStubModules, "sdk_test_current_android")
+	} else {
+		// Use stub modules built from source
+		publicStubModules = append(publicStubModules, "android_stubs_current")
+		systemStubModules = append(systemStubModules, "android_system_stubs_current")
+		testStubModules = append(testStubModules, "android_test_stubs_current")
 	}
+	// We do not have prebuilts of the core platform api yet
+	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
 
 	// Add the android.test.base to the set of stubs only if the android.test.base module is on
 	// the boot jars list as the runtime will only enforce hiddenapi access against modules on
 	// that list.
-	if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().AlwaysUsePrebuiltSdks() {
-		publicStubModules = append(publicStubModules, "android.test.base.stubs")
-	}
-
-	// System API stubs
-	systemStubModules := []string{
-		"android_system_stubs_current",
-	}
-
-	// Test API stubs
-	testStubModules := []string{
-		"android_test_stubs_current",
-	}
-
-	// Core Platform API stubs
-	corePlatformStubModules := []string{
-		"legacy.core.platform.api.stubs",
+	if inList("android.test.base", ctx.Config().BootJars()) {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
+			publicStubModules = append(publicStubModules, "sdk_public_current_android.test.base")
+		} else {
+			publicStubModules = append(publicStubModules, "android.test.base.stubs")
+		}
 	}
 
 	// Allow products to define their own stubs for custom product jars that apps can use.
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index bcca93a..dbdab7a 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -16,8 +16,11 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 	"strings"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func testConfigWithBootJars(bp string, bootJars []string) android.Config {
@@ -32,19 +35,30 @@
 	return ctx
 }
 
-func testHiddenAPI(t *testing.T, bp string, bootJars []string) (*android.TestContext, android.Config) {
+func testHiddenAPIWithConfig(t *testing.T, config android.Config) *android.TestContext {
 	t.Helper()
 
-	config := testConfigWithBootJars(bp, bootJars)
 	ctx := testContextWithHiddenAPI()
 
 	run(t, ctx, config)
+	return ctx
+}
 
-	return ctx, config
+func testHiddenAPIBootJars(t *testing.T, bp string, bootJars []string) (*android.TestContext, android.Config) {
+	config := testConfigWithBootJars(bp, bootJars)
+
+	return testHiddenAPIWithConfig(t, config), config
+}
+
+func testHiddenAPIUnbundled(t *testing.T, unbundled bool) (*android.TestContext, android.Config) {
+	config := testConfig(nil, ``, nil)
+	config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(unbundled)
+
+	return testHiddenAPIWithConfig(t, config), config
 }
 
 func TestHiddenAPISingleton(t *testing.T) {
-	ctx, _ := testHiddenAPI(t, `
+	ctx, _ := testHiddenAPIBootJars(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -61,7 +75,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
-	ctx, _ := testHiddenAPI(t, `
+	ctx, _ := testHiddenAPIBootJars(t, `
 		java_import {
 			name: "foo",
 			jars: ["a.jar"],
@@ -78,7 +92,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
-	ctx, _ := testHiddenAPI(t, `
+	ctx, _ := testHiddenAPIBootJars(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -107,7 +121,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
-	ctx, _ := testHiddenAPI(t, `
+	ctx, _ := testHiddenAPIBootJars(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -134,3 +148,72 @@
 		t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command)
 	}
 }
+
+func TestHiddenAPISingletonSdks(t *testing.T) {
+	testCases := []struct {
+		name             string
+		unbundledBuild   bool
+		publicStub       string
+		systemStub       string
+		testStub         string
+		corePlatformStub string
+	}{
+		{
+			name:             "testBundled",
+			unbundledBuild:   false,
+			publicStub:       "android_stubs_current",
+			systemStub:       "android_system_stubs_current",
+			testStub:         "android_test_stubs_current",
+			corePlatformStub: "legacy.core.platform.api.stubs",
+		}, {
+			name:             "testUnbundled",
+			unbundledBuild:   true,
+			publicStub:       "sdk_public_current_android",
+			systemStub:       "sdk_system_current_android",
+			testStub:         "sdk_test_current_android",
+			corePlatformStub: "legacy.core.platform.api.stubs",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx, _ := testHiddenAPIUnbundled(t, tc.unbundledBuild)
+
+			hiddenAPI := ctx.SingletonForTests("hiddenapi")
+			hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantPublicStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantPublicStubs, hiddenapiRule.RuleParams.Command)
+			}
+
+			wantSystemStubs := "--system-stub-classpath=" + generateSdkDexPath(tc.systemStub, tc.unbundledBuild)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantSystemStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantSystemStubs, hiddenapiRule.RuleParams.Command)
+			}
+
+			wantTestStubs := "--test-stub-classpath=" + generateSdkDexPath(tc.testStub, tc.unbundledBuild)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantTestStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantTestStubs, hiddenapiRule.RuleParams.Command)
+			}
+
+			wantCorePlatformStubs := "--core-platform-stub-classpath=" + generateDexPath(tc.corePlatformStub)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantCorePlatformStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantCorePlatformStubs, hiddenapiRule.RuleParams.Command)
+			}
+		})
+	}
+}
+
+func generateDexedPath(subDir, dex, module string) string {
+	return fmt.Sprintf("%s/.intermediates/%s/android_common/%s/%s.jar", buildDir, subDir, dex, module)
+}
+
+func generateDexPath(module string) string {
+	return generateDexedPath(module, "dex", module)
+}
+
+func generateSdkDexPath(module string, unbundled bool) string {
+	if unbundled {
+		return generateDexedPath("prebuilts/sdk/"+module, "dex", module)
+	}
+	return generateDexPath(module)
+}
diff --git a/java/java.go b/java/java.go
index d67e9e0..1d7eaa7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1658,7 +1658,7 @@
 			if v := sdkSpec.version; v.isNumbered() {
 				return v.String()
 			} else {
-				return ctx.Config().DefaultAppTargetSdk()
+				return ctx.Config().DefaultAppTargetSdk(ctx).String()
 			}
 		}
 
@@ -1876,7 +1876,8 @@
 	return j.depIsInSameApex(ctx, dep)
 }
 
-func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	sdkSpec := j.minSdkVersion()
 	if !sdkSpec.specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
@@ -1888,7 +1889,7 @@
 	if err != nil {
 		return err
 	}
-	if int(ver) > sdkVersion {
+	if ver.ApiLevel(ctx).GreaterThan(sdkVersion) {
 		return fmt.Errorf("newer SDK(%v)", ver)
 	}
 	return nil
@@ -2753,7 +2754,8 @@
 	return j.depIsInSameApex(ctx, dep)
 }
 
-func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
 	return nil
 }
@@ -2936,7 +2938,8 @@
 	return j.dexJarFile
 }
 
-func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	// we don't check prebuilt modules for sdk_version
 	return nil
 }
diff --git a/java/java_test.go b/java/java_test.go
index 9e63577..f16639a 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1487,6 +1487,12 @@
 			libs: ["foo"],
 			sdk_version: "system_29",
 		}
+		java_library {
+			name: "baz-module-30",
+			srcs: ["c.java"],
+			libs: ["foo"],
+			sdk_version: "module_30",
+		}
 		`)
 
 	// check the existence of the internal modules
@@ -1533,6 +1539,13 @@
 			"prebuilts/sdk/29/system/foo.jar")
 	}
 
+	bazModule30Javac := ctx.ModuleForTests("baz-module-30", "android_common").Rule("javac")
+	// tests if "baz-module-30" is actually linked to the module 30 stubs lib
+	if !strings.Contains(bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar") {
+		t.Errorf("baz-module-30 javac classpath %v does not contain %q", bazModule30Javac.Args["classpath"],
+			"prebuilts/sdk/30/module-lib/foo.jar")
+	}
+
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := ctx.ModuleForTests("qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
diff --git a/java/robolectric.go b/java/robolectric.go
index ec112bc..04fc117 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -31,7 +31,6 @@
 }
 
 var robolectricDefaultLibs = []string{
-	"robolectric_android-all-stub",
 	"Robolectric_all-target",
 	"mockito-robolectric-prebuilt",
 	"truth-prebuilt",
@@ -99,7 +98,8 @@
 
 	ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
 
-	ctx.AddVariationDependencies(nil, roboRuntimesTag, "robolectric-android-all-prebuilts")
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
+		roboRuntimesTag, "robolectric-android-all-prebuilts")
 }
 
 func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -335,7 +335,7 @@
 func robolectricRuntimesFactory() android.Module {
 	module := &robolectricRuntimes{}
 	module.AddProperties(&module.props)
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibCommon)
 	return module
 }
 
@@ -365,6 +365,10 @@
 }
 
 func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if ctx.Target().Os != ctx.Config().BuildOSCommonTarget.Os {
+		return
+	}
+
 	files := android.PathsForModuleSrc(ctx, r.props.Jars)
 
 	androidAllDir := android.PathForModuleInstall(ctx, "android-all")
diff --git a/java/sdk.go b/java/sdk.go
index 56fa12b..971791f 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -107,7 +107,7 @@
 
 const (
 	// special version number for a not-yet-frozen SDK
-	sdkVersionCurrent sdkVersion = sdkVersion(android.FutureApiLevel)
+	sdkVersionCurrent sdkVersion = sdkVersion(android.FutureApiLevelInt)
 	// special version number to be used for SDK specs where version number doesn't
 	// make sense, e.g. "none", "", etc.
 	sdkVersionNone sdkVersion = sdkVersion(0)
@@ -133,6 +133,10 @@
 	return "(no version)"
 }
 
+func (v sdkVersion) ApiLevel(ctx android.EarlyModuleContext) android.ApiLevel {
+	return android.ApiLevelOrPanic(ctx, v.String())
+}
+
 // asNumberString directly converts the numeric value of this sdk version as a string.
 // When isNumbered() is true, this method is the same as String(). However, for sdkVersionCurrent
 // and sdkVersionNone, this returns 10000 and 0 while String() returns "current" and "(no version"),
@@ -218,7 +222,7 @@
 		return ctx.Config().AlwaysUsePrebuiltSdks()
 	} else if s.version.isNumbered() {
 		// validation check
-		if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest {
+		if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest && s.kind != sdkModule {
 			panic(fmt.Errorf("prebuilt SDK is not not available for sdkKind=%q", s.kind))
 			return false
 		}
@@ -243,7 +247,7 @@
 	if s.version.isNumbered() {
 		return s.version, nil
 	}
-	return sdkVersion(ctx.Config().DefaultAppTargetSdkInt()), nil
+	return sdkVersion(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt()), nil
 }
 
 // effectiveVersionString converts an sdkSpec into the concrete version string that the module
@@ -251,8 +255,8 @@
 // it returns the codename (P, Q, R, etc.)
 func (s sdkSpec) effectiveVersionString(ctx android.EarlyModuleContext) (string, error) {
 	ver, err := s.effectiveVersion(ctx)
-	if err == nil && int(ver) == ctx.Config().DefaultAppTargetSdkInt() {
-		return ctx.Config().DefaultAppTargetSdk(), nil
+	if err == nil && int(ver) == ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt() {
+		return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
 	}
 	return ver.String(), err
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 1a5ef54..60924a6 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -589,6 +589,9 @@
 	// An Android shared library is one that can be referenced in a <uses-library> element
 	// in an AndroidManifest.xml.
 	Shared_library *bool
+
+	// Files containing information about supported java doc tags.
+	Doctag_files []string `android:"path"`
 }
 
 // Common code between sdk library and sdk library import
@@ -601,6 +604,9 @@
 
 	commonSdkLibraryProperties commonToSdkLibraryAndImportProperties
 
+	// Paths to commonSdkLibraryProperties.Doctag_files
+	doctagPaths android.Paths
+
 	// Functionality related to this being used as a component of a java_sdk_library.
 	EmbeddableSdkLibraryComponent
 }
@@ -633,6 +639,10 @@
 	return true
 }
 
+func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) {
+	c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files)
+}
+
 // Module name of the runtime implementation library
 func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
 	return c.moduleBase.BaseModuleName() + ".impl"
@@ -732,6 +742,14 @@
 		}
 
 	} else {
+		switch tag {
+		case ".doctags":
+			if c.doctagPaths != nil {
+				return c.doctagPaths, nil
+			} else {
+				return nil, fmt.Errorf("no doctag_files specified on %s", c.moduleBase.BaseModuleName())
+			}
+		}
 		return nil, nil
 	}
 }
@@ -1014,6 +1032,8 @@
 }
 
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	module.generateCommonBuildActions(ctx)
+
 	// Only build an implementation library if required.
 	if module.requiresRuntimeImplementationLibrary() {
 		module.Library.GenerateAndroidBuildActions(ctx)
@@ -1203,6 +1223,7 @@
 		Sdk_version                      *string
 		System_modules                   *string
 		Libs                             []string
+		Output_javadoc_comments          *bool
 		Arg_files                        []string
 		Args                             *string
 		Java_version                     *string
@@ -1278,6 +1299,11 @@
 	}
 	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
 
+	// Output Javadoc comments for public scope.
+	if apiScope == apiScopePublic {
+		props.Output_javadoc_comments = proptools.BoolPtr(true)
+	}
+
 	// Add in scope specific arguments.
 	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
 	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
@@ -1878,7 +1904,8 @@
 	return false
 }
 
-func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	// we don't check prebuilt modules for sdk_version
 	return nil
 }
@@ -1888,6 +1915,8 @@
 }
 
 func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	module.generateCommonBuildActions(ctx)
+
 	// Record the paths to the prebuilt stubs library and stubs source.
 	ctx.VisitDirectDeps(func(to android.Module) {
 		tag := ctx.OtherModuleDependencyTag(to)
@@ -2078,7 +2107,8 @@
 	// do nothing
 }
 
-func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
 	return nil
 }
@@ -2179,6 +2209,9 @@
 	// True if the java_sdk_library_import is for a shared library, false
 	// otherwise.
 	Shared_library *bool
+
+	// The paths to the doctag files to add to the prebuilt.
+	Doctag_paths android.Paths
 }
 
 type scopeProperties struct {
@@ -2218,6 +2251,7 @@
 	s.Libs = sdk.properties.Libs
 	s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme
 	s.Shared_library = proptools.BoolPtr(sdk.sharedLibrary())
+	s.Doctag_paths = sdk.doctagPaths
 }
 
 func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -2266,6 +2300,16 @@
 		}
 	}
 
+	if len(s.Doctag_paths) > 0 {
+		dests := []string{}
+		for _, p := range s.Doctag_paths {
+			dest := filepath.Join("doctags", p.Rel())
+			ctx.SnapshotBuilder().CopyToSnapshot(p, dest)
+			dests = append(dests, dest)
+		}
+		propertySet.AddProperty("doctag_files", dests)
+	}
+
 	if len(s.Libs) > 0 {
 		propertySet.AddPropertyWithTag("libs", s.Libs, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(false))
 	}
diff --git a/java/testing.go b/java/testing.go
index 322dc9e..461fd3f 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -55,6 +55,8 @@
 		"prebuilts/sdk/30/public/framework.aidl":                   nil,
 		"prebuilts/sdk/30/system/android.jar":                      nil,
 		"prebuilts/sdk/30/system/foo.jar":                          nil,
+		"prebuilts/sdk/30/module-lib/android.jar":                  nil,
+		"prebuilts/sdk/30/module-lib/foo.jar":                      nil,
 		"prebuilts/sdk/30/public/core-for-system-modules.jar":      nil,
 		"prebuilts/sdk/current/core/android.jar":                   nil,
 		"prebuilts/sdk/current/public/android.jar":                 nil,
@@ -88,7 +90,7 @@
 		"prebuilts/sdk/30/system/api/bar-removed.txt":              nil,
 		"prebuilts/sdk/30/test/api/bar-removed.txt":                nil,
 		"prebuilts/sdk/tools/core-lambda-stubs.jar":                nil,
-		"prebuilts/sdk/Android.bp":                                 []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"],}`),
+		"prebuilts/sdk/Android.bp":                                 []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"], imports_sdk_version: "none", imports_compile_dex:true,}`),
 
 		"bin.py": nil,
 		python.StubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%'
diff --git a/python/androidmk.go b/python/androidmk.go
index 247b80d..8ad5889 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -15,11 +15,12 @@
 package python
 
 import (
-	"android/soong/android"
 	"fmt"
 	"io"
 	"path/filepath"
 	"strings"
+
+	"android/soong/android"
 )
 
 type subAndroidMkProvider interface {
@@ -74,6 +75,11 @@
 		if !BoolDefault(p.binaryProperties.Auto_gen_config, true) {
 			fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
 		}
+
+		if len(p.data) > 0 {
+			fmt.Fprintln(w, "LOCAL_TEST_DATA :=",
+				strings.Join(android.AndroidMkDataPaths(p.data), " "))
+		}
 	})
 	base.subAndroidMk(ret, p.binaryDecorator.pythonInstaller)
 }
diff --git a/python/binary.go b/python/binary.go
index 5a74926..1d2400e 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -79,7 +79,7 @@
 }
 
 func PythonBinaryHostFactory() android.Module {
-	module, _ := NewBinary(android.HostSupportedNoCross)
+	module, _ := NewBinary(android.HostSupported)
 
 	return module.Init()
 }
diff --git a/python/library.go b/python/library.go
index 65c1352..0c8d613 100644
--- a/python/library.go
+++ b/python/library.go
@@ -26,7 +26,7 @@
 }
 
 func PythonLibraryHostFactory() android.Module {
-	module := newModule(android.HostSupportedNoCross, android.MultilibFirst)
+	module := newModule(android.HostSupported, android.MultilibFirst)
 
 	return module.Init()
 }
diff --git a/python/test.go b/python/test.go
index a669c73..434e71a 100644
--- a/python/test.go
+++ b/python/test.go
@@ -34,6 +34,10 @@
 	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
 	// should be installed with the module.
 	Test_config_template *string `android:"path,arch_variant"`
+
+	// list of files or filegroup modules that provide data that should be installed alongside
+	// the test
+	Data []string `android:"path,arch_variant"`
 }
 
 type testDecorator struct {
@@ -42,6 +46,8 @@
 	testProperties TestProperties
 
 	testConfig android.Path
+
+	data []android.DataPath
 }
 
 func (test *testDecorator) bootstrapperProps() []interface{} {
@@ -59,6 +65,12 @@
 	test.binaryDecorator.pythonInstaller.relative = ctx.ModuleName()
 
 	test.binaryDecorator.pythonInstaller.install(ctx, file)
+
+	dataSrcPaths := android.PathsForModuleSrc(ctx, test.testProperties.Data)
+
+	for _, dataSrcPath := range dataSrcPaths {
+		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+	}
 }
 
 func NewTest(hod android.HostOrDeviceSupported) *Module {
diff --git a/rust/androidmk.go b/rust/androidmk.go
index edae0e6..5a33f77 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -178,6 +178,10 @@
 }
 
 func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	if compiler.path == (android.InstallPath{}) {
+		return
+	}
+
 	var unstrippedOutputFile android.OptionalPath
 	// Soong installation is only supported for host modules. Have Make
 	// installation trigger Soong installation.
diff --git a/rust/binary.go b/rust/binary.go
index 1d02453..af39d38 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -24,6 +24,16 @@
 }
 
 type BinaryCompilerProperties struct {
+	// Change the rustlibs linkage to select rlib linkage by default for device targets.
+	// Also link libstd as an rlib as well on device targets.
+	// Note: This is the default behavior for host targets.
+	Prefer_rlib *bool `android:"arch_variant"`
+
+	// Builds this binary as a static binary. Implies prefer_rlib true.
+	//
+	// Static executables currently only support for bionic targets. Non-bionic targets will not produce a fully static
+	// binary, but will still implicitly imply prefer_rlib true.
+	Static_executable *bool `android:"arch_variant"`
 }
 
 type binaryDecorator struct {
@@ -68,6 +78,11 @@
 			"-Wl,--gc-sections",
 			"-Wl,-z,nocopyreloc",
 			"-Wl,--no-undefined-version")
+
+		if Bool(binary.Properties.Static_executable) {
+			flags.LinkFlags = append(flags.LinkFlags, "-static")
+			flags.RustFlags = append(flags.RustFlags, "-C relocation-model=static")
+		}
 	}
 
 	return flags
@@ -77,8 +92,12 @@
 	deps = binary.baseCompiler.compilerDeps(ctx, deps)
 
 	if ctx.toolchain().Bionic() {
-		deps = bionicDeps(deps)
-		deps.CrtBegin = "crtbegin_dynamic"
+		deps = bionicDeps(deps, Bool(binary.Properties.Static_executable))
+		if Bool(binary.Properties.Static_executable) {
+			deps.CrtBegin = "crtbegin_static"
+		} else {
+			deps.CrtBegin = "crtbegin_dynamic"
+		}
 		deps.CrtEnd = "crtend_android"
 	}
 
@@ -95,6 +114,10 @@
 	return true
 }
 
+func (binary *binaryDecorator) preferRlib() bool {
+	return Bool(binary.Properties.Prefer_rlib) || Bool(binary.Properties.Static_executable)
+}
+
 func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
 	srcPath, _ := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
@@ -131,9 +154,19 @@
 
 func (binary *binaryDecorator) autoDep(ctx BaseModuleContext) autoDep {
 	// Binaries default to dylib dependencies for device, rlib for host.
+	if binary.preferRlib() {
+		return rlibAutoDep
+	}
 	if ctx.Device() {
 		return dylibAutoDep
 	} else {
 		return rlibAutoDep
 	}
 }
+
+func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+	if binary.preferRlib() {
+		return RlibLinkage
+	}
+	return binary.baseCompiler.stdLinkage(ctx)
+}
diff --git a/rust/binary_test.go b/rust/binary_test.go
index cfef57a..b44a5bc 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -30,6 +30,13 @@
 			rustlibs: ["libfoo"],
 			host_supported: true,
 		}
+		rust_binary {
+			name: "rlib_linked",
+			srcs: ["foo.rs"],
+			rustlibs: ["libfoo"],
+			host_supported: true,
+			prefer_rlib: true,
+		}
 		rust_library {
 			name: "libfoo",
 			srcs: ["foo.rs"],
@@ -40,7 +47,7 @@
 	fizzBuzzHost := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
 	fizzBuzzDevice := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
 
-	if !android.InList("libfoo", fizzBuzzHost.Properties.AndroidMkRlibs) {
+	if !android.InList("libfoo.rlib-std", fizzBuzzHost.Properties.AndroidMkRlibs) {
 		t.Errorf("rustlibs dependency libfoo should be an rlib dep for host modules")
 	}
 
@@ -49,6 +56,34 @@
 	}
 }
 
+// Test that prefer_rlib links in libstd statically as well as rustlibs.
+func TestBinaryPreferRlib(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "rlib_linked",
+			srcs: ["foo.rs"],
+			rustlibs: ["libfoo"],
+			host_supported: true,
+			prefer_rlib: true,
+		}
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			host_supported: true,
+		}`)
+
+	mod := ctx.ModuleForTests("rlib_linked", "android_arm64_armv8-a").Module().(*Module)
+
+	if !android.InList("libfoo.rlib-std", mod.Properties.AndroidMkRlibs) {
+		t.Errorf("rustlibs dependency libfoo should be an rlib dep when prefer_rlib is defined")
+	}
+
+	if !android.InList("libstd", mod.Properties.AndroidMkRlibs) {
+		t.Errorf("libstd dependency should be an rlib dep when prefer_rlib is defined")
+	}
+}
+
 // Test that the path returned by HostToolPath is correct
 func TestHostToolPath(t *testing.T) {
 	ctx := testRust(t, `
@@ -79,6 +114,34 @@
 	}
 }
 
+func TestStaticBinaryFlags(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz",
+			srcs: ["foo.rs"],
+			static_executable: true,
+		}`)
+
+	fizzOut := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Output("fizz")
+	fizzMod := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
+
+	flags := fizzOut.Args["rustcFlags"]
+	linkFlags := fizzOut.Args["linkFlags"]
+	if !strings.Contains(flags, "-C relocation-model=static") {
+		t.Errorf("static binary missing '-C relocation-model=static' in rustcFlags, found: %#v", flags)
+	}
+	if !strings.Contains(linkFlags, "-static") {
+		t.Errorf("static binary missing '-static' in linkFlags, found: %#v", flags)
+	}
+
+	if !android.InList("libc", fizzMod.Properties.AndroidMkStaticLibs) {
+		t.Errorf("static binary not linking against libc as a static library")
+	}
+	if len(fizzMod.Properties.AndroidMkSharedLibs) > 0 {
+		t.Errorf("static binary incorrectly linking against shared libraries")
+	}
+}
+
 func TestLinkObjects(t *testing.T) {
 	ctx := testRust(t, `
 		rust_binary {
diff --git a/rust/bindgen.go b/rust/bindgen.go
index cafdb8b..ac33ff7 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -21,6 +21,8 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/cc"
+	cc_config "android/soong/cc/config"
 )
 
 var (
@@ -56,38 +58,67 @@
 var _ SourceProvider = (*bindgenDecorator)(nil)
 
 type BindgenProperties struct {
-	// The wrapper header file
+	// The wrapper header file. By default this is assumed to be a C header unless the extension is ".hh" or ".hpp".
+	// This is used to specify how to interpret the header and determines which '-std' flag to use by default.
+	//
+	// If your C++ header must have some other extension, then the default behavior can be overridden by setting the
+	// cpp_std property.
 	Wrapper_src *string `android:"path,arch_variant"`
 
 	// list of bindgen-specific flags and options
 	Bindgen_flags []string `android:"arch_variant"`
 
-	// list of clang flags required to correctly interpret the headers.
-	Cflags []string `android:"arch_variant"`
-
-	// list of directories relative to the Blueprints file that will
-	// be added to the include path using -I
-	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
-
-	// list of static libraries that provide headers for this binding.
-	Static_libs []string `android:"arch_variant,variant_prepend"`
-
-	// list of shared libraries that provide headers for this binding.
-	Shared_libs []string `android:"arch_variant"`
-
 	// module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom
 	// binary must expect arguments in a similar fashion to bindgen, e.g.
 	//
 	// "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]"
 	Custom_bindgen string `android:"path"`
-
-	//TODO(b/161141999) Add support for headers from cc_library_header modules.
 }
 
 type bindgenDecorator struct {
 	*BaseSourceProvider
 
-	Properties BindgenProperties
+	Properties      BindgenProperties
+	ClangProperties cc.RustBindgenClangProperties
+}
+
+func (b *bindgenDecorator) getStdVersion(ctx ModuleContext, src android.Path) (string, bool) {
+	// Assume headers are C headers
+	isCpp := false
+	stdVersion := ""
+
+	switch src.Ext() {
+	case ".hpp", ".hh":
+		isCpp = true
+	}
+
+	if String(b.ClangProperties.Cpp_std) != "" && String(b.ClangProperties.C_std) != "" {
+		ctx.PropertyErrorf("c_std", "c_std and cpp_std cannot both be defined at the same time.")
+	}
+
+	if String(b.ClangProperties.Cpp_std) != "" {
+		if String(b.ClangProperties.Cpp_std) == "experimental" {
+			stdVersion = cc_config.ExperimentalCppStdVersion
+		} else if String(b.ClangProperties.Cpp_std) == "default" {
+			stdVersion = cc_config.CppStdVersion
+		} else {
+			stdVersion = String(b.ClangProperties.Cpp_std)
+		}
+	} else if b.ClangProperties.C_std != nil {
+		if String(b.ClangProperties.C_std) == "experimental" {
+			stdVersion = cc_config.ExperimentalCStdVersion
+		} else if String(b.ClangProperties.C_std) == "default" {
+			stdVersion = cc_config.CStdVersion
+		} else {
+			stdVersion = String(b.ClangProperties.C_std)
+		}
+	} else if isCpp {
+		stdVersion = cc_config.CppStdVersion
+	} else {
+		stdVersion = cc_config.CStdVersion
+	}
+
+	return stdVersion, isCpp
 }
 
 func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
@@ -120,8 +151,8 @@
 	esc := proptools.NinjaAndShellEscapeList
 
 	// Module defined clang flags and include paths
-	cflags = append(cflags, esc(b.Properties.Cflags)...)
-	for _, include := range b.Properties.Local_include_dirs {
+	cflags = append(cflags, esc(b.ClangProperties.Cflags)...)
+	for _, include := range b.ClangProperties.Local_include_dirs {
 		cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
 		implicits = append(implicits, android.PathForModuleSrc(ctx, include))
 	}
@@ -134,6 +165,19 @@
 		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
 	}
 
+	// Add C std version flag
+	stdVersion, isCpp := b.getStdVersion(ctx, wrapperFile.Path())
+	cflags = append(cflags, "-std="+stdVersion)
+
+	// Specify the header source language to avoid ambiguity.
+	if isCpp {
+		cflags = append(cflags, "-x c++")
+		// Add any C++ only flags.
+		cflags = append(cflags, esc(b.ClangProperties.Cppflags)...)
+	} else {
+		cflags = append(cflags, "-x c")
+	}
+
 	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
 	var cmd, cmdDesc string
@@ -164,12 +208,14 @@
 
 func (b *bindgenDecorator) SourceProviderProps() []interface{} {
 	return append(b.BaseSourceProvider.SourceProviderProps(),
-		&b.Properties)
+		&b.Properties, &b.ClangProperties)
 }
 
 // rust_bindgen generates Rust FFI bindings to C libraries using bindgen given a wrapper header as the primary input.
 // Bindgen has a number of flags to control the generated source, and additional flags can be passed to clang to ensure
-// the header and generated source is appropriately handled.
+// the header and generated source is appropriately handled. It is recommended to add it as a dependency in the
+// rlibs, dylibs or rustlibs property. It may also be added in the srcs property for external crates, using the ":"
+// prefix.
 func RustBindgenFactory() android.Module {
 	module, _ := NewRustBindgen(android.HostAndDeviceSupported)
 	return module.Init()
@@ -184,6 +230,7 @@
 	bindgen := &bindgenDecorator{
 		BaseSourceProvider: NewSourceProvider(),
 		Properties:         BindgenProperties{},
+		ClangProperties:    cc.RustBindgenClangProperties{},
 	}
 
 	module := NewSourceProviderModule(hod, bindgen, false)
@@ -194,10 +241,10 @@
 func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
-		deps = bionicDeps(deps)
+		deps = bionicDeps(deps, false)
 	}
 
-	deps.SharedLibs = append(deps.SharedLibs, b.Properties.Shared_libs...)
-	deps.StaticLibs = append(deps.StaticLibs, b.Properties.Static_libs...)
+	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
+	deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...)
 	return deps
 }
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 191da9b..9cccf13 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -23,6 +23,7 @@
 	ctx := testRust(t, `
 		rust_bindgen {
 			name: "libbindgen",
+			defaults: ["cc_defaults_flags"],
 			wrapper_src: "src/any.h",
 			crate_name: "bindgen",
 			stem: "libbindgen",
@@ -40,8 +41,12 @@
 			name: "libfoo_static",
 			export_include_dirs: ["static_include"],
 		}
+		cc_defaults {
+			name: "cc_defaults_flags",
+			cflags: ["--default-flag"],
+		}
 	`)
-	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a").Output("bindings.rs")
+	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
 	// Ensure that the flags are present and escaped
 	if !strings.Contains(libbindgen.Args["flags"], "'--bindgen-flag.*'") {
 		t.Errorf("missing bindgen flags in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
@@ -55,6 +60,9 @@
 	if !strings.Contains(libbindgen.Args["cflags"], "-Istatic_include") {
 		t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
 	}
+	if !strings.Contains(libbindgen.Args["cflags"], "--default-flag") {
+		t.Errorf("rust_bindgen missing cflags defined in cc_defaults: cflags %#v", libbindgen.Args["cflags"])
+	}
 }
 
 func TestRustBindgenCustomBindgen(t *testing.T) {
@@ -73,7 +81,7 @@
 		}
 	`)
 
-	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a").Output("bindings.rs")
+	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
 
 	// The rule description should contain the custom binary name rather than bindgen, so checking the description
 	// should be sufficient.
@@ -82,3 +90,47 @@
 			libbindgen.Description)
 	}
 }
+
+func TestRustBindgenStdVersions(t *testing.T) {
+	testRustError(t, "c_std and cpp_std cannot both be defined at the same time.", `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			c_std: "somevalue",
+			cpp_std: "somevalue",
+		}
+	`)
+
+	ctx := testRust(t, `
+		rust_bindgen {
+			name: "libbindgen_cstd",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			c_std: "foo"
+		}
+		rust_bindgen {
+			name: "libbindgen_cppstd",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			cpp_std: "foo"
+		}
+	`)
+
+	libbindgen_cstd := ctx.ModuleForTests("libbindgen_cstd", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgen_cppstd := ctx.ModuleForTests("libbindgen_cppstd", "android_arm64_armv8-a_source").Output("bindings.rs")
+
+	if !strings.Contains(libbindgen_cstd.Args["cflags"], "-std=foo") {
+		t.Errorf("c_std value not passed in to rust_bindgen as a clang flag")
+	}
+
+	if !strings.Contains(libbindgen_cppstd.Args["cflags"], "-std=foo") {
+		t.Errorf("cpp_std value not passed in to rust_bindgen as a clang flag")
+	}
+}
diff --git a/rust/compiler.go b/rust/compiler.go
index 664578d..8d2f09c 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -24,6 +24,14 @@
 	"android/soong/rust/config"
 )
 
+type RustLinkage int
+
+const (
+	DefaultLinkage RustLinkage = iota
+	RlibLinkage
+	DylibLinkage
+)
+
 func (compiler *baseCompiler) edition() string {
 	return proptools.StringDefault(compiler.Properties.Edition, config.DefaultEdition)
 }
@@ -146,8 +154,13 @@
 	panic("baseCompiler does not implement coverageOutputZipPath()")
 }
 
-func (compiler *baseCompiler) static() bool {
-	return false
+func (compiler *baseCompiler) stdLinkage(ctx *depsContext) RustLinkage {
+	// For devices, we always link stdlibs in as dylibs by default.
+	if ctx.Device() {
+		return DylibLinkage
+	} else {
+		return RlibLinkage
+	}
 }
 
 var _ compiler = (*baseCompiler)(nil)
@@ -221,25 +234,24 @@
 				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
 			}
 
-			// For devices, we always link stdlibs in as dylibs except for ffi static libraries.
-			// (rustc does not support linking libstd as a dylib for ffi static libraries)
-			if ctx.Host() {
-				deps.Rustlibs = append(deps.Rustlibs, stdlib)
-			} else if ctx.RustModule().compiler.static() {
-				deps.Rlibs = append(deps.Rlibs, stdlib)
-			} else {
-				deps.Dylibs = append(deps.Dylibs, stdlib)
-			}
+			deps.Stdlibs = append(deps.Stdlibs, stdlib)
 		}
 	}
 	return deps
 }
 
-func bionicDeps(deps Deps) Deps {
-	deps.SharedLibs = append(deps.SharedLibs, "liblog")
-	deps.SharedLibs = append(deps.SharedLibs, "libc")
-	deps.SharedLibs = append(deps.SharedLibs, "libm")
-	deps.SharedLibs = append(deps.SharedLibs, "libdl")
+func bionicDeps(deps Deps, static bool) Deps {
+	bionicLibs := []string{}
+	bionicLibs = append(bionicLibs, "liblog")
+	bionicLibs = append(bionicLibs, "libc")
+	bionicLibs = append(bionicLibs, "libm")
+	bionicLibs = append(bionicLibs, "libdl")
+
+	if static {
+		deps.StaticLibs = append(deps.StaticLibs, bionicLibs...)
+	} else {
+		deps.SharedLibs = append(deps.SharedLibs, bionicLibs...)
+	}
 
 	//TODO(b/141331117) libstd requires libgcc on Android
 	deps.StaticLibs = append(deps.StaticLibs, "libgcc")
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 56a8ef8..a25523c 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -191,7 +191,7 @@
 			crate_name: "foo",
 		}`)
 	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
-	fooRlib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib").Module().(*Module)
+	fooRlib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
 	fooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
 
 	if !android.InList("libstd", fizz.Properties.AndroidMkDylibs) {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 62d469e..483dddb 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -1,6 +1,10 @@
 package config
 
 var (
+	// When adding a new path below, add a rustfmt.toml file at the root of
+	// the repository and enable the rustfmt repo hook. See aosp/1347562
+	// for an example.
+	// TODO(b/160223496): enable rustfmt globally.
 	RustAllowedPaths = []string{
 		"external/minijail",
 		"external/rust",
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 7d13c42..21b22a4 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -28,6 +28,7 @@
 	Arm64ArchVariantRustFlags = map[string][]string{
 		"armv8-a":  []string{},
 		"armv8-2a": []string{},
+		"armv8-2a-dotprod": []string{},
 	}
 )
 
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
index ac4f1c6..adfe917 100644
--- a/rust/config/arm_device.go
+++ b/rust/config/arm_device.go
@@ -52,7 +52,7 @@
 }
 
 func (t *toolchainArm) RustTriple() string {
-	return "arm-linux-androideabi"
+	return "armv7-linux-androideabi"
 }
 
 func (t *toolchainArm) ToolchainLinkFlags() string {
diff --git a/rust/config/global.go b/rust/config/global.go
index 6a5251b..71c4240 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.45.2"
+	RustDefaultVersion = "1.46.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 73673d0..90155ca 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -154,12 +154,12 @@
 	}
 
 	// Make sure the expected inputs are provided to the zip rule.
-	if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
+	if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
 		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") ||
 		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_cov/fizz.gcno") {
 		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
 	}
-	if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
+	if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
 		!android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.dylib.gcno") {
 		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
 	}
diff --git a/rust/library.go b/rust/library.go
index a442933..3bba089 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -15,12 +15,18 @@
 package rust
 
 import (
+	"fmt"
 	"regexp"
 	"strings"
 
 	"android/soong/android"
 )
 
+var (
+	DylibStdlibSuffix = ".dylib-std"
+	RlibStdlibSuffix  = ".rlib-std"
+)
+
 func init() {
 	android.RegisterModuleType("rust_library", RustLibraryFactory)
 	android.RegisterModuleType("rust_library_dylib", RustLibraryDylibFactory)
@@ -49,6 +55,9 @@
 
 	// path to include directories to pass to cc_* modules, only relevant for static/shared variants.
 	Include_dirs []string `android:"path,arch_variant"`
+
+	// Whether this library is part of the Rust toolchain sysroot.
+	Sysroot *bool
 }
 
 type LibraryMutatedProperties struct {
@@ -69,10 +78,15 @@
 	VariantIsShared bool `blueprint:"mutated"`
 	// This variant is a static library
 	VariantIsStatic bool `blueprint:"mutated"`
+	// This variant is a source provider
+	VariantIsSource bool `blueprint:"mutated"`
 
 	// This variant is disabled and should not be compiled
 	// (used for SourceProvider variants that produce only source)
 	VariantIsDisabled bool `blueprint:"mutated"`
+
+	// Whether this library variant should be link libstd via rlibs
+	VariantIsStaticStd bool `blueprint:"mutated"`
 }
 
 type libraryDecorator struct {
@@ -91,6 +105,8 @@
 	dylib() bool
 	static() bool
 	shared() bool
+	sysroot() bool
+	source() bool
 
 	// Returns true if the build options for the module have selected a particular build type
 	buildRlib() bool
@@ -103,6 +119,11 @@
 	setDylib()
 	setShared()
 	setStatic()
+	setSource()
+
+	// Set libstd linkage
+	setRlibStd()
+	setDylibStd()
 
 	// Build a specific library variant
 	BuildOnlyFFI()
@@ -121,6 +142,10 @@
 	return library.MutatedProperties.VariantIsRlib
 }
 
+func (library *libraryDecorator) sysroot() bool {
+	return Bool(library.Properties.Sysroot)
+}
+
 func (library *libraryDecorator) dylib() bool {
 	return library.MutatedProperties.VariantIsDylib
 }
@@ -133,6 +158,18 @@
 	return library.MutatedProperties.VariantIsStatic
 }
 
+func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+	// libraries should only request the RlibLinkage when building a static FFI or when variant is StaticStd
+	if library.static() || library.MutatedProperties.VariantIsStaticStd {
+		return RlibLinkage
+	}
+	return DefaultLinkage
+}
+
+func (library *libraryDecorator) source() bool {
+	return library.MutatedProperties.VariantIsSource
+}
+
 func (library *libraryDecorator) buildRlib() bool {
 	return library.MutatedProperties.BuildRlib && BoolDefault(library.Properties.Rlib.Enabled, true)
 }
@@ -163,6 +200,14 @@
 	library.MutatedProperties.VariantIsShared = false
 }
 
+func (library *libraryDecorator) setRlibStd() {
+	library.MutatedProperties.VariantIsStaticStd = true
+}
+
+func (library *libraryDecorator) setDylibStd() {
+	library.MutatedProperties.VariantIsStaticStd = false
+}
+
 func (library *libraryDecorator) setShared() {
 	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = true
@@ -177,13 +222,17 @@
 	library.MutatedProperties.VariantIsDylib = false
 }
 
+func (library *libraryDecorator) setSource() {
+	library.MutatedProperties.VariantIsSource = true
+}
+
 func (library *libraryDecorator) autoDep(ctx BaseModuleContext) autoDep {
 	if library.rlib() || library.static() {
 		return rlibAutoDep
 	} else if library.dylib() || library.shared() {
 		return dylibAutoDep
 	} else {
-		panic("autoDep called on library" + ctx.ModuleName() + "that has no enabled variants.")
+		panic(fmt.Errorf("autoDep called on library %q that has no enabled variants.", ctx.ModuleName()))
 	}
 }
 
@@ -347,7 +396,7 @@
 	deps = library.baseCompiler.compilerDeps(ctx, deps)
 
 	if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
-		deps = bionicDeps(deps)
+		deps = bionicDeps(deps, false)
 		deps.CrtBegin = "crtbegin_so"
 		deps.CrtEnd = "crtend_so"
 	}
@@ -450,6 +499,13 @@
 	return stem + String(library.baseCompiler.Properties.Suffix)
 }
 
+func (library *libraryDecorator) install(ctx ModuleContext) {
+	// Only shared and dylib variants make sense to install.
+	if library.shared() || library.dylib() {
+		library.baseCompiler.install(ctx)
+	}
+}
+
 func (library *libraryDecorator) Disabled() bool {
 	return library.MutatedProperties.VariantIsDisabled
 }
@@ -478,39 +534,85 @@
 	}
 }
 
+// LibraryMutator mutates the libraries into variants according to the
+// build{Rlib,Dylib} attributes.
 func LibraryMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && m.compiler != nil {
+	// Only mutate on Rust libraries.
+	m, ok := mctx.Module().(*Module)
+	if !ok || m.compiler == nil {
+		return
+	}
+	library, ok := m.compiler.(libraryInterface)
+	if !ok {
+		return
+	}
+
+	var variants []string
+	// The source variant is used for SourceProvider modules. The other variants (i.e. rlib and dylib)
+	// depend on this variant. It must be the first variant to be declared.
+	sourceVariant := false
+	if m.sourceProvider != nil {
+		variants = append(variants, "source")
+		sourceVariant = true
+	}
+	if library.buildRlib() {
+		variants = append(variants, rlibVariation)
+	}
+	if library.buildDylib() {
+		variants = append(variants, dylibVariation)
+	}
+
+	if len(variants) == 0 {
+		return
+	}
+	modules := mctx.CreateLocalVariations(variants...)
+
+	// The order of the variations (modules) matches the variant names provided. Iterate
+	// through the new variation modules and set their mutated properties.
+	for i, v := range modules {
+		switch variants[i] {
+		case rlibVariation:
+			v.(*Module).compiler.(libraryInterface).setRlib()
+		case dylibVariation:
+			v.(*Module).compiler.(libraryInterface).setDylib()
+		case "source":
+			v.(*Module).compiler.(libraryInterface).setSource()
+			// The source variant does not produce any library.
+			// Disable the compilation steps.
+			v.(*Module).compiler.SetDisabled()
+		}
+	}
+
+	// If a source variant is created, add an inter-variant dependency
+	// between the other variants and the source variant.
+	if sourceVariant {
+		sv := modules[0]
+		for _, v := range modules[1:] {
+			if !v.Enabled() {
+				continue
+			}
+			mctx.AddInterVariantDependency(sourceDepTag, v, sv)
+		}
+		// Alias the source variation so it can be named directly in "srcs" properties.
+		mctx.AliasVariation("source")
+	}
+}
+
+func LibstdMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() {
 		switch library := m.compiler.(type) {
 		case libraryInterface:
-			if library.buildRlib() && library.buildDylib() {
-				variants := []string{"rlib", "dylib"}
-				if m.sourceProvider != nil {
-					variants = append(variants, "")
-				}
+			// Only create a variant if a library is actually being built.
+			if library.rlib() && !library.sysroot() {
+				variants := []string{"rlib-std", "dylib-std"}
 				modules := mctx.CreateLocalVariations(variants...)
 
 				rlib := modules[0].(*Module)
 				dylib := modules[1].(*Module)
-				rlib.compiler.(libraryInterface).setRlib()
-				dylib.compiler.(libraryInterface).setDylib()
-
-				if m.sourceProvider != nil {
-					// This library is SourceProvider generated, so the non-library-producing
-					// variant needs to disable it's compiler and skip installation.
-					sourceProvider := modules[2].(*Module)
-					sourceProvider.compiler.SetDisabled()
-				}
-			} else if library.buildRlib() {
-				modules := mctx.CreateLocalVariations("rlib")
-				modules[0].(*Module).compiler.(libraryInterface).setRlib()
-			} else if library.buildDylib() {
-				modules := mctx.CreateLocalVariations("dylib")
-				modules[0].(*Module).compiler.(libraryInterface).setDylib()
-			}
-
-			if m.sourceProvider != nil {
-				// Alias the non-library variant to the empty-string variant.
-				mctx.AliasVariation("")
+				rlib.compiler.(libraryInterface).setRlibStd()
+				dylib.compiler.(libraryInterface).setDylibStd()
+				rlib.Properties.SubName += RlibStdlibSuffix
+				dylib.Properties.SubName += DylibStdlibSuffix
 			}
 		}
 	}
diff --git a/rust/library_test.go b/rust/library_test.go
index f1bc050..fec3992 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -37,7 +37,7 @@
                 }`)
 
 	// Test all variants are being built.
-	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib").Output("libfoo.rlib")
+	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Output("libfoo.rlib")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
 	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Output("libfoo.ffi.a")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Output("libfoo.ffi.so")
@@ -182,14 +182,14 @@
                         rustlibs: ["libbar"],
                 }`)
 
-	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib")
+	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
 	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
 
 	for _, static := range []android.TestingModule{libfooRlib, libfooStatic} {
-		if !android.InList("libbar", static.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar not present as static dependency in static lib")
+		if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) {
+			t.Errorf("libbar not present as rlib dependency in static lib")
 		}
 		if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) {
 			t.Errorf("libbar present as dynamic dependency in static lib")
@@ -200,8 +200,8 @@
 		if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) {
 			t.Errorf("libbar not present as dynamic dependency in dynamic lib")
 		}
-		if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar present as static dependency in dynamic lib")
+		if android.InList("libbar.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
+			t.Errorf("libbar present as rlib dependency in dynamic lib")
 		}
 
 	}
@@ -238,3 +238,45 @@
 		t.Errorf("stripped version of bar has been generated")
 	}
 }
+
+func TestLibstdLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		rust_ffi {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			rustlibs: ["libfoo"],
+		}`)
+
+	libfooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+	libfooRlibStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
+	libfooRlibDynamic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
+
+	libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
+	libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module)
+
+	if !android.InList("libstd", libfooRlibStatic.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for device rust_library_rlib does not link libstd as an rlib")
+	}
+	if !android.InList("libstd", libfooRlibDynamic.Properties.AndroidMkDylibs) {
+		t.Errorf("dylib-std variant for device rust_library_rlib does not link libstd as an dylib")
+	}
+	if !android.InList("libstd", libfooDylib.Properties.AndroidMkDylibs) {
+		t.Errorf("Device rust_library_dylib does not link libstd as an dylib")
+	}
+
+	if !android.InList("libstd", libbarShared.Properties.AndroidMkDylibs) {
+		t.Errorf("Device rust_ffi_shared does not link libstd as an dylib")
+	}
+	if !android.InList("libstd", libbarStatic.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_static does not link libstd as an rlib")
+	}
+	if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
+	}
+}
diff --git a/rust/project_json.go b/rust/project_json.go
index 8310479..8d161b2 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -30,16 +30,6 @@
 //
 //   $ SOONG_GEN_RUST_PROJECT=1 m nothing
 
-func init() {
-	android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
-}
-
-func rustProjectGeneratorSingleton() android.Singleton {
-	return &projectGeneratorSingleton{}
-}
-
-type projectGeneratorSingleton struct{}
-
 const (
 	// Environment variables used to control the behavior of this singleton.
 	envVariableCollectRustDeps = "SOONG_GEN_RUST_PROJECT"
@@ -49,6 +39,7 @@
 // The format of rust-project.json is not yet finalized. A current description is available at:
 // https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc#non-cargo-based-projects
 type rustProjectDep struct {
+	// The Crate attribute is the index of the dependency in the Crates array in rustProjectJson.
 	Crate int    `json:"crate"`
 	Name  string `json:"name"`
 }
@@ -71,12 +62,50 @@
 	Deps map[string]int
 }
 
-func mergeDependencies(ctx android.SingletonContext, project *rustProjectJson,
-	knownCrates map[string]crateInfo, module android.Module,
-	crate *rustProjectCrate, deps map[string]int) {
+type projectGeneratorSingleton struct {
+	project     rustProjectJson
+	knownCrates map[string]crateInfo
+}
+
+func rustProjectGeneratorSingleton() android.Singleton {
+	return &projectGeneratorSingleton{}
+}
+
+func init() {
+	android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+}
+
+// librarySource finds the main source file (.rs) for a crate.
+func librarySource(ctx android.SingletonContext, rModule *Module, rustLib *libraryDecorator) (string, bool) {
+	srcs := rustLib.baseCompiler.Properties.Srcs
+	if len(srcs) != 0 {
+		return path.Join(ctx.ModuleDir(rModule), srcs[0]), true
+	}
+	if !rustLib.source() {
+		return "", false
+	}
+	// It is a SourceProvider module. If this module is host only, uses the variation for the host.
+	// Otherwise, use the variation for the primary target.
+	switch rModule.hod {
+	case android.HostSupported:
+	case android.HostSupportedNoCross:
+		if rModule.Target().String() != ctx.Config().BuildOSTarget.String() {
+			return "", false
+		}
+	default:
+		if rModule.Target().String() != ctx.Config().Targets[android.Android][0].String() {
+			return "", false
+		}
+	}
+	src := rustLib.sourceProvider.Srcs()[0]
+	return src.String(), true
+}
+
+func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext,
+	module android.Module, crate *rustProjectCrate, deps map[string]int) {
 
 	ctx.VisitDirectDeps(module, func(child android.Module) {
-		childId, childCrateName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
+		childId, childCrateName, ok := singleton.appendLibraryAndDeps(ctx, child)
 		if !ok {
 			return
 		}
@@ -88,12 +117,10 @@
 	})
 }
 
-// appendLibraryAndDeps creates a rustProjectCrate for the module argument and
-// appends it to the rustProjectJson struct.  It visits the dependencies of the
-// module depth-first. If the current module is already in knownCrates, its
-// dependencies are merged. Returns a tuple (id, crate_name, ok).
-func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson,
-	knownCrates map[string]crateInfo, module android.Module) (int, string, bool) {
+// appendLibraryAndDeps creates a rustProjectCrate for the module argument and appends it to singleton.project.
+// It visits the dependencies of the module depth-first so the dependency ID can be added to the current module. If the
+// current module is already in singleton.knownCrates, its dependencies are merged. Returns a tuple (id, crate_name, ok).
+func (singleton *projectGeneratorSingleton) appendLibraryAndDeps(ctx android.SingletonContext, module android.Module) (int, string, bool) {
 	rModule, ok := module.(*Module)
 	if !ok {
 		return 0, "", false
@@ -107,46 +134,45 @@
 	}
 	moduleName := ctx.ModuleName(module)
 	crateName := rModule.CrateName()
-	if cInfo, ok := knownCrates[moduleName]; ok {
+	if cInfo, ok := singleton.knownCrates[moduleName]; ok {
 		// We have seen this crate already; merge any new dependencies.
-		crate := project.Crates[cInfo.ID]
-		mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps)
-		project.Crates[cInfo.ID] = crate
+		crate := singleton.project.Crates[cInfo.ID]
+		singleton.mergeDependencies(ctx, module, &crate, cInfo.Deps)
+		singleton.project.Crates[cInfo.ID] = crate
 		return cInfo.ID, crateName, true
 	}
 	crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)}
-	srcs := rustLib.baseCompiler.Properties.Srcs
-	if len(srcs) == 0 {
+	rootModule, ok := librarySource(ctx, rModule, rustLib)
+	if !ok {
 		return 0, "", false
 	}
-	crate.RootModule = path.Join(ctx.ModuleDir(rModule), srcs[0])
+	crate.RootModule = rootModule
 	crate.Edition = rustLib.baseCompiler.edition()
 
 	deps := make(map[string]int)
-	mergeDependencies(ctx, project, knownCrates, module, &crate, deps)
+	singleton.mergeDependencies(ctx, module, &crate, deps)
 
-	id := len(project.Crates)
-	knownCrates[moduleName] = crateInfo{ID: id, Deps: deps}
-	project.Crates = append(project.Crates, crate)
+	id := len(singleton.project.Crates)
+	singleton.knownCrates[moduleName] = crateInfo{ID: id, Deps: deps}
+	singleton.project.Crates = append(singleton.project.Crates, crate)
 	// rust-analyzer requires that all crates belong to at least one root:
 	// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
-	project.Roots = append(project.Roots, path.Dir(crate.RootModule))
+	singleton.project.Roots = append(singleton.project.Roots, path.Dir(crate.RootModule))
 	return id, crateName, true
 }
 
-func (r *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	if !ctx.Config().IsEnvTrue(envVariableCollectRustDeps) {
 		return
 	}
 
-	project := rustProjectJson{}
-	knownCrates := make(map[string]crateInfo)
+	singleton.knownCrates = make(map[string]crateInfo)
 	ctx.VisitAllModules(func(module android.Module) {
-		appendLibraryAndDeps(ctx, &project, knownCrates, module)
+		singleton.appendLibraryAndDeps(ctx, module)
 	})
 
 	path := android.PathForOutput(ctx, rustProjectJsonFileName)
-	err := createJsonFile(project, path)
+	err := createJsonFile(singleton.project, path)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 8521940..11964f3 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -18,6 +18,7 @@
 	"encoding/json"
 	"io/ioutil"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -100,22 +101,51 @@
 	rust_library {
 		name: "liba",
 		srcs: ["src/lib.rs"],
-		rlibs: ["libbindings"],
+		rlibs: ["libbindings1"],
 		crate_name: "a"
 	}
 	rust_bindgen {
-		name: "libbindings",
-		crate_name: "bindings",
-		source_stem: "bindings",
+		name: "libbindings1",
+		crate_name: "bindings1",
+		source_stem: "bindings1",
 		host_supported: true,
 		wrapper_src: "src/any.h",
 	}
+	rust_library_host {
+		name: "libb",
+		srcs: ["src/lib.rs"],
+		rustlibs: ["libbindings2"],
+		crate_name: "b"
+	}
+	rust_bindgen_host {
+		name: "libbindings2",
+		crate_name: "bindings2",
+		source_stem: "bindings2",
+		wrapper_src: "src/any.h",
+	}
 	` + GatherRequiredDepsForTest()
 	fs := map[string][]byte{
 		"src/lib.rs": nil,
 	}
 	jsonContent := testProjectJson(t, bp, fs)
-	validateJsonCrates(t, jsonContent)
+	crates := validateJsonCrates(t, jsonContent)
+	for _, c := range crates {
+		crate, ok := c.(map[string]interface{})
+		if !ok {
+			t.Fatalf("Unexpected type for crate: %v", c)
+		}
+		rootModule, ok := crate["root_module"].(string)
+		if !ok {
+			t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
+		}
+		if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") {
+			t.Errorf("The source path for libbindings1 does not contain android_arm64, got %v", rootModule)
+		}
+		if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, android.BuildOs.String()) {
+			t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
+				rootModule, android.BuildOs.String())
+		}
+	}
 }
 
 func TestProjectJsonMultiVersion(t *testing.T) {
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index a9dbf39..bd11a5a 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -29,7 +29,7 @@
 		}
 	`)
 	// Check that there's a rule to generate the expected output
-	_ = ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Output("buf.rs")
+	_ = ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Output("buf.rs")
 
 	// Check that libprotobuf is added as a dependency.
 	librust_proto := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Module().(*Module)
diff --git a/rust/rust.go b/rust/rust.go
index b98992c..f7207aa 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -40,6 +40,7 @@
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
@@ -86,8 +87,7 @@
 	sourceProvider   SourceProvider
 	subAndroidMkOnce map[SubAndroidMkProvider]bool
 
-	outputFile    android.OptionalPath
-	generatedFile android.OptionalPath
+	outputFile android.OptionalPath
 }
 
 func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -237,6 +237,7 @@
 	Dylibs     []string
 	Rlibs      []string
 	Rustlibs   []string
+	Stdlibs    []string
 	ProcMacros []string
 	SharedLibs []string
 	StaticLibs []string
@@ -293,7 +294,7 @@
 	Disabled() bool
 	SetDisabled()
 
-	static() bool
+	stdLinkage(ctx *depsContext) RustLinkage
 }
 
 type exportedFlagsProducer interface {
@@ -386,6 +387,7 @@
 	module.AddProperties(props...)
 	module.AddProperties(
 		&BaseProperties{},
+		&BindgenProperties{},
 		&BaseCompilerProperties{},
 		&BinaryCompilerProperties{},
 		&LibraryCompilerProperties{},
@@ -394,6 +396,7 @@
 		&SourceProviderProperties{},
 		&TestProperties{},
 		&cc.CoverageProperties{},
+		&cc.RustBindgenClangProperties{},
 		&ClippyProperties{},
 	)
 
@@ -458,12 +461,20 @@
 	panic("SetBuildStubs not yet implemented for rust modules")
 }
 
-func (mod *Module) SetStubsVersions(string) {
-	panic("SetStubsVersions not yet implemented for rust modules")
+func (mod *Module) SetStubsVersion(string) {
+	panic("SetStubsVersion not yet implemented for rust modules")
 }
 
 func (mod *Module) StubsVersion() string {
-	panic("SetStubsVersions not yet implemented for rust modules")
+	panic("StubsVersion not yet implemented for rust modules")
+}
+
+func (mod *Module) SetAllStubsVersions([]string) {
+	panic("SetAllStubsVersions not yet implemented for rust modules")
+}
+
+func (mod *Module) AllStubsVersions() []string {
+	return nil
 }
 
 func (mod *Module) BuildStaticVariant() bool {
@@ -677,12 +688,25 @@
 		flags, deps = mod.clippy.flags(ctx, flags, deps)
 	}
 
-	// SourceProvider needs to call GenerateSource() before compiler calls compile() so it can provide the source.
-	// TODO(b/162588681) This shouldn't have to run for every variant.
+	// SourceProvider needs to call GenerateSource() before compiler calls
+	// compile() so it can provide the source. A SourceProvider has
+	// multiple variants (e.g. source, rlib, dylib). Only the "source"
+	// variant is responsible for effectively generating the source. The
+	// remaining variants relies on the "source" variant output.
 	if mod.sourceProvider != nil {
-		generatedFile := mod.sourceProvider.GenerateSource(ctx, deps)
-		mod.generatedFile = android.OptionalPathForPath(generatedFile)
-		mod.sourceProvider.setSubName(ctx.ModuleSubDir())
+		if mod.compiler.(libraryInterface).source() {
+			mod.sourceProvider.GenerateSource(ctx, deps)
+			mod.sourceProvider.setSubName(ctx.ModuleSubDir())
+			if lib, ok := mod.compiler.(*libraryDecorator); ok {
+				lib.flagExporter.linkDirs = nil
+				lib.flagExporter.linkObjects = nil
+				lib.flagExporter.depFlags = nil
+			}
+		} else {
+			sourceMod := actx.GetDirectDepWithTag(mod.Name(), sourceDepTag)
+			sourceLib := sourceMod.(*Module).compiler.(*libraryDecorator)
+			mod.sourceProvider.setOutputFile(sourceLib.sourceProvider.Srcs()[0])
+		}
 	}
 
 	if mod.compiler != nil && !mod.compiler.Disabled() {
@@ -733,6 +757,7 @@
 	dylibDepTag         = dependencyTag{name: "dylib", library: true}
 	procMacroDepTag     = dependencyTag{name: "procMacro", proc_macro: true}
 	testPerSrcDepTag    = dependencyTag{name: "rust_unit_tests"}
+	sourceDepTag        = dependencyTag{name: "source"}
 )
 
 type autoDep struct {
@@ -741,8 +766,10 @@
 }
 
 var (
-	rlibAutoDep  = autoDep{variation: "rlib", depTag: rlibDepTag}
-	dylibAutoDep = autoDep{variation: "dylib", depTag: dylibDepTag}
+	rlibVariation  = "rlib"
+	dylibVariation = "dylib"
+	rlibAutoDep    = autoDep{variation: rlibVariation, depTag: rlibDepTag}
+	dylibAutoDep   = autoDep{variation: dylibVariation, depTag: dylibDepTag}
 )
 
 type autoDeppable interface {
@@ -782,14 +809,15 @@
 				directDylibDeps = append(directDylibDeps, rustDep)
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, depName)
 			case rlibDepTag:
+
 				rlib, ok := rustDep.compiler.(libraryInterface)
 				if !ok || !rlib.rlib() {
-					ctx.ModuleErrorf("mod %q not an rlib library", depName)
+					ctx.ModuleErrorf("mod %q not an rlib library", depName+rustDep.Properties.SubName)
 					return
 				}
 				depPaths.coverageFiles = append(depPaths.coverageFiles, rustDep.CoverageFiles()...)
 				directRlibDeps = append(directRlibDeps, rustDep)
-				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName)
+				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName+rustDep.Properties.SubName)
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, depName)
@@ -976,23 +1004,50 @@
 		commonDepVariations = append(commonDepVariations,
 			blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 	}
+
+	stdLinkage := "dylib-std"
+	if mod.compiler.stdLinkage(ctx) == RlibLinkage {
+		stdLinkage = "rlib-std"
+	}
+
+	rlibDepVariations := commonDepVariations
+	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
+		rlibDepVariations = append(rlibDepVariations,
+			blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage})
+	}
+
 	actx.AddVariationDependencies(
-		append(commonDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: "rlib"}}...),
+		append(rlibDepVariations, []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: rlibVariation}}...),
 		rlibDepTag, deps.Rlibs...)
 	actx.AddVariationDependencies(
 		append(commonDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: "dylib"}}...),
+			{Mutator: "rust_libraries", Variation: dylibVariation}}...),
 		dylibDepTag, deps.Dylibs...)
 
 	if deps.Rustlibs != nil && !mod.compiler.Disabled() {
 		autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
-		actx.AddVariationDependencies(
-			append(commonDepVariations, []blueprint.Variation{
-				{Mutator: "rust_libraries", Variation: autoDep.variation}}...),
-			autoDep.depTag, deps.Rustlibs...)
+		if autoDep.depTag == rlibDepTag {
+			actx.AddVariationDependencies(
+				append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
+				autoDep.depTag, deps.Rustlibs...)
+		} else {
+			actx.AddVariationDependencies(
+				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
+				autoDep.depTag, deps.Rustlibs...)
+		}
 	}
-
+	if deps.Stdlibs != nil {
+		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
+			actx.AddVariationDependencies(
+				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "rlib"}),
+				rlibDepTag, deps.Stdlibs...)
+		} else {
+			actx.AddVariationDependencies(
+				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}),
+				dylibDepTag, deps.Stdlibs...)
+		}
+	}
 	actx.AddVariationDependencies(append(commonDepVariations,
 		blueprint.Variation{Mutator: "link", Variation: "shared"}),
 		cc.SharedDepTag(), deps.SharedLibs...)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 89ce359..4842a4c 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -189,7 +189,7 @@
 		t.Errorf("Dylib dependency not detected (dependency missing from AndroidMkDylibs)")
 	}
 
-	if !android.InList("librlib", module.Properties.AndroidMkRlibs) {
+	if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) {
 		t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)")
 	}
 
@@ -253,7 +253,7 @@
         }
 	`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib").Rule("rustc")
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
 	if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") {
 		t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
 	}
@@ -279,15 +279,15 @@
 
 	// Check that our bindings are picked up as crate dependencies as well
 	libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
-	if !android.InList("libbindings", libfooMod.Properties.AndroidMkRlibs) {
+	if !android.InList("libbindings.dylib-std", libfooMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
 	fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
-	if !android.InList("libbindings", fizzBuzzMod.Properties.AndroidMkRlibs) {
+	if !android.InList("libbindings.dylib-std", fizzBuzzMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
 	libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module)
-	if !android.InList("libbindings", libprocmacroMod.Properties.AndroidMkRlibs) {
+	if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
 
@@ -365,6 +365,6 @@
 			crate_name: "foo",
 		}`)
 
-	_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib")
-	_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib")
+	_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std")
+	_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
 }
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 755a369..03adf9e 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -43,6 +43,7 @@
 	SourceProviderProps() []interface{}
 	SourceProviderDeps(ctx DepsContext, deps Deps) Deps
 	setSubName(subName string)
+	setOutputFile(outputFile android.Path)
 }
 
 func (sp *BaseSourceProvider) Srcs() android.Paths {
@@ -95,3 +96,7 @@
 func (sp *BaseSourceProvider) setSubName(subName string) {
 	sp.subName = subName
 }
+
+func (sp *BaseSourceProvider) setOutputFile(outputFile android.Path) {
+	sp.OutputFile = outputFile
+}
diff --git a/rust/test.go b/rust/test.go
index d93fc31..bc7f53c 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -133,3 +133,7 @@
 	module, _ := NewRustTest(android.HostSupported)
 	return module.Init()
 }
+
+func (test *testDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+	return RlibLinkage
+}
diff --git a/rust/test_test.go b/rust/test_test.go
index 2382b18..fea2ad0 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestRustTest(t *testing.T) {
@@ -33,3 +35,35 @@
 		t.Errorf("wrong output path: %v;  expected: %v", outPath, expectedOut)
 	}
 }
+
+func TestRustTestLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_test {
+			name: "my_test",
+			srcs: ["foo.rs"],
+			rustlibs: ["libfoo"],
+            rlibs: ["libbar"],
+		}
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		rust_library {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+		}`)
+
+	testingModule := ctx.ModuleForTests("my_test", "android_arm64_armv8-a").Module().(*Module)
+
+	if !android.InList("libfoo.rlib-std", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for libfoo not detected as a rustlib-defined rlib dependency for device rust_test module")
+	}
+	if !android.InList("libbar.rlib-std", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for libbar not detected as an rlib dependency for device rust_test module")
+	}
+	if !android.InList("libstd", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_test module 'my_test' does not link libstd as an rlib")
+	}
+}
diff --git a/rust/testing.go b/rust/testing.go
index 0144c82..42b0da1 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -32,6 +32,7 @@
                                     srcs: ["libstd.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		rust_prebuilt_library {
 				name: "libtest_x86_64-unknown-linux-gnu",
@@ -43,6 +44,7 @@
                                     srcs: ["libtest.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		rust_prebuilt_library {
 				name: "libstd_x86_64-apple-darwin",
@@ -54,6 +56,7 @@
                                     srcs: ["libstd.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		rust_prebuilt_library {
 				name: "libtest_x86_64-apple-darwin",
@@ -65,6 +68,7 @@
                                     srcs: ["libtest.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		//////////////////////////////
 		// Device module requirements
@@ -82,6 +86,7 @@
 			no_stdlibs: true,
 			host_supported: true,
                         native_coverage: false,
+			sysroot: true,
 		}
 		rust_library {
 			name: "libtest",
@@ -90,6 +95,7 @@
 			no_stdlibs: true,
 			host_supported: true,
                         native_coverage: false,
+			sysroot: true,
 		}
 		rust_library {
 			name: "libprotobuf",
@@ -111,6 +117,7 @@
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
 	ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
+	ctx.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory)
 	ctx.RegisterModuleType("rust_test", RustTestFactory)
 	ctx.RegisterModuleType("rust_test_host", RustTestHostFactory)
 	ctx.RegisterModuleType("rust_library", RustLibraryFactory)
@@ -134,6 +141,7 @@
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		// rust mutators
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
diff --git a/scripts/update-apex-allowed-deps.sh b/scripts/update-apex-allowed-deps.sh
new file mode 100755
index 0000000..872d746
--- /dev/null
+++ b/scripts/update-apex-allowed-deps.sh
@@ -0,0 +1,39 @@
+#!/bin/bash -e
+#
+# The script to run locally to re-generate global allowed list of dependencies
+# for updatable modules.
+
+if [ ! -e "build/envsetup.sh" ]; then
+  echo "ERROR: $0 must be run from the top of the tree"
+  exit 1
+fi
+
+source build/envsetup.sh > /dev/null || exit 1
+
+readonly OUT_DIR=$(get_build_var OUT_DIR)
+
+readonly ALLOWED_DEPS_FILE="build/soong/apex/allowed_deps.txt"
+readonly NEW_ALLOWED_DEPS_FILE="${OUT_DIR}/soong/apex/depsinfo/new-allowed-deps.txt"
+
+# If the script is run after droidcore failure, ${NEW_ALLOWED_DEPS_FILE}
+# should already be built. If running the script manually, make sure it exists.
+m "${NEW_ALLOWED_DEPS_FILE}" -j
+
+cat > "${ALLOWED_DEPS_FILE}" << EndOfFileComment
+# A list of allowed dependencies for all updatable modules.
+#
+# The list tracks all direct and transitive dependencies that end up within any
+# of the updatable binaries; specifically excluding external dependencies
+# required to compile those binaries. This prevents potential regressions in
+# case a new dependency is not aware of the different functional and
+# non-functional requirements being part of an updatable module, for example
+# setting correct min_sdk_version.
+#
+# To update the list, run:
+# repo-root$ build/soong/scripts/update-apex-allowed-deps.sh
+#
+# See go/apex-allowed-deps-error for more details.
+# TODO(b/157465465): introduce automated quality signals and remove this list.
+EndOfFileComment
+
+cat "${NEW_ALLOWED_DEPS_FILE}" >> "${ALLOWED_DEPS_FILE}"
diff --git a/sdk/bp.go b/sdk/bp.go
index 68fe7ab..11ec8c6 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -16,6 +16,8 @@
 
 import (
 	"fmt"
+	"reflect"
+	"strings"
 
 	"android/soong/android"
 )
@@ -33,7 +35,82 @@
 	s.tags = make(map[string]android.BpPropertyTag)
 }
 
+// Converts the given value, which is assumed to be a struct, to a
+// bpPropertySet.
+func convertToPropertySet(value reflect.Value) *bpPropertySet {
+	res := newPropertySet()
+	structType := value.Type()
+
+	for i := 0; i < structType.NumField(); i++ {
+		field := structType.Field(i)
+		fieldVal := value.Field(i)
+
+		switch fieldVal.Type().Kind() {
+		case reflect.Ptr:
+			if fieldVal.IsNil() {
+				continue // nil pointer means the property isn't set.
+			}
+			fieldVal = fieldVal.Elem()
+		case reflect.Slice:
+			if fieldVal.IsNil() {
+				continue // Ignore a nil slice (but not one with length zero).
+			}
+		}
+
+		if fieldVal.Type().Kind() == reflect.Struct {
+			fieldVal = fieldVal.Addr() // Avoid struct copy below.
+		}
+		res.AddProperty(strings.ToLower(field.Name), fieldVal.Interface())
+	}
+
+	return res
+}
+
+// Converts the given value to something that can be set in a property.
+func coercePropertyValue(value interface{}) interface{} {
+	val := reflect.ValueOf(value)
+	switch val.Kind() {
+	case reflect.Struct:
+		// convertToPropertySet requires an addressable struct, and this is probably
+		// a mistake.
+		panic(fmt.Sprintf("Value is a struct, not a pointer to one: %v", value))
+	case reflect.Ptr:
+		if _, ok := value.(*bpPropertySet); !ok {
+			derefValue := reflect.Indirect(val)
+			if derefValue.Kind() != reflect.Struct {
+				panic(fmt.Sprintf("A pointer must be to a struct, got: %v", value))
+			}
+			return convertToPropertySet(derefValue)
+		}
+	}
+	return value
+}
+
+// Merges the fields of the given property set into s.
+func (s *bpPropertySet) mergePropertySet(propSet *bpPropertySet) {
+	for _, name := range propSet.order {
+		if tag, ok := propSet.tags[name]; ok {
+			s.AddPropertyWithTag(name, propSet.properties[name], tag)
+		} else {
+			s.AddProperty(name, propSet.properties[name])
+		}
+	}
+}
+
 func (s *bpPropertySet) AddProperty(name string, value interface{}) {
+	value = coercePropertyValue(value)
+
+	if propSetValue, ok := value.(*bpPropertySet); ok {
+		if curValue, ok := s.properties[name]; ok {
+			if curSet, ok := curValue.(*bpPropertySet); ok {
+				curSet.mergePropertySet(propSetValue)
+				return
+			}
+			// If the current value isn't a property set we got conflicting types.
+			// Continue down to the check below to complain about it.
+		}
+	}
+
 	if s.properties[name] != nil {
 		panic(fmt.Sprintf("Property %q already exists in property set", name))
 	}
@@ -48,9 +125,8 @@
 }
 
 func (s *bpPropertySet) AddPropertySet(name string) android.BpPropertySet {
-	set := newPropertySet()
-	s.AddProperty(name, set)
-	return set
+	s.AddProperty(name, newPropertySet())
+	return s.properties[name].(android.BpPropertySet)
 }
 
 func (s *bpPropertySet) getValue(name string) interface{} {
diff --git a/sdk/bp_test.go b/sdk/bp_test.go
index c630c25..e1edc51 100644
--- a/sdk/bp_test.go
+++ b/sdk/bp_test.go
@@ -18,8 +18,142 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
+func propertySetFixture() interface{} {
+	set := newPropertySet()
+	set.AddProperty("x", "taxi")
+	set.AddPropertyWithTag("y", 1729, "tag_y")
+	subset := set.AddPropertySet("sub")
+	subset.AddPropertyWithTag("x", "taxi", "tag_x")
+	subset.AddProperty("y", 1729)
+	return set
+}
+
+func intPtr(i int) *int { return &i }
+
+type propertyStruct struct {
+	X     *string
+	Y     *int
+	Unset *bool
+	Sub   struct {
+		X     *string
+		Y     *int
+		Unset *bool
+	}
+}
+
+func propertyStructFixture() interface{} {
+	str := &propertyStruct{}
+	str.X = proptools.StringPtr("taxi")
+	str.Y = intPtr(1729)
+	str.Sub.X = proptools.StringPtr("taxi")
+	str.Sub.Y = intPtr(1729)
+	return str
+}
+
+func checkPropertySetFixture(h *TestHelper, val interface{}, hasTags bool) {
+	set := val.(*bpPropertySet)
+	h.AssertDeepEquals("wrong x value", "taxi", set.getValue("x"))
+	h.AssertDeepEquals("wrong y value", 1729, set.getValue("y"))
+
+	subset := set.getValue("sub").(*bpPropertySet)
+	h.AssertDeepEquals("wrong sub.x value", "taxi", subset.getValue("x"))
+	h.AssertDeepEquals("wrong sub.y value", 1729, subset.getValue("y"))
+
+	if hasTags {
+		h.AssertDeepEquals("wrong y tag", "tag_y", set.getTag("y"))
+		h.AssertDeepEquals("wrong sub.x tag", "tag_x", subset.getTag("x"))
+	} else {
+		h.AssertDeepEquals("wrong y tag", nil, set.getTag("y"))
+		h.AssertDeepEquals("wrong sub.x tag", nil, subset.getTag("x"))
+	}
+}
+
+func TestAddPropertySimple(t *testing.T) {
+	h := &TestHelper{t}
+	set := newPropertySet()
+	for name, val := range map[string]interface{}{
+		"x":   "taxi",
+		"y":   1729,
+		"t":   true,
+		"f":   false,
+		"arr": []string{"a", "b", "c"},
+	} {
+		set.AddProperty(name, val)
+		h.AssertDeepEquals("wrong value", val, set.getValue(name))
+	}
+	h.AssertPanic("adding x again should panic",
+		func() { set.AddProperty("x", "taxi") })
+	h.AssertPanic("adding arr again should panic",
+		func() { set.AddProperty("arr", []string{"d"}) })
+}
+
+func TestAddPropertySubset(t *testing.T) {
+	h := &TestHelper{t}
+	getFixtureMap := map[string]func() interface{}{
+		"property set":    propertySetFixture,
+		"property struct": propertyStructFixture,
+	}
+
+	t.Run("add new subset", func(t *testing.T) {
+		for name, getFixture := range getFixtureMap {
+			t.Run(name, func(t *testing.T) {
+				set := propertySetFixture().(*bpPropertySet)
+				set.AddProperty("new", getFixture())
+				checkPropertySetFixture(h, set, true)
+				checkPropertySetFixture(h, set.getValue("new"), name == "property set")
+			})
+		}
+	})
+
+	t.Run("merge existing subset", func(t *testing.T) {
+		for name, getFixture := range getFixtureMap {
+			t.Run(name, func(t *testing.T) {
+				set := newPropertySet()
+				subset := set.AddPropertySet("sub")
+				subset.AddProperty("flag", false)
+				subset.AddPropertySet("sub")
+				set.AddProperty("sub", getFixture())
+				merged := set.getValue("sub").(*bpPropertySet)
+				h.AssertDeepEquals("wrong flag value", false, merged.getValue("flag"))
+				checkPropertySetFixture(h, merged, name == "property set")
+			})
+		}
+	})
+
+	t.Run("add conflicting subset", func(t *testing.T) {
+		set := propertySetFixture().(*bpPropertySet)
+		h.AssertPanic("adding x again should panic",
+			func() { set.AddProperty("x", propertySetFixture()) })
+	})
+
+	t.Run("add non-pointer struct", func(t *testing.T) {
+		set := propertySetFixture().(*bpPropertySet)
+		str := propertyStructFixture().(*propertyStruct)
+		h.AssertPanic("adding a non-pointer struct should panic",
+			func() { set.AddProperty("new", *str) })
+	})
+}
+
+func TestAddPropertySetNew(t *testing.T) {
+	h := &TestHelper{t}
+	set := newPropertySet()
+	subset := set.AddPropertySet("sub")
+	subset.AddProperty("new", "d^^b")
+	h.AssertDeepEquals("wrong sub.new value", "d^^b", set.getValue("sub").(*bpPropertySet).getValue("new"))
+}
+
+func TestAddPropertySetExisting(t *testing.T) {
+	h := &TestHelper{t}
+	set := propertySetFixture().(*bpPropertySet)
+	subset := set.AddPropertySet("sub")
+	subset.AddProperty("new", "d^^b")
+	h.AssertDeepEquals("wrong sub.new value", "d^^b", set.getValue("sub").(*bpPropertySet).getValue("new"))
+}
+
 type removeFredTransformation struct {
 	identityTransformation
 }
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 8c9e228..c214e75 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -108,6 +108,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_sdkmember@current",
     sdk_member_name: "sdkmember",
+    visibility: ["//visibility:public"],
     host_supported: true,
     installable: false,
     stl: "none",
@@ -131,6 +132,7 @@
 cc_prebuilt_library_shared {
     name: "sdkmember",
     prefer: false,
+    visibility: ["//visibility:public"],
     host_supported: true,
     stl: "none",
     compile_multilib: "64",
@@ -152,6 +154,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     host_supported: true,
     native_shared_libs: ["mysdk_sdkmember@current"],
     compile_multilib: "64",
@@ -353,6 +356,7 @@
 cc_prebuilt_object {
     name: "mysdk_crtobj@current",
     sdk_member_name: "crtobj",
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     sanitize: {
@@ -371,6 +375,7 @@
 cc_prebuilt_object {
     name: "crtobj",
     prefer: false,
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     sanitize: {
@@ -388,6 +393,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     native_objects: ["mysdk_crtobj@current"],
 }
 `),
@@ -435,8 +441,10 @@
 	)
 }
 
-// Verify that when the shared library has some common and some arch specific properties that the generated
-// snapshot is optimized properly.
+// Verify that when the shared library has some common and some arch specific
+// properties that the generated snapshot is optimized properly. Substruct
+// handling is tested with the sanitize clauses (but note there's a lot of
+// built-in logic in sanitize.go that can affect those flags).
 func TestSnapshotWithCcSharedLibraryCommonProperties(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
@@ -451,9 +459,18 @@
 				"aidl/foo/bar/Test.aidl",
 			],
 			export_include_dirs: ["include"],
+			sanitize: {
+				fuzzer: false,
+				integer_overflow: true,
+				diag: { undefined: false },
+			},
 			arch: {
 				arm64: {
 					export_system_include_dirs: ["arm64/include"],
+					sanitize: {
+						hwaddress: true,
+						integer_overflow: false,
+					},
 				},
 			},
 			stl: "none",
@@ -467,17 +484,31 @@
 cc_prebuilt_library_shared {
     name: "mysdk_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     installable: false,
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
+    sanitize: {
+        fuzzer: false,
+        diag: {
+            undefined: false,
+        },
+    },
     arch: {
         arm64: {
             srcs: ["arm64/lib/mynativelib.so"],
             export_system_include_dirs: ["arm64/include/arm64/include"],
+            sanitize: {
+                hwaddress: true,
+                integer_overflow: false,
+            },
         },
         arm: {
             srcs: ["arm/lib/mynativelib.so"],
+            sanitize: {
+                integer_overflow: true,
+            },
         },
     },
 }
@@ -485,28 +516,43 @@
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
+    sanitize: {
+        fuzzer: false,
+        diag: {
+            undefined: false,
+        },
+    },
     arch: {
         arm64: {
             srcs: ["arm64/lib/mynativelib.so"],
             export_system_include_dirs: ["arm64/include/arm64/include"],
+            sanitize: {
+                hwaddress: true,
+                integer_overflow: false,
+            },
         },
         arm: {
             srcs: ["arm/lib/mynativelib.so"],
+            sanitize: {
+                integer_overflow: true,
+            },
         },
     },
 }
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     native_shared_libs: ["mysdk_mynativelib@current"],
 }
 `),
 		checkAllCopyRules(`
 include/Test.h -> include/include/Test.h
-.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm64_armv8-a_shared_hwasan/mynativelib.so -> arm64/lib/mynativelib.so
 arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
 	)
@@ -535,6 +581,7 @@
 cc_prebuilt_binary {
     name: "mymodule_exports_mynativebinary@current",
     sdk_member_name: "mynativebinary",
+    visibility: ["//visibility:public"],
     installable: false,
     compile_multilib: "both",
     arch: {
@@ -550,6 +597,7 @@
 cc_prebuilt_binary {
     name: "mynativebinary",
     prefer: false,
+    visibility: ["//visibility:public"],
     compile_multilib: "both",
     arch: {
         arm64: {
@@ -563,6 +611,7 @@
 
 module_exports_snapshot {
     name: "mymodule_exports@current",
+    visibility: ["//visibility:public"],
     native_binaries: ["mymodule_exports_mynativebinary@current"],
 }
 `),
@@ -611,6 +660,7 @@
 cc_prebuilt_binary {
     name: "myexports_mynativebinary@current",
     sdk_member_name: "mynativebinary",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -642,6 +692,7 @@
 cc_prebuilt_binary {
     name: "mynativebinary",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -671,6 +722,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_binaries: ["myexports_mynativebinary@current"],
@@ -749,6 +801,7 @@
 cc_prebuilt_binary {
     name: "myexports_mynativebinary@current",
     sdk_member_name: "mynativebinary",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -770,6 +823,7 @@
 cc_prebuilt_binary {
     name: "mynativebinary",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -790,6 +844,7 @@
 cc_prebuilt_library_shared {
     name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -811,6 +866,7 @@
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -830,6 +886,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_binaries: ["myexports_mynativebinary@current"],
@@ -883,6 +940,7 @@
 cc_prebuilt_binary {
     name: "mymodule_exports_linker@current",
     sdk_member_name: "linker",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -909,6 +967,7 @@
 cc_prebuilt_binary {
     name: "linker",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -933,6 +992,7 @@
 
 module_exports_snapshot {
     name: "mymodule_exports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_binaries: ["mymodule_exports_linker@current"],
@@ -982,6 +1042,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     apex_available: [
         "apex1",
         "apex2",
@@ -1005,6 +1066,7 @@
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     apex_available: [
         "apex1",
         "apex2",
@@ -1026,6 +1088,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     native_shared_libs: ["mysdk_mynativelib@current"],
 }
 `),
@@ -1110,6 +1173,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     installable: false,
     stl: "none",
     compile_multilib: "both",
@@ -1130,6 +1194,7 @@
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     shared_libs: [
@@ -1149,6 +1214,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_myothernativelib@current",
     sdk_member_name: "myothernativelib",
+    visibility: ["//visibility:public"],
     installable: false,
     stl: "none",
     compile_multilib: "both",
@@ -1166,6 +1232,7 @@
 cc_prebuilt_library_shared {
     name: "myothernativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     system_shared_libs: ["libm"],
@@ -1182,6 +1249,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_mysystemnativelib@current",
     sdk_member_name: "mysystemnativelib",
+    visibility: ["//visibility:public"],
     installable: false,
     stl: "none",
     compile_multilib: "both",
@@ -1198,6 +1266,7 @@
 cc_prebuilt_library_shared {
     name: "mysystemnativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     arch: {
@@ -1212,6 +1281,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     native_shared_libs: [
         "mysdk_mynativelib@current",
         "mysdk_myothernativelib@current",
@@ -1263,6 +1333,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -1291,6 +1362,7 @@
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     sdk_version: "minimum",
@@ -1317,6 +1389,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_shared_libs: ["mysdk_mynativelib@current"],
@@ -1381,6 +1454,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -1412,6 +1486,7 @@
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -1441,6 +1516,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_shared_libs: ["mysdk_mynativelib@current"],
@@ -1494,6 +1570,7 @@
 cc_prebuilt_library_static {
     name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     installable: false,
     stl: "none",
     compile_multilib: "both",
@@ -1513,6 +1590,7 @@
 cc_prebuilt_library_static {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
@@ -1530,6 +1608,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     native_static_libs: ["myexports_mynativelib@current"],
 }
 `),
@@ -1579,6 +1658,7 @@
 cc_prebuilt_library_static {
     name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -1606,6 +1686,7 @@
 cc_prebuilt_library_static {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -1631,6 +1712,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_static_libs: ["myexports_mynativelib@current"],
@@ -1684,6 +1766,7 @@
 cc_prebuilt_library {
     name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     installable: false,
     recovery_available: true,
     vendor_available: true,
@@ -1713,6 +1796,7 @@
 cc_prebuilt_library {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     recovery_available: true,
     vendor_available: true,
     stl: "none",
@@ -1740,6 +1824,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     native_libs: ["myexports_mynativelib@current"],
 }
 `),
@@ -1789,6 +1874,7 @@
 cc_prebuilt_library_static {
     name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     installable: false,
@@ -1812,6 +1898,7 @@
 cc_prebuilt_library_static {
     name: "mynativelib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -1833,6 +1920,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_static_libs: ["myexports_mynativelib@current"],
@@ -1877,6 +1965,7 @@
 cc_prebuilt_library_headers {
     name: "mysdk_mynativeheaders@current",
     sdk_member_name: "mynativeheaders",
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
@@ -1885,6 +1974,7 @@
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
+    visibility: ["//visibility:public"],
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/include"],
@@ -1892,6 +1982,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     native_header_libs: ["mysdk_mynativeheaders@current"],
 }
 `),
@@ -1926,6 +2017,7 @@
 cc_prebuilt_library_headers {
     name: "mysdk_mynativeheaders@current",
     sdk_member_name: "mynativeheaders",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -1944,6 +2036,7 @@
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stl: "none",
@@ -1961,6 +2054,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     native_header_libs: ["mysdk_mynativeheaders@current"],
@@ -2011,6 +2105,7 @@
 cc_prebuilt_library_headers {
     name: "mysdk_mynativeheaders@current",
     sdk_member_name: "mynativeheaders",
+    visibility: ["//visibility:public"],
     host_supported: true,
     stl: "none",
     compile_multilib: "both",
@@ -2032,6 +2127,7 @@
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
+    visibility: ["//visibility:public"],
     host_supported: true,
     stl: "none",
     compile_multilib: "both",
@@ -2052,6 +2148,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     host_supported: true,
     native_header_libs: ["mysdk_mynativeheaders@current"],
     target: {
@@ -2102,6 +2199,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_sslnil@current",
     sdk_member_name: "sslnil",
+    visibility: ["//visibility:public"],
     installable: false,
     compile_multilib: "both",
     arch: {
@@ -2117,6 +2215,7 @@
 cc_prebuilt_library_shared {
     name: "sslnil",
     prefer: false,
+    visibility: ["//visibility:public"],
     compile_multilib: "both",
     arch: {
         arm64: {
@@ -2131,6 +2230,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_sslempty@current",
     sdk_member_name: "sslempty",
+    visibility: ["//visibility:public"],
     installable: false,
     compile_multilib: "both",
     system_shared_libs: [],
@@ -2147,6 +2247,7 @@
 cc_prebuilt_library_shared {
     name: "sslempty",
     prefer: false,
+    visibility: ["//visibility:public"],
     compile_multilib: "both",
     system_shared_libs: [],
     arch: {
@@ -2162,6 +2263,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_sslnonempty@current",
     sdk_member_name: "sslnonempty",
+    visibility: ["//visibility:public"],
     installable: false,
     compile_multilib: "both",
     system_shared_libs: ["mysdk_sslnil@current"],
@@ -2178,6 +2280,7 @@
 cc_prebuilt_library_shared {
     name: "sslnonempty",
     prefer: false,
+    visibility: ["//visibility:public"],
     compile_multilib: "both",
     system_shared_libs: ["sslnil"],
     arch: {
@@ -2192,6 +2295,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     native_shared_libs: [
         "mysdk_sslnil@current",
         "mysdk_sslempty@current",
@@ -2225,6 +2329,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_sslvariants@current",
     sdk_member_name: "sslvariants",
+    visibility: ["//visibility:public"],
     host_supported: true,
     installable: false,
     compile_multilib: "both",
@@ -2256,6 +2361,7 @@
 cc_prebuilt_library_shared {
     name: "sslvariants",
     prefer: false,
+    visibility: ["//visibility:public"],
     host_supported: true,
     compile_multilib: "both",
     target: {
@@ -2285,6 +2391,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     host_supported: true,
     native_shared_libs: ["mysdk_sslvariants@current"],
     target: {
@@ -2327,10 +2434,15 @@
 cc_prebuilt_library_shared {
     name: "mysdk_stubslib@current",
     sdk_member_name: "stubslib",
+    visibility: ["//visibility:public"],
     installable: false,
     compile_multilib: "both",
     stubs: {
-        versions: ["3"],
+        versions: [
+            "1",
+            "2",
+            "3",
+        ],
     },
     arch: {
         arm64: {
@@ -2345,9 +2457,14 @@
 cc_prebuilt_library_shared {
     name: "stubslib",
     prefer: false,
+    visibility: ["//visibility:public"],
     compile_multilib: "both",
     stubs: {
-        versions: ["3"],
+        versions: [
+            "1",
+            "2",
+            "3",
+        ],
     },
     arch: {
         arm64: {
@@ -2361,6 +2478,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     native_shared_libs: ["mysdk_stubslib@current"],
 }
 `))
@@ -2397,11 +2515,16 @@
 cc_prebuilt_library_shared {
     name: "mysdk_stubslib@current",
     sdk_member_name: "stubslib",
+    visibility: ["//visibility:public"],
     host_supported: true,
     installable: false,
     compile_multilib: "both",
     stubs: {
-        versions: ["3"],
+        versions: [
+            "1",
+            "2",
+            "3",
+        ],
     },
     target: {
         host: {
@@ -2428,10 +2551,15 @@
 cc_prebuilt_library_shared {
     name: "stubslib",
     prefer: false,
+    visibility: ["//visibility:public"],
     host_supported: true,
     compile_multilib: "both",
     stubs: {
-        versions: ["3"],
+        versions: [
+            "1",
+            "2",
+            "3",
+        ],
     },
     target: {
         host: {
@@ -2457,6 +2585,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     host_supported: true,
     native_shared_libs: ["mysdk_stubslib@current"],
     target: {
@@ -2493,6 +2622,7 @@
 cc_prebuilt_library_shared {
     name: "mysdk_mylib@current",
     sdk_member_name: "mylib",
+    visibility: ["//visibility:public"],
     host_supported: true,
     installable: false,
     unique_host_soname: true,
@@ -2522,6 +2652,7 @@
 cc_prebuilt_library_shared {
     name: "mylib",
     prefer: false,
+    visibility: ["//visibility:public"],
     host_supported: true,
     unique_host_soname: true,
     compile_multilib: "both",
@@ -2549,6 +2680,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     host_supported: true,
     native_shared_libs: ["mysdk_mylib@current"],
     target: {
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 20e2521..aa1200f 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -49,17 +49,20 @@
 java_import {
     name: "myexports_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     jars: ["java/myjavalib.jar"],
 }
 
 java_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     jars: ["java/myjavalib.jar"],
 }
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     java_libs: ["myexports_myjavalib@current"],
 }
 `))
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index a7ee8d1..d6828c9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -43,6 +43,7 @@
 		"api/system-server-current.txt":                     nil,
 		"api/system-server-removed.txt":                     nil,
 		"build/soong/scripts/gen-java-current-api-files.sh": nil,
+		"docs/known_doctags":                                nil,
 	}
 
 	// for java_sdk_library tests
@@ -120,17 +121,20 @@
 java_import {
     name: "mysdk_sdkmember@current",
     sdk_member_name: "sdkmember",
+    visibility: ["//visibility:public"],
     jars: ["java/sdkmember.jar"],
 }
 
 java_import {
     name: "sdkmember",
     prefer: false,
+    visibility: ["//visibility:public"],
     jars: ["java/sdkmember.jar"],
 }
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_header_libs: ["mysdk_sdkmember@current"],
 }
 `))
@@ -242,17 +246,20 @@
 java_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     jars: ["java/myjavalib.jar"],
 }
 
 java_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     jars: ["java/myjavalib.jar"],
 }
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_header_libs: ["mysdk_myjavalib@current"],
 }
 
@@ -294,6 +301,7 @@
 java_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavalib.jar"],
@@ -302,6 +310,7 @@
 java_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavalib.jar"],
@@ -309,6 +318,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     java_header_libs: ["mysdk_myjavalib@current"],
@@ -346,6 +356,7 @@
 java_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     host_supported: true,
     target: {
         android: {
@@ -360,6 +371,7 @@
 java_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     host_supported: true,
     target: {
         android: {
@@ -373,6 +385,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     host_supported: true,
     java_header_libs: ["mysdk_myjavalib@current"],
 }
@@ -412,17 +425,20 @@
 java_import {
     name: "myexports_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     jars: ["java/myjavalib.jar"],
 }
 
 java_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     jars: ["java/myjavalib.jar"],
 }
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     java_libs: ["myexports_myjavalib@current"],
 }
 
@@ -464,6 +480,7 @@
 java_import {
     name: "myexports_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavalib.jar"],
@@ -472,6 +489,7 @@
 java_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavalib.jar"],
@@ -479,6 +497,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     java_libs: ["myexports_myjavalib@current"],
@@ -515,6 +534,7 @@
 java_test_import {
     name: "myexports_myjavatests@current",
     sdk_member_name: "myjavatests",
+    visibility: ["//visibility:public"],
     jars: ["java/myjavatests.jar"],
     test_config: "java/myjavatests-AndroidTest.xml",
 }
@@ -522,12 +542,14 @@
 java_test_import {
     name: "myjavatests",
     prefer: false,
+    visibility: ["//visibility:public"],
     jars: ["java/myjavatests.jar"],
     test_config: "java/myjavatests-AndroidTest.xml",
 }
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     java_tests: ["myexports_myjavatests@current"],
 }
 `),
@@ -565,6 +587,7 @@
 java_test_import {
     name: "myexports_myjavatests@current",
     sdk_member_name: "myjavatests",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavatests.jar"],
@@ -574,6 +597,7 @@
 java_test_import {
     name: "myjavatests",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavatests.jar"],
@@ -582,6 +606,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     java_tests: ["myexports_myjavatests@current"],
@@ -659,17 +684,20 @@
 prebuilt_stubs_sources {
     name: "myexports_myjavaapistubs@current",
     sdk_member_name: "myjavaapistubs",
+    visibility: ["//visibility:public"],
     srcs: ["java/myjavaapistubs_stubs_sources"],
 }
 
 prebuilt_stubs_sources {
     name: "myjavaapistubs",
     prefer: false,
+    visibility: ["//visibility:public"],
     srcs: ["java/myjavaapistubs_stubs_sources"],
 }
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     stubs_sources: ["myexports_myjavaapistubs@current"],
 }
 
@@ -705,6 +733,7 @@
 prebuilt_stubs_sources {
     name: "myexports_myjavaapistubs@current",
     sdk_member_name: "myjavaapistubs",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     srcs: ["java/myjavaapistubs_stubs_sources"],
@@ -713,6 +742,7 @@
 prebuilt_stubs_sources {
     name: "myjavaapistubs",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     srcs: ["java/myjavaapistubs_stubs_sources"],
@@ -720,6 +750,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     stubs_sources: ["myexports_myjavaapistubs@current"],
@@ -765,12 +796,14 @@
 java_import {
     name: "mysdk_exported-system-module@current",
     sdk_member_name: "exported-system-module",
+    visibility: ["//visibility:public"],
     jars: ["java/exported-system-module.jar"],
 }
 
 java_import {
     name: "exported-system-module",
     prefer: false,
+    visibility: ["//visibility:public"],
     jars: ["java/exported-system-module.jar"],
 }
 
@@ -791,6 +824,7 @@
 java_system_modules_import {
     name: "mysdk_my-system-modules@current",
     sdk_member_name: "my-system-modules",
+    visibility: ["//visibility:public"],
     libs: [
         "mysdk_system-module@current",
         "mysdk_exported-system-module@current",
@@ -800,6 +834,7 @@
 java_system_modules_import {
     name: "my-system-modules",
     prefer: false,
+    visibility: ["//visibility:public"],
     libs: [
         "mysdk_system-module",
         "exported-system-module",
@@ -808,6 +843,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_header_libs: ["mysdk_exported-system-module@current"],
     java_system_modules: ["mysdk_my-system-modules@current"],
 }
@@ -870,6 +906,7 @@
 java_system_modules_import {
     name: "mysdk_my-system-modules@current",
     sdk_member_name: "my-system-modules",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     libs: ["mysdk_system-module@current"],
@@ -878,6 +915,7 @@
 java_system_modules_import {
     name: "my-system-modules",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     libs: ["mysdk_system-module"],
@@ -885,6 +923,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     java_system_modules: ["mysdk_my-system-modules@current"],
@@ -938,6 +977,7 @@
 java_import {
     name: "myexports_hostjavalib@current",
     sdk_member_name: "hostjavalib",
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/hostjavalib.jar"],
@@ -946,6 +986,7 @@
 java_import {
     name: "hostjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     device_supported: false,
     host_supported: true,
     jars: ["java/hostjavalib.jar"],
@@ -954,18 +995,21 @@
 java_import {
     name: "myexports_androidjavalib@current",
     sdk_member_name: "androidjavalib",
+    visibility: ["//visibility:public"],
     jars: ["java/androidjavalib.jar"],
 }
 
 java_import {
     name: "androidjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     jars: ["java/androidjavalib.jar"],
 }
 
 java_import {
     name: "myexports_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     host_supported: true,
     target: {
         android: {
@@ -980,6 +1024,7 @@
 java_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     host_supported: true,
     target: {
         android: {
@@ -993,6 +1038,7 @@
 
 module_exports_snapshot {
     name: "myexports@current",
+    visibility: ["//visibility:public"],
     host_supported: true,
     java_libs: ["myexports_myjavalib@current"],
     target: {
@@ -1039,6 +1085,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: false,
     public: {
@@ -1067,6 +1114,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: false,
     public: {
@@ -1094,6 +1142,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_sdk_libs: ["mysdk_myjavalib@current"],
 }
 `),
@@ -1137,6 +1186,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1150,6 +1200,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1162,6 +1213,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_sdk_libs: ["mysdk_myjavalib@current"],
 }
 `),
@@ -1201,6 +1253,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1214,6 +1267,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1226,6 +1280,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_sdk_libs: ["mysdk_myjavalib@current"],
 }
 `),
@@ -1268,6 +1323,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
     public: {
@@ -1289,6 +1345,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
     public: {
@@ -1309,6 +1366,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_sdk_libs: ["mysdk_myjavalib@current"],
 }
 `),
@@ -1358,6 +1416,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
     public: {
@@ -1386,6 +1445,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
     public: {
@@ -1413,6 +1473,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_sdk_libs: ["mysdk_myjavalib@current"],
 }
 `),
@@ -1463,6 +1524,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
     public: {
@@ -1484,6 +1546,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
     public: {
@@ -1504,6 +1567,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_sdk_libs: ["mysdk_myjavalib@current"],
 }
 `),
@@ -1548,6 +1612,7 @@
 java_sdk_library_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     naming_scheme: "default",
     shared_library: true,
@@ -1563,6 +1628,7 @@
 java_sdk_library_import {
     name: "myjavalib",
     prefer: false,
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     naming_scheme: "default",
     shared_library: true,
@@ -1577,6 +1643,7 @@
 
 sdk_snapshot {
     name: "mysdk@current",
+    visibility: ["//visibility:public"],
     java_sdk_libs: ["mysdk_myjavalib@current"],
 }
 `),
@@ -1590,3 +1657,75 @@
 		),
 	)
 }
+
+func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			sdk_version: "current",
+			public: {
+				enabled: true,
+			},
+			doctag_files: ["docs/known_doctags"],
+		}
+
+		filegroup {
+			name: "mygroup",
+			srcs: [":myjavalib{.doctags}"],
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
+    shared_library: true,
+    doctag_files: ["doctags/docs/known_doctags"],
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    shared_library: true,
+    doctag_files: ["doctags/docs/known_doctags"],
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+docs/known_doctags -> doctags/docs/known_doctags
+`),
+	)
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 7591020..50b0886 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -75,6 +75,20 @@
 
 	// True if this is a module_exports (or module_exports_snapshot) module type.
 	Module_exports bool `blueprint:"mutated"`
+
+	// The additional visibility to add to the prebuilt modules to allow them to
+	// reference each other.
+	//
+	// This can only be used to widen the visibility of the members:
+	//
+	// * Specifying //visibility:public here will make all members visible and
+	//   essentially ignore their own visibility.
+	// * Specifying //visibility:private here is an error.
+	// * Specifying any other rule here will add it to the members visibility and
+	//   be output to the member prebuilt in the snapshot. Duplicates will be
+	//   dropped. Adding a rule to members that have //visibility:private will
+	//   cause the //visibility:private to be discarded.
+	Prebuilt_visibility []string
 }
 
 // Contains information about the sdk properties that list sdk members, e.g.
@@ -211,6 +225,9 @@
 	// properties for the member type specific list properties.
 	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
+
+	// Make sure that the prebuilt visibility property is verified for errors.
+	android.AddVisibilityProperty(s, "prebuilt_visibility", &s.properties.Prebuilt_visibility)
 	android.InitCommonOSAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(s)
 	android.AddLoadHook(s, func(ctx android.LoadHookContext) {
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index ef62b79..2e6c62a 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -108,6 +108,9 @@
 				// generated sdk_snapshot.
 				":__subpackages__",
 			],
+			prebuilt_visibility: [
+				"//prebuilts/mysdk",
+			],
 			java_header_libs: [
 				"myjavalib",
 				"mypublicjavalib",
@@ -176,6 +179,7 @@
     visibility: [
         "//other/foo",
         "//package",
+        "//prebuilts/mysdk",
     ],
     jars: ["java/myjavalib.jar"],
 }
@@ -186,6 +190,7 @@
     visibility: [
         "//other/foo",
         "//package",
+        "//prebuilts/mysdk",
     ],
     jars: ["java/myjavalib.jar"],
 }
@@ -210,6 +215,7 @@
     visibility: [
         "//other/bar",
         "//package",
+        "//prebuilts/mysdk",
     ],
     jars: ["java/mydefaultedjavalib.jar"],
 }
@@ -220,6 +226,7 @@
     visibility: [
         "//other/bar",
         "//package",
+        "//prebuilts/mysdk",
     ],
     jars: ["java/mydefaultedjavalib.jar"],
 }
@@ -227,14 +234,20 @@
 java_import {
     name: "mysdk_myprivatejavalib@current",
     sdk_member_name: "myprivatejavalib",
-    visibility: ["//package"],
+    visibility: [
+        "//package",
+        "//prebuilts/mysdk",
+    ],
     jars: ["java/myprivatejavalib.jar"],
 }
 
 java_import {
     name: "myprivatejavalib",
     prefer: false,
-    visibility: ["//package"],
+    visibility: [
+        "//package",
+        "//prebuilts/mysdk",
+    ],
     jars: ["java/myprivatejavalib.jar"],
 }
 
@@ -254,6 +267,40 @@
 `))
 }
 
+func TestPrebuiltVisibilityProperty_IsValidated(t *testing.T) {
+	testSdkError(t, `prebuilt_visibility: cannot mix "//visibility:private" with any other visibility rules`, `
+		sdk {
+			name: "mysdk",
+			prebuilt_visibility: [
+				"//foo",
+				"//visibility:private",
+			],
+		}
+`)
+}
+
+func TestPrebuiltVisibilityProperty_AddPrivate(t *testing.T) {
+	testSdkError(t, `prebuilt_visibility: "//visibility:private" does not widen the visibility`, `
+		sdk {
+			name: "mysdk",
+			prebuilt_visibility: [
+				"//visibility:private",
+			],
+			java_header_libs: [
+				"myjavalib",
+			],
+		}
+
+		java_library {
+			name: "myjavalib",
+			// Uses package default visibility
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+`)
+}
+
 func TestSDkInstall(t *testing.T) {
 	sdk := `
 		sdk {
diff --git a/sdk/testing.go b/sdk/testing.go
index b53558d..0b280ef 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -68,14 +68,14 @@
 	// Add windows as a default disable OS to test behavior when some OS variants
 	// are disabled.
 	config.Targets[android.Windows] = []android.Target{
-		{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", ""},
+		{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
 	}
 
 	for _, extraOsType := range extraOsTypes {
 		switch extraOsType {
 		case android.LinuxBionic:
 			config.Targets[android.LinuxBionic] = []android.Target{
-				{android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", ""},
+				{android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", false},
 			}
 		}
 	}
@@ -89,11 +89,12 @@
 	android.RegisterAndroidMkBuildComponents(ctx)
 	android.SetInMakeForTests(config)
 	config.Targets[android.CommonOS] = []android.Target{
-		{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", ""},
+		{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", "", true},
 	}
 
 	// from android package
 	android.RegisterPackageBuildComponents(ctx)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
@@ -217,6 +218,22 @@
 	}
 }
 
+func (h *TestHelper) AssertPanic(message string, funcThatShouldPanic func()) {
+	h.t.Helper()
+	panicked := false
+	func() {
+		defer func() {
+			if x := recover(); x != nil {
+				panicked = true
+			}
+		}()
+		funcThatShouldPanic()
+	}()
+	if !panicked {
+		h.t.Error(message)
+	}
+}
+
 // Encapsulates result of processing an SDK definition. Provides support for
 // checking the state of the build structures.
 type testSdkResult struct {
diff --git a/sdk/update.go b/sdk/update.go
index 537ab13..f29b5a0 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -300,7 +300,7 @@
 	snapshotModule.AddProperty("name", snapshotName)
 
 	// Make sure that the snapshot has the same visibility as the sdk.
-	visibility := android.EffectiveVisibilityRules(ctx, s)
+	visibility := android.EffectiveVisibilityRules(ctx, s).Strings()
 	if len(visibility) != 0 {
 		snapshotModule.AddProperty("visibility", visibility)
 	}
@@ -371,8 +371,7 @@
 			osPropertySet := targetPropertySet.AddPropertySet(sdkVariant.Target().Os.Name)
 
 			// Enable the variant explicitly when we've disabled it by default on host.
-			if hasHostOsDependentMember &&
-				(osType.Class == android.Host || osType.Class == android.HostCross) {
+			if hasHostOsDependentMember && osType.Class == android.Host {
 				osPropertySet.AddProperty("enabled", true)
 			}
 
@@ -720,7 +719,15 @@
 	} else {
 		// Extract visibility information from a member variant. All variants have the same
 		// visibility so it doesn't matter which one is used.
-		visibility := android.EffectiveVisibilityRules(s.ctx, variant)
+		visibilityRules := android.EffectiveVisibilityRules(s.ctx, variant)
+
+		// Add any additional visibility rules needed for the prebuilts to reference each other.
+		err := visibilityRules.Widen(s.sdk.properties.Prebuilt_visibility)
+		if err != nil {
+			s.ctx.PropertyErrorf("prebuilt_visibility", "%s", err)
+		}
+
+		visibility := visibilityRules.Strings()
 		if len(visibility) != 0 {
 			m.AddProperty("visibility", visibility)
 		}
@@ -731,7 +738,7 @@
 
 	for _, variant := range member.Variants() {
 		osClass := variant.Target().Os.Class
-		if osClass == android.Host || osClass == android.HostCross {
+		if osClass == android.Host {
 			hostSupported = true
 		} else if osClass == android.Device {
 			deviceSupported = true
@@ -1061,8 +1068,7 @@
 		archPropertySet = targetPropertySet
 
 		// Enable the variant explicitly when we've disabled it by default on host.
-		if ctx.memberType.IsHostOsDependent() &&
-			(osType.Class == android.Host || osType.Class == android.HostCross) {
+		if ctx.memberType.IsHostOsDependent() && osType.Class == android.Host {
 			osPropertySet.AddProperty("enabled", true)
 		}
 
@@ -1086,7 +1092,7 @@
 
 func (osInfo *osTypeSpecificInfo) isHostVariant() bool {
 	osClass := osInfo.osType.Class
-	return osClass == android.Host || osClass == android.HostCross
+	return osClass == android.Host
 }
 
 var _ isHostVariant = (*osTypeSpecificInfo)(nil)
@@ -1323,7 +1329,7 @@
 			}
 		}
 		if s.HostSupported() {
-			if osType.Class == android.Host || osType.Class == android.HostCross {
+			if osType.Class == android.Host {
 				osTypes = append(osTypes, osType)
 			}
 		}
@@ -1348,7 +1354,8 @@
 
 // A property that can be optimized by the commonValueExtractor.
 type extractorProperty struct {
-	// The name of the field for this property.
+	// The name of the field for this property. It is a "."-separated path for
+	// fields in non-anonymous substructs.
 	name string
 
 	// Filter that can use metadata associated with the properties being optimized
@@ -1385,18 +1392,18 @@
 func newCommonValueExtractor(propertiesStruct interface{}) *commonValueExtractor {
 	structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
 	extractor := &commonValueExtractor{}
-	extractor.gatherFields(structType, nil)
+	extractor.gatherFields(structType, nil, "")
 	return extractor
 }
 
 // Gather the fields from the supplied structure type from which common values will
 // be extracted.
 //
-// This is recursive function. If it encounters an embedded field (no field name)
-// that is a struct then it will recurse into that struct passing in the accessor
-// for the field. That will then be used in the accessors for the fields in the
-// embedded struct.
-func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingStructAccessor fieldAccessorFunc) {
+// This is recursive function. If it encounters a struct then it will recurse
+// into it, passing in the accessor for the field and the struct name as prefix
+// for the nested fields. That will then be used in the accessors for the fields
+// in the embedded struct.
+func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingStructAccessor fieldAccessorFunc, namePrefix string) {
 	for f := 0; f < structType.NumField(); f++ {
 		field := structType.Field(f)
 		if field.PkgPath != "" {
@@ -1426,7 +1433,7 @@
 		// Save a copy of the field index for use in the function.
 		fieldIndex := f
 
-		name := field.Name
+		name := namePrefix + field.Name
 
 		fieldGetter := func(value reflect.Value) reflect.Value {
 			if containingStructAccessor != nil {
@@ -1448,9 +1455,15 @@
 			return value.Field(fieldIndex)
 		}
 
-		if field.Type.Kind() == reflect.Struct && field.Anonymous {
-			// Gather fields from the embedded structure.
-			e.gatherFields(field.Type, fieldGetter)
+		if field.Type.Kind() == reflect.Struct {
+			// Gather fields from the nested or embedded structure.
+			var subNamePrefix string
+			if field.Anonymous {
+				subNamePrefix = namePrefix
+			} else {
+				subNamePrefix = name + "."
+			}
+			e.gatherFields(field.Type, fieldGetter, subNamePrefix)
 		} else {
 			property := extractorProperty{
 				name,
@@ -1514,7 +1527,8 @@
 // Iterates over each exported field (capitalized name) and checks to see whether they
 // have the same value (using DeepEquals) across all the input properties. If it does not then no
 // change is made. Otherwise, the common value is stored in the field in the commonProperties
-// and the field in each of the input properties structure is set to its default value.
+// and the field in each of the input properties structure is set to its default value. Nested
+// structs are visited recursively and their non-struct fields are compared.
 func (e *commonValueExtractor) extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) error {
 	commonPropertiesValue := reflect.ValueOf(commonProperties)
 	commonStructValue := commonPropertiesValue.Elem()
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index f3f4a4a..7c3cdbd 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -313,6 +313,15 @@
 		options := []tradefed.Option{{Name: "force-root", Value: "false"}}
 		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
 	}
+	if len(s.testProperties.Data_device_bins) > 0 {
+		moduleName := s.Name()
+		remoteDir := "/data/local/tests/unrestricted/" + moduleName + "/"
+		options := []tradefed.Option{{Name: "cleanup", Value: "true"}}
+		for _, bin := range s.testProperties.Data_device_bins {
+			options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: remoteDir + bin})
+		}
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options})
+	}
 	s.testConfig = tradefed.AutoGenShellTestConfig(ctx, s.testProperties.Test_config,
 		s.testProperties.Test_config_template, s.testProperties.Test_suites, configs, s.testProperties.Auto_gen_config, s.outputFilePath.Base())
 
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 768c8e5..480f9b7 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -310,7 +310,8 @@
 		}}
 }
 
-func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
 	return fmt.Errorf("sysprop_library is not supposed to be part of apex modules")
 }
 
diff --git a/ui/build/config.go b/ui/build/config.go
index e9a8fc9..fe74ace 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -24,6 +24,7 @@
 	"time"
 
 	"android/soong/shared"
+
 	"github.com/golang/protobuf/proto"
 
 	smpb "android/soong/ui/metrics/metrics_proto"
@@ -183,6 +184,17 @@
 		"EMPTY_NINJA_FILE",
 	)
 
+	if ret.UseGoma() {
+		ctx.Println("Goma for Android is being deprecated and replaced with RBE. See go/rbe_for_android for instructions on how to use RBE.")
+		ctx.Println()
+		ctx.Println("See go/goma_android_exceptions for exceptions.")
+		ctx.Fatalln("USE_GOMA flag is no longer supported.")
+	}
+
+	if ret.ForceUseGoma() {
+		ret.environ.Set("USE_GOMA", "true")
+	}
+
 	// Tell python not to spam the source tree with .pyc files.
 	ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
 
@@ -286,8 +298,9 @@
 	}
 
 	b := &smpb.BuildConfig{
-		UseGoma: proto.Bool(config.UseGoma()),
-		UseRbe:  proto.Bool(config.UseRBE()),
+		ForceUseGoma: proto.Bool(config.ForceUseGoma()),
+		UseGoma:      proto.Bool(config.UseGoma()),
+		UseRbe:       proto.Bool(config.UseRBE()),
 	}
 	ctx.Metrics.BuildConfig(b)
 }
@@ -778,6 +791,18 @@
 	return c.totalRAM
 }
 
+// ForceUseGoma determines whether we should override Goma deprecation
+// and use Goma for the current build or not.
+func (c *configImpl) ForceUseGoma() bool {
+	if v, ok := c.environ.Get("FORCE_USE_GOMA"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
 func (c *configImpl) UseGoma() bool {
 	if v, ok := c.environ.Get("USE_GOMA"); ok {
 		v = strings.TrimSpace(v)
@@ -827,6 +852,11 @@
 }
 
 func (c *configImpl) logDir() string {
+	for _, f := range []string{"RBE_log_dir", "FLAG_log_dir"} {
+		if v, ok := c.environ.Get(f); ok {
+			return v
+		}
+	}
 	if c.Dist() {
 		return filepath.Join(c.DistDir(), "logs")
 	}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index c4b829d..182c544 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -151,13 +151,3 @@
 		ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err)
 	}
 }
-
-// PrintGomaDeprecation prints a PSA on the deprecation of Goma if it is set for the build.
-func PrintGomaDeprecation(ctx Context, config Config) {
-	if config.UseGoma() {
-		fmt.Fprintln(ctx.Writer, "")
-		fmt.Fprintln(ctx.Writer, "Goma for Android is being deprecated and replaced with RBE.")
-		fmt.Fprintln(ctx.Writer, "See go/goma_android_deprecation for more details.")
-		fmt.Fprintln(ctx.Writer, "")
-	}
-}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index d7c53ec..05efe13 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -399,6 +399,7 @@
 type BuildConfig struct {
 	UseGoma              *bool    `protobuf:"varint,1,opt,name=use_goma,json=useGoma" json:"use_goma,omitempty"`
 	UseRbe               *bool    `protobuf:"varint,2,opt,name=use_rbe,json=useRbe" json:"use_rbe,omitempty"`
+	ForceUseGoma         *bool    `protobuf:"varint,3,opt,name=force_use_goma,json=forceUseGoma" json:"force_use_goma,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -443,6 +444,13 @@
 	return false
 }
 
+func (m *BuildConfig) GetForceUseGoma() bool {
+	if m != nil && m.ForceUseGoma != nil {
+		return *m.ForceUseGoma
+	}
+	return false
+}
+
 type PerfInfo struct {
 	// The description for the phase/action/part while the tool running.
 	Desc *string `protobuf:"bytes,1,opt,name=desc" json:"desc,omitempty"`
@@ -764,69 +772,70 @@
 }
 
 var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 1021 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xef, 0x6e, 0xdb, 0x36,
-	0x10, 0xaf, 0x12, 0x27, 0xb6, 0x4e, 0xb1, 0xab, 0x30, 0xed, 0xa2, 0xb6, 0x08, 0x66, 0x18, 0x6b,
-	0x11, 0x0c, 0x6b, 0x5a, 0x64, 0x45, 0x50, 0x04, 0xc5, 0x00, 0xc7, 0x09, 0xb2, 0x2e, 0x48, 0x5c,
-	0x30, 0x7f, 0x56, 0x6c, 0x1f, 0x04, 0x5a, 0xa2, 0x13, 0x75, 0x96, 0x28, 0x90, 0x54, 0x91, 0xf4,
-	0x1d, 0xf6, 0x54, 0x7b, 0x96, 0xbd, 0xc6, 0x30, 0xf0, 0x28, 0xd9, 0xca, 0xe6, 0xad, 0x41, 0xbf,
-	0x89, 0xf7, 0xfb, 0xc3, 0x3b, 0xf2, 0x78, 0x36, 0xb4, 0x53, 0xae, 0x65, 0x12, 0xa9, 0xad, 0x5c,
-	0x0a, 0x2d, 0xc8, 0x9a, 0x12, 0x22, 0xbb, 0x0c, 0x47, 0x45, 0x32, 0x89, 0xc3, 0x12, 0xea, 0xfd,
-	0x05, 0xe0, 0x1d, 0xdb, 0xef, 0x3d, 0xa6, 0x38, 0x79, 0x09, 0x0f, 0x2c, 0x21, 0x66, 0x9a, 0x87,
-	0x3a, 0x49, 0xb9, 0xd2, 0x2c, 0xcd, 0x03, 0xa7, 0xeb, 0x6c, 0x2e, 0x52, 0x82, 0xd8, 0x3e, 0xd3,
-	0xfc, 0xac, 0x42, 0xc8, 0x23, 0x68, 0x59, 0x45, 0x12, 0x07, 0x0b, 0x5d, 0x67, 0xd3, 0xa5, 0x4d,
-	0x5c, 0xbf, 0x8d, 0xc9, 0x2e, 0x3c, 0xca, 0x27, 0x4c, 0x8f, 0x85, 0x4c, 0xc3, 0x8f, 0x5c, 0xaa,
-	0x44, 0x64, 0x61, 0x24, 0x62, 0x9e, 0xb1, 0x94, 0x07, 0x8b, 0xc8, 0x5d, 0xaf, 0x08, 0x17, 0x16,
-	0x1f, 0x94, 0x30, 0x79, 0x0a, 0x1d, 0xcd, 0xe4, 0x25, 0xd7, 0x61, 0x2e, 0x45, 0x5c, 0x44, 0x3a,
-	0x68, 0xa0, 0xa0, 0x6d, 0xa3, 0xef, 0x6c, 0x90, 0xc4, 0xf0, 0xa0, 0xa4, 0xd9, 0x24, 0x3e, 0x32,
-	0x99, 0xb0, 0x4c, 0x07, 0x4b, 0x5d, 0x67, 0xb3, 0xb3, 0xfd, 0x7c, 0x6b, 0x4e, 0xcd, 0x5b, 0xb5,
-	0x7a, 0xb7, 0xf6, 0x0c, 0x72, 0x61, 0x45, 0xbb, 0x8b, 0x07, 0x27, 0x87, 0x94, 0x58, 0xbf, 0x3a,
-	0x40, 0x86, 0xe0, 0x95, 0xbb, 0x30, 0x19, 0x5d, 0x05, 0xcb, 0x68, 0xfe, 0xf4, 0xb3, 0xe6, 0x7d,
-	0x19, 0x5d, 0xed, 0x36, 0xcf, 0x4f, 0x8e, 0x4e, 0x86, 0x3f, 0x9f, 0x50, 0xb0, 0x16, 0x26, 0x48,
-	0xb6, 0x60, 0xad, 0x66, 0x38, 0xcd, 0xba, 0x89, 0x25, 0xae, 0xce, 0x88, 0x55, 0x02, 0xdf, 0x41,
-	0x99, 0x56, 0x18, 0xe5, 0xc5, 0x94, 0xde, 0x42, 0xba, 0x6f, 0x91, 0x41, 0x5e, 0x54, 0xec, 0x23,
-	0x70, 0xaf, 0x84, 0x2a, 0x93, 0x75, 0xbf, 0x28, 0xd9, 0x96, 0x31, 0xc0, 0x54, 0x29, 0xb4, 0xd1,
-	0x6c, 0x3b, 0x8b, 0xad, 0x21, 0x7c, 0x91, 0xa1, 0x67, 0x4c, 0xb6, 0xb3, 0x18, 0x3d, 0xd7, 0xa1,
-	0x89, 0x9e, 0x42, 0x05, 0x1e, 0xd6, 0xb0, 0x6c, 0x96, 0x43, 0x45, 0x7a, 0xe5, 0x66, 0x42, 0x85,
-	0xfc, 0x5a, 0x4b, 0x16, 0xac, 0x20, 0xec, 0x59, 0xf8, 0xc0, 0x84, 0xa6, 0x9c, 0x48, 0x0a, 0xa5,
-	0x8c, 0x45, 0x7b, 0xc6, 0x19, 0x98, 0xd8, 0x50, 0x91, 0x67, 0x70, 0xbf, 0xc6, 0xc1, 0xb4, 0x3b,
-	0xb6, 0x7d, 0xa6, 0x2c, 0x4c, 0xe4, 0x39, 0xac, 0xd5, 0x78, 0xd3, 0x12, 0xef, 0xdb, 0x83, 0x9d,
-	0x72, 0x6b, 0x79, 0x8b, 0x42, 0x87, 0x71, 0x22, 0x03, 0xdf, 0xe6, 0x2d, 0x0a, 0xbd, 0x9f, 0x48,
-	0xf2, 0x03, 0x78, 0x8a, 0xeb, 0x22, 0x0f, 0xb5, 0x10, 0x13, 0x15, 0xac, 0x76, 0x17, 0x37, 0xbd,
-	0xed, 0x8d, 0xb9, 0x47, 0xf4, 0x8e, 0xcb, 0xf1, 0xdb, 0x6c, 0x2c, 0x28, 0xa0, 0xe2, 0xcc, 0x08,
-	0xc8, 0x2e, 0xb8, 0xbf, 0x31, 0x9d, 0x84, 0xb2, 0xc8, 0x54, 0x40, 0xee, 0xa2, 0x6e, 0x19, 0x3e,
-	0x2d, 0x32, 0x45, 0xde, 0x00, 0x58, 0x26, 0x8a, 0xd7, 0xee, 0x22, 0x76, 0x11, 0xad, 0xd4, 0x59,
-	0x92, 0x7d, 0x60, 0x56, 0xfd, 0xe0, 0x4e, 0x6a, 0x14, 0xa0, 0xfa, 0x7b, 0x58, 0xd2, 0x42, 0xb3,
-	0x49, 0xf0, 0xb0, 0xeb, 0x7c, 0x5e, 0x68, 0xb9, 0xe4, 0x02, 0xe6, 0x8d, 0xa2, 0xe0, 0x2b, 0xb4,
-	0x78, 0x36, 0xd7, 0xe2, 0xd4, 0xc4, 0xf0, 0x49, 0x96, 0x1d, 0x46, 0x57, 0xd5, 0x3f, 0x43, 0x64,
-	0x00, 0x2b, 0x56, 0x15, 0x89, 0x6c, 0x9c, 0x5c, 0x06, 0xeb, 0x68, 0xd8, 0x9d, 0x6b, 0x88, 0xc2,
-	0x01, 0xf2, 0xa8, 0x37, 0x9a, 0x2d, 0x7a, 0x2f, 0x61, 0xe5, 0xd6, 0xd3, 0x6f, 0x41, 0xe3, 0xfc,
-	0xf4, 0x80, 0xfa, 0xf7, 0x48, 0x1b, 0x5c, 0xf3, 0xb5, 0x7f, 0xb0, 0x77, 0x7e, 0xe8, 0x3b, 0xa4,
-	0x09, 0x66, 0x5c, 0xf8, 0x0b, 0xbd, 0x37, 0xd0, 0xc0, 0xe6, 0xf0, 0xa0, 0x6a, 0x76, 0xff, 0x9e,
-	0x41, 0xfb, 0xf4, 0xd8, 0x77, 0x88, 0x0b, 0x4b, 0x7d, 0x7a, 0xbc, 0xf3, 0xca, 0x5f, 0x30, 0xb1,
-	0xf7, 0xaf, 0x77, 0xfc, 0x45, 0x02, 0xb0, 0xfc, 0xfe, 0xf5, 0x4e, 0xb8, 0xf3, 0xca, 0x6f, 0xf4,
-	0xfa, 0xe0, 0xd5, 0x72, 0x31, 0xd3, 0xb4, 0x50, 0x3c, 0xbc, 0x14, 0x29, 0xc3, 0x99, 0xdb, 0xa2,
-	0xcd, 0x42, 0xf1, 0x43, 0x91, 0x32, 0xd3, 0x7c, 0x06, 0x92, 0x23, 0x8e, 0x73, 0xb6, 0x45, 0x97,
-	0x0b, 0xc5, 0xe9, 0x88, 0xf7, 0x7e, 0x77, 0xa0, 0x55, 0x9d, 0x31, 0x21, 0xd0, 0x88, 0xb9, 0x8a,
-	0x50, 0xec, 0x52, 0xfc, 0x36, 0x31, 0x1c, 0xb9, 0x76, 0x3c, 0xe3, 0x37, 0xd9, 0x00, 0x50, 0x9a,
-	0x49, 0x8d, 0x33, 0x1e, 0x87, 0x71, 0x83, 0xba, 0x18, 0x31, 0xa3, 0x9d, 0x3c, 0x01, 0x57, 0x72,
-	0x36, 0xb1, 0x68, 0x03, 0xd1, 0x96, 0x09, 0x20, 0xb8, 0x01, 0x90, 0xf2, 0x54, 0xc8, 0x9b, 0xb0,
-	0x50, 0x1c, 0x47, 0x6d, 0x83, 0xba, 0x36, 0x72, 0xae, 0x78, 0xef, 0x4f, 0x07, 0x3a, 0xc7, 0x22,
-	0x2e, 0x26, 0xfc, 0xec, 0x26, 0xe7, 0x98, 0xd5, 0xaf, 0xd5, 0xd5, 0xa8, 0x1b, 0xa5, 0x79, 0x8a,
-	0xd9, 0x75, 0xb6, 0x5f, 0xcc, 0x9f, 0x21, 0xb7, 0xa4, 0xf6, 0xa6, 0x4e, 0x51, 0x56, 0x9b, 0x26,
-	0xa3, 0x59, 0x94, 0x7c, 0x0d, 0x5e, 0x8a, 0x9a, 0x50, 0xdf, 0xe4, 0x55, 0x95, 0x90, 0x4e, 0x6d,
-	0xc8, 0x37, 0xd0, 0xc9, 0x8a, 0x34, 0x14, 0xe3, 0xd0, 0x06, 0x15, 0xd6, 0xdb, 0xa6, 0x2b, 0x59,
-	0x91, 0x0e, 0xc7, 0x76, 0x3f, 0xd5, 0x7b, 0x51, 0xde, 0x44, 0xe9, 0x7a, 0xeb, 0x3a, 0x5d, 0x58,
-	0x3a, 0x1d, 0x0e, 0x4f, 0xcc, 0xbd, 0xb7, 0xa0, 0x71, 0xdc, 0x3f, 0x3a, 0xf0, 0x17, 0x7a, 0x13,
-	0x78, 0x3c, 0x90, 0x89, 0x4e, 0x22, 0x36, 0x39, 0x57, 0x5c, 0xfe, 0x24, 0x0a, 0x99, 0xf1, 0x9b,
-	0xaa, 0x1b, 0xab, 0x43, 0x77, 0x6a, 0x87, 0xbe, 0x0b, 0xcd, 0xaa, 0xdb, 0x17, 0xfe, 0xa7, 0x39,
-	0x6b, 0x53, 0x94, 0x56, 0x82, 0xde, 0x08, 0x9e, 0xcc, 0xd9, 0x4d, 0xcd, 0x9a, 0xbf, 0x11, 0x15,
-	0x1f, 0x54, 0xe0, 0xe0, 0x0b, 0x9e, 0x7f, 0xb2, 0xff, 0x9d, 0x2d, 0x45, 0x71, 0xef, 0x0f, 0x07,
-	0x56, 0xff, 0xf5, 0xd4, 0x48, 0x00, 0xcd, 0xea, 0xdc, 0x1c, 0x3c, 0xb7, 0x6a, 0x49, 0x1e, 0x43,
-	0xab, 0xfc, 0x2d, 0xb2, 0x05, 0xb5, 0xe9, 0x74, 0x4d, 0xbe, 0x85, 0x55, 0x7c, 0xee, 0x21, 0x9b,
-	0x4c, 0x44, 0x14, 0x46, 0xa2, 0xc8, 0x74, 0xd9, 0x67, 0xf7, 0x11, 0xe8, 0x9b, 0xf8, 0xc0, 0x84,
-	0xc9, 0x26, 0xf8, 0x75, 0xae, 0x4a, 0x3e, 0x55, 0x4d, 0xd7, 0x99, 0x51, 0x4f, 0x93, 0x4f, 0xdc,
-	0x0c, 0xff, 0x94, 0x5d, 0x87, 0x57, 0x9c, 0xe5, 0x96, 0x66, 0xbb, 0xcf, 0x4b, 0xd9, 0xf5, 0x8f,
-	0x9c, 0xe5, 0x86, 0xb3, 0xf7, 0xf0, 0x97, 0x72, 0xbe, 0x94, 0x75, 0x87, 0xf8, 0xff, 0xe7, 0xef,
-	0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x03, 0x26, 0x59, 0x0f, 0x09, 0x00, 0x00,
+	// 1036 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xef, 0x4e, 0x1b, 0x47,
+	0x10, 0xcf, 0x61, 0x83, 0x7d, 0x73, 0xd8, 0x1c, 0x0b, 0x29, 0x97, 0x44, 0xa8, 0x96, 0xd5, 0x44,
+	0xa8, 0x6a, 0x48, 0x44, 0x23, 0x14, 0xa1, 0xa8, 0x12, 0x18, 0x44, 0x53, 0x04, 0x8e, 0x16, 0x4c,
+	0xa3, 0xf6, 0xc3, 0x69, 0x7d, 0xb7, 0x86, 0x4b, 0x7d, 0xb7, 0xd6, 0xee, 0x5e, 0x04, 0x79, 0x87,
+	0x3e, 0x55, 0x9f, 0xa5, 0xaf, 0x51, 0x55, 0x3b, 0x7b, 0x67, 0x1f, 0xad, 0xdb, 0xa0, 0x7c, 0xf3,
+	0xce, 0xef, 0xcf, 0xce, 0xec, 0xce, 0xce, 0x19, 0x5a, 0x29, 0xd7, 0x32, 0x89, 0xd4, 0xf6, 0x44,
+	0x0a, 0x2d, 0xc8, 0x9a, 0x12, 0x22, 0xbb, 0x0a, 0x87, 0x79, 0x32, 0x8e, 0xc3, 0x02, 0xea, 0xfe,
+	0x05, 0xe0, 0x9d, 0xda, 0xdf, 0x07, 0x4c, 0x71, 0xf2, 0x12, 0xd6, 0x2d, 0x21, 0x66, 0x9a, 0x87,
+	0x3a, 0x49, 0xb9, 0xd2, 0x2c, 0x9d, 0x04, 0x4e, 0xc7, 0xd9, 0xaa, 0x51, 0x82, 0xd8, 0x21, 0xd3,
+	0xfc, 0xa2, 0x44, 0xc8, 0x23, 0x68, 0x5a, 0x45, 0x12, 0x07, 0x0b, 0x1d, 0x67, 0xcb, 0xa5, 0x0d,
+	0x5c, 0xbf, 0x8d, 0xc9, 0x1e, 0x3c, 0x9a, 0x8c, 0x99, 0x1e, 0x09, 0x99, 0x86, 0x1f, 0xb9, 0x54,
+	0x89, 0xc8, 0xc2, 0x48, 0xc4, 0x3c, 0x63, 0x29, 0x0f, 0x6a, 0xc8, 0xdd, 0x28, 0x09, 0x97, 0x16,
+	0xef, 0x15, 0x30, 0x79, 0x0a, 0x6d, 0xcd, 0xe4, 0x15, 0xd7, 0xe1, 0x44, 0x8a, 0x38, 0x8f, 0x74,
+	0x50, 0x47, 0x41, 0xcb, 0x46, 0xdf, 0xd9, 0x20, 0x89, 0x61, 0xbd, 0xa0, 0xd9, 0x24, 0x3e, 0x32,
+	0x99, 0xb0, 0x4c, 0x07, 0x8b, 0x1d, 0x67, 0xab, 0xbd, 0xf3, 0x7c, 0x7b, 0x4e, 0xcd, 0xdb, 0x95,
+	0x7a, 0xb7, 0x0f, 0x0c, 0x72, 0x69, 0x45, 0x7b, 0xb5, 0xa3, 0xb3, 0x63, 0x4a, 0xac, 0x5f, 0x15,
+	0x20, 0x7d, 0xf0, 0x8a, 0x5d, 0x98, 0x8c, 0xae, 0x83, 0x25, 0x34, 0x7f, 0xfa, 0x59, 0xf3, 0x7d,
+	0x19, 0x5d, 0xef, 0x35, 0x06, 0x67, 0x27, 0x67, 0xfd, 0x9f, 0xcf, 0x28, 0x58, 0x0b, 0x13, 0x24,
+	0xdb, 0xb0, 0x56, 0x31, 0x9c, 0x66, 0xdd, 0xc0, 0x12, 0x57, 0x67, 0xc4, 0x32, 0x81, 0xef, 0xa0,
+	0x48, 0x2b, 0x8c, 0x26, 0xf9, 0x94, 0xde, 0x44, 0xba, 0x6f, 0x91, 0xde, 0x24, 0x2f, 0xd9, 0x27,
+	0xe0, 0x5e, 0x0b, 0x55, 0x24, 0xeb, 0x7e, 0x51, 0xb2, 0x4d, 0x63, 0x80, 0xa9, 0x52, 0x68, 0xa1,
+	0xd9, 0x4e, 0x16, 0x5b, 0x43, 0xf8, 0x22, 0x43, 0xcf, 0x98, 0xec, 0x64, 0x31, 0x7a, 0x6e, 0x40,
+	0x03, 0x3d, 0x85, 0x0a, 0x3c, 0xac, 0x61, 0xc9, 0x2c, 0xfb, 0x8a, 0x74, 0x8b, 0xcd, 0x84, 0x0a,
+	0xf9, 0x8d, 0x96, 0x2c, 0x58, 0x46, 0xd8, 0xb3, 0xf0, 0x91, 0x09, 0x4d, 0x39, 0x91, 0x14, 0x4a,
+	0x19, 0x8b, 0xd6, 0x8c, 0xd3, 0x33, 0xb1, 0xbe, 0x22, 0xcf, 0x60, 0xa5, 0xc2, 0xc1, 0xb4, 0xdb,
+	0xb6, 0x7d, 0xa6, 0x2c, 0x4c, 0xe4, 0x39, 0xac, 0x55, 0x78, 0xd3, 0x12, 0x57, 0xec, 0xc1, 0x4e,
+	0xb9, 0x95, 0xbc, 0x45, 0xae, 0xc3, 0x38, 0x91, 0x81, 0x6f, 0xf3, 0x16, 0xb9, 0x3e, 0x4c, 0x24,
+	0xf9, 0x01, 0x3c, 0xc5, 0x75, 0x3e, 0x09, 0xb5, 0x10, 0x63, 0x15, 0xac, 0x76, 0x6a, 0x5b, 0xde,
+	0xce, 0xe6, 0xdc, 0x23, 0x7a, 0xc7, 0xe5, 0xe8, 0x6d, 0x36, 0x12, 0x14, 0x50, 0x71, 0x61, 0x04,
+	0x64, 0x0f, 0xdc, 0xdf, 0x98, 0x4e, 0x42, 0x99, 0x67, 0x2a, 0x20, 0xf7, 0x51, 0x37, 0x0d, 0x9f,
+	0xe6, 0x99, 0x22, 0x6f, 0x00, 0x2c, 0x13, 0xc5, 0x6b, 0xf7, 0x11, 0xbb, 0x88, 0x96, 0xea, 0x2c,
+	0xc9, 0x3e, 0x30, 0xab, 0x5e, 0xbf, 0x97, 0x1a, 0x05, 0xa8, 0xfe, 0x1e, 0x16, 0xb5, 0xd0, 0x6c,
+	0x1c, 0x3c, 0xec, 0x38, 0x9f, 0x17, 0x5a, 0x2e, 0xb9, 0x84, 0x79, 0xa3, 0x28, 0xf8, 0x0a, 0x2d,
+	0x9e, 0xcd, 0xb5, 0x38, 0x37, 0x31, 0x7c, 0x92, 0x45, 0x87, 0xd1, 0x55, 0xf5, 0xcf, 0x10, 0xe9,
+	0xc1, 0xb2, 0x55, 0x45, 0x22, 0x1b, 0x25, 0x57, 0xc1, 0x06, 0x1a, 0x76, 0xe6, 0x1a, 0xa2, 0xb0,
+	0x87, 0x3c, 0xea, 0x0d, 0x67, 0x8b, 0xee, 0x4b, 0x58, 0xbe, 0xf3, 0xf4, 0x9b, 0x50, 0x1f, 0x9c,
+	0x1f, 0x51, 0xff, 0x01, 0x69, 0x81, 0x6b, 0x7e, 0x1d, 0x1e, 0x1d, 0x0c, 0x8e, 0x7d, 0x87, 0x34,
+	0xc0, 0x8c, 0x0b, 0x7f, 0xa1, 0xfb, 0x06, 0xea, 0xd8, 0x1c, 0x1e, 0x94, 0xcd, 0xee, 0x3f, 0x30,
+	0xe8, 0x3e, 0x3d, 0xf5, 0x1d, 0xe2, 0xc2, 0xe2, 0x3e, 0x3d, 0xdd, 0x7d, 0xe5, 0x2f, 0x98, 0xd8,
+	0xfb, 0xd7, 0xbb, 0x7e, 0x8d, 0x00, 0x2c, 0xbd, 0x7f, 0xbd, 0x1b, 0xee, 0xbe, 0xf2, 0xeb, 0xdd,
+	0x2b, 0xf0, 0x2a, 0xb9, 0x98, 0x69, 0x9a, 0x2b, 0x1e, 0x5e, 0x89, 0x94, 0xe1, 0xcc, 0x6d, 0xd2,
+	0x46, 0xae, 0xf8, 0xb1, 0x48, 0x99, 0x69, 0x3e, 0x03, 0xc9, 0x21, 0xc7, 0x39, 0xdb, 0xa4, 0x4b,
+	0xb9, 0xe2, 0x74, 0xc8, 0xc9, 0x37, 0xd0, 0x1e, 0x09, 0x19, 0xf1, 0x70, 0xaa, 0xac, 0x21, 0xbe,
+	0x8c, 0xd1, 0x81, 0x95, 0x77, 0x7f, 0x77, 0xa0, 0x59, 0xde, 0x04, 0x21, 0x50, 0x8f, 0xb9, 0x8a,
+	0x70, 0x0b, 0x97, 0xe2, 0x6f, 0x13, 0xc3, 0xc1, 0x6c, 0x87, 0x38, 0xfe, 0x26, 0x9b, 0x00, 0x4a,
+	0x33, 0xa9, 0xf1, 0x4b, 0x80, 0xb6, 0x75, 0xea, 0x62, 0xc4, 0x7c, 0x00, 0xc8, 0x13, 0x70, 0x25,
+	0x67, 0x63, 0x8b, 0xd6, 0x11, 0x6d, 0x9a, 0x00, 0x82, 0x9b, 0x00, 0x29, 0x4f, 0x85, 0xbc, 0x35,
+	0x79, 0xe1, 0x40, 0xae, 0x53, 0xd7, 0x46, 0x06, 0x8a, 0x77, 0xff, 0x74, 0xa0, 0x7d, 0x2a, 0xe2,
+	0x7c, 0xcc, 0x2f, 0x6e, 0x27, 0x1c, 0xb3, 0xfa, 0xb5, 0xbc, 0x40, 0x75, 0xab, 0x34, 0x4f, 0x31,
+	0xbb, 0xf6, 0xce, 0x8b, 0xf9, 0x93, 0xe6, 0x8e, 0xd4, 0xde, 0xe7, 0x39, 0xca, 0x2a, 0x33, 0x67,
+	0x38, 0x8b, 0x92, 0xaf, 0xc1, 0x4b, 0x51, 0x13, 0xea, 0xdb, 0x49, 0x59, 0x25, 0xa4, 0x53, 0x1b,
+	0x73, 0x8c, 0x59, 0x9e, 0x86, 0x62, 0x14, 0xda, 0xa0, 0xc2, 0x7a, 0x5b, 0x74, 0x39, 0xcb, 0xd3,
+	0xfe, 0xc8, 0xee, 0xa7, 0xba, 0x2f, 0x8a, 0xfb, 0x2a, 0x5c, 0xef, 0x5c, 0xba, 0x0b, 0x8b, 0xe7,
+	0xfd, 0xfe, 0x99, 0xe9, 0x8e, 0x26, 0xd4, 0x4f, 0xf7, 0x4f, 0x8e, 0xfc, 0x85, 0xee, 0x18, 0x1e,
+	0xf7, 0x64, 0xa2, 0x93, 0x88, 0x8d, 0x07, 0x8a, 0xcb, 0x9f, 0x44, 0x2e, 0x33, 0x7e, 0x5b, 0xf6,
+	0x6c, 0x79, 0xe8, 0x4e, 0xe5, 0xd0, 0xf7, 0xa0, 0x51, 0xbe, 0x89, 0x85, 0xff, 0x69, 0xe1, 0xca,
+	0xac, 0xa5, 0xa5, 0xa0, 0x3b, 0x84, 0x27, 0x73, 0x76, 0x53, 0xb3, 0x27, 0x52, 0x8f, 0xf2, 0x0f,
+	0x2a, 0x70, 0xf0, 0x9d, 0xcf, 0x3f, 0xd9, 0xff, 0xce, 0x96, 0xa2, 0xb8, 0xfb, 0x87, 0x03, 0xab,
+	0xff, 0x7a, 0x90, 0x24, 0x80, 0x46, 0x79, 0x6e, 0x0e, 0x9e, 0x5b, 0xb9, 0x24, 0x8f, 0xa1, 0x59,
+	0x7c, 0xb1, 0x6c, 0x41, 0x2d, 0x3a, 0x5d, 0x93, 0x6f, 0x61, 0x15, 0x87, 0x42, 0xc8, 0xc6, 0x63,
+	0x11, 0x85, 0x91, 0xc8, 0x33, 0x5d, 0xf4, 0xd9, 0x0a, 0x02, 0xfb, 0x26, 0xde, 0x33, 0x61, 0xb2,
+	0x05, 0x7e, 0x95, 0xab, 0x92, 0x4f, 0x65, 0xd3, 0xb5, 0x67, 0xd4, 0xf3, 0xe4, 0x13, 0x37, 0x9f,
+	0x88, 0x94, 0xdd, 0x84, 0xd7, 0x9c, 0x4d, 0x2c, 0xcd, 0x76, 0x9f, 0x97, 0xb2, 0x9b, 0x1f, 0x39,
+	0x9b, 0x18, 0xce, 0xc1, 0xc3, 0x5f, 0x8a, 0x29, 0x54, 0xd4, 0x1d, 0xe2, 0xbf, 0xa4, 0xbf, 0x03,
+	0x00, 0x00, 0xff, 0xff, 0x85, 0xc5, 0xe0, 0x4b, 0x35, 0x09, 0x00, 0x00,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 6559ba3..4d6118b 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -102,6 +102,8 @@
   optional bool use_goma = 1;
 
   optional bool use_rbe = 2;
+
+  optional bool force_use_goma = 3;
 }
 
 message PerfInfo {