Merge "Move PrimaryModule, FinalModule, VisitAllModuleVariants to BaseModuleContext"
diff --git a/android/api_levels.go b/android/api_levels.go
index 0872066..ddcdbb7 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -24,6 +24,192 @@
 	RegisterSingletonType("api_levels", ApiLevelsSingleton)
 }
 
+// An API level, which may be a finalized (numbered) API, a preview (codenamed)
+// API, or the future API level (10000). Can be parsed from a string with
+// ApiLevelFromUser or ApiLevelOrPanic.
+//
+// The different *types* of API levels are handled separately. Currently only
+// Java has these, and they're managed with the sdkKind enum of the sdkSpec. A
+// future cleanup should be to migrate sdkSpec to using ApiLevel instead of its
+// sdkVersion int, and to move sdkSpec into this package.
+type ApiLevel struct {
+	// The string representation of the API level.
+	value string
+
+	// A number associated with the API level. The exact value depends on
+	// whether this API level is a preview or final API.
+	//
+	// For final API levels, this is the assigned version number.
+	//
+	// For preview API levels, this value has no meaning except to index known
+	// previews to determine ordering.
+	number int
+
+	// Identifies this API level as either a preview or final API level.
+	isPreview bool
+}
+
+// Returns the canonical name for this API level. For a finalized API level
+// this will be the API number as a string. For a preview API level this
+// will be the codename, or "current".
+func (this ApiLevel) String() string {
+	return this.value
+}
+
+// Returns true if this is a non-final API level.
+func (this ApiLevel) IsPreview() bool {
+	return this.isPreview
+}
+
+// Returns true if this is the unfinalized "current" API level. This means
+// different things across Java and native. Java APIs do not use explicit
+// codenames, so all non-final codenames are grouped into "current". For native
+// explicit codenames are typically used, and current is the union of all
+// non-final APIs, including those that may not yet be in any codename.
+//
+// Note that in a build where the platform is final, "current" will not be a
+// preview API level but will instead be canonicalized to the final API level.
+func (this ApiLevel) IsCurrent() bool {
+	return this.value == "current"
+}
+
+// Returns -1 if the current API level is less than the argument, 0 if they
+// are equal, and 1 if it is greater than the argument.
+func (this ApiLevel) CompareTo(other ApiLevel) int {
+	if this.IsPreview() && !other.IsPreview() {
+		return 1
+	} else if !this.IsPreview() && other.IsPreview() {
+		return -1
+	}
+
+	if this.number < other.number {
+		return -1
+	} else if this.number == other.number {
+		return 0
+	} else {
+		return 1
+	}
+}
+
+func (this ApiLevel) EqualTo(other ApiLevel) bool {
+	return this.CompareTo(other) == 0
+}
+
+func (this ApiLevel) GreaterThan(other ApiLevel) bool {
+	return this.CompareTo(other) > 0
+}
+
+func (this ApiLevel) GreaterThanOrEqualTo(other ApiLevel) bool {
+	return this.CompareTo(other) >= 0
+}
+
+func (this ApiLevel) LessThan(other ApiLevel) bool {
+	return this.CompareTo(other) < 0
+}
+
+func (this ApiLevel) LessThanOrEqualTo(other ApiLevel) bool {
+	return this.CompareTo(other) <= 0
+}
+
+func uncheckedFinalApiLevel(num int) ApiLevel {
+	return ApiLevel{
+		value:     strconv.Itoa(num),
+		number:    num,
+		isPreview: false,
+	}
+}
+
+// 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.
+	number:    -1,
+	isPreview: true,
+}
+
+// The first version that introduced 64-bit ABIs.
+var FirstLp64Version = uncheckedFinalApiLevel(21)
+
+// The first API level that does not require NDK code to link
+// libandroid_support.
+var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21)
+
+// If the `raw` input is the codename of an API level has been finalized, this
+// function returns the API level number associated with that API level. If the
+// input is *not* a finalized codename, the input is returned unmodified.
+//
+// For example, at the time of writing, R has been finalized as API level 30,
+// but S is in development so it has no number assigned. For the following
+// inputs:
+//
+// * "30" -> "30"
+// * "R" -> "30"
+// * "S" -> "S"
+func ReplaceFinalizedCodenames(ctx EarlyModuleContext, raw string) string {
+	num, ok := getFinalCodenamesMap(ctx.Config())[raw]
+	if !ok {
+		return raw
+	}
+
+	return strconv.Itoa(num)
+}
+
+// Converts the given string `raw` to an ApiLevel, possibly returning an error.
+//
+// `raw` must be non-empty. Passing an empty string results in a panic.
+//
+// "current" will return CurrentApiLevel, which is the ApiLevel associated with
+// an arbitrary future release (often referred to as API level 10000).
+//
+// Finalized codenames will be interpreted as their final API levels, not the
+// preview of the associated releases. R is now API 30, not the R preview.
+//
+// Future codenames return a preview API level that has no associated integer.
+//
+// Inputs that are not "current", known previews, or convertible to an integer
+// will return an error.
+func ApiLevelFromUser(ctx EarlyModuleContext, raw string) (ApiLevel, error) {
+	if raw == "" {
+		panic("API level string must be non-empty")
+	}
+
+	if raw == "current" {
+		return CurrentApiLevel, nil
+	}
+
+	for _, preview := range ctx.Config().PreviewApiLevels() {
+		if raw == preview.String() {
+			return preview, nil
+		}
+	}
+
+	canonical := ReplaceFinalizedCodenames(ctx, raw)
+	asInt, err := strconv.Atoi(canonical)
+	if err != nil {
+		return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical)
+	}
+
+	apiLevel := uncheckedFinalApiLevel(asInt)
+	return apiLevel, nil
+}
+
+// Converts an API level string `raw` into an ApiLevel in the same method as
+// `ApiLevelFromUser`, but the input is assumed to have no errors and any errors
+// will panic instead of returning an error.
+func ApiLevelOrPanic(ctx EarlyModuleContext, raw string) ApiLevel {
+	value, err := ApiLevelFromUser(ctx, raw)
+	if err != nil {
+		panic(err.Error())
+	}
+	return value
+}
+
 func ApiLevelsSingleton() Singleton {
 	return &apiLevelsSingleton{}
 }
@@ -52,6 +238,37 @@
 	return PathForOutput(ctx, "api_levels.json")
 }
 
+var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap")
+
+func getFinalCodenamesMap(config Config) map[string]int {
+	return config.Once(finalCodenamesMapKey, func() interface{} {
+		apiLevelsMap := map[string]int{
+			"G":     9,
+			"I":     14,
+			"J":     16,
+			"J-MR1": 17,
+			"J-MR2": 18,
+			"K":     19,
+			"L":     21,
+			"L-MR1": 22,
+			"M":     23,
+			"N":     24,
+			"N-MR1": 25,
+			"O":     26,
+			"O-MR1": 27,
+			"P":     28,
+			"Q":     29,
+			"R":     30,
+		}
+
+		if Bool(config.productVariables.Platform_sdk_final) {
+			apiLevelsMap["current"] = config.PlatformSdkVersionInt()
+		}
+
+		return apiLevelsMap
+	}).(map[string]int)
+}
+
 var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
 
 func getApiLevelsMap(config Config) map[string]int {
diff --git a/android/arch.go b/android/arch.go
index ba113b2..4141138 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -125,6 +125,7 @@
 	Arm64: {
 		"armv8_a",
 		"armv8_2a",
+		"armv8-2a-dotprod",
 		"cortex-a53",
 		"cortex-a55",
 		"cortex-a72",
@@ -172,6 +173,9 @@
 	Arm: {
 		"neon",
 	},
+	Arm64: {
+		"dotprod",
+	},
 	X86: {
 		"ssse3",
 		"sse4",
@@ -209,6 +213,11 @@
 			"neon",
 		},
 	},
+	Arm64: {
+		"armv8-2a-dotprod": {
+			"dotprod",
+		},
+	},
 	X86: {
 		"amberlake": {
 			"ssse3",
@@ -579,7 +588,7 @@
 
 	osArchTypeMap = map[OsType][]ArchType{
 		Linux:       []ArchType{X86, X86_64},
-		LinuxBionic: []ArchType{X86_64},
+		LinuxBionic: []ArchType{Arm64, X86_64},
 		Darwin:      []ArchType{X86_64},
 		Windows:     []ArchType{X86, X86_64},
 		Android:     []ArchType{Arm, Arm64, X86, X86_64},
@@ -1434,20 +1443,15 @@
 			//         key: value,
 			//     },
 			// },
-			// TODO(ccross): is this still necessary with native bridge?
 			if os.Class == Device {
-				if (arch.ArchType == X86 && (hasArmAbi(arch) ||
-					hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
-					(arch.ArchType == Arm &&
-						hasX86AndroidArch(ctx.Config().Targets[Android])) {
+				if arch.ArchType == X86 && (hasArmAbi(arch) ||
+					hasArmAndroidArch(ctx.Config().Targets[Android])) {
 					field := "Arm_on_x86"
 					prefix := "target.arm_on_x86"
 					m.appendProperties(ctx, genProps, targetProp, field, prefix)
 				}
-				if (arch.ArchType == X86_64 && (hasArmAbi(arch) ||
-					hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
-					(arch.ArchType == Arm &&
-						hasX8664AndroidArch(ctx.Config().Targets[Android])) {
+				if arch.ArchType == X86_64 && (hasArmAbi(arch) ||
+					hasArmAndroidArch(ctx.Config().Targets[Android])) {
 					field := "Arm_on_x86_64"
 					prefix := "target.arm_on_x86_64"
 					m.appendProperties(ctx, genProps, targetProp, field, prefix)
@@ -1594,27 +1598,7 @@
 // hasArmArch returns true if targets has at least non-native_bridge arm Android arch
 func hasArmAndroidArch(targets []Target) bool {
 	for _, target := range targets {
-		if target.Os == Android && target.Arch.ArchType == Arm && target.NativeBridge == NativeBridgeDisabled {
-			return true
-		}
-	}
-	return false
-}
-
-// hasX86Arch returns true if targets has at least x86 Android arch
-func hasX86AndroidArch(targets []Target) bool {
-	for _, target := range targets {
-		if target.Os == Android && target.Arch.ArchType == X86 {
-			return true
-		}
-	}
-	return false
-}
-
-// hasX8664Arch returns true if targets has at least x86_64 Android arch
-func hasX8664AndroidArch(targets []Target) bool {
-	for _, target := range targets {
-		if target.Os == Android && target.Arch.ArchType == X86_64 {
+		if target.Os == Android && target.Arch.ArchType == Arm {
 			return true
 		}
 	}
@@ -1653,9 +1637,10 @@
 		{"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
 		{"arm64", "armv8-a", "exynos-m1", []string{"arm64-v8a"}},
 		{"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
-		{"arm64", "armv8-2a", "cortex-a75", []string{"arm64-v8a"}},
-		{"arm64", "armv8-2a", "cortex-a76", []string{"arm64-v8a"}},
 		{"arm64", "armv8-2a", "kryo385", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a-dotprod", "cortex-a55", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a-dotprod", "cortex-a75", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a-dotprod", "cortex-a76", []string{"arm64-v8a"}},
 		{"x86", "", "", []string{"x86"}},
 		{"x86", "atom", "", []string{"x86"}},
 		{"x86", "haswell", "", []string{"x86"}},
diff --git a/android/config.go b/android/config.go
index dd622e5..1c06e8c 100644
--- a/android/config.go
+++ b/android/config.go
@@ -642,8 +642,34 @@
 	return String(c.productVariables.Platform_base_os)
 }
 
-func (c *config) MinSupportedSdkVersion() int {
-	return 16
+func (c *config) MinSupportedSdkVersion() ApiLevel {
+	return uncheckedFinalApiLevel(16)
+}
+
+func (c *config) FinalApiLevels() []ApiLevel {
+	var levels []ApiLevel
+	for i := 1; i <= c.PlatformSdkVersionInt(); i++ {
+		levels = append(levels, uncheckedFinalApiLevel(i))
+	}
+	return levels
+}
+
+func (c *config) PreviewApiLevels() []ApiLevel {
+	var levels []ApiLevel
+	for i, codename := range c.PlatformVersionActiveCodenames() {
+		levels = append(levels, ApiLevel{
+			value:     codename,
+			number:    i,
+			isPreview: true,
+		})
+	}
+	return levels
+}
+
+func (c *config) AllSupportedApiLevels() []ApiLevel {
+	var levels []ApiLevel
+	levels = append(levels, c.FinalApiLevels()...)
+	return append(levels, c.PreviewApiLevels()...)
 }
 
 func (c *config) DefaultAppTargetSdkInt() int {
diff --git a/android/makevars.go b/android/makevars.go
index 86f4b42..374986e 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -18,7 +18,6 @@
 	"bytes"
 	"fmt"
 	"sort"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -31,7 +30,7 @@
 }
 
 func androidMakeVarsProvider(ctx MakeVarsContext) {
-	ctx.Strict("MIN_SUPPORTED_SDK_VERSION", strconv.Itoa(ctx.Config().MinSupportedSdkVersion()))
+	ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String())
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -234,7 +233,7 @@
 	}
 
 	ctx.VisitAllModules(func(m Module) {
-		if provider, ok := m.(ModuleMakeVarsProvider); ok {
+		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() {
 			mctx := &makeVarsContext{
 				SingletonContext: ctx,
 			}
diff --git a/android/module.go b/android/module.go
index 35491ca..d3341d2 100644
--- a/android/module.go
+++ b/android/module.go
@@ -408,6 +408,8 @@
 	SkipInstall()
 	IsSkipInstall() bool
 	MakeUninstallable()
+	ReplacedByPrebuilt()
+	IsReplacedByPrebuilt() bool
 	ExportedToMake() bool
 	InitRc() Paths
 	VintfFragments() Paths
@@ -564,7 +566,7 @@
 	// control whether this module compiles for 32-bit, 64-bit, or both.  Possible values
 	// are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
 	// architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
-	// platform
+	// platform).
 	Compile_multilib *string `android:"arch_variant"`
 
 	Target struct {
@@ -692,6 +694,9 @@
 
 	SkipInstall bool `blueprint:"mutated"`
 
+	// Whether the module has been replaced by a prebuilt
+	ReplacedByPrebuilt bool `blueprint:"mutated"`
+
 	// Disabled by mutators. If set to true, it overrides Enabled property.
 	ForcedDisabled bool `blueprint:"mutated"`
 
@@ -1217,6 +1222,15 @@
 	m.SkipInstall()
 }
 
+func (m *ModuleBase) ReplacedByPrebuilt() {
+	m.commonProperties.ReplacedByPrebuilt = true
+	m.SkipInstall()
+}
+
+func (m *ModuleBase) IsReplacedByPrebuilt() bool {
+	return m.commonProperties.ReplacedByPrebuilt
+}
+
 func (m *ModuleBase) ExportedToMake() bool {
 	return m.commonProperties.NamespaceExportedToMake
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 269ad5d..734871b 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -253,7 +253,7 @@
 			p := m.(PrebuiltInterface).Prebuilt()
 			if p.usePrebuilt(ctx, s) {
 				p.properties.UsePrebuilt = true
-				s.SkipInstall()
+				s.ReplacedByPrebuilt()
 			}
 		})
 	}
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index 1dcf199..e2555e4 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -14,6 +14,8 @@
 
 package android
 
+import "path/filepath"
+
 func init() {
 	RegisterModuleType("prebuilt_build_tool", prebuiltBuildToolFactory)
 }
@@ -58,13 +60,18 @@
 	installedPath := PathForModuleOut(ctx, t.ModuleBase.Name())
 	deps := PathsForModuleSrc(ctx, t.properties.Deps)
 
+	var fromPath = sourcePath.String()
+	if !filepath.IsAbs(fromPath) {
+		fromPath = "$$PWD/" + fromPath
+	}
+
 	ctx.Build(pctx, BuildParams{
 		Rule:      Symlink,
 		Output:    installedPath,
 		Input:     sourcePath,
 		Implicits: deps,
 		Args: map[string]string{
-			"fromPath": "$$PWD/" + sourcePath.String(),
+			"fromPath": fromPath,
 		},
 	})
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 610f667..5c49667 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2282,30 +2282,40 @@
 	ensureListContains(t, requireNativeLibs, ":vndk")
 }
 
-func TestVendorApex_withPrebuiltFirmware(t *testing.T) {
-	ctx, _ := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			prebuilts: ["myfirmware"],
-			vendor: true,
-		}
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-		prebuilt_firmware {
-			name: "myfirmware",
-			src: "myfirmware.bin",
-			filename_from_src: true,
-			vendor: true,
-		}
-	`)
-
-	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
-		"firmware/myfirmware.bin",
-	})
+func TestApex_withPrebuiltFirmware(t *testing.T) {
+	testCases := []struct {
+		name           string
+		additionalProp string
+	}{
+		{"system apex with prebuilt_firmware", ""},
+		{"vendor apex with prebuilt_firmware", "vendor: true,"},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx, _ := testApex(t, `
+				apex {
+					name: "myapex",
+					key: "myapex.key",
+					prebuilts: ["myfirmware"],
+					`+tc.additionalProp+`
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				prebuilt_firmware {
+					name: "myfirmware",
+					src: "myfirmware.bin",
+					filename_from_src: true,
+					`+tc.additionalProp+`
+				}
+			`)
+			ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+				"etc/firmware/myfirmware.bin",
+			})
+		})
+	}
 }
 
 func TestAndroidMk_UseVendorRequired(t *testing.T) {
diff --git a/cc/Android.bp b/cc/Android.bp
index 831911e..ff2cdf3 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -13,6 +13,7 @@
     ],
     srcs: [
         "androidmk.go",
+        "api_level.go",
         "builder.go",
         "cc.go",
         "ccdeps.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 380b4e9..5bdbac6 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -451,7 +451,7 @@
 }
 
 func (c *stubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.SubName = ndkLibrarySuffix + "." + c.properties.ApiLevel
+	entries.SubName = ndkLibrarySuffix + "." + c.apiLevel.String()
 	entries.Class = "SHARED_LIBRARIES"
 
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
diff --git a/cc/api_level.go b/cc/api_level.go
new file mode 100644
index 0000000..c93d6ed
--- /dev/null
+++ b/cc/api_level.go
@@ -0,0 +1,71 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"fmt"
+
+	"android/soong/android"
+)
+
+func minApiForArch(ctx android.BaseModuleContext,
+	arch android.ArchType) android.ApiLevel {
+
+	switch arch {
+	case android.Arm, android.X86:
+		return ctx.Config().MinSupportedSdkVersion()
+	case android.Arm64, android.X86_64:
+		return android.FirstLp64Version
+	default:
+		panic(fmt.Errorf("Unknown arch %q", arch))
+	}
+}
+
+func nativeApiLevelFromUser(ctx android.BaseModuleContext,
+	raw string) (android.ApiLevel, error) {
+
+	min := minApiForArch(ctx, ctx.Arch().ArchType)
+	if raw == "minimum" {
+		return min, nil
+	}
+
+	value, err := android.ApiLevelFromUser(ctx, raw)
+	if err != nil {
+		return android.NoneApiLevel, err
+	}
+
+	if value.LessThan(min) {
+		return min, nil
+	}
+
+	return value, nil
+}
+
+func nativeApiLevelFromUserWithDefault(ctx android.BaseModuleContext,
+	raw string, defaultValue string) (android.ApiLevel, error) {
+	if raw == "" {
+		raw = defaultValue
+	}
+	return nativeApiLevelFromUser(ctx, raw)
+}
+
+func nativeApiLevelOrPanic(ctx android.BaseModuleContext,
+	raw string) android.ApiLevel {
+	value, err := nativeApiLevelFromUser(ctx, raw)
+	if err != nil {
+		panic(err.Error())
+	}
+	return value
+}
diff --git a/cc/cc.go b/cc/cc.go
index fb78b20..6584380 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -630,7 +630,7 @@
 func (c *Module) ApiLevel() string {
 	if c.linker != nil {
 		if stub, ok := c.linker.(*stubDecorator); ok {
-			return stub.properties.ApiLevel
+			return stub.apiLevel.String()
 		}
 	}
 	panic(fmt.Errorf("ApiLevel() called on non-stub library module: %q", c.BaseModuleName()))
@@ -1704,11 +1704,13 @@
 		feature.begin(ctx)
 	}
 	if ctx.useSdk() && c.IsSdkVariant() {
-		version, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch())
+		version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion())
 		if err != nil {
 			ctx.PropertyErrorf("sdk_version", err.Error())
+			c.Properties.Sdk_version = nil
+		} else {
+			c.Properties.Sdk_version = StringPtr(version.String())
 		}
-		c.Properties.Sdk_version = StringPtr(version)
 	}
 }
 
@@ -3143,13 +3145,6 @@
 	return c.Properties.IsSdkVariant || c.AlwaysSdk()
 }
 
-func getCurrentNdkPrebuiltVersion(ctx DepsContext) string {
-	if ctx.Config().PlatformSdkVersionInt() > config.NdkMaxPrebuiltVersionInt {
-		return strconv.Itoa(config.NdkMaxPrebuiltVersionInt)
-	}
-	return ctx.Config().PlatformSdkVersion()
-}
-
 func kytheExtractAllFactory() android.Singleton {
 	return &kytheExtractAllSingleton{}
 }
diff --git a/cc/compiler.go b/cc/compiler.go
index c268dec..f745820 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -358,6 +358,10 @@
 		}
 	}
 
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_NATIVE_BRIDGE__")
+	}
+
 	instructionSet := String(compiler.Properties.Instruction_set)
 	if flags.RequiredInstructionSet != "" {
 		instructionSet = flags.RequiredInstructionSet
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 6275064..ce4bdfb 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -23,6 +23,8 @@
         "x86_linux_host.go",
         "x86_linux_bionic_host.go",
         "x86_windows_host.go",
+
+        "arm64_linux_host.go",
     ],
     testSrcs: [
         "tidy_test.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 62d8cc8..e6024aa 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -34,6 +34,9 @@
 		"armv8-2a": []string{
 			"-march=armv8.2-a",
 		},
+		"armv8-2a-dotprod": []string{
+			"-march=armv8.2-a+dotprod",
+		},
 	}
 
 	arm64Ldflags = []string{
@@ -100,6 +103,7 @@
 
 	pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
 	pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
+	pctx.StaticVariable("Arm64ClangArmv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
 
 	pctx.StaticVariable("Arm64ClangCortexA53Cflags",
 		strings.Join(arm64ClangCpuVariantCflags["cortex-a53"], " "))
@@ -121,6 +125,7 @@
 	arm64ClangArchVariantCflagsVar = map[string]string{
 		"armv8-a":  "${config.Arm64ClangArmv8ACflags}",
 		"armv8-2a": "${config.Arm64ClangArmv82ACflags}",
+		"armv8-2a-dotprod": "${config.Arm64ClangArmv82ADotprodCflags}",
 	}
 
 	arm64ClangCpuVariantCflagsVar = map[string]string{
@@ -198,6 +203,7 @@
 	switch arch.ArchVariant {
 	case "armv8-a":
 	case "armv8-2a":
+	case "armv8-2a-dotprod":
 		// Nothing extra for armv8-a/armv8-2a
 	default:
 		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
new file mode 100644
index 0000000..74642c2
--- /dev/null
+++ b/cc/config/arm64_linux_host.go
@@ -0,0 +1,94 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"android/soong/android"
+	"strings"
+)
+
+var (
+	// This is a host toolchain but flags for device toolchain are required
+	// as the flags are actually for Bionic-based builds.
+	linuxCrossCflags = ClangFilterUnknownCflags(append(deviceGlobalCflags,
+		// clang by default enables PIC when the clang triple is set to *-android.
+		// See toolchain/llvm-project/clang/lib/Driver/ToolChains/CommonArgs.cpp#920.
+		// However, for this host target, we don't set "-android" to avoid __ANDROID__ macro
+		// which stands for "Android device target". Keeping PIC on is required because
+		// many modules we have (e.g. Bionic) assume PIC.
+		"-fpic",
+	))
+
+	linuxCrossLdflags = ClangFilterUnknownCflags([]string{
+		"-Wl,-z,noexecstack",
+		"-Wl,-z,relro",
+		"-Wl,-z,now",
+		"-Wl,--build-id=md5",
+		"-Wl,--warn-shared-textrel",
+		"-Wl,--fatal-warnings",
+		"-Wl,--hash-style=gnu",
+		"-Wl,--no-undefined-version",
+	})
+)
+
+func init() {
+	pctx.StaticVariable("LinuxBionicArm64Cflags", strings.Join(linuxCrossCflags, " "))
+	pctx.StaticVariable("LinuxBionicArm64Ldflags", strings.Join(linuxCrossLdflags, " "))
+}
+
+// toolchain config for ARM64 Linux CrossHost. Almost everything is the same as the ARM64 Android
+// target. The overridden methods below show the differences.
+type toolchainLinuxArm64 struct {
+	toolchainArm64
+}
+
+func (toolchainLinuxArm64) ClangTriple() string {
+	// Note the absence of "-android" suffix. The compiler won't define __ANDROID__
+	return "aarch64-linux"
+}
+
+func (toolchainLinuxArm64) ClangCflags() string {
+	// The inherited flags + extra flags
+	return "${config.Arm64ClangCflags} ${config.LinuxBionicArm64Cflags}"
+}
+
+func linuxArm64ToolchainFactory(arch android.Arch) Toolchain {
+	archVariant := "armv8-a" // for host, default to armv8-a
+	toolchainClangCflags := []string{arm64ClangArchVariantCflagsVar[archVariant]}
+
+	// We don't specify CPU architecture for host. Conservatively assume
+	// the host CPU needs the fix
+	extraLdflags := "-Wl,--fix-cortex-a53-843419"
+
+	ret := toolchainLinuxArm64{}
+
+	// add the extra ld and lld flags
+	ret.toolchainArm64.ldflags = strings.Join([]string{
+		"${config.Arm64Ldflags}",
+		"${config.LinuxBionicArm64Ldflags}",
+		extraLdflags,
+	}, " ")
+	ret.toolchainArm64.lldflags = strings.Join([]string{
+		"${config.Arm64Lldflags}",
+		"${config.LinuxBionicArm64Ldflags}",
+		extraLdflags,
+	}, " ")
+	ret.toolchainArm64.toolchainClangCflags = strings.Join(toolchainClangCflags, " ")
+	return &ret
+}
+
+func init() {
+	registerToolchainFactory(android.LinuxBionic, android.Arm64, linuxArm64ToolchainFactory)
+}
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 7db405c..441bff2 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -42,7 +42,6 @@
 	"-Wno-literal-suffix",
 	"-Wno-maybe-uninitialized",
 	"-Wno-old-style-declaration",
-	"-Wno-psabi",
 	"-Wno-unused-but-set-parameter",
 	"-Wno-unused-but-set-variable",
 	"-Wno-unused-local-typedefs",
@@ -93,7 +92,9 @@
 // updated, some checks enabled by this module may be disabled if they have
 // become more strict, or if they are a new match for a wildcard group like
 // `modernize-*`.
-var ClangTidyDisableChecks = []string{}
+var ClangTidyDisableChecks = []string{
+	"misc-no-recursion",
+}
 
 func init() {
 	pctx.StaticVariable("ClangExtraCflags", strings.Join([]string{
@@ -103,6 +104,10 @@
 		// not emit the table by default on Android since NDK still uses GNU binutils.
 		"-faddrsig",
 
+		// Turn on -fcommon explicitly, since Clang now defaults to -fno-common. The cleanup bug
+		// tracking this is http://b/151457797.
+		"-fcommon",
+
 		// Help catch common 32/64-bit errors.
 		"-Werror=int-conversion",
 
@@ -183,6 +188,8 @@
 		"-Wno-enum-enum-conversion",                 // http://b/154138986
 		"-Wno-enum-float-conversion",                // http://b/154255917
 		"-Wno-pessimizing-move",                     // http://b/154270751
+		// New warnings to be fixed after clang-r399163
+		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
 	}, " "))
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -205,6 +212,9 @@
 		"-Wno-xor-used-as-pow",
 		// http://b/145211022
 		"-Wno-final-dtor-non-final-class",
+
+		// http://b/165945989
+		"-Wno-psabi",
 	}, " "))
 }
 
diff --git a/cc/config/global.go b/cc/config/global.go
index 32f163d..f9b3cc8 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -114,6 +114,12 @@
 	noOverrideGlobalCflags = []string{
 		"-Werror=int-to-pointer-cast",
 		"-Werror=pointer-to-int-cast",
+		// http://b/161386391 for -Wno-void-pointer-to-enum-cast
+		"-Wno-void-pointer-to-enum-cast",
+		// http://b/161386391 for -Wno-void-pointer-to-int-cast
+		"-Wno-void-pointer-to-int-cast",
+		// http://b/161386391 for -Wno-pointer-to-int-cast
+		"-Wno-pointer-to-int-cast",
 		"-Werror=fortify-source",
 	}
 
@@ -126,12 +132,10 @@
 	ExperimentalCStdVersion   = "gnu11"
 	ExperimentalCppStdVersion = "gnu++2a"
 
-	NdkMaxPrebuiltVersionInt = 27
-
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r383902b"
-	ClangDefaultShortVersion = "11.0.2"
+	ClangDefaultVersion      = "clang-r399163"
+	ClangDefaultShortVersion = "11.0.4"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index b5b5553..b77df79 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -39,6 +39,9 @@
 		// Get 64-bit off_t and related functions.
 		"-D_FILE_OFFSET_BITS=64",
 
+		// Don't adjust the layout of bitfields like msvc does.
+		"-mno-ms-bitfields",
+
 		"--sysroot ${WindowsGccRoot}/${WindowsGccTriple}",
 	}
 	windowsClangCflags = append(ClangFilterUnknownCflags(windowsCflags), []string{}...)
diff --git a/cc/lto.go b/cc/lto.go
index 9868cdf..d1903b8 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -52,6 +52,9 @@
 
 	// Use clang lld instead of gnu ld.
 	Use_clang_lld *bool
+
+	// Use -fwhole-program-vtables cflag.
+	Whole_program_vtables *bool
 }
 
 type lto struct {
@@ -97,6 +100,10 @@
 		flags.Local.CFlags = append(flags.Local.CFlags, ltoFlag)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoFlag)
 
+		if Bool(lto.Properties.Whole_program_vtables) {
+			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) {
 			// Set appropriate ThinLTO cache policy
 			cacheDirFormat := "-Wl,--thinlto-cache-dir="
@@ -110,12 +117,11 @@
 			flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy)
 		}
 
-		// If the module does not have a profile, be conservative and do not inline
-		// or unroll loops during LTO, in order to prevent significant size bloat.
+		// If the module does not have a profile, be conservative and limit cross TU inline
+		// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
 		if !ctx.isPgoCompile() {
 			flags.Local.LdFlags = append(flags.Local.LdFlags,
-				"-Wl,-plugin-opt,-inline-threshold=0",
-				"-Wl,-plugin-opt,-unroll-threshold=0")
+				"-Wl,-plugin-opt,-import-instr-limit=5")
 		}
 	}
 	return flags
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index fe3efc0..4c6d98c 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -52,6 +51,10 @@
 	ndkKnownLibsLock sync.Mutex
 )
 
+// The First_version and Unversioned_until properties of this struct should not
+// be used directly, but rather through the ApiLevel returning methods
+// firstVersion() and unversionedUntil().
+
 // Creates a stub shared library based on the provided version file.
 //
 // Example:
@@ -77,9 +80,7 @@
 	// https://github.com/android-ndk/ndk/issues/265.
 	Unversioned_until *string
 
-	// Private property for use by the mutator that splits per-API level. Can be
-	// one of <number:sdk_version> or <codename> or "current" passed to
-	// "ndkstubgen.py" as it is
+	// Use via apiLevel on the stubDecorator.
 	ApiLevel string `blueprint:"mutated"`
 
 	// True if this API is not yet ready to be shipped in the NDK. It will be
@@ -96,125 +97,33 @@
 	versionScriptPath     android.ModuleGenPath
 	parsedCoverageXmlPath android.ModuleOutPath
 	installPath           android.Path
+
+	apiLevel         android.ApiLevel
+	firstVersion     android.ApiLevel
+	unversionedUntil android.ApiLevel
 }
 
-// OMG GO
-func intMax(a int, b int) int {
-	if a > b {
-		return a
-	} else {
-		return b
-	}
-}
-
-func normalizeNdkApiLevel(ctx android.BaseModuleContext, apiLevel string,
-	arch android.Arch) (string, error) {
-
-	if apiLevel == "" {
-		panic("empty apiLevel not allowed")
-	}
-
-	if apiLevel == "current" {
-		return apiLevel, nil
-	}
-
-	minVersion := ctx.Config().MinSupportedSdkVersion()
-	firstArchVersions := map[android.ArchType]int{
-		android.Arm:    minVersion,
-		android.Arm64:  21,
-		android.X86:    minVersion,
-		android.X86_64: 21,
-	}
-
-	firstArchVersion, ok := firstArchVersions[arch.ArchType]
-	if !ok {
-		panic(fmt.Errorf("Arch %q not found in firstArchVersions", arch.ArchType))
-	}
-
-	if apiLevel == "minimum" {
-		return strconv.Itoa(firstArchVersion), nil
-	}
-
-	// If the NDK drops support for a platform version, we don't want to have to
-	// fix up every module that was using it as its SDK version. Clip to the
-	// supported version here instead.
-	version, err := strconv.Atoi(apiLevel)
-	if err != nil {
-		// Non-integer API levels are codenames.
-		return apiLevel, nil
-	}
-	version = intMax(version, minVersion)
-
-	return strconv.Itoa(intMax(version, firstArchVersion)), nil
-}
-
-func getFirstGeneratedVersion(firstSupportedVersion string, platformVersion int) (int, error) {
-	if firstSupportedVersion == "current" {
-		return platformVersion + 1, nil
-	}
-
-	return strconv.Atoi(firstSupportedVersion)
-}
-
-func shouldUseVersionScript(ctx android.BaseModuleContext, stub *stubDecorator) (bool, error) {
-	// unversioned_until is normally empty, in which case we should use the version script.
-	if String(stub.properties.Unversioned_until) == "" {
-		return true, nil
-	}
-
-	if String(stub.properties.Unversioned_until) == "current" {
-		if stub.properties.ApiLevel == "current" {
-			return true, nil
-		} else {
-			return false, nil
-		}
-	}
-
-	if stub.properties.ApiLevel == "current" {
-		return true, nil
-	}
-
-	unversionedUntil, err := android.ApiStrToNum(ctx, String(stub.properties.Unversioned_until))
-	if err != nil {
-		return true, err
-	}
-
-	version, err := android.ApiStrToNum(ctx, stub.properties.ApiLevel)
-	if err != nil {
-		return true, err
-	}
-
-	return version >= unversionedUntil, nil
+func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool {
+	return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil)
 }
 
 func generatePerApiVariants(ctx android.BottomUpMutatorContext, m *Module,
-	propName string, propValue string, perSplit func(*Module, string)) {
-	platformVersion := ctx.Config().PlatformSdkVersionInt()
+	from android.ApiLevel, perSplit func(*Module, android.ApiLevel)) {
 
-	firstSupportedVersion, err := normalizeNdkApiLevel(ctx, propValue,
-		ctx.Arch())
-	if err != nil {
-		ctx.PropertyErrorf(propName, err.Error())
+	var versions []android.ApiLevel
+	versionStrs := []string{}
+	for _, version := range ctx.Config().AllSupportedApiLevels() {
+		if version.GreaterThanOrEqualTo(from) {
+			versions = append(versions, version)
+			versionStrs = append(versionStrs, version.String())
+		}
 	}
-
-	firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion,
-		platformVersion)
-	if err != nil {
-		// In theory this is impossible because we've already run this through
-		// normalizeNdkApiLevel above.
-		ctx.PropertyErrorf(propName, err.Error())
-	}
-
-	var versionStrs []string
-	for version := firstGenVersion; version <= platformVersion; version++ {
-		versionStrs = append(versionStrs, strconv.Itoa(version))
-	}
-	versionStrs = append(versionStrs, ctx.Config().PlatformVersionActiveCodenames()...)
-	versionStrs = append(versionStrs, "current")
+	versions = append(versions, android.CurrentApiLevel)
+	versionStrs = append(versionStrs, android.CurrentApiLevel.String())
 
 	modules := ctx.CreateVariations(versionStrs...)
 	for i, module := range modules {
-		perSplit(module.(*Module), versionStrs[i])
+		perSplit(module.(*Module), versions[i])
 	}
 }
 
@@ -228,25 +137,56 @@
 					ctx.Module().Disable()
 					return
 				}
-				generatePerApiVariants(ctx, m, "first_version",
-					String(compiler.properties.First_version),
-					func(m *Module, version string) {
+				firstVersion, err := nativeApiLevelFromUser(ctx,
+					String(compiler.properties.First_version))
+				if err != nil {
+					ctx.PropertyErrorf("first_version", err.Error())
+					return
+				}
+				generatePerApiVariants(ctx, m, firstVersion,
+					func(m *Module, version android.ApiLevel) {
 						m.compiler.(*stubDecorator).properties.ApiLevel =
-							version
+							version.String()
 					})
 			} else if m.SplitPerApiLevel() && m.IsSdkVariant() {
 				if ctx.Os() != android.Android {
 					return
 				}
-				generatePerApiVariants(ctx, m, "min_sdk_version",
-					m.MinSdkVersion(), func(m *Module, version string) {
-						m.Properties.Sdk_version = &version
+				from, err := nativeApiLevelFromUser(ctx, m.MinSdkVersion())
+				if err != nil {
+					ctx.PropertyErrorf("min_sdk_version", err.Error())
+					return
+				}
+				generatePerApiVariants(ctx, m, from,
+					func(m *Module, version android.ApiLevel) {
+						m.Properties.Sdk_version = StringPtr(version.String())
 					})
 			}
 		}
 	}
 }
 
+func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool {
+	this.apiLevel = nativeApiLevelOrPanic(ctx, this.properties.ApiLevel)
+
+	var err error
+	this.firstVersion, err = nativeApiLevelFromUser(ctx,
+		String(this.properties.First_version))
+	if err != nil {
+		ctx.PropertyErrorf("first_version", err.Error())
+		return false
+	}
+
+	this.unversionedUntil, err = nativeApiLevelFromUserWithDefault(ctx,
+		String(this.properties.Unversioned_until), "minimum")
+	if err != nil {
+		ctx.PropertyErrorf("unversioned_until", err.Error())
+		return false
+	}
+
+	return true
+}
+
 func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
 	c.baseCompiler.compilerInit(ctx)
 
@@ -340,11 +280,16 @@
 		ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
 	}
 
+	if !c.initializeProperties(ctx) {
+		// Emits its own errors, so we don't need to.
+		return Objects{}
+	}
+
 	symbolFile := String(c.properties.Symbol_file)
 	objs, versionScript := compileStubLibrary(ctx, flags, symbolFile,
-		c.properties.ApiLevel, "")
+		c.apiLevel.String(), "")
 	c.versionScriptPath = versionScript
-	if c.properties.ApiLevel == "current" && ctx.PrimaryArch() {
+	if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
 		c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile)
 	}
 	return objs
@@ -366,12 +311,7 @@
 func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
 	objs Objects) android.Path {
 
-	useVersionScript, err := shouldUseVersionScript(ctx, stub)
-	if err != nil {
-		ctx.ModuleErrorf(err.Error())
-	}
-
-	if useVersionScript {
+	if shouldUseVersionScript(ctx, stub) {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
 		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
@@ -386,8 +326,6 @@
 
 func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
 	arch := ctx.Target().Arch.ArchType.Name
-	apiLevel := stub.properties.ApiLevel
-
 	// arm64 isn't actually a multilib toolchain, so unlike the other LP64
 	// architectures it's just installed to lib.
 	libDir := "lib"
@@ -396,7 +334,7 @@
 	}
 
 	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
-		"platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir))
+		"platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir))
 	stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
 }
 
diff --git a/cc/pgo.go b/cc/pgo.go
index 6bf0ad0..439d2f7 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -70,6 +70,7 @@
 	PgoPresent          bool `blueprint:"mutated"`
 	ShouldProfileModule bool `blueprint:"mutated"`
 	PgoCompile          bool `blueprint:"mutated"`
+	PgoInstrLink        bool `blueprint:"mutated"`
 }
 
 type pgo struct {
@@ -89,13 +90,12 @@
 }
 
 func (props *PgoProperties) addInstrumentationProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
-
-	flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag)
-	// The profile runtime is added below in deps().  Add the below
-	// flag, which is the only other link-time action performed by
-	// the Clang driver during link.
-	flags.Local.LdFlags = append(flags.Local.LdFlags, "-u__llvm_profile_runtime")
+	// Add to C flags iff PGO is explicitly enabled for this module.
+	if props.ShouldProfileModule {
+		flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag)
+	}
+	flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrumentFlag)
 	return flags
 }
 func (props *PgoProperties) addSamplingProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
@@ -250,10 +250,12 @@
 
 	if pgoBenchmarksMap["all"] == true || pgoBenchmarksMap["ALL"] == true {
 		pgo.Properties.ShouldProfileModule = true
+		pgo.Properties.PgoInstrLink = pgo.Properties.isInstrumentation()
 	} else {
 		for _, b := range pgo.Properties.Pgo.Benchmarks {
 			if pgoBenchmarksMap[b] == true {
 				pgo.Properties.ShouldProfileModule = true
+				pgo.Properties.PgoInstrLink = pgo.Properties.isInstrumentation()
 				break
 			}
 		}
@@ -286,10 +288,42 @@
 		return flags
 	}
 
-	props := pgo.Properties
+	// Deduce PgoInstrLink property i.e. whether this module needs to be
+	// linked with profile-generation flags.  Here, we're setting it if any
+	// dependency needs PGO instrumentation.  It is initially set in
+	// begin() if PGO is directly enabled for this module.
+	if ctx.static() && !ctx.staticBinary() {
+		// For static libraries, check if any whole_static_libs are
+		// linked with profile generation
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
+				if depTag.static() && depTag.wholeStatic {
+					if cc, ok := m.(*Module); ok {
+						if cc.pgo.Properties.PgoInstrLink {
+							pgo.Properties.PgoInstrLink = true
+						}
+					}
+				}
+			}
+		})
+	} else {
+		// For executables and shared libraries, check all static dependencies.
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
+				if depTag.static() {
+					if cc, ok := m.(*Module); ok {
+						if cc.pgo.Properties.PgoInstrLink {
+							pgo.Properties.PgoInstrLink = true
+						}
+					}
+				}
+			}
+		})
+	}
 
+	props := pgo.Properties
 	// Add flags to profile this module based on its profile_kind
-	if props.ShouldProfileModule && props.isInstrumentation() {
+	if (props.ShouldProfileModule && props.isInstrumentation()) || props.PgoInstrLink {
 		// Instrumentation PGO use and gather flags cannot coexist.
 		return props.addInstrumentationProfileGatherFlags(ctx, flags)
 	} else if props.ShouldProfileModule && props.isSampling() {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 3af65d6..9d1b016 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"path/filepath"
 )
 
 func init() {
@@ -324,35 +325,73 @@
 type prebuiltBinaryLinker struct {
 	*binaryDecorator
 	prebuiltLinker
+
+	toolPath android.OptionalPath
 }
 
 var _ prebuiltLinkerInterface = (*prebuiltBinaryLinker)(nil)
 
+func (p *prebuiltBinaryLinker) hostToolPath() android.OptionalPath {
+	return p.toolPath
+}
+
 func (p *prebuiltBinaryLinker) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 	// TODO(ccross): verify shared library dependencies
 	if len(p.properties.Srcs) > 0 {
-		stripFlags := flagsToStripFlags(flags)
-
 		fileName := p.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
 		in := p.Prebuilt.SingleSourcePath(ctx)
-
+		outputFile := android.PathForModuleOut(ctx, fileName)
 		p.unstrippedOutputFile = in
 
-		if p.stripper.NeedsStrip(ctx) {
-			stripped := android.PathForModuleOut(ctx, "stripped", fileName)
-			p.stripper.StripExecutableOrSharedLib(ctx, in, stripped, stripFlags)
-			in = stripped
-		}
+		if ctx.Host() {
+			// Host binaries are symlinked to their prebuilt source locations. That
+			// way they are executed directly from there so the linker resolves their
+			// shared library dependencies relative to that location (using
+			// $ORIGIN/../lib(64):$ORIGIN/lib(64) as RUNPATH). This way the prebuilt
+			// repository can supply the expected versions of the shared libraries
+			// without interference from what is in the out tree.
 
-		// Copy binaries to a name matching the final installed name
-		outputFile := android.PathForModuleOut(ctx, fileName)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        android.CpExecutable,
-			Description: "prebuilt",
-			Output:      outputFile,
-			Input:       in,
-		})
+			// These shared lib paths may point to copies of the libs in
+			// .intermediates, which isn't where the binary will load them from, but
+			// it's fine for dependency tracking. If a library dependency is updated,
+			// the symlink will get a new timestamp, along with any installed symlinks
+			// handled in make.
+			sharedLibPaths := deps.EarlySharedLibs
+			sharedLibPaths = append(sharedLibPaths, deps.SharedLibs...)
+			sharedLibPaths = append(sharedLibPaths, deps.LateSharedLibs...)
+
+			var fromPath = in.String()
+			if !filepath.IsAbs(fromPath) {
+				fromPath = "$$PWD/" + fromPath
+			}
+
+			ctx.Build(pctx, android.BuildParams{
+				Rule:      android.Symlink,
+				Output:    outputFile,
+				Input:     in,
+				Implicits: sharedLibPaths,
+				Args: map[string]string{
+					"fromPath": fromPath,
+				},
+			})
+
+			p.toolPath = android.OptionalPathForPath(outputFile)
+		} else {
+			if p.stripper.NeedsStrip(ctx) {
+				stripped := android.PathForModuleOut(ctx, "stripped", fileName)
+				p.stripper.StripExecutableOrSharedLib(ctx, in, stripped, flagsToStripFlags(flags))
+				in = stripped
+			}
+
+			// Copy binaries to a name matching the final installed name
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        android.CpExecutable,
+				Description: "prebuilt",
+				Output:      outputFile,
+				Input:       in,
+			})
+		}
 
 		return outputFile
 	}
@@ -379,6 +418,7 @@
 		binaryDecorator: binary,
 	}
 	module.linker = prebuilt
+	module.installer = prebuilt
 
 	module.AddProperties(&prebuilt.properties)
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index adb44bd..52416ac 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"path/filepath"
 	"testing"
 
 	"android/soong/android"
@@ -271,3 +272,52 @@
 	shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module)
 	assertString(t, shared.OutputFile().Path().Base(), "libbar.so")
 }
+
+func TestPrebuiltSymlinkedHostBinary(t *testing.T) {
+	if android.BuildOs != android.Linux {
+		t.Skipf("Skipping host prebuilt testing that is only supported on %s not %s", android.Linux, android.BuildOs)
+	}
+
+	ctx := testPrebuilt(t, `
+	cc_prebuilt_library_shared {
+		name: "libfoo",
+		device_supported: false,
+		host_supported: true,
+		target: {
+			linux_glibc_x86_64: {
+				srcs: ["linux_glibc_x86_64/lib64/libfoo.so"],
+			},
+		},
+	}
+
+	cc_prebuilt_binary {
+		name: "foo",
+		device_supported: false,
+		host_supported: true,
+		shared_libs: ["libfoo"],
+		target: {
+			linux_glibc_x86_64: {
+				srcs: ["linux_glibc_x86_64/bin/foo"],
+			},
+		},
+	}
+	`, map[string][]byte{
+		"libfoo.so": nil,
+		"foo":       nil,
+	})
+
+	fooRule := ctx.ModuleForTests("foo", "linux_glibc_x86_64").Rule("Symlink")
+	assertString(t, fooRule.Output.String(),
+		filepath.Join(buildDir, ".intermediates/foo/linux_glibc_x86_64/foo"))
+	assertString(t, fooRule.Args["fromPath"], "$$PWD/linux_glibc_x86_64/bin/foo")
+
+	var libfooDep android.Path
+	for _, dep := range fooRule.Implicits {
+		if dep.Base() == "libfoo.so" {
+			libfooDep = dep
+			break
+		}
+	}
+	assertString(t, libfooDep.String(),
+		filepath.Join(buildDir, ".intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so"))
+}
diff --git a/cc/stl.go b/cc/stl.go
index e18fe95..406fa3a 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -17,7 +17,6 @@
 import (
 	"android/soong/android"
 	"fmt"
-	"strconv"
 )
 
 func getNdkStlFamily(m LinkableInterface) string {
@@ -136,23 +135,8 @@
 }
 
 func needsLibAndroidSupport(ctx BaseModuleContext) bool {
-	versionStr, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch())
-	if err != nil {
-		ctx.PropertyErrorf("sdk_version", err.Error())
-	}
-
-	if versionStr == "current" {
-		return false
-	}
-
-	version, err := strconv.Atoi(versionStr)
-	if err != nil {
-		panic(fmt.Sprintf(
-			"invalid API level returned from normalizeNdkApiLevel: %q",
-			versionStr))
-	}
-
-	return version < 21
+	version := nativeApiLevelOrPanic(ctx, ctx.sdkVersion())
+	return version.LessThan(android.FirstNonLibAndroidSupportVersion)
 }
 
 func staticUnwinder(ctx android.BaseModuleContext) string {
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 0219b84..2819f49 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -541,6 +541,11 @@
 	if !m.Enabled() || m.Properties.HideFromMake {
 		return false
 	}
+	// When android/prebuilt.go selects between source and prebuilt, it sets
+	// SkipInstall on the other one to avoid duplicate install rules in make.
+	if m.IsSkipInstall() {
+		return false
+	}
 	// skip proprietary modules, but include all VNDK (static)
 	if inVendorProprietaryPath && !m.IsVndk() {
 		return false
diff --git a/cmd/soong_build/bazel_overlay.go b/cmd/soong_build/bazel_overlay.go
index 308076d..72e0fbd 100644
--- a/cmd/soong_build/bazel_overlay.go
+++ b/cmd/soong_build/bazel_overlay.go
@@ -24,16 +24,19 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap/bpdoc"
 	"github.com/google/blueprint/proptools"
 )
 
 const (
+	// The default `load` preamble for every generated BUILD file.
 	soongModuleLoad = `package(default_visibility = ["//visibility:public"])
 load("//:soong_module.bzl", "soong_module")
 
 `
 
-	// A BUILD file target snippet representing a Soong module
+	// A macro call in the BUILD file representing a Soong module, with space
+	// for expanding more attributes.
 	soongModuleTarget = `soong_module(
     name = "%s",
     module_name = "%s",
@@ -42,24 +45,24 @@
     module_deps = %s,
 %s)`
 
-	// The soong_module rule implementation in a .bzl file
-	soongModuleBzl = `SoongModuleInfo = provider(
+	// A simple provider to mark and differentiate Soong module rule shims from
+	// regular Bazel rules. Every Soong module rule shim returns a
+	// SoongModuleInfo provider, and can only depend on rules returning
+	// SoongModuleInfo in the `module_deps` attribute.
+	providersBzl = `SoongModuleInfo = provider(
     fields = {
         "name": "Name of module",
         "type": "Type of module",
         "variant": "Variant of module",
     },
 )
+`
 
-def _merge_dicts(*dicts):
-    """Adds a list of dictionaries into a single dictionary."""
+	// The soong_module rule implementation in a .bzl file.
+	soongModuleBzl = `
+%s
 
-    # If keys are repeated in multiple dictionaries, the latter one "wins".
-    result = {}
-    for d in dicts:
-        result.update(d)
-
-    return result
+load(":providers.bzl", "SoongModuleInfo")
 
 def _generic_soong_module_impl(ctx):
     return [
@@ -70,37 +73,31 @@
         ),
     ]
 
-_COMMON_ATTRS = {
-    "module_name": attr.string(mandatory = True),
-    "module_type": attr.string(mandatory = True),
-    "module_variant": attr.string(),
-    "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-}
-
-
 generic_soong_module = rule(
     implementation = _generic_soong_module_impl,
-    attrs = _COMMON_ATTRS,
-)
-
-# TODO(jingwen): auto generate Soong module shims
-def _soong_filegroup_impl(ctx):
-    return [SoongModuleInfo(),]
-
-soong_filegroup = rule(
-    implementation = _soong_filegroup_impl,
-    # Matches https://cs.android.com/android/platform/superproject/+/master:build/soong/android/filegroup.go;l=25-40;drc=6a6478d49e78703ba22a432c41d819c8df79ef6c
-    attrs = _merge_dicts(_COMMON_ATTRS, {
-        "srcs": attr.string_list(doc = "srcs lists files that will be included in this filegroup"),
-        "exclude_srcs": attr.string_list(),
-        "path": attr.string(doc = "The base path to the files. May be used by other modules to determine which portion of the path to use. For example, when a filegroup is used as data in a cc_test rule, the base path is stripped off the path and the remaining path is used as the installation directory."),
-        "export_to_make_var": attr.string(doc = "Create a make variable with the specified name that contains the list of files in the filegroup, relative to the root of the source tree."),
-    })
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_type": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+    },
 )
 
 soong_module_rule_map = {
-    "filegroup": soong_filegroup,
-}
+%s}
+
+_SUPPORTED_TYPES = ["bool", "int", "string"]
+
+def _is_supported_type(value):
+    if type(value) in _SUPPORTED_TYPES:
+        return True
+    elif type(value) == "list":
+        supported = True
+        for v in value:
+            supported = supported and type(v) in _SUPPORTED_TYPES
+        return supported
+    else:
+        return False
 
 # soong_module is a macro that supports arbitrary kwargs, and uses module_type to
 # expand to the right underlying shim.
@@ -118,12 +115,76 @@
             module_deps = kwargs.pop("module_deps", []),
         )
     else:
+        supported_kwargs = dict()
+        for key, value in kwargs.items():
+            if _is_supported_type(value):
+                supported_kwargs[key] = value
         soong_module_rule(
             name = name,
-            module_type = module_type,
-            **kwargs,
+            **supported_kwargs,
         )
 `
+
+	// A rule shim for representing a Soong module type and its properties.
+	moduleRuleShim = `
+def _%[1]s_impl(ctx):
+    return [SoongModuleInfo()]
+
+%[1]s = rule(
+    implementation = _%[1]s_impl,
+    attrs = %[2]s
+)
+`
+)
+
+var (
+	// An allowlist of prop types that are surfaced from module props to rule
+	// attributes. (nested) dictionaries are notably absent here, because while
+	// Soong supports multi value typed and nested dictionaries, Bazel's rule
+	// attr() API supports only single-level string_dicts.
+	allowedPropTypes = map[string]bool{
+		"int":         true, // e.g. 42
+		"bool":        true, // e.g. True
+		"string_list": true, // e.g. ["a", "b"]
+		"string":      true, // e.g. "a"
+	}
+
+	// TODO(b/166563303): Specific properties of some module types aren't
+	// recognized by the documentation generator. As a workaround, hardcode a
+	// mapping of the module type to prop name to prop type here, and ultimately
+	// fix the documentation generator to also parse these properties correctly.
+	additionalPropTypes = map[string]map[string]string{
+		// sdk and module_exports props are created at runtime using reflection.
+		// bpdocs isn't wired up to read runtime generated structs.
+		"sdk": {
+			"java_header_libs":    "string_list",
+			"java_sdk_libs":       "string_list",
+			"java_system_modules": "string_list",
+			"native_header_libs":  "string_list",
+			"native_libs":         "string_list",
+			"native_objects":      "string_list",
+			"native_shared_libs":  "string_list",
+			"native_static_libs":  "string_list",
+		},
+		"module_exports": {
+			"java_libs":          "string_list",
+			"java_tests":         "string_list",
+			"native_binaries":    "string_list",
+			"native_shared_libs": "string_list",
+		},
+	}
+
+	// Certain module property names are blocklisted/ignored here, for the reasons commented.
+	ignoredPropNames = map[string]bool{
+		"name":       true, // redundant, since this is explicitly generated for every target
+		"from":       true, // reserved keyword
+		"in":         true, // reserved keyword
+		"arch":       true, // interface prop type is not supported yet.
+		"multilib":   true, // interface prop type is not supported yet.
+		"target":     true, // interface prop type is not supported yet.
+		"visibility": true, // Bazel has native visibility semantics. Handle later.
+		"features":   true, // There is already a built-in attribute 'features' which cannot be overridden.
+	}
 )
 
 func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
@@ -206,9 +267,7 @@
 		structProps := extractStructProperties(propertyValue, indent)
 		for _, k := range android.SortedStringKeys(structProps) {
 			ret += makeIndent(indent + 1)
-			ret += "\"" + k + "\": "
-			ret += structProps[k]
-			ret += ",\n"
+			ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
 		}
 		ret += makeIndent(indent)
 		ret += "}"
@@ -223,6 +282,10 @@
 	return ret, nil
 }
 
+// Converts a reflected property struct value into a map of property names and property values,
+// which each property value correctly pretty-printed and indented at the right nest level,
+// since property structs can be nested. In Starlark, nested structs are represented as nested
+// dicts: https://docs.bazel.build/skylark/lib/dict.html
 func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
 	if structValue.Kind() != reflect.Struct {
 		panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
@@ -296,6 +359,102 @@
 	return ret
 }
 
+// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
+// testonly = True, forcing other rules that depend on _test rules to also be
+// marked as testonly = True. This semantic constraint is not present in Soong.
+// To work around, rename "*_test" rules to "*_test_".
+func canonicalizeModuleType(moduleName string) string {
+	if strings.HasSuffix(moduleName, "_test") {
+		return moduleName + "_"
+	}
+
+	return moduleName
+}
+
+type RuleShim struct {
+	// The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
+	rules []string
+
+	// The generated string content of the bzl file.
+	content string
+}
+
+// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
+// user-specified Go plugins.
+//
+// This function reuses documentation generation APIs to ensure parity between modules-as-docs
+// and modules-as-code, including the names and types of module properties.
+func createRuleShims(packages []*bpdoc.Package) (map[string]RuleShim, error) {
+	var propToAttr func(prop bpdoc.Property, propName string) string
+	propToAttr = func(prop bpdoc.Property, propName string) string {
+		// dots are not allowed in Starlark attribute names. Substitute them with double underscores.
+		propName = strings.ReplaceAll(propName, ".", "__")
+		if !shouldGenerateAttribute(propName) {
+			return ""
+		}
+
+		// Canonicalize and normalize module property types to Bazel attribute types
+		starlarkAttrType := prop.Type
+		if starlarkAttrType == "list of strings" {
+			starlarkAttrType = "string_list"
+		} else if starlarkAttrType == "int64" {
+			starlarkAttrType = "int"
+		} else if starlarkAttrType == "" {
+			var attr string
+			for _, nestedProp := range prop.Properties {
+				nestedAttr := propToAttr(nestedProp, propName+"__"+nestedProp.Name)
+				if nestedAttr != "" {
+					// TODO(b/167662930): Fix nested props resulting in too many attributes.
+					// Let's still generate these, but comment them out.
+					attr += "# " + nestedAttr
+				}
+			}
+			return attr
+		}
+
+		if !allowedPropTypes[starlarkAttrType] {
+			return ""
+		}
+
+		return fmt.Sprintf("        %q: attr.%s(),\n", propName, starlarkAttrType)
+	}
+
+	ruleShims := map[string]RuleShim{}
+	for _, pkg := range packages {
+		content := "load(\":providers.bzl\", \"SoongModuleInfo\")\n"
+
+		bzlFileName := strings.ReplaceAll(pkg.Path, "android/soong/", "")
+		bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
+		bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
+
+		rules := []string{}
+
+		for _, moduleTypeTemplate := range moduleTypeDocsToTemplates(pkg.ModuleTypes) {
+			attrs := `{
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+`
+			for _, prop := range moduleTypeTemplate.Properties {
+				attrs += propToAttr(prop, prop.Name)
+			}
+
+			for propName, propType := range additionalPropTypes[moduleTypeTemplate.Name] {
+				attrs += fmt.Sprintf("        %q: attr.%s(),\n", propName, propType)
+			}
+
+			attrs += "    },"
+
+			rule := canonicalizeModuleType(moduleTypeTemplate.Name)
+			content += fmt.Sprintf(moduleRuleShim, rule, attrs)
+			rules = append(rules, rule)
+		}
+
+		ruleShims[bzlFileName] = RuleShim{content: content, rules: rules}
+	}
+	return ruleShims, nil
+}
+
 func createBazelOverlay(ctx *android.Context, bazelOverlayDir string) error {
 	blueprintCtx := ctx.Context
 	blueprintCtx.VisitAllModules(func(module blueprint.Module) {
@@ -316,21 +475,50 @@
 		return err
 	}
 
-	return writeReadOnlyFile(bazelOverlayDir, "soong_module.bzl", soongModuleBzl)
+	if err := writeReadOnlyFile(bazelOverlayDir, "providers.bzl", providersBzl); err != nil {
+		return err
+	}
+
+	packages, err := getPackages(ctx)
+	if err != nil {
+		return err
+	}
+	ruleShims, err := createRuleShims(packages)
+	if err != nil {
+		return err
+	}
+
+	for bzlFileName, ruleShim := range ruleShims {
+		if err := writeReadOnlyFile(bazelOverlayDir, bzlFileName+".bzl", ruleShim.content); err != nil {
+			return err
+		}
+	}
+
+	return writeReadOnlyFile(bazelOverlayDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims))
 }
 
-var ignoredProps map[string]bool = map[string]bool{
-	"name":       true, // redundant, since this is explicitly generated for every target
-	"from":       true, // reserved keyword
-	"in":         true, // reserved keyword
-	"arch":       true, // interface prop type is not supported yet.
-	"multilib":   true, // interface prop type is not supported yet.
-	"target":     true, // interface prop type is not supported yet.
-	"visibility": true, // Bazel has native visibility semantics. Handle later.
+// Generate the content of soong_module.bzl with the rule shim load statements
+// and mapping of module_type to rule shim map for every module type in Soong.
+func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
+	var loadStmts string
+	var moduleRuleMap string
+	for bzlFileName, ruleShim := range bzlLoads {
+		loadStmt := "load(\"//:"
+		loadStmt += bzlFileName
+		loadStmt += ".bzl\""
+		for _, rule := range ruleShim.rules {
+			loadStmt += fmt.Sprintf(", %q", rule)
+			moduleRuleMap += "    \"" + rule + "\": " + rule + ",\n"
+		}
+		loadStmt += ")\n"
+		loadStmts += loadStmt
+	}
+
+	return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
 }
 
 func shouldGenerateAttribute(prop string) bool {
-	return !ignoredProps[prop]
+	return !ignoredPropNames[prop]
 }
 
 // props is an unsorted map. This function ensures that
@@ -367,9 +555,7 @@
 
 	depLabelList := "[\n"
 	for depLabel, _ := range depLabels {
-		depLabelList += "        \""
-		depLabelList += depLabel
-		depLabelList += "\",\n"
+		depLabelList += fmt.Sprintf("        %q,\n", depLabel)
 	}
 	depLabelList += "    ]"
 
@@ -377,7 +563,7 @@
 		soongModuleTarget,
 		targetNameWithVariant(blueprintCtx, module),
 		blueprintCtx.ModuleName(module),
-		blueprintCtx.ModuleType(module),
+		canonicalizeModuleType(blueprintCtx.ModuleType(module)),
 		blueprintCtx.ModuleSubDir(module),
 		depLabelList,
 		attributes)
@@ -410,11 +596,12 @@
 	return f, nil
 }
 
-// The overlay directory should be read-only, sufficient for bazel query.
+// The overlay directory should be read-only, sufficient for bazel query. The files
+// are not intended to be edited by end users.
 func writeReadOnlyFile(dir string, baseName string, content string) error {
-	workspaceFile := filepath.Join(bazelOverlayDir, baseName)
+	pathToFile := filepath.Join(bazelOverlayDir, baseName)
 	// 0444 is read-only
-	return ioutil.WriteFile(workspaceFile, []byte(content), 0444)
+	return ioutil.WriteFile(pathToFile, []byte(content), 0444)
 }
 
 func isZero(value reflect.Value) bool {
diff --git a/cmd/soong_build/bazel_overlay_test.go b/cmd/soong_build/bazel_overlay_test.go
index 8db784d..f0c8515 100644
--- a/cmd/soong_build/bazel_overlay_test.go
+++ b/cmd/soong_build/bazel_overlay_test.go
@@ -18,7 +18,10 @@
 	"android/soong/android"
 	"io/ioutil"
 	"os"
+	"strings"
 	"testing"
+
+	"github.com/google/blueprint/bootstrap/bpdoc"
 )
 
 var buildDir string
@@ -253,3 +256,209 @@
 		}
 	}
 }
+
+func createPackageFixtures() []*bpdoc.Package {
+	properties := []bpdoc.Property{
+		bpdoc.Property{
+			Name: "int64_prop",
+			Type: "int64",
+		},
+		bpdoc.Property{
+			Name: "int_prop",
+			Type: "int",
+		},
+		bpdoc.Property{
+			Name: "bool_prop",
+			Type: "bool",
+		},
+		bpdoc.Property{
+			Name: "string_prop",
+			Type: "string",
+		},
+		bpdoc.Property{
+			Name: "string_list_prop",
+			Type: "list of strings",
+		},
+		bpdoc.Property{
+			Name: "nested_prop",
+			Type: "",
+			Properties: []bpdoc.Property{
+				bpdoc.Property{
+					Name: "int_prop",
+					Type: "int",
+				},
+				bpdoc.Property{
+					Name: "bool_prop",
+					Type: "bool",
+				},
+				bpdoc.Property{
+					Name: "string_prop",
+					Type: "string",
+				},
+			},
+		},
+		bpdoc.Property{
+			Name: "unknown_type",
+			Type: "unknown",
+		},
+	}
+
+	fooPropertyStruct := &bpdoc.PropertyStruct{
+		Name:       "FooProperties",
+		Properties: properties,
+	}
+
+	moduleTypes := []*bpdoc.ModuleType{
+		&bpdoc.ModuleType{
+			Name: "foo_library",
+			PropertyStructs: []*bpdoc.PropertyStruct{
+				fooPropertyStruct,
+			},
+		},
+
+		&bpdoc.ModuleType{
+			Name: "foo_binary",
+			PropertyStructs: []*bpdoc.PropertyStruct{
+				fooPropertyStruct,
+			},
+		},
+		&bpdoc.ModuleType{
+			Name: "foo_test",
+			PropertyStructs: []*bpdoc.PropertyStruct{
+				fooPropertyStruct,
+			},
+		},
+	}
+
+	return [](*bpdoc.Package){
+		&bpdoc.Package{
+			Name:        "foo_language",
+			Path:        "android/soong/foo",
+			ModuleTypes: moduleTypes,
+		},
+	}
+}
+
+func TestGenerateModuleRuleShims(t *testing.T) {
+	ruleShims, err := createRuleShims(createPackageFixtures())
+	if err != nil {
+		panic(err)
+	}
+
+	if len(ruleShims) != 1 {
+		t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
+	}
+
+	fooRuleShim := ruleShims["foo"]
+	expectedRules := []string{"foo_binary", "foo_library", "foo_test_"}
+
+	if len(fooRuleShim.rules) != 3 {
+		t.Errorf("Expected 3 rules, but got %d", len(fooRuleShim.rules))
+	}
+
+	for i, rule := range fooRuleShim.rules {
+		if rule != expectedRules[i] {
+			t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
+		}
+	}
+
+	expectedBzl := `load(":providers.bzl", "SoongModuleInfo")
+
+def _foo_binary_impl(ctx):
+    return [SoongModuleInfo()]
+
+foo_binary = rule(
+    implementation = _foo_binary_impl,
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "bool_prop": attr.bool(),
+        "int64_prop": attr.int(),
+        "int_prop": attr.int(),
+#         "nested_prop__int_prop": attr.int(),
+#         "nested_prop__bool_prop": attr.bool(),
+#         "nested_prop__string_prop": attr.string(),
+        "string_list_prop": attr.string_list(),
+        "string_prop": attr.string(),
+    },
+)
+
+def _foo_library_impl(ctx):
+    return [SoongModuleInfo()]
+
+foo_library = rule(
+    implementation = _foo_library_impl,
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "bool_prop": attr.bool(),
+        "int64_prop": attr.int(),
+        "int_prop": attr.int(),
+#         "nested_prop__int_prop": attr.int(),
+#         "nested_prop__bool_prop": attr.bool(),
+#         "nested_prop__string_prop": attr.string(),
+        "string_list_prop": attr.string_list(),
+        "string_prop": attr.string(),
+    },
+)
+
+def _foo_test__impl(ctx):
+    return [SoongModuleInfo()]
+
+foo_test_ = rule(
+    implementation = _foo_test__impl,
+    attrs = {
+        "module_name": attr.string(mandatory = True),
+        "module_variant": attr.string(),
+        "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "bool_prop": attr.bool(),
+        "int64_prop": attr.int(),
+        "int_prop": attr.int(),
+#         "nested_prop__int_prop": attr.int(),
+#         "nested_prop__bool_prop": attr.bool(),
+#         "nested_prop__string_prop": attr.string(),
+        "string_list_prop": attr.string_list(),
+        "string_prop": attr.string(),
+    },
+)
+`
+
+	if fooRuleShim.content != expectedBzl {
+		t.Errorf(
+			"Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
+			expectedBzl,
+			fooRuleShim.content)
+	}
+}
+
+func TestGenerateSoongModuleBzl(t *testing.T) {
+	ruleShims, err := createRuleShims(createPackageFixtures())
+	if err != nil {
+		panic(err)
+	}
+	actualSoongModuleBzl := generateSoongModuleBzl(ruleShims)
+
+	expectedLoad := "load(\"//:foo.bzl\", \"foo_binary\", \"foo_library\", \"foo_test_\")"
+	expectedRuleMap := `soong_module_rule_map = {
+    "foo_binary": foo_binary,
+    "foo_library": foo_library,
+    "foo_test_": foo_test_,
+}`
+	if !strings.Contains(actualSoongModuleBzl, expectedLoad) {
+		t.Errorf(
+			"Generated soong_module.bzl:\n\n%s\n\n"+
+				"Could not find the load statement in the generated soong_module.bzl:\n%s",
+			actualSoongModuleBzl,
+			expectedLoad)
+	}
+
+	if !strings.Contains(actualSoongModuleBzl, expectedRuleMap) {
+		t.Errorf(
+			"Generated soong_module.bzl:\n\n%s\n\n"+
+				"Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
+			actualSoongModuleBzl,
+			expectedRuleMap)
+	}
+}
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index c136846..5fb6e6b 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -95,14 +95,17 @@
 	return result
 }
 
-func writeDocs(ctx *android.Context, filename string) error {
+func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
 	moduleTypeFactories := android.ModuleTypeFactories()
 	bpModuleTypeFactories := make(map[string]reflect.Value)
 	for moduleType, factory := range moduleTypeFactories {
 		bpModuleTypeFactories[moduleType] = reflect.ValueOf(factory)
 	}
+	return bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
+}
 
-	packages, err := bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
+func writeDocs(ctx *android.Context, filename string) error {
+	packages, err := getPackages(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 4aa62be..69e4f69 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -173,7 +173,6 @@
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
 	defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
-	defer build.PrintGomaDeprecation(buildCtx, config)
 
 	os.MkdirAll(logsDir, 0777)
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 3ef8b8d..21f7bb3 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -100,6 +100,21 @@
 	ConstructContext android.Path
 }
 
+// These libs are added as optional dependencies (<uses-library> with android:required set to false).
+// This is because they haven't existed prior to certain SDK version, but classes in them were in
+// bootclasspath jars, etc. So making them hard dependencies (android:required=true) would prevent
+// apps from being installed to such legacy devices.
+var OptionalCompatUsesLibs = []string{
+	"org.apache.http.legacy",
+	"android.test.base",
+	"android.test.mock",
+}
+
+var CompatUsesLibs = []string{
+	"android.hidl.base-V1.0-java",
+	"android.hidl.manager-V1.0-java",
+}
+
 const UnknownInstallLibraryPath = "error"
 
 // LibraryPath contains paths to the library DEX jar on host and on device.
@@ -112,7 +127,29 @@
 type LibraryPaths map[string]*LibraryPath
 
 // Add a new library path to the map, unless a path for this library already exists.
-func (libPaths LibraryPaths) addLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
+// If necessary, check that the build and install paths exist.
+func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleContext, lib string,
+	hostPath, installPath android.Path, strict bool) {
+
+	// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
+	// not found. However, this is likely to result is disabling dexpreopt, as it won't be
+	// possible to construct class loader context without on-host and on-device library paths.
+	strict = strict && !ctx.Config().AllowMissingDependencies()
+
+	if hostPath == nil && strict {
+		android.ReportPathErrorf(ctx, "unknown build path to <uses-library> '%s'", lib)
+	}
+
+	if installPath == nil {
+		if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
+			// Assume that compatibility libraries are installed in /system/framework.
+			installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
+		} else if strict {
+			android.ReportPathErrorf(ctx, "unknown install path to <uses-library> '%s'", lib)
+		}
+	}
+
+	// Add a library only if the build and install path to it is known.
 	if _, present := libPaths[lib]; !present {
 		var devicePath string
 		if installPath != nil {
@@ -128,31 +165,17 @@
 	}
 }
 
-// Add a new library path to the map. Ensure that the build path to the library exists.
-func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
-	if hostPath != nil && installPath != nil {
-		// Add a library only if the build and install path to it is known.
-		libPaths.addLibraryPath(ctx, lib, hostPath, installPath)
-	} else if ctx.Config().AllowMissingDependencies() {
-		// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
-		// not found. However, this is likely to result is disabling dexpreopt, as it won't be
-		// possible to construct class loader context without on-host and on-device library paths.
-	} else {
-		// Error on libraries with unknown paths.
-		if hostPath == nil {
-			android.ReportPathErrorf(ctx, "unknown build path to <uses-library> '%s'", lib)
-		} else {
-			android.ReportPathErrorf(ctx, "unknown install path to <uses-library> '%s'", lib)
-		}
-	}
+// Add a new library path to the map. Enforce checks that the library paths exist.
+func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleContext, lib string, hostPath, installPath android.Path) {
+	libPaths.addLibraryPath(ctx, lib, hostPath, installPath, true)
 }
 
 // Add a new library path to the map, if the library exists (name is not nil).
-func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.PathContext, lib *string, hostPath, installPath android.Path) {
+// Don't enforce checks that the library paths exist. Some libraries may be missing from the build,
+// but their names still need to be added to <uses-library> tags in the manifest.
+func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleContext, lib *string, hostPath, installPath android.Path) {
 	if lib != nil {
-		// Don't check the build paths, add in any case. Some libraries may be missing from the
-		// build, but their names still need to be added to <uses-library> tags in the manifest.
-		libPaths.addLibraryPath(ctx, *lib, hostPath, installPath)
+		libPaths.addLibraryPath(ctx, *lib, hostPath, installPath, false)
 	}
 }
 
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 5dd2a86..8e35679 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -172,13 +172,7 @@
 }
 
 func (p *PrebuiltEtc) BaseDir() string {
-	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
-	// socInstallDirBase.
-	installBaseDir := p.installDirBase
-	if p.SocSpecific() && p.socInstallDirBase != "" {
-		installBaseDir = p.socInstallDirBase
-	}
-	return installBaseDir
+	return p.installDirBase
 }
 
 func (p *PrebuiltEtc) Installable() bool {
@@ -205,7 +199,13 @@
 		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
 	}
 
-	p.installDirPath = android.PathForModuleInstall(ctx, p.BaseDir(), p.SubDir())
+	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
+	// socInstallDirBase.
+	installBaseDir := p.installDirBase
+	if p.SocSpecific() && p.socInstallDirBase != "" {
+		installBaseDir = p.socInstallDirBase
+	}
+	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir())
 
 	// This ensures that outputFilePath has the correct name for others to
 	// use, as the source file may have a different name.
diff --git a/java/Android.bp b/java/Android.bp
index e345014..92e8ca4 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -59,6 +59,7 @@
         "device_host_converter_test.go",
         "dexpreopt_test.go",
         "dexpreopt_bootjars_test.go",
+        "hiddenapi_singleton_test.go",
         "java_test.go",
         "jdeps_test.go",
         "kotlin_test.go",
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 41fcafe..62cd112 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -42,16 +42,6 @@
 	},
 	"args", "libs")
 
-// These two libs are added as optional dependencies (<uses-library> with
-// android:required set to false). This is because they haven't existed in pre-P
-// devices, but classes in them were in bootclasspath jars, etc. So making them
-// hard dependencies (android:required=true) would prevent apps from being
-// installed to such legacy devices.
-var optionalUsesLibs = []string{
-	"android.test.base",
-	"android.test.mock",
-}
-
 // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
 func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries dexpreopt.LibraryPaths,
 	isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
@@ -81,7 +71,7 @@
 	}
 
 	for _, usesLib := range android.SortedStringKeys(sdkLibraries) {
-		if inList(usesLib, optionalUsesLibs) {
+		if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) {
 			args = append(args, "--optional-uses-library", usesLib)
 		} else {
 			args = append(args, "--uses-library", usesLib)
diff --git a/java/app.go b/java/app.go
index dbc09e9..ae7373f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -787,7 +787,7 @@
 
 	// Add implicit SDK libraries to <uses-library> list.
 	for _, usesLib := range android.SortedStringKeys(a.aapt.sdkLibraries) {
-		a.usesLibrary.addLib(usesLib, inList(usesLib, optionalUsesLibs))
+		a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs))
 	}
 
 	// Check that the <uses-library> list is coherent with the manifest.
@@ -1947,11 +1947,8 @@
 		if hasFrameworkLibs {
 			// Dexpreopt needs paths to the dex jars of these libraries in order to construct
 			// class loader context for dex2oat. Add them as a dependency with a special tag.
-			ctx.AddVariationDependencies(nil, usesLibCompatTag,
-				"org.apache.http.legacy",
-				"android.hidl.base-V1.0-java",
-				"android.hidl.manager-V1.0-java")
-			ctx.AddVariationDependencies(nil, usesLibCompatTag, optionalUsesLibs...)
+			ctx.AddVariationDependencies(nil, usesLibTag, dexpreopt.CompatUsesLibs...)
+			ctx.AddVariationDependencies(nil, usesLibTag, dexpreopt.OptionalCompatUsesLibs...)
 		}
 	}
 }
@@ -1969,27 +1966,14 @@
 	usesLibPaths := make(dexpreopt.LibraryPaths)
 
 	if !ctx.Config().UnbundledBuild() {
-		ctx.VisitDirectDeps(func(m android.Module) {
-			tag := ctx.OtherModuleDependencyTag(m)
-			if tag == usesLibTag || tag == usesLibCompatTag {
-				dep := ctx.OtherModuleName(m)
-
-				if lib, ok := m.(Dependency); ok {
-					buildPath := lib.DexJarBuildPath()
-					installPath := lib.DexJarInstallPath()
-					if installPath == nil && tag == usesLibCompatTag {
-						// assume that compatibility libraries are in /system/framework
-						installPath = android.PathForModuleInstall(ctx, "framework", dep+".jar")
-					}
-					usesLibPaths.AddLibraryPath(ctx, dep, buildPath, installPath)
-
-				} else if ctx.Config().AllowMissingDependencies() {
-					ctx.AddMissingDependencies([]string{dep})
-
-				} else {
-					ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be "+
-						"a java library", dep)
-				}
+		ctx.VisitDirectDepsWithTag(usesLibTag, func(m android.Module) {
+			dep := ctx.OtherModuleName(m)
+			if lib, ok := m.(Dependency); ok {
+				usesLibPaths.AddLibraryPath(ctx, dep, lib.DexJarBuildPath(), lib.DexJarInstallPath())
+			} else if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{dep})
+			} else {
+				ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library", dep)
 			}
 		})
 	}
diff --git a/java/dex.go b/java/dex.go
index 21a5926..055d479 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -135,6 +135,7 @@
 		"$r8Template": &remoteexec.REParams{
 			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
 			Inputs:          []string{"$implicits", "${config.R8Jar}"},
+			OutputFiles:     []string{"${outUsage}"},
 			ExecStrategy:    "${config.RER8ExecStrategy}",
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 7073eff..85a61dd 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -181,12 +181,6 @@
 	// filegroup or genrule can be included within this property.
 	Knowntags []string `android:"path"`
 
-	// the generated public API filename by Doclava.
-	Api_filename *string
-
-	// the generated removed API filename by Doclava.
-	Removed_api_filename *string
-
 	// if set to true, generate docs through Dokka instead of Doclava.
 	Dokka_enabled *bool
 
@@ -195,10 +189,10 @@
 }
 
 type DroidstubsProperties struct {
-	// the generated public API filename by Metalava.
+	// The generated public API filename by Metalava, defaults to <module>_api.txt
 	Api_filename *string
 
-	// the generated removed API filename by Metalava.
+	// the generated removed API filename by Metalava, defaults to <module>_removed.txt
 	Removed_api_filename *string
 
 	// the generated removed Dex API filename by Metalava.
@@ -1127,7 +1121,8 @@
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
 		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Api_filename) != "" {
-		d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
+		filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
+		d.apiFile = android.PathForModuleOut(ctx, filename)
 		cmd.FlagWithOutput("--api ", d.apiFile)
 		d.apiFilePath = d.apiFile
 	}
@@ -1135,7 +1130,8 @@
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
 		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Removed_api_filename) != "" {
-		d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
+		filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
+		d.removedApiFile = android.PathForModuleOut(ctx, filename)
 		cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
 	}
 
@@ -1302,6 +1298,7 @@
 
 	cmd.BuiltTool(ctx, "metalava").
 		Flag(config.JavacVmFlags).
+		Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithArg("-source ", javaVersion.String()).
 		FlagWithRspFileInputList("@", srcs).
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 29b6bcd..61a9b97 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -92,31 +92,34 @@
 // stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
 // modules.
 func stubFlagsRule(ctx android.SingletonContext) {
-	// Public API stubs
-	publicStubModules := []string{
-		"android_stubs_current",
+	var publicStubModules []string
+	var systemStubModules []string
+	var testStubModules []string
+	var corePlatformStubModules []string
+
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
+		// Build configuration mandates using prebuilt stub modules
+		publicStubModules = append(publicStubModules, "sdk_public_current_android")
+		systemStubModules = append(systemStubModules, "sdk_system_current_android")
+		testStubModules = append(testStubModules, "sdk_test_current_android")
+	} else {
+		// Use stub modules built from source
+		publicStubModules = append(publicStubModules, "android_stubs_current")
+		systemStubModules = append(systemStubModules, "android_system_stubs_current")
+		testStubModules = append(testStubModules, "android_test_stubs_current")
 	}
+	// We do not have prebuilts of the core platform api yet
+	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
 
 	// Add the android.test.base to the set of stubs only if the android.test.base module is on
 	// the boot jars list as the runtime will only enforce hiddenapi access against modules on
 	// that list.
-	if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().AlwaysUsePrebuiltSdks() {
-		publicStubModules = append(publicStubModules, "android.test.base.stubs")
-	}
-
-	// System API stubs
-	systemStubModules := []string{
-		"android_system_stubs_current",
-	}
-
-	// Test API stubs
-	testStubModules := []string{
-		"android_test_stubs_current",
-	}
-
-	// Core Platform API stubs
-	corePlatformStubModules := []string{
-		"legacy.core.platform.api.stubs",
+	if inList("android.test.base", ctx.Config().BootJars()) {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
+			publicStubModules = append(publicStubModules, "sdk_public_current_android.test.base")
+		} else {
+			publicStubModules = append(publicStubModules, "android.test.base.stubs")
+		}
 	}
 
 	// Allow products to define their own stubs for custom product jars that apps can use.
@@ -163,6 +166,7 @@
 						return
 					}
 				}
+
 				bootDexJars = append(bootDexJars, jar)
 			}
 		}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
new file mode 100644
index 0000000..dbdab7a
--- /dev/null
+++ b/java/hiddenapi_singleton_test.go
@@ -0,0 +1,219 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func testConfigWithBootJars(bp string, bootJars []string) android.Config {
+	config := testConfig(nil, bp, nil)
+	config.TestProductVariables.BootJars = bootJars
+	return config
+}
+
+func testContextWithHiddenAPI() *android.TestContext {
+	ctx := testContext()
+	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+	return ctx
+}
+
+func testHiddenAPIWithConfig(t *testing.T, config android.Config) *android.TestContext {
+	t.Helper()
+
+	ctx := testContextWithHiddenAPI()
+
+	run(t, ctx, config)
+	return ctx
+}
+
+func testHiddenAPIBootJars(t *testing.T, bp string, bootJars []string) (*android.TestContext, android.Config) {
+	config := testConfigWithBootJars(bp, bootJars)
+
+	return testHiddenAPIWithConfig(t, config), config
+}
+
+func testHiddenAPIUnbundled(t *testing.T, unbundled bool) (*android.TestContext, android.Config) {
+	config := testConfig(nil, ``, nil)
+	config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(unbundled)
+
+	return testHiddenAPIWithConfig(t, config), config
+}
+
+func TestHiddenAPISingleton(t *testing.T) {
+	ctx, _ := testHiddenAPIBootJars(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+	}
+	`, []string{":foo"})
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, want) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
+	ctx, _ := testHiddenAPIBootJars(t, `
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+	}
+	`, []string{":foo"})
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, want) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
+	ctx, _ := testHiddenAPIBootJars(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+	}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+			prefer: false,
+	}
+	`, []string{":foo"})
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command)
+	}
+
+	prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar"
+	if strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) {
+		t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
+	ctx, _ := testHiddenAPIBootJars(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+	}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+			prefer: true,
+	}
+	`, []string{":foo"})
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command)
+	}
+
+	fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	if strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) {
+		t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonSdks(t *testing.T) {
+	testCases := []struct {
+		name             string
+		unbundledBuild   bool
+		publicStub       string
+		systemStub       string
+		testStub         string
+		corePlatformStub string
+	}{
+		{
+			name:             "testBundled",
+			unbundledBuild:   false,
+			publicStub:       "android_stubs_current",
+			systemStub:       "android_system_stubs_current",
+			testStub:         "android_test_stubs_current",
+			corePlatformStub: "legacy.core.platform.api.stubs",
+		}, {
+			name:             "testUnbundled",
+			unbundledBuild:   true,
+			publicStub:       "sdk_public_current_android",
+			systemStub:       "sdk_system_current_android",
+			testStub:         "sdk_test_current_android",
+			corePlatformStub: "legacy.core.platform.api.stubs",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx, _ := testHiddenAPIUnbundled(t, tc.unbundledBuild)
+
+			hiddenAPI := ctx.SingletonForTests("hiddenapi")
+			hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantPublicStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantPublicStubs, hiddenapiRule.RuleParams.Command)
+			}
+
+			wantSystemStubs := "--system-stub-classpath=" + generateSdkDexPath(tc.systemStub, tc.unbundledBuild)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantSystemStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantSystemStubs, hiddenapiRule.RuleParams.Command)
+			}
+
+			wantTestStubs := "--test-stub-classpath=" + generateSdkDexPath(tc.testStub, tc.unbundledBuild)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantTestStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantTestStubs, hiddenapiRule.RuleParams.Command)
+			}
+
+			wantCorePlatformStubs := "--core-platform-stub-classpath=" + generateDexPath(tc.corePlatformStub)
+			if !strings.Contains(hiddenapiRule.RuleParams.Command, wantCorePlatformStubs) {
+				t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantCorePlatformStubs, hiddenapiRule.RuleParams.Command)
+			}
+		})
+	}
+}
+
+func generateDexedPath(subDir, dex, module string) string {
+	return fmt.Sprintf("%s/.intermediates/%s/android_common/%s/%s.jar", buildDir, subDir, dex, module)
+}
+
+func generateDexPath(module string) string {
+	return generateDexedPath(module, "dex", module)
+}
+
+func generateSdkDexPath(module string, unbundled bool) string {
+	if unbundled {
+		return generateDexedPath("prebuilts/sdk/"+module, "dex", module)
+	}
+	return generateDexPath(module)
+}
diff --git a/java/java.go b/java/java.go
index 9830c51..d67e9e0 100644
--- a/java/java.go
+++ b/java/java.go
@@ -570,7 +570,6 @@
 	certificateTag        = dependencyTag{name: "certificate"}
 	instrumentationForTag = dependencyTag{name: "instrumentation_for"}
 	usesLibTag            = dependencyTag{name: "uses-library"}
-	usesLibCompatTag      = dependencyTag{name: "uses-library-compat"}
 	extraLintCheckTag     = dependencyTag{name: "extra-lint-check"}
 )
 
@@ -1615,6 +1614,9 @@
 
 		configurationName := j.ConfigurationName()
 		primary := configurationName == ctx.ModuleName()
+		// If the prebuilt is being used rather than the from source, skip this
+		// module to prevent duplicated classes
+		primary = primary && !j.IsReplacedByPrebuilt()
 
 		// Hidden API CSV generation and dex encoding
 		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile,
@@ -2685,6 +2687,13 @@
 			return
 		}
 
+		configurationName := j.BaseModuleName()
+		primary := j.Prebuilt().UsePrebuilt()
+
+		// Hidden API CSV generation and dex encoding
+		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
+			proptools.Bool(j.dexProperties.Uncompress_dex))
+
 		j.dexJarFile = dexOutputFile
 	}
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index ec112bc..04fc117 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -31,7 +31,6 @@
 }
 
 var robolectricDefaultLibs = []string{
-	"robolectric_android-all-stub",
 	"Robolectric_all-target",
 	"mockito-robolectric-prebuilt",
 	"truth-prebuilt",
@@ -99,7 +98,8 @@
 
 	ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
 
-	ctx.AddVariationDependencies(nil, roboRuntimesTag, "robolectric-android-all-prebuilts")
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
+		roboRuntimesTag, "robolectric-android-all-prebuilts")
 }
 
 func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -335,7 +335,7 @@
 func robolectricRuntimesFactory() android.Module {
 	module := &robolectricRuntimes{}
 	module.AddProperties(&module.props)
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibCommon)
 	return module
 }
 
@@ -365,6 +365,10 @@
 }
 
 func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if ctx.Target().Os != ctx.Config().BuildOSCommonTarget.Os {
+		return
+	}
+
 	files := android.PathsForModuleSrc(ctx, r.props.Jars)
 
 	androidAllDir := android.PathForModuleInstall(ctx, "android-all")
diff --git a/java/testing.go b/java/testing.go
index 70c857f..a472413 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -88,7 +88,7 @@
 		"prebuilts/sdk/30/system/api/bar-removed.txt":              nil,
 		"prebuilts/sdk/30/test/api/bar-removed.txt":                nil,
 		"prebuilts/sdk/tools/core-lambda-stubs.jar":                nil,
-		"prebuilts/sdk/Android.bp":                                 []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"],}`),
+		"prebuilts/sdk/Android.bp":                                 []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"], imports_sdk_version: "none", imports_compile_dex:true,}`),
 
 		"bin.py": nil,
 		python.StubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%'
@@ -145,6 +145,7 @@
 				srcs: ["a.java"],
 				sdk_version: "none",
 				system_modules: "stable-core-platform-api-stubs-system-modules",
+				compile_dex: true,
 			}
 		`, extra)
 	}
diff --git a/rust/androidmk.go b/rust/androidmk.go
index edae0e6..5a33f77 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -178,6 +178,10 @@
 }
 
 func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	if compiler.path == (android.InstallPath{}) {
+		return
+	}
+
 	var unstrippedOutputFile android.OptionalPath
 	// Soong installation is only supported for host modules. Have Make
 	// installation trigger Soong installation.
diff --git a/rust/binary_test.go b/rust/binary_test.go
index cfef57a..394abfc 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -40,7 +40,7 @@
 	fizzBuzzHost := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
 	fizzBuzzDevice := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
 
-	if !android.InList("libfoo", fizzBuzzHost.Properties.AndroidMkRlibs) {
+	if !android.InList("libfoo.rlib-std", fizzBuzzHost.Properties.AndroidMkRlibs) {
 		t.Errorf("rustlibs dependency libfoo should be an rlib dep for host modules")
 	}
 
diff --git a/rust/compiler.go b/rust/compiler.go
index ddf1fac..aeb904b 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -146,8 +146,13 @@
 	panic("baseCompiler does not implement coverageOutputZipPath()")
 }
 
-func (compiler *baseCompiler) static() bool {
-	return false
+func (compiler *baseCompiler) staticStd(ctx *depsContext) bool {
+	// For devices, we always link stdlibs in as dylibs by default.
+	if ctx.Device() {
+		return false
+	} else {
+		return true
+	}
 }
 
 var _ compiler = (*baseCompiler)(nil)
@@ -216,20 +221,12 @@
 
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
-			// If we're building for the primary host target, use the compiler's stdlibs
-			if ctx.Host() && ctx.TargetPrimary() {
+			// If we're building for the primary arch of the build host, use the compiler's stdlibs
+			if ctx.Target().Os == android.BuildOs && ctx.TargetPrimary() {
 				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
 			}
 
-			// For devices, we always link stdlibs in as dylibs except for ffi static libraries.
-			// (rustc does not support linking libstd as a dylib for ffi static libraries)
-			if ctx.Host() {
-				deps.Rustlibs = append(deps.Rustlibs, stdlib)
-			} else if ctx.RustModule().compiler.static() {
-				deps.Rlibs = append(deps.Rlibs, stdlib)
-			} else {
-				deps.Dylibs = append(deps.Dylibs, stdlib)
-			}
+			deps.Stdlibs = append(deps.Stdlibs, stdlib)
 		}
 	}
 	return deps
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 56a8ef8..a25523c 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -191,7 +191,7 @@
 			crate_name: "foo",
 		}`)
 	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
-	fooRlib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib").Module().(*Module)
+	fooRlib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
 	fooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
 
 	if !android.InList("libstd", fizz.Properties.AndroidMkDylibs) {
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index bcfac7c..e0cc4ce 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -16,5 +16,6 @@
         "x86_linux_host.go",
         "x86_device.go",
         "x86_64_device.go",
+        "arm64_linux_host.go",
     ],
 }
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index a0c496d..21b22a4 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -28,6 +28,7 @@
 	Arm64ArchVariantRustFlags = map[string][]string{
 		"armv8-a":  []string{},
 		"armv8-2a": []string{},
+		"armv8-2a-dotprod": []string{},
 	}
 )
 
@@ -71,9 +72,16 @@
 }
 
 func Arm64ToolchainFactory(arch android.Arch) Toolchain {
+	archVariant := arch.ArchVariant
+	if archVariant == "" {
+		// arch variants defaults to armv8-a. This is mostly for
+		// the host target which borrows toolchain configs from here.
+		archVariant = "armv8-a"
+	}
+
 	toolchainRustFlags := []string{
 		"${config.Arm64ToolchainRustFlags}",
-		"${config.Arm64" + arch.ArchVariant + "VariantRustFlags}",
+		"${config.Arm64" + archVariant + "VariantRustFlags}",
 	}
 
 	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
diff --git a/rust/config/arm64_linux_host.go b/rust/config/arm64_linux_host.go
new file mode 100644
index 0000000..baf9cf8
--- /dev/null
+++ b/rust/config/arm64_linux_host.go
@@ -0,0 +1,24 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	// Linux_cross-arm64 uses the same rust toolchain as the Android-arm64
+	registerToolchainFactory(android.LinuxBionic, android.Arm64, Arm64ToolchainFactory)
+}
diff --git a/rust/config/global.go b/rust/config/global.go
index 6a5251b..71c4240 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.45.2"
+	RustDefaultVersion = "1.46.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 73673d0..90155ca 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -154,12 +154,12 @@
 	}
 
 	// Make sure the expected inputs are provided to the zip rule.
-	if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
+	if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
 		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") ||
 		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_cov/fizz.gcno") {
 		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
 	}
-	if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
+	if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
 		!android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.dylib.gcno") {
 		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
 	}
diff --git a/rust/library.go b/rust/library.go
index a442933..4931f19 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -21,6 +21,11 @@
 	"android/soong/android"
 )
 
+var (
+	DylibStdlibSuffix = ".dylib-std"
+	RlibStdlibSuffix  = ".rlib-std"
+)
+
 func init() {
 	android.RegisterModuleType("rust_library", RustLibraryFactory)
 	android.RegisterModuleType("rust_library_dylib", RustLibraryDylibFactory)
@@ -49,6 +54,9 @@
 
 	// path to include directories to pass to cc_* modules, only relevant for static/shared variants.
 	Include_dirs []string `android:"path,arch_variant"`
+
+	// Whether this library is part of the Rust toolchain sysroot.
+	Sysroot *bool
 }
 
 type LibraryMutatedProperties struct {
@@ -73,6 +81,9 @@
 	// This variant is disabled and should not be compiled
 	// (used for SourceProvider variants that produce only source)
 	VariantIsDisabled bool `blueprint:"mutated"`
+
+	// Whether this library variant should be link libstd via rlibs
+	VariantIsStaticStd bool `blueprint:"mutated"`
 }
 
 type libraryDecorator struct {
@@ -91,6 +102,7 @@
 	dylib() bool
 	static() bool
 	shared() bool
+	sysroot() bool
 
 	// Returns true if the build options for the module have selected a particular build type
 	buildRlib() bool
@@ -104,6 +116,10 @@
 	setShared()
 	setStatic()
 
+	// Set libstd linkage
+	setRlibStd()
+	setDylibStd()
+
 	// Build a specific library variant
 	BuildOnlyFFI()
 	BuildOnlyRust()
@@ -121,6 +137,10 @@
 	return library.MutatedProperties.VariantIsRlib
 }
 
+func (library *libraryDecorator) sysroot() bool {
+	return Bool(library.Properties.Sysroot)
+}
+
 func (library *libraryDecorator) dylib() bool {
 	return library.MutatedProperties.VariantIsDylib
 }
@@ -133,6 +153,11 @@
 	return library.MutatedProperties.VariantIsStatic
 }
 
+func (library *libraryDecorator) staticStd(ctx *depsContext) bool {
+	// libraries should only request the staticStd when building a static FFI or when variant is staticStd
+	return library.static() || library.MutatedProperties.VariantIsStaticStd
+}
+
 func (library *libraryDecorator) buildRlib() bool {
 	return library.MutatedProperties.BuildRlib && BoolDefault(library.Properties.Rlib.Enabled, true)
 }
@@ -163,6 +188,14 @@
 	library.MutatedProperties.VariantIsShared = false
 }
 
+func (library *libraryDecorator) setRlibStd() {
+	library.MutatedProperties.VariantIsStaticStd = true
+}
+
+func (library *libraryDecorator) setDylibStd() {
+	library.MutatedProperties.VariantIsStaticStd = false
+}
+
 func (library *libraryDecorator) setShared() {
 	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = true
@@ -450,6 +483,13 @@
 	return stem + String(library.baseCompiler.Properties.Suffix)
 }
 
+func (library *libraryDecorator) install(ctx ModuleContext) {
+	// Only shared and dylib variants make sense to install.
+	if library.shared() || library.dylib() {
+		library.baseCompiler.install(ctx)
+	}
+}
+
 func (library *libraryDecorator) Disabled() bool {
 	return library.MutatedProperties.VariantIsDisabled
 }
@@ -493,7 +533,6 @@
 				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.
@@ -515,3 +554,23 @@
 		}
 	}
 }
+
+func LibstdMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() {
+		switch library := m.compiler.(type) {
+		case libraryInterface:
+			// Only create a variant if a library is actually being built.
+			if library.rlib() && !library.sysroot() {
+				variants := []string{"rlib-std", "dylib-std"}
+				modules := mctx.CreateLocalVariations(variants...)
+
+				rlib := modules[0].(*Module)
+				dylib := modules[1].(*Module)
+				rlib.compiler.(libraryInterface).setRlibStd()
+				dylib.compiler.(libraryInterface).setDylibStd()
+				rlib.Properties.SubName += RlibStdlibSuffix
+				dylib.Properties.SubName += DylibStdlibSuffix
+			}
+		}
+	}
+}
diff --git a/rust/library_test.go b/rust/library_test.go
index f1bc050..fec3992 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -37,7 +37,7 @@
                 }`)
 
 	// Test all variants are being built.
-	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib").Output("libfoo.rlib")
+	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Output("libfoo.rlib")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
 	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Output("libfoo.ffi.a")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Output("libfoo.ffi.so")
@@ -182,14 +182,14 @@
                         rustlibs: ["libbar"],
                 }`)
 
-	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib")
+	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
 	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
 
 	for _, static := range []android.TestingModule{libfooRlib, libfooStatic} {
-		if !android.InList("libbar", static.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar not present as static dependency in static lib")
+		if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) {
+			t.Errorf("libbar not present as rlib dependency in static lib")
 		}
 		if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) {
 			t.Errorf("libbar present as dynamic dependency in static lib")
@@ -200,8 +200,8 @@
 		if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) {
 			t.Errorf("libbar not present as dynamic dependency in dynamic lib")
 		}
-		if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar present as static dependency in dynamic lib")
+		if android.InList("libbar.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
+			t.Errorf("libbar present as rlib dependency in dynamic lib")
 		}
 
 	}
@@ -238,3 +238,45 @@
 		t.Errorf("stripped version of bar has been generated")
 	}
 }
+
+func TestLibstdLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		rust_ffi {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			rustlibs: ["libfoo"],
+		}`)
+
+	libfooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+	libfooRlibStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
+	libfooRlibDynamic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
+
+	libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
+	libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module)
+
+	if !android.InList("libstd", libfooRlibStatic.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for device rust_library_rlib does not link libstd as an rlib")
+	}
+	if !android.InList("libstd", libfooRlibDynamic.Properties.AndroidMkDylibs) {
+		t.Errorf("dylib-std variant for device rust_library_rlib does not link libstd as an dylib")
+	}
+	if !android.InList("libstd", libfooDylib.Properties.AndroidMkDylibs) {
+		t.Errorf("Device rust_library_dylib does not link libstd as an dylib")
+	}
+
+	if !android.InList("libstd", libbarShared.Properties.AndroidMkDylibs) {
+		t.Errorf("Device rust_ffi_shared does not link libstd as an dylib")
+	}
+	if !android.InList("libstd", libbarStatic.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_static does not link libstd as an rlib")
+	}
+	if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
+	}
+}
diff --git a/rust/rust.go b/rust/rust.go
index 92be0f3..d22acea 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -40,6 +40,7 @@
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
@@ -237,6 +238,7 @@
 	Dylibs     []string
 	Rlibs      []string
 	Rustlibs   []string
+	Stdlibs    []string
 	ProcMacros []string
 	SharedLibs []string
 	StaticLibs []string
@@ -293,7 +295,7 @@
 	Disabled() bool
 	SetDisabled()
 
-	static() bool
+	staticStd(ctx *depsContext) bool
 }
 
 type exportedFlagsProducer interface {
@@ -568,21 +570,6 @@
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
 
 	android.InitDefaultableModule(mod)
-
-	// Explicitly disable unsupported targets.
-	android.AddLoadHook(mod, func(ctx android.LoadHookContext) {
-		disableTargets := struct {
-			Target struct {
-				Linux_bionic struct {
-					Enabled *bool
-				}
-			}
-		}{}
-		disableTargets.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
-
-		ctx.AppendProperties(&disableTargets)
-	})
-
 	return mod
 }
 
@@ -805,14 +792,15 @@
 				directDylibDeps = append(directDylibDeps, rustDep)
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, depName)
 			case rlibDepTag:
+
 				rlib, ok := rustDep.compiler.(libraryInterface)
 				if !ok || !rlib.rlib() {
-					ctx.ModuleErrorf("mod %q not an rlib library", depName)
+					ctx.ModuleErrorf("mod %q not an rlib library", depName+rustDep.Properties.SubName)
 					return
 				}
 				depPaths.coverageFiles = append(depPaths.coverageFiles, rustDep.CoverageFiles()...)
 				directRlibDeps = append(directRlibDeps, rustDep)
-				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName)
+				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName+rustDep.Properties.SubName)
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, depName)
@@ -999,8 +987,19 @@
 		commonDepVariations = append(commonDepVariations,
 			blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 	}
+	stdLinkage := "dylib-std"
+	if mod.compiler.staticStd(ctx) {
+		stdLinkage = "rlib-std"
+	}
+
+	rlibDepVariations := commonDepVariations
+	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
+		rlibDepVariations = append(rlibDepVariations,
+			blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage})
+	}
+
 	actx.AddVariationDependencies(
-		append(commonDepVariations, []blueprint.Variation{
+		append(rlibDepVariations, []blueprint.Variation{
 			{Mutator: "rust_libraries", Variation: "rlib"}}...),
 		rlibDepTag, deps.Rlibs...)
 	actx.AddVariationDependencies(
@@ -1010,12 +1009,27 @@
 
 	if deps.Rustlibs != nil && !mod.compiler.Disabled() {
 		autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
-		actx.AddVariationDependencies(
-			append(commonDepVariations, []blueprint.Variation{
-				{Mutator: "rust_libraries", Variation: autoDep.variation}}...),
-			autoDep.depTag, deps.Rustlibs...)
+		if autoDep.depTag == rlibDepTag {
+			actx.AddVariationDependencies(
+				append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
+				autoDep.depTag, deps.Rustlibs...)
+		} else {
+			actx.AddVariationDependencies(
+				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
+				autoDep.depTag, deps.Rustlibs...)
+		}
 	}
-
+	if deps.Stdlibs != nil {
+		if mod.compiler.staticStd(ctx) {
+			actx.AddVariationDependencies(
+				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "rlib"}),
+				rlibDepTag, deps.Stdlibs...)
+		} else {
+			actx.AddVariationDependencies(
+				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}),
+				dylibDepTag, deps.Stdlibs...)
+		}
+	}
 	actx.AddVariationDependencies(append(commonDepVariations,
 		blueprint.Variation{Mutator: "link", Variation: "shared"}),
 		cc.SharedDepTag(), deps.SharedLibs...)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 89ce359..4842a4c 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -189,7 +189,7 @@
 		t.Errorf("Dylib dependency not detected (dependency missing from AndroidMkDylibs)")
 	}
 
-	if !android.InList("librlib", module.Properties.AndroidMkRlibs) {
+	if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) {
 		t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)")
 	}
 
@@ -253,7 +253,7 @@
         }
 	`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib").Rule("rustc")
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
 	if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") {
 		t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
 	}
@@ -279,15 +279,15 @@
 
 	// Check that our bindings are picked up as crate dependencies as well
 	libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
-	if !android.InList("libbindings", libfooMod.Properties.AndroidMkRlibs) {
+	if !android.InList("libbindings.dylib-std", libfooMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
 	fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
-	if !android.InList("libbindings", fizzBuzzMod.Properties.AndroidMkRlibs) {
+	if !android.InList("libbindings.dylib-std", fizzBuzzMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
 	libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module)
-	if !android.InList("libbindings", libprocmacroMod.Properties.AndroidMkRlibs) {
+	if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
 
@@ -365,6 +365,6 @@
 			crate_name: "foo",
 		}`)
 
-	_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib")
-	_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib")
+	_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std")
+	_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
 }
diff --git a/rust/test.go b/rust/test.go
index d93fc31..0679448 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -133,3 +133,7 @@
 	module, _ := NewRustTest(android.HostSupported)
 	return module.Init()
 }
+
+func (test *testDecorator) staticStd(ctx *depsContext) bool {
+	return true
+}
diff --git a/rust/test_test.go b/rust/test_test.go
index 2382b18..fea2ad0 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestRustTest(t *testing.T) {
@@ -33,3 +35,35 @@
 		t.Errorf("wrong output path: %v;  expected: %v", outPath, expectedOut)
 	}
 }
+
+func TestRustTestLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_test {
+			name: "my_test",
+			srcs: ["foo.rs"],
+			rustlibs: ["libfoo"],
+            rlibs: ["libbar"],
+		}
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		rust_library {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+		}`)
+
+	testingModule := ctx.ModuleForTests("my_test", "android_arm64_armv8-a").Module().(*Module)
+
+	if !android.InList("libfoo.rlib-std", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for libfoo not detected as a rustlib-defined rlib dependency for device rust_test module")
+	}
+	if !android.InList("libbar.rlib-std", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for libbar not detected as an rlib dependency for device rust_test module")
+	}
+	if !android.InList("libstd", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_test module 'my_test' does not link libstd as an rlib")
+	}
+}
diff --git a/rust/testing.go b/rust/testing.go
index 0144c82..ee303ed 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -32,6 +32,7 @@
                                     srcs: ["libstd.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		rust_prebuilt_library {
 				name: "libtest_x86_64-unknown-linux-gnu",
@@ -43,6 +44,7 @@
                                     srcs: ["libtest.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		rust_prebuilt_library {
 				name: "libstd_x86_64-apple-darwin",
@@ -54,6 +56,7 @@
                                     srcs: ["libstd.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		rust_prebuilt_library {
 				name: "libtest_x86_64-apple-darwin",
@@ -65,6 +68,7 @@
                                     srcs: ["libtest.so"],
                                 },
 				host_supported: true,
+				sysroot: true,
 		}
 		//////////////////////////////
 		// Device module requirements
@@ -82,6 +86,7 @@
 			no_stdlibs: true,
 			host_supported: true,
                         native_coverage: false,
+			sysroot: true,
 		}
 		rust_library {
 			name: "libtest",
@@ -90,6 +95,7 @@
 			no_stdlibs: true,
 			host_supported: true,
                         native_coverage: false,
+			sysroot: true,
 		}
 		rust_library {
 			name: "libprotobuf",
@@ -134,6 +140,7 @@
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		// rust mutators
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
diff --git a/ui/build/config.go b/ui/build/config.go
index e9a8fc9..9bd0877 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -24,6 +24,7 @@
 	"time"
 
 	"android/soong/shared"
+
 	"github.com/golang/protobuf/proto"
 
 	smpb "android/soong/ui/metrics/metrics_proto"
@@ -183,6 +184,17 @@
 		"EMPTY_NINJA_FILE",
 	)
 
+	if ret.UseGoma() {
+		ctx.Println("Goma for Android is being deprecated and replaced with RBE. See go/rbe_for_android for instructions on how to use RBE.")
+		ctx.Println()
+		ctx.Println("See go/goma_android_exceptions for exceptions.")
+		ctx.Fatalln("USE_GOMA flag is no longer supported.")
+	}
+
+	if ret.ForceUseGoma() {
+		ret.environ.Set("USE_GOMA", "true")
+	}
+
 	// Tell python not to spam the source tree with .pyc files.
 	ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
 
@@ -778,6 +790,18 @@
 	return c.totalRAM
 }
 
+// ForceUseGoma determines whether we should override Goma deprecation
+// and use Goma for the current build or not.
+func (c *configImpl) ForceUseGoma() bool {
+	if v, ok := c.environ.Get("FORCE_USE_GOMA"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
 func (c *configImpl) UseGoma() bool {
 	if v, ok := c.environ.Get("USE_GOMA"); ok {
 		v = strings.TrimSpace(v)
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index c4b829d..182c544 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -151,13 +151,3 @@
 		ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err)
 	}
 }
-
-// PrintGomaDeprecation prints a PSA on the deprecation of Goma if it is set for the build.
-func PrintGomaDeprecation(ctx Context, config Config) {
-	if config.UseGoma() {
-		fmt.Fprintln(ctx.Writer, "")
-		fmt.Fprintln(ctx.Writer, "Goma for Android is being deprecated and replaced with RBE.")
-		fmt.Fprintln(ctx.Writer, "See go/goma_android_deprecation for more details.")
-		fmt.Fprintln(ctx.Writer, "")
-	}
-}