Merge "Propagate all sanitizer flags in SDK snapshots."
diff --git a/android/androidmk.go b/android/androidmk.go
index fafbfd6..e90e5f0 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_"
 
diff --git a/android/apex.go b/android/apex.go
index f857ec6..7ae46d4 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
 		}
@@ -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 ddcdbb7..9768340 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -49,6 +49,14 @@
 	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".
@@ -119,13 +127,6 @@
 	}
 }
 
-// TODO: Merge with FutureApiLevel
-var CurrentApiLevel = ApiLevel{
-	value:     "current",
-	number:    10000,
-	isPreview: true,
-}
-
 var NoneApiLevel = ApiLevel{
 	value: "(no version)",
 	// Not 0 because we don't want this to compare equal with the first preview.
@@ -180,7 +181,7 @@
 	}
 
 	if raw == "current" {
-		return CurrentApiLevel, nil
+		return FutureApiLevel, nil
 	}
 
 	for _, preview := range ctx.Config().PreviewApiLevels() {
@@ -261,8 +262,19 @@
 			"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.PlatformSdkVersionInt()
+			apiLevelsMap["current"] = config.PlatformSdkVersion().FinalOrFutureInt()
 		}
 
 		return apiLevelsMap
@@ -300,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 4141138..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
+        },
     },
 }
 */
@@ -578,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)
 
@@ -609,7 +621,6 @@
 	Generic OsClass = iota
 	Device
 	Host
-	HostCross
 )
 
 func (class OsClass) String() string {
@@ -620,8 +631,6 @@
 		return "device"
 	case Host:
 		return "host"
-	case HostCross:
-		return "host cross"
 	default:
 		panic(fmt.Errorf("unknown class %d", class))
 	}
@@ -681,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 {
@@ -739,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 {
@@ -913,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)
@@ -964,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 == "" {
@@ -1240,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)
@@ -1280,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)
@@ -1508,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,
@@ -1515,6 +1548,7 @@
 				NativeBridge:             nativeBridgeEnabled,
 				NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
 				NativeBridgeRelativePath: nativeBridgeRelativePathStr,
+				HostCross:                hostCross,
 			})
 	}
 
@@ -1531,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)
@@ -1771,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 1c06e8c..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 {
@@ -648,7 +652,7 @@
 
 func (c *config) FinalApiLevels() []ApiLevel {
 	var levels []ApiLevel
-	for i := 1; i <= c.PlatformSdkVersionInt(); i++ {
+	for i := 1; i <= c.PlatformSdkVersion().FinalOrFutureInt(); i++ {
 		levels = append(levels, uncheckedFinalApiLevel(i))
 	}
 	return levels
@@ -672,19 +676,18 @@
 	return append(levels, c.PreviewApiLevels()...)
 }
 
-func (c *config) DefaultAppTargetSdkInt() int {
-	if Bool(c.productVariables.Platform_sdk_final) {
-		return c.PlatformSdkVersionInt()
-	} else {
-		return FutureApiLevel
-	}
-}
-
-func (c *config) DefaultAppTargetSdk() string {
+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/module.go b/android/module.go
index d794dc2..c4e43c2 100644
--- a/android/module.go
+++ b/android/module.go
@@ -176,6 +176,31 @@
 	// 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
@@ -245,6 +270,24 @@
 	// 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
@@ -324,24 +367,6 @@
 	// additional dependencies.
 	Phony(phony string, deps ...Path)
 
-	// 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))
-
 	// GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods,
 	// but do not exist.
 	GetMissingDependencies() []string
@@ -600,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"`
@@ -923,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) {}
@@ -950,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
 }
 
@@ -1046,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 {
@@ -1066,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
 	}
 }
 
@@ -1681,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)
@@ -2001,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(, )?`)
@@ -2036,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()
 }
@@ -2075,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 {
@@ -2535,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 5acd992..7a10477 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -315,6 +315,14 @@
 	// 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 {
@@ -550,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_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/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/apex/androidmk.go b/apex/androidmk.go
index 1b53a67..c4fe3a3 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -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..e8294a8 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -795,7 +795,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()},
@@ -1952,27 +1952,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 +2040,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_test.go b/apex/apex_test.go
index 5c49667..2099109 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 {
@@ -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 {
@@ -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")
 }
@@ -6130,7 +6223,7 @@
 			name: "mylib",
 			srcs: ["mylib.cpp"],
 			stubs: {
-				versions: ["10000"],
+				versions: ["current"],
 			},
 			apex_available: ["myapex"],
 		}
@@ -6140,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/androidmk.go b/cc/androidmk.go
index 5bdbac6..f0e6152 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -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...)
 				}
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 6584380..a813428 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -100,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
@@ -238,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"`
@@ -354,7 +360,7 @@
 	useClangLld(actx ModuleContext) bool
 	isForPlatform() bool
 	apexVariationName() string
-	apexSdkVersion() int
+	apexSdkVersion() android.ApiLevel
 	hasStubsVariants() bool
 	isStubs() bool
 	bootstrap() bool
@@ -615,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 {
@@ -950,9 +956,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)
@@ -1328,7 +1334,7 @@
 	return ctx.mod.ApexVariationName()
 }
 
-func (ctx *moduleContextImpl) apexSdkVersion() int {
+func (ctx *moduleContextImpl) apexSdkVersion() android.ApiLevel {
 	return ctx.mod.apexSdkVersion
 }
 
@@ -1839,6 +1845,8 @@
 
 	deps := c.deps(ctx)
 
+	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
+
 	variantNdkLibs := []string{}
 	variantLateNdkLibs := []string{}
 	if ctx.Os() == android.Android {
@@ -2317,7 +2325,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()) {
@@ -2421,7 +2429,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
 			}
 
@@ -2465,7 +2473,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
@@ -2488,7 +2496,7 @@
 						// 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
@@ -2559,17 +2567,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(
@@ -3012,21 +3023,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
@@ -3050,11 +3048,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
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 132d091..e0d4640 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3780,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 f745820..bb5c7bf 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -354,7 +354,9 @@
 			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()))
 		}
 	}
 
@@ -390,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
 		}
diff --git a/cc/library.go b/cc/library.go
index 7b09b11..bf7868f 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -19,8 +19,6 @@
 	"io"
 	"path/filepath"
 	"regexp"
-	"sort"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -340,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
 
@@ -684,7 +678,6 @@
 }
 
 type libraryInterface interface {
-	getWholeStaticMissingDeps() []string
 	static() bool
 	shared() bool
 	objs() Objects
@@ -891,8 +884,6 @@
 
 	library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
-	library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
-
 	ctx.CheckbuildFile(outputFile)
 
 	return outputFile
@@ -1184,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
 }
@@ -1520,20 +1507,18 @@
 }
 
 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
 	}
 }
 
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/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 d1903b8..acdc767 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -45,6 +45,8 @@
 		Thin  *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
+	GlobalThin *bool `blueprint:"mutated"`
+
 	// Dep properties indicate that this module needs to be built with LTO
 	// since it is an object dependency of an LTO module.
 	FullDep bool `blueprint:"mutated"`
@@ -68,6 +70,8 @@
 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") {
+		lto.Properties.GlobalThin = boolPtr(true)
 	}
 }
 
@@ -91,7 +95,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"
@@ -104,7 +108,7 @@
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables")
 		}
 
-		if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && lto.useClangLld(ctx) {
+		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()
@@ -129,25 +133,37 @@
 
 // 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 {
+	if Bool(lto.Properties.GlobalThin) {
+		if !lto.Never() && !lto.FullLTO() {
+			return true
+		}
+	}
+
+	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")
 		}
@@ -168,11 +184,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
 				}
 			}
@@ -189,19 +205,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 4c6d98c..5682d1c 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -118,8 +118,8 @@
 			versionStrs = append(versionStrs, version.String())
 		}
 	}
-	versions = append(versions, android.CurrentApiLevel)
-	versionStrs = append(versionStrs, android.CurrentApiLevel.String())
+	versions = append(versions, android.FutureApiLevel)
+	versionStrs = append(versionStrs, android.FutureApiLevel.String())
 
 	modules := ctx.CreateVariations(versionStrs...)
 	for i, module := range modules {
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/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/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/droiddoc.go b/java/droiddoc.go
index 85a61dd..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")
@@ -1412,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/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/sdk.go b/java/sdk.go
index 56fa12b..f599265 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"),
@@ -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..0d29a37 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1203,6 +1203,7 @@
 		Sdk_version                      *string
 		System_modules                   *string
 		Libs                             []string
+		Output_javadoc_comments          *bool
 		Arg_files                        []string
 		Args                             *string
 		Java_version                     *string
@@ -1278,6 +1279,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 +1884,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
 }
@@ -2078,7 +2085,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
 }
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/rust/binary.go b/rust/binary.go
index 1d02453..e95cb3a 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -24,6 +24,10 @@
 }
 
 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"`
 }
 
 type binaryDecorator struct {
@@ -131,9 +135,16 @@
 
 func (binary *binaryDecorator) autoDep(ctx BaseModuleContext) autoDep {
 	// Binaries default to dylib dependencies for device, rlib for host.
+	if Bool(binary.Properties.Prefer_rlib) {
+		return rlibAutoDep
+	}
 	if ctx.Device() {
 		return dylibAutoDep
 	} else {
 		return rlibAutoDep
 	}
 }
+
+func (binary *binaryDecorator) staticStd(ctx *depsContext) bool {
+	return binary.baseCompiler.staticStd(ctx) || Bool(binary.Properties.Prefer_rlib)
+}
diff --git a/rust/binary_test.go b/rust/binary_test.go
index 394abfc..f31a7fc 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"],
@@ -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, `
diff --git a/rust/bindgen.go b/rust/bindgen.go
index cafdb8b..d8d126d 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -21,6 +21,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	cc_config "android/soong/cc/config"
 )
 
 var (
@@ -56,7 +57,11 @@
 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
@@ -81,6 +86,22 @@
 	// "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]"
 	Custom_bindgen string `android:"path"`
 
+	// 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.
 }
 
@@ -90,6 +111,45 @@
 	Properties BindgenProperties
 }
 
+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.Properties.Cpp_std) != "" && String(b.Properties.C_std) != "" {
+		ctx.PropertyErrorf("c_std", "c_std and cpp_std cannot both be defined at the same time.")
+	}
+
+	if String(b.Properties.Cpp_std) != "" {
+		if String(b.Properties.Cpp_std) == "experimental" {
+			stdVersion = cc_config.ExperimentalCppStdVersion
+		} else if String(b.Properties.Cpp_std) == "default" {
+			stdVersion = cc_config.CppStdVersion
+		} else {
+			stdVersion = String(b.Properties.Cpp_std)
+		}
+	} else if b.Properties.C_std != nil {
+		if String(b.Properties.C_std) == "experimental" {
+			stdVersion = cc_config.ExperimentalCStdVersion
+		} else if String(b.Properties.C_std) == "default" {
+			stdVersion = cc_config.CStdVersion
+		} else {
+			stdVersion = String(b.Properties.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 {
 	ccToolchain := ctx.RustModule().ccToolchain(ctx)
 
@@ -134,6 +194,17 @@
 		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++")
+	} else {
+		cflags = append(cflags, "-x c")
+	}
+
 	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
 	var cmd, cmdDesc string
@@ -169,7 +240,9 @@
 
 // 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()
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 191da9b..e69bce2 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -41,7 +41,7 @@
 			export_include_dirs: ["static_include"],
 		}
 	`)
-	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"])
@@ -73,7 +73,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 +82,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/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/library.go b/rust/library.go
index 4931f19..2792c5b 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -15,6 +15,7 @@
 package rust
 
 import (
+	"fmt"
 	"regexp"
 	"strings"
 
@@ -77,6 +78,8 @@
 	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)
@@ -103,6 +106,7 @@
 	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
@@ -115,6 +119,7 @@
 	setDylib()
 	setShared()
 	setStatic()
+	setSource()
 
 	// Set libstd linkage
 	setRlibStd()
@@ -158,6 +163,10 @@
 	return library.static() || library.MutatedProperties.VariantIsStaticStd
 }
 
+func (library *libraryDecorator) source() bool {
+	return library.MutatedProperties.VariantIsSource
+}
+
 func (library *libraryDecorator) buildRlib() bool {
 	return library.MutatedProperties.BuildRlib && BoolDefault(library.Properties.Rlib.Enabled, true)
 }
@@ -210,13 +219,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()))
 	}
 }
 
@@ -518,41 +531,68 @@
 	}
 }
 
+// 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 {
-		switch library := m.compiler.(type) {
-		case libraryInterface:
-			if library.buildRlib() && library.buildDylib() {
-				variants := []string{"rlib", "dylib"}
-				if m.sourceProvider != nil {
-					variants = append(variants, "")
-				}
-				modules := mctx.CreateLocalVariations(variants...)
+	// Only mutate on Rust libraries.
+	m, ok := mctx.Module().(*Module)
+	if !ok || m.compiler == nil {
+		return
+	}
+	library, ok := m.compiler.(libraryInterface)
+	if !ok {
+		return
+	}
 
-				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()
-			}
+	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 m.sourceProvider != nil {
-				// Alias the non-library variant to the empty-string variant.
-				mctx.AliasVariation("")
-			}
+	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) {
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 d22acea..1f8b904 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -87,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) {
@@ -687,12 +686,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() {
@@ -743,6 +755,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 {
@@ -751,8 +764,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 {
@@ -1000,11 +1015,11 @@
 
 	actx.AddVariationDependencies(
 		append(rlibDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: "rlib"}}...),
+			{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() {
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/sdk/testing.go b/sdk/testing.go
index 68058ee..ae1e448 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,7 +89,7 @@
 	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
diff --git a/sdk/update.go b/sdk/update.go
index 65baa5e..a10e852 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -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)
 			}
 
@@ -731,7 +730,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 +1060,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 +1084,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 +1321,7 @@
 			}
 		}
 		if s.HostSupported() {
-			if osType.Class == android.Host || osType.Class == android.HostCross {
+			if osType.Class == android.Host {
 				osTypes = append(osTypes, osType)
 			}
 		}
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 9bd0877..fe74ace 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -298,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)
 }
@@ -851,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/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 {