Merge "Update Rust OWNERS"
diff --git a/README.md b/README.md
index 18c6604..caffd3d 100644
--- a/README.md
+++ b/README.md
@@ -550,6 +550,26 @@
 and produces build rules.  The build rules are collected by blueprint and
 written to a [ninja](http://ninja-build.org) build file.
 
+## Environment Variables Config File
+
+Soong can optionally load environment variables from a pre-specified
+configuration file during startup. These environment variables can be used
+to control the behavior of the build. For example, these variables can determine
+whether remote-execution should be used for the build or not.
+
+The `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment variable specifies the
+directory in which the config file should be searched for. The
+`ANDROID_BUILD_ENVIRONMENT_CONFIG` variable determines the name of the config
+file to be searched for within the config directory. For example, the following
+build comand will load `ENV_VAR_1` and `ENV_VAR_2` environment variables from
+the `example_config.json` file inside the `build/soong` directory.
+
+```
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong \
+  ANDROID_BUILD_ENVIRONMENT_CONFIG=example_config \
+  build/soong/soong_ui.bash
+```
+
 ## Other documentation
 
 * [Best Practices](docs/best_practices.md)
diff --git a/android/arch.go b/android/arch.go
index 96a4cbf..67158e0 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -829,7 +829,7 @@
 	const maxArchTypeNameSize = 500
 
 	// Convert the type to a new set of types that contains only the arch-specific properties
-	// (those that are tagged with `android:"arch_specific"`), and sharded into multiple types
+	// (those that are tagged with `android:"arch_variant"`), and sharded into multiple types
 	// to keep the runtime-generated names under the limit.
 	propShards, _ := proptools.FilterPropertyStructSharded(props, maxArchTypeNameSize, filterArchStruct)
 
@@ -864,6 +864,10 @@
 				archVariant := variantReplacer.Replace(archVariant)
 				variants = append(variants, proptools.FieldNameForProperty(archVariant))
 			}
+			for _, cpuVariant := range cpuVariants[arch] {
+				cpuVariant := variantReplacer.Replace(cpuVariant)
+				variants = append(variants, proptools.FieldNameForProperty(cpuVariant))
+			}
 			for _, feature := range archFeatures[arch] {
 				feature := variantReplacer.Replace(feature)
 				variants = append(variants, proptools.FieldNameForProperty(feature))
@@ -917,7 +921,8 @@
 			for _, archType := range osArchTypeMap[os] {
 				targets = append(targets, GetCompoundTargetField(os, archType))
 
-				// Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
+				// Also add the special "linux_<arch>", "bionic_<arch>" , "glibc_<arch>", and
+				// "musl_<arch>" property structs.
 				if os.Linux() {
 					target := "Linux_" + archType.Name
 					if !InList(target, targets) {
@@ -930,6 +935,18 @@
 						targets = append(targets, target)
 					}
 				}
+				if os == Linux {
+					target := "Glibc_" + archType.Name
+					if !InList(target, targets) {
+						targets = append(targets, target)
+					}
+				}
+				if os == LinuxMusl {
+					target := "Musl_" + archType.Name
+					if !InList(target, targets) {
+						targets = append(targets, target)
+					}
+				}
 			}
 		}
 
@@ -1379,11 +1396,25 @@
 			result = append(result, osArchProperties)
 		}
 
+		if os == Linux {
+			field := "Glibc_" + archType.Name
+			userFriendlyField := "target.glibc_" + "_" + archType.Name
+			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, osArchProperties)
+			}
+		}
+
 		if os == LinuxMusl {
+			field := "Musl_" + archType.Name
+			userFriendlyField := "target.musl_" + "_" + archType.Name
+			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, osArchProperties)
+			}
+
 			// Special case:  to ease the transition from glibc to musl, apply linux_glibc
 			// properties (which has historically mean host linux) to musl variants.
-			field := "Linux_glibc_" + archType.Name
-			userFriendlyField := "target.linux_glibc_" + archType.Name
+			field = "Linux_glibc_" + archType.Name
+			userFriendlyField = "target.linux_glibc_" + archType.Name
 			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
 				result = append(result, osArchProperties)
 			}
@@ -1714,6 +1745,18 @@
 		a.CpuVariant = ""
 	}
 
+	if a.ArchVariant != "" {
+		if validArchVariants := archVariants[archType]; !InList(a.ArchVariant, validArchVariants) {
+			return Arch{}, fmt.Errorf("[%q] unknown arch variant %q, support variants: %q", archType, a.ArchVariant, validArchVariants)
+		}
+	}
+
+	if a.CpuVariant != "" {
+		if validCpuVariants := cpuVariants[archType]; !InList(a.CpuVariant, validCpuVariants) {
+			return Arch{}, fmt.Errorf("[%q] unknown cpu variant %q, support variants: %q", archType, a.CpuVariant, validCpuVariants)
+		}
+	}
+
 	// Filter empty ABIs out of the list.
 	for i := 0; i < len(a.Abi); i++ {
 		if a.Abi[i] == "" {
diff --git a/android/arch_list.go b/android/arch_list.go
index d68a0d1..79ad4af 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -22,38 +22,12 @@
 		"armv7-a-neon",
 		"armv8-a",
 		"armv8-2a",
-		"cortex-a7",
-		"cortex-a8",
-		"cortex-a9",
-		"cortex-a15",
-		"cortex-a53",
-		"cortex-a53-a57",
-		"cortex-a55",
-		"cortex-a72",
-		"cortex-a73",
-		"cortex-a75",
-		"cortex-a76",
-		"krait",
-		"kryo",
-		"kryo385",
-		"exynos-m1",
-		"exynos-m2",
 	},
 	Arm64: {
-		"armv8_a",
-		"armv8_a_branchprot",
-		"armv8_2a",
+		"armv8-a",
+		"armv8-a-branchprot",
+		"armv8-2a",
 		"armv8-2a-dotprod",
-		"cortex-a53",
-		"cortex-a55",
-		"cortex-a72",
-		"cortex-a73",
-		"cortex-a75",
-		"cortex-a76",
-		"kryo",
-		"kryo385",
-		"exynos-m1",
-		"exynos-m2",
 	},
 	X86: {
 		"amberlake",
@@ -87,6 +61,41 @@
 	},
 }
 
+var cpuVariants = map[ArchType][]string{
+	Arm: {
+		"cortex-a7",
+		"cortex-a8",
+		"cortex-a9",
+		"cortex-a15",
+		"cortex-a53",
+		"cortex-a53.a57",
+		"cortex-a55",
+		"cortex-a72",
+		"cortex-a73",
+		"cortex-a75",
+		"cortex-a76",
+		"krait",
+		"kryo",
+		"kryo385",
+		"exynos-m1",
+		"exynos-m2",
+	},
+	Arm64: {
+		"cortex-a53",
+		"cortex-a55",
+		"cortex-a72",
+		"cortex-a73",
+		"cortex-a75",
+		"cortex-a76",
+		"kryo",
+		"kryo385",
+		"exynos-m1",
+		"exynos-m2",
+	},
+	X86:    {},
+	X86_64: {},
+}
+
 var archFeatures = map[ArchType][]string{
 	Arm: {
 		"neon",
diff --git a/android/bazel.go b/android/bazel.go
index 7e5736f..f4fc038 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -198,6 +198,7 @@
 		// build/bazel is not recursive. Instead list each subdirectory under
 		// build/bazel explicitly.
 		"build/bazel":/* recursive = */ false,
+		"build/bazel/ci/dist":/* recursive = */ false,
 		"build/bazel/examples/android_app":/* recursive = */ true,
 		"build/bazel/examples/java":/* recursive = */ true,
 		"build/bazel/bazel_skylib":/* recursive = */ true,
@@ -515,6 +516,8 @@
 
 		"art-script",     // depends on unconverted modules: dalvikvm, dex2oat
 		"dex2oat-script", // depends on unconverted modules: dex2oat
+
+		"error_prone_checkerframework_dataflow_nullaway", // TODO(b/219908977): "Error in fail: deps not allowed without srcs; move to runtime_deps?"
 	}
 
 	// Per-module denylist of cc_library modules to only generate the static
@@ -537,6 +540,31 @@
 		"libadb_pairing_connection",
 		"libadb_pairing_connection_static",
 		"libadb_pairing_server", "libadb_pairing_server_static",
+
+		// TODO(b/204811222) support suffix in cc_binary
+		"acvp_modulewrapper",
+		"android.hardware.media.c2@1.0-service-v4l2",
+		"app_process",
+		"bar_test",
+		"bench_cxa_atexit",
+		"bench_noop",
+		"bench_noop_nostl",
+		"bench_noop_static",
+		"boringssl_self_test",
+		"boringssl_self_test_vendor",
+		"bssl",
+		"cavp",
+		"crash_dump",
+		"crasher",
+		"libcxx_test_template",
+		"linker",
+		"memory_replay",
+		"native_bridge_guest_linker",
+		"native_bridge_stub_library_defaults",
+		"noop",
+		"simpleperf_ndk",
+		"toybox-static",
+		"zlib_bench",
 	}
 
 	// Used for quicker lookups
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 0052551..804a5fb 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -73,7 +73,7 @@
 }
 
 type BazelContext interface {
-	// The below methods involve queuing cquery requests to be later invoked
+	// The methods below involve queuing cquery requests to be later invoked
 	// by bazel. If any of these methods return (_, false), then the request
 	// has been queued to be run later.
 
@@ -561,7 +561,7 @@
     return id_string + ">>" + %s(target)
 `
 
-	for requestType, _ := range requestTypeToCqueryIdEntries {
+	for requestType := range requestTypeToCqueryIdEntries {
 		labelMapName := requestType.Name() + "_Labels"
 		functionName := requestType.Name() + "_Fn"
 		labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
diff --git a/android/config.go b/android/config.go
index f10732b..4a7e0d9 100644
--- a/android/config.go
+++ b/android/config.go
@@ -350,18 +350,19 @@
 
 	config := &config{
 		productVariables: productVariables{
-			DeviceName:                        stringPtr("test_device"),
-			Platform_sdk_version:              intPtr(30),
-			Platform_sdk_codename:             stringPtr("S"),
-			Platform_version_active_codenames: []string{"S", "Tiramisu"},
-			DeviceSystemSdkVersions:           []string{"14", "15"},
-			Platform_systemsdk_versions:       []string{"29", "30"},
-			AAPTConfig:                        []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
-			AAPTPreferredConfig:               stringPtr("xhdpi"),
-			AAPTCharacteristics:               stringPtr("nosdcard"),
-			AAPTPrebuiltDPI:                   []string{"xhdpi", "xxhdpi"},
-			UncompressPrivAppDex:              boolPtr(true),
-			ShippingApiLevel:                  stringPtr("30"),
+			DeviceName:                          stringPtr("test_device"),
+			Platform_sdk_version:                intPtr(30),
+			Platform_sdk_codename:               stringPtr("S"),
+			Platform_base_sdk_extension_version: intPtr(1),
+			Platform_version_active_codenames:   []string{"S", "Tiramisu"},
+			DeviceSystemSdkVersions:             []string{"14", "15"},
+			Platform_systemsdk_versions:         []string{"29", "30"},
+			AAPTConfig:                          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+			AAPTPreferredConfig:                 stringPtr("xhdpi"),
+			AAPTCharacteristics:                 stringPtr("nosdcard"),
+			AAPTPrebuiltDPI:                     []string{"xhdpi", "xxhdpi"},
+			UncompressPrivAppDex:                boolPtr(true),
+			ShippingApiLevel:                    stringPtr("30"),
 		},
 
 		outDir:       buildDir,
@@ -742,6 +743,14 @@
 	return String(c.productVariables.Platform_sdk_codename)
 }
 
+func (c *config) PlatformSdkExtensionVersion() int {
+	return *c.productVariables.Platform_sdk_extension_version
+}
+
+func (c *config) PlatformBaseSdkExtensionVersion() int {
+	return *c.productVariables.Platform_base_sdk_extension_version
+}
+
 func (c *config) PlatformSecurityPatch() string {
 	return String(c.productVariables.Platform_security_patch)
 }
@@ -1530,6 +1539,18 @@
 	return c.config.productVariables.BoardProductPrivatePrebuiltDirs
 }
 
+func (c *deviceConfig) SystemExtSepolicyPrebuiltApiDir() string {
+	return String(c.config.productVariables.SystemExtSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) ProductSepolicyPrebuiltApiDir() string {
+	return String(c.config.productVariables.ProductSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool {
+	return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != ""
+}
+
 func (c *deviceConfig) DirectedVendorSnapshot() bool {
 	return c.config.productVariables.DirectedVendorSnapshot
 }
@@ -1982,3 +2003,8 @@
 func (c *config) RBEWrapper() string {
 	return c.GetenvWithDefault("RBE_WRAPPER", remoteexec.DefaultWrapperPath)
 }
+
+// UseHostMusl returns true if the host target has been configured to build against musl libc.
+func (c *config) UseHostMusl() bool {
+	return Bool(c.productVariables.HostMusl)
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index c932ffa..d2ff97d 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -18,6 +18,8 @@
 	"strings"
 
 	"android/soong/bazel"
+
+	"github.com/google/blueprint"
 )
 
 func init() {
@@ -28,6 +30,11 @@
 	ctx.RegisterModuleType("filegroup", FileGroupFactory)
 })
 
+// IsFilegroup checks that a module is a filegroup type
+func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool {
+	return ctx.OtherModuleType(m) == "filegroup"
+}
+
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
 	Srcs bazel.LabelListAttribute
diff --git a/android/license.go b/android/license.go
index 587cb36..ebee055 100644
--- a/android/license.go
+++ b/android/license.go
@@ -63,7 +63,7 @@
 func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	// license modules have no licenses, but license_kinds must refer to license_kind modules
 	mergeStringProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
-	mergePathProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, m.properties.License_text)...)
+	namePathProps(&m.base().commonProperties.Effective_license_text, m.properties.Package_name, PathsForModuleSrc(ctx, m.properties.License_text)...)
 	for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
 		if lk, ok := module.(*licenseKindModule); ok {
 			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index 2ce921b..b17defe 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -90,7 +90,10 @@
 	// Populate the properties from the variant.
 	l := variant.(*licenseModule)
 	p.License_kinds = l.properties.License_kinds
-	p.License_text = l.base().commonProperties.Effective_license_text
+	p.License_text = make(Paths, 0, len(l.base().commonProperties.Effective_license_text))
+	for _, np := range l.base().commonProperties.Effective_license_text {
+		p.License_text = append(p.License_text, np.Path)
+	}
 }
 
 func (p *licenseSdkMemberProperties) AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet) {
diff --git a/android/licenses.go b/android/licenses.go
index b82d8d7..b51a06b 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -213,7 +213,7 @@
 				m.base().commonProperties.Effective_package_name = l.properties.Package_name
 			}
 			mergeStringProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
-			mergePathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+			mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
 			mergeStringProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
 			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
 		} else {
@@ -239,10 +239,24 @@
 	*prop = SortedUniqueStrings(*prop)
 }
 
-// Update a property Path array with a distinct union of its values and a list of new values.
-func mergePathProps(prop *Paths, values ...Path) {
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func namePathProps(prop *NamedPaths, name *string, values ...Path) {
+	if name == nil {
+		for _, value := range values {
+			*prop = append(*prop, NamedPath{value, ""})
+		}
+	} else {
+		for _, value := range values {
+			*prop = append(*prop, NamedPath{value, *name})
+		}
+	}
+	*prop = SortedUniqueNamedPaths(*prop)
+}
+
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func mergeNamedPathProps(prop *NamedPaths, values ...NamedPath) {
 	*prop = append(*prop, values...)
-	*prop = SortedUniquePaths(*prop)
+	*prop = SortedUniqueNamedPaths(*prop)
 }
 
 // Get the licenses property falling back to the package default.
diff --git a/android/licenses_test.go b/android/licenses_test.go
index 70160fa..8a81e12 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -90,9 +90,9 @@
 			"libother":    []string{"shownotice"},
 		},
 		effectiveNotices: map[string][]string{
-			"libexample1": []string{"top/LICENSE", "top/NOTICE"},
-			"libnested":   []string{"top/LICENSE", "top/NOTICE"},
-			"libother":    []string{"top/LICENSE", "top/NOTICE"},
+			"libexample1": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+			"libnested":   []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+			"libother":    []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
 		},
 	},
 
diff --git a/android/module.go b/android/module.go
index d0807c3..03d3f80 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,11 +16,13 @@
 
 import (
 	"fmt"
+	"net/url"
 	"os"
 	"path"
 	"path/filepath"
 	"reflect"
 	"regexp"
+	"sort"
 	"strings"
 	"text/scanner"
 
@@ -616,6 +618,53 @@
 	Tag *string `android:"arch_variant"`
 }
 
+// NamedPath associates a path with a name. e.g. a license text path with a package name
+type NamedPath struct {
+	Path Path
+	Name string
+}
+
+// String returns an escaped string representing the `NamedPath`.
+func (p NamedPath) String() string {
+	if len(p.Name) > 0 {
+		return p.Path.String() + ":" + url.QueryEscape(p.Name)
+	}
+	return p.Path.String()
+}
+
+// NamedPaths describes a list of paths each associated with a name.
+type NamedPaths []NamedPath
+
+// Strings returns a list of escaped strings representing each `NamedPath` in the list.
+func (l NamedPaths) Strings() []string {
+	result := make([]string, 0, len(l))
+	for _, p := range l {
+		result = append(result, p.String())
+	}
+	return result
+}
+
+// SortedUniqueNamedPaths modifies `l` in place to return the sorted unique subset.
+func SortedUniqueNamedPaths(l NamedPaths) NamedPaths {
+	if len(l) == 0 {
+		return l
+	}
+	sort.Slice(l, func(i, j int) bool {
+		return l[i].String() < l[j].String()
+	})
+	k := 0
+	for i := 1; i < len(l); i++ {
+		if l[i].String() == l[k].String() {
+			continue
+		}
+		k++
+		if k < i {
+			l[k] = l[i]
+		}
+	}
+	return l[:k+1]
+}
+
 type nameProperties struct {
 	// The name of the module.  Must be unique across all modules.
 	Name *string
@@ -684,7 +733,7 @@
 	// Override of module name when reporting licenses
 	Effective_package_name *string `blueprint:"mutated"`
 	// Notice files
-	Effective_license_text Paths `blueprint:"mutated"`
+	Effective_license_text NamedPaths `blueprint:"mutated"`
 	// License names
 	Effective_license_kinds []string `blueprint:"mutated"`
 	// License conditions
@@ -1801,7 +1850,11 @@
 }
 
 func (m *ModuleBase) EffectiveLicenseFiles() Paths {
-	return m.commonProperties.Effective_license_text
+	result := make(Paths, 0, len(m.commonProperties.Effective_license_text))
+	for _, p := range m.commonProperties.Effective_license_text {
+		result = append(result, p.Path)
+	}
+	return result
 }
 
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
diff --git a/android/module_test.go b/android/module_test.go
index c35e66e..1dcddf7 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,12 +15,9 @@
 package android
 
 import (
-	"bytes"
 	"path/filepath"
 	"runtime"
 	"testing"
-
-	mkparser "android/soong/androidmk/parser"
 )
 
 func TestSrcIsModule(t *testing.T) {
@@ -475,21 +472,10 @@
 		prepareForModuleTests,
 		PrepareForTestWithArchMutator,
 		FixtureModifyConfig(SetKatiEnabledForTests),
-		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-			ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
-		}),
+		PrepareForTestWithMakevars,
 	).RunTestWithBp(t, bp)
 
-	installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
-	buf := bytes.NewBuffer(append([]byte(nil), installs...))
-	parser := mkparser.NewParser("makevars", buf)
-
-	nodes, errs := parser.Parse()
-	if len(errs) > 0 {
-		t.Fatalf("error parsing install rules: %s", errs[0])
-	}
-
-	rules := parseMkRules(t, result.Config, nodes)
+	rules := result.InstallMakeRulesForTesting(t)
 
 	module := func(name string, host bool) TestingModule {
 		variant := "android_common"
@@ -501,121 +487,78 @@
 
 	outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
 
-	ruleForOutput := func(output string) installMakeRule {
+	ruleForOutput := func(output string) InstallMakeRule {
 		for _, rule := range rules {
-			if rule.target == output {
+			if rule.Target == output {
 				return rule
 			}
 		}
 		t.Fatalf("no make install rule for %s", output)
-		return installMakeRule{}
+		return InstallMakeRule{}
 	}
 
-	installRule := func(name string) installMakeRule {
+	installRule := func(name string) InstallMakeRule {
 		return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
 	}
 
-	symlinkRule := func(name string) installMakeRule {
+	symlinkRule := func(name string) InstallMakeRule {
 		return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
 	}
 
 	hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
 
-	hostInstallRule := func(name string) installMakeRule {
+	hostInstallRule := func(name string) InstallMakeRule {
 		return ruleForOutput(filepath.Join("out/host/linux-x86", name))
 	}
 
-	hostSymlinkRule := func(name string) installMakeRule {
+	hostSymlinkRule := func(name string) InstallMakeRule {
 		return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
 	}
 
-	assertDeps := func(rule installMakeRule, deps ...string) {
+	assertDeps := func(rule InstallMakeRule, deps ...string) {
 		t.Helper()
-		AssertArrayString(t, "expected inputs", deps, rule.deps)
+		AssertArrayString(t, "expected inputs", deps, rule.Deps)
 	}
 
-	assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
+	assertOrderOnlys := func(rule InstallMakeRule, orderonlys ...string) {
 		t.Helper()
-		AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
+		AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.OrderOnlyDeps)
 	}
 
 	// Check host install rule dependencies
 	assertDeps(hostInstallRule("foo"),
 		hostOutputRule("foo").Output.String(),
-		hostInstallRule("bar").target,
-		hostSymlinkRule("bar").target,
-		hostInstallRule("baz").target,
-		hostSymlinkRule("baz").target,
-		hostInstallRule("qux").target,
-		hostSymlinkRule("qux").target,
+		hostInstallRule("bar").Target,
+		hostSymlinkRule("bar").Target,
+		hostInstallRule("baz").Target,
+		hostSymlinkRule("baz").Target,
+		hostInstallRule("qux").Target,
+		hostSymlinkRule("qux").Target,
 	)
 	assertOrderOnlys(hostInstallRule("foo"))
 
 	// Check host symlink rule dependencies.  Host symlinks must use a normal dependency, not an
 	// order-only dependency, so that the tool gets updated when the symlink is depended on.
-	assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").target)
+	assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").Target)
 	assertOrderOnlys(hostSymlinkRule("foo"))
 
 	// Check device install rule dependencies
 	assertDeps(installRule("foo"), outputRule("foo").Output.String())
 	assertOrderOnlys(installRule("foo"),
-		installRule("bar").target,
-		symlinkRule("bar").target,
-		installRule("baz").target,
-		symlinkRule("baz").target,
-		installRule("qux").target,
-		symlinkRule("qux").target,
+		installRule("bar").Target,
+		symlinkRule("bar").Target,
+		installRule("baz").Target,
+		symlinkRule("baz").Target,
+		installRule("qux").Target,
+		symlinkRule("qux").Target,
 	)
 
 	// Check device symlink rule dependencies.  Device symlinks could use an order-only dependency,
 	// but the current implementation uses a normal dependency.
-	assertDeps(symlinkRule("foo"), installRule("foo").target)
+	assertDeps(symlinkRule("foo"), installRule("foo").Target)
 	assertOrderOnlys(symlinkRule("foo"))
 }
 
-type installMakeRule struct {
-	target        string
-	deps          []string
-	orderOnlyDeps []string
-}
-
-func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
-	var rules []installMakeRule
-	for _, node := range nodes {
-		if mkParserRule, ok := node.(*mkparser.Rule); ok {
-			var rule installMakeRule
-
-			if targets := mkParserRule.Target.Words(); len(targets) == 0 {
-				t.Fatalf("no targets for rule %s", mkParserRule.Dump())
-			} else if len(targets) > 1 {
-				t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
-			} else if !targets[0].Const() {
-				t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
-			} else {
-				rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
-			}
-
-			prereqList := &rule.deps
-			for _, prereq := range mkParserRule.Prerequisites.Words() {
-				if !prereq.Const() {
-					t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
-				}
-
-				if prereq.Value(nil) == "|" {
-					prereqList = &rule.orderOnlyDeps
-					continue
-				}
-
-				*prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
-			}
-
-			rules = append(rules, rule)
-		}
-	}
-
-	return rules
-}
-
 type PropsTestModuleEmbedded struct {
 	Embedded_prop *string
 }
@@ -816,3 +759,120 @@
 		})
 	}
 }
+
+func TestSortedUniqueNamedPaths(t *testing.T) {
+	type np struct {
+		path, name string
+	}
+	makePaths := func(l []np) NamedPaths {
+		result := make(NamedPaths, 0, len(l))
+		for _, p := range l {
+			result = append(result, NamedPath{PathForTesting(p.path), p.name})
+		}
+		return result
+	}
+
+	tests := []struct {
+		name        string
+		in          []np
+		expectedOut []np
+	}{
+		{
+			name:        "empty",
+			in:          []np{},
+			expectedOut: []np{},
+		},
+		{
+			name: "all_same",
+			in: []np{
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+			},
+			expectedOut: []np{
+				{"a.txt", "A"},
+			},
+		},
+		{
+			name: "same_path_different_names",
+			in: []np{
+				{"a.txt", "C"},
+				{"a.txt", "A"},
+				{"a.txt", "D"},
+				{"a.txt", "B"},
+				{"a.txt", "E"},
+			},
+			expectedOut: []np{
+				{"a.txt", "A"},
+				{"a.txt", "B"},
+				{"a.txt", "C"},
+				{"a.txt", "D"},
+				{"a.txt", "E"},
+			},
+		},
+		{
+			name: "different_paths_same_name",
+			in: []np{
+				{"b/b.txt", "A"},
+				{"a/a.txt", "A"},
+				{"a/txt", "A"},
+				{"b", "A"},
+				{"a/b/d", "A"},
+			},
+			expectedOut: []np{
+				{"a/a.txt", "A"},
+				{"a/b/d", "A"},
+				{"a/txt", "A"},
+				{"b/b.txt", "A"},
+				{"b", "A"},
+			},
+		},
+		{
+			name: "all_different",
+			in: []np{
+				{"b/b.txt", "A"},
+				{"a/a.txt", "B"},
+				{"a/txt", "D"},
+				{"b", "C"},
+				{"a/b/d", "E"},
+			},
+			expectedOut: []np{
+				{"a/a.txt", "B"},
+				{"a/b/d", "E"},
+				{"a/txt", "D"},
+				{"b/b.txt", "A"},
+				{"b", "C"},
+			},
+		},
+		{
+			name: "some_different",
+			in: []np{
+				{"b/b.txt", "A"},
+				{"a/a.txt", "B"},
+				{"a/txt", "D"},
+				{"a/b/d", "E"},
+				{"b", "C"},
+				{"a/a.txt", "B"},
+				{"a/b/d", "E"},
+			},
+			expectedOut: []np{
+				{"a/a.txt", "B"},
+				{"a/b/d", "E"},
+				{"a/txt", "D"},
+				{"b/b.txt", "A"},
+				{"b", "C"},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			actual := SortedUniqueNamedPaths(makePaths(tt.in))
+			expected := makePaths(tt.expectedOut)
+			t.Logf("actual: %v", actual)
+			t.Logf("expected: %v", expected)
+			AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual)
+		})
+	}
+}
diff --git a/android/proto.go b/android/proto.go
index 64d4d05..c3759f8 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/bazel"
+	"regexp"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -26,6 +27,14 @@
 	canonicalPathFromRootDefault = true
 )
 
+var (
+	// ignoring case, checks for proto or protos as an independent word in the name, whether at the
+	// beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
+	filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
+
+	ProtoSrcLabelPartition = bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup}
+)
+
 // TODO(ccross): protos are often used to communicate between multiple modules.  If the only
 // way to convert a proto to source is to reference it as a source file, and external modules cannot
 // reference source files in other modules, then every module that owns a proto file will need to
@@ -165,12 +174,11 @@
 
 // Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
 // information necessary for language-specific handling.
-func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, module Module, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
+func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
 	var info Bp2buildProtoInfo
 	if srcs.IsEmpty() {
 		return info, false
 	}
-	m := module.base()
 
 	info.Name = m.Name() + "_proto"
 	attrs := protoAttrs{
@@ -187,7 +195,7 @@
 			if axis == bazel.NoConfigAxis {
 				info.Type = props.Proto.Type
 
-				if proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
+				if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
 					// an empty string indicates to strips the package path
 					path := ""
 					attrs.Strip_import_prefix = &path
@@ -205,3 +213,13 @@
 
 	return info, true
 }
+
+func isProtoFilegroup(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+	m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+	labelStr := label.Label
+	if !exists || !IsFilegroup(ctx, m) {
+		return labelStr, false
+	}
+	likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
+	return labelStr, likelyProtos
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 1c6b1c0..098c1fc 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -470,7 +470,7 @@
 
 func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
 	return r.Command().
-		BuiltTool("dep_fixer").
+		builtToolWithoutDeps("dep_fixer").
 		Inputs(depFiles.Paths())
 }
 
@@ -638,7 +638,7 @@
 		}
 		sboxCmd.Text("rm -rf").Output(r.outDir)
 		sboxCmd.Text("&&")
-		sboxCmd.BuiltTool("sbox").
+		sboxCmd.builtToolWithoutDeps("sbox").
 			Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
 			Flag("--manifest").Input(r.sboxManifestPath)
 
@@ -1040,6 +1040,19 @@
 // It is equivalent to:
 //  cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
 func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+	if c.rule.ctx.Config().UseHostMusl() {
+		// If the host is using musl, assume that the tool was built against musl libc and include
+		// libc_musl.so in the sandbox.
+		// TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
+		// this could be a dependency + TransitivePackagingSpecs.
+		c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
+	}
+	return c.builtToolWithoutDeps(tool)
+}
+
+// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies.  It is used
+// internally by RuleBuilder for helper tools that are known to be compiled statically.
+func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
 	return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
 }
 
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index eb5554c..9d98478 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -46,8 +46,8 @@
 	PrepareForTestWithAndroidMk,
 	FixtureRegisterWithContext(func(ctx RegistrationContext) {
 		ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
-		ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
 	}),
+	PrepareForTestWithMakevars,
 )
 
 func TestSingletonModule(t *testing.T) {
diff --git a/android/testing.go b/android/testing.go
index 39864e1..a9632e9 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"bytes"
 	"fmt"
 	"path/filepath"
 	"regexp"
@@ -23,6 +24,8 @@
 	"sync"
 	"testing"
 
+	mkparser "android/soong/androidmk/parser"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -115,6 +118,10 @@
 	ctx.PreArchMutators(RegisterNamespaceMutator)
 })
 
+var PrepareForTestWithMakevars = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+})
+
 // Test fixture preparer that will register most java build components.
 //
 // Singletons and mutators should only be added here if they are needed for a majority of java
@@ -602,6 +609,62 @@
 		"\nall singletons: %v", name, allSingletonNames))
 }
 
+type InstallMakeRule struct {
+	Target        string
+	Deps          []string
+	OrderOnlyDeps []string
+}
+
+func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []InstallMakeRule {
+	var rules []InstallMakeRule
+	for _, node := range nodes {
+		if mkParserRule, ok := node.(*mkparser.Rule); ok {
+			var rule InstallMakeRule
+
+			if targets := mkParserRule.Target.Words(); len(targets) == 0 {
+				t.Fatalf("no targets for rule %s", mkParserRule.Dump())
+			} else if len(targets) > 1 {
+				t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
+			} else if !targets[0].Const() {
+				t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
+			} else {
+				rule.Target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
+			}
+
+			prereqList := &rule.Deps
+			for _, prereq := range mkParserRule.Prerequisites.Words() {
+				if !prereq.Const() {
+					t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
+				}
+
+				if prereq.Value(nil) == "|" {
+					prereqList = &rule.OrderOnlyDeps
+					continue
+				}
+
+				*prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
+			}
+
+			rules = append(rules, rule)
+		}
+	}
+
+	return rules
+}
+
+func (ctx *TestContext) InstallMakeRulesForTesting(t *testing.T) []InstallMakeRule {
+	installs := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
+	buf := bytes.NewBuffer(append([]byte(nil), installs...))
+	parser := mkparser.NewParser("makevars", buf)
+
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		t.Fatalf("error parsing install rules: %s", errs[0])
+	}
+
+	return parseMkRules(t, ctx.config, nodes)
+}
+
 func (ctx *TestContext) Config() Config {
 	return ctx.config
 }
diff --git a/android/util.go b/android/util.go
index 0ee253e..47c4583 100644
--- a/android/util.go
+++ b/android/util.go
@@ -65,21 +65,55 @@
 	return buf.String()
 }
 
-// SorterStringKeys returns the keys of the given string-keyed map in the ascending order
+// SorterStringKeys returns the keys of the given string-keyed map in the ascending order.
 func SortedStringKeys(m interface{}) []string {
 	v := reflect.ValueOf(m)
 	if v.Kind() != reflect.Map {
 		panic(fmt.Sprintf("%#v is not a map", m))
 	}
-	keys := v.MapKeys()
-	s := make([]string, 0, len(keys))
-	for _, key := range keys {
-		s = append(s, key.String())
+	if v.Len() == 0 {
+		return nil
+	}
+	iter := v.MapRange()
+	s := make([]string, 0, v.Len())
+	for iter.Next() {
+		s = append(s, iter.Key().String())
 	}
 	sort.Strings(s)
 	return s
 }
 
+// stringValues returns the values of the given string-valued map in randomized map order.
+func stringValues(m interface{}) []string {
+	v := reflect.ValueOf(m)
+	if v.Kind() != reflect.Map {
+		panic(fmt.Sprintf("%#v is not a map", m))
+	}
+	if v.Len() == 0 {
+		return nil
+	}
+	iter := v.MapRange()
+	s := make([]string, 0, v.Len())
+	for iter.Next() {
+		s = append(s, iter.Value().String())
+	}
+	return s
+}
+
+// SortedStringValues returns the values of the given string-valued map in the ascending order.
+func SortedStringValues(m interface{}) []string {
+	s := stringValues(m)
+	sort.Strings(s)
+	return s
+}
+
+// SortedUniqueStringValues returns the values of the given string-valued map in the ascending order
+// with duplicates removed.
+func SortedUniqueStringValues(m interface{}) []string {
+	s := stringValues(m)
+	return SortedUniqueStrings(s)
+}
+
 // IndexList returns the index of the first occurrence of the given string in the list or -1
 func IndexList(s string, list []string) int {
 	for i, l := range list {
diff --git a/android/util_test.go b/android/util_test.go
index 09bec01..9b9253b 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -640,3 +640,117 @@
 		})
 	}
 }
+
+func TestSortedStringKeys(t *testing.T) {
+	testCases := []struct {
+		name     string
+		in       interface{}
+		expected []string
+	}{
+		{
+			name:     "nil",
+			in:       map[string]string(nil),
+			expected: nil,
+		},
+		{
+			name:     "empty",
+			in:       map[string]string{},
+			expected: nil,
+		},
+		{
+			name:     "simple",
+			in:       map[string]string{"a": "foo", "b": "bar"},
+			expected: []string{"a", "b"},
+		},
+		{
+			name:     "interface values",
+			in:       map[string]interface{}{"a": nil, "b": nil},
+			expected: []string{"a", "b"},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := SortedStringKeys(tt.in)
+			if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+				t.Errorf("wanted %q, got %q", w, g)
+			}
+		})
+	}
+}
+
+func TestSortedStringValues(t *testing.T) {
+	testCases := []struct {
+		name     string
+		in       interface{}
+		expected []string
+	}{
+		{
+			name:     "nil",
+			in:       map[string]string(nil),
+			expected: nil,
+		},
+		{
+			name:     "empty",
+			in:       map[string]string{},
+			expected: nil,
+		},
+		{
+			name:     "simple",
+			in:       map[string]string{"foo": "a", "bar": "b"},
+			expected: []string{"a", "b"},
+		},
+		{
+			name:     "duplicates",
+			in:       map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+			expected: []string{"a", "b", "b"},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := SortedStringValues(tt.in)
+			if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+				t.Errorf("wanted %q, got %q", w, g)
+			}
+		})
+	}
+}
+
+func TestSortedUniqueStringValues(t *testing.T) {
+	testCases := []struct {
+		name     string
+		in       interface{}
+		expected []string
+	}{
+		{
+			name:     "nil",
+			in:       map[string]string(nil),
+			expected: nil,
+		},
+		{
+			name:     "empty",
+			in:       map[string]string{},
+			expected: nil,
+		},
+		{
+			name:     "simple",
+			in:       map[string]string{"foo": "a", "bar": "b"},
+			expected: []string{"a", "b"},
+		},
+		{
+			name:     "duplicates",
+			in:       map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+			expected: []string{"a", "b"},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := SortedUniqueStringValues(tt.in)
+			if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+				t.Errorf("wanted %q, got %q", w, g)
+			}
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index ff77fef..627d9bd 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -191,6 +191,7 @@
 	Platform_sdk_version_or_codename          *string  `json:",omitempty"`
 	Platform_sdk_final                        *bool    `json:",omitempty"`
 	Platform_sdk_extension_version            *int     `json:",omitempty"`
+	Platform_base_sdk_extension_version       *int     `json:",omitempty"`
 	Platform_version_active_codenames         []string `json:",omitempty"`
 	Platform_vndk_version                     *string  `json:",omitempty"`
 	Platform_systemsdk_versions               []string `json:",omitempty"`
@@ -364,6 +365,9 @@
 	PlatformSepolicyVersion *string `json:",omitempty"`
 	TotSepolicyVersion      *string `json:",omitempty"`
 
+	SystemExtSepolicyPrebuiltApiDir *string `json:",omitempty"`
+	ProductSepolicyPrebuiltApiDir   *string `json:",omitempty"`
+
 	PlatformSepolicyCompatVersions []string `json:",omitempty"`
 
 	VendorVars map[string]map[string]string `json:",omitempty"`
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 6fac79d..295b0e5 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -68,6 +68,8 @@
 	"LOCAL_MODULE_PATH":                    prebuiltModulePath,
 	"LOCAL_REPLACE_PREBUILT_APK_INSTALLED": prebuiltPreprocessed,
 
+	"LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG": invert("auto_gen_config"),
+
 	// composite functions
 	"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
 
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 81b5c30..e8b6f78 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1645,6 +1645,36 @@
 }
 `,
 	},
+	{
+		desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is true",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true
+include $(BUILD_PACKAGE)
+		`,
+		expected: `
+android_app {
+	name: "foo",
+	auto_gen_config: false,
+}
+`,
+	},
+	{
+		desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is false",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := false
+include $(BUILD_PACKAGE)
+		`,
+		expected: `
+android_app {
+	name: "foo",
+	auto_gen_config: true,
+}
+`,
+	},
 }
 
 func TestEndToEnd(t *testing.T) {
diff --git a/apex/apex.go b/apex/apex.go
index d12a786..9ef5e4b 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -416,8 +416,12 @@
 	mergedNotices android.NoticeOutputs
 
 	// The built APEX file. This is the main product.
+	// Could be .apex or .capex
 	outputFile android.WritablePath
 
+	// The built uncompressed .apex file.
+	outputApexFile android.WritablePath
+
 	// The built APEX file in app bundle format. This file is not directly installed to the
 	// device. For an APEX, multiple app bundles are created each of which is for a specific ABI
 	// like arm, arm64, x86, etc. Then they are processed again (outside of the Android build
@@ -1284,6 +1288,12 @@
 	case "", android.DefaultDistTag:
 		// This is the default dist path.
 		return android.Paths{a.outputFile}, nil
+	case imageApexSuffix:
+		// uncompressed one
+		if a.outputApexFile != nil {
+			return android.Paths{a.outputApexFile}, nil
+		}
+		fallthrough
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 6d77b06..1c183a0 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -8661,6 +8661,54 @@
 	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherapex")
 }
 
+func TestApexOutputFileProducer(t *testing.T) {
+	for _, tc := range []struct {
+		name          string
+		ref           string
+		expected_data []string
+	}{
+		{
+			name:          "test_using_output",
+			ref:           ":myapex",
+			expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.capex:myapex.capex"},
+		},
+		{
+			name:          "test_using_apex",
+			ref:           ":myapex{.apex}",
+			expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.apex:myapex.apex"},
+		},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := testApex(t, `
+					apex {
+						name: "myapex",
+						key: "myapex.key",
+						compressible: true,
+						updatable: false,
+					}
+
+					apex_key {
+						name: "myapex.key",
+						public_key: "testkey.avbpubkey",
+						private_key: "testkey.pem",
+					}
+
+					java_test {
+						name: "`+tc.name+`",
+						srcs: ["a.java"],
+						data: ["`+tc.ref+`"],
+					}
+				`,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.CompressedApex = proptools.BoolPtr(true)
+				}))
+			javaTest := ctx.ModuleForTests(tc.name, "android_common").Module().(*java.Test)
+			data := android.AndroidMkEntriesForTest(t, ctx, javaTest)[0].EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+			android.AssertStringPathsRelativeToTopEquals(t, "data", ctx.Config(), tc.expected_data, data)
+		})
+	}
+}
+
 func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
 		PrepareForTestWithApexBuildComponents,
diff --git a/apex/builder.go b/apex/builder.go
index fc4bf8a..183c215 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -810,6 +810,9 @@
 		Implicits:   implicits,
 		Args:        args,
 	})
+	if suffix == imageApexSuffix {
+		a.outputApexFile = signedOutputFile
+	}
 	a.outputFile = signedOutputFile
 
 	if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index f9adc78..8d94079 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -459,7 +459,6 @@
 	name: "foo",
 	srcs: ["foo.proto"],
 	proto: {
-		canonical_path_from_root: false,
 	},
 	include_build_directory: false,
 }`,
@@ -483,7 +482,6 @@
 	srcs: ["foo.proto"],
 	static_executable: true,
 	proto: {
-		canonical_path_from_root: false,
 	},
 	include_build_directory: false,
 }`,
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 3bf0221..8fde655 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1998,8 +1998,7 @@
 }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
-				"srcs":                `["foo.proto"]`,
-				"strip_import_prefix": `""`,
+				"srcs": `["foo.proto"]`,
 			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
 				"deps": `[":foo_proto"]`,
 			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2024,7 +2023,8 @@
 }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
-				"srcs": `["foo.proto"]`,
+				"srcs":                `["foo.proto"]`,
+				"strip_import_prefix": `""`,
 			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
 				"deps": `[":foo_proto"]`,
 			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2049,8 +2049,7 @@
 }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
-				"srcs":                `["foo.proto"]`,
-				"strip_import_prefix": `""`,
+				"srcs": `["foo.proto"]`,
 			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
 				"deps": `[":foo_proto"]`,
 			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2071,7 +2070,6 @@
 	name: "foo",
 	srcs: ["foo.proto"],
 	proto: {
-		canonical_path_from_root: false,
 		type: "full",
 	},
 	include_build_directory: false,
@@ -2099,7 +2097,6 @@
 	name: "foo",
 	srcs: ["foo.proto"],
 	proto: {
-		canonical_path_from_root: false,
 		type: "lite",
 	},
 	include_build_directory: false,
@@ -2127,7 +2124,6 @@
 	name: "foo",
 	srcs: ["foo.proto"],
 	proto: {
-		canonical_path_from_root: false,
 		export_proto_headers: true,
 	},
 	include_build_directory: false,
@@ -2161,7 +2157,6 @@
 	name: "a",
 	srcs: [":a_fg_proto"],
 	proto: {
-		canonical_path_from_root: false,
 		export_proto_headers: true,
 	},
 	include_build_directory: false,
@@ -2171,7 +2166,6 @@
 	name: "b",
 	srcs: [":b_protos"],
 	proto: {
-		canonical_path_from_root: false,
 		export_proto_headers: true,
 	},
 	include_build_directory: false,
@@ -2181,7 +2175,6 @@
 	name: "c",
 	srcs: [":c-proto-srcs"],
 	proto: {
-		canonical_path_from_root: false,
 		export_proto_headers: true,
 	},
 	include_build_directory: false,
@@ -2191,7 +2184,6 @@
 	name: "d",
 	srcs: [":proto-srcs-d"],
 	proto: {
-		canonical_path_from_root: false,
 		export_proto_headers: true,
 	},
 	include_build_directory: false,
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index e8ba573..78192fe 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -431,7 +431,6 @@
 	name: "foo",
 	srcs: ["foo.proto"],
 	proto: {
-		canonical_path_from_root: false,
 		export_proto_headers: true,
 	},
 	include_build_directory: false,
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index f1684c4..205bf4d 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1436,7 +1436,6 @@
 	name: "foo",
 	srcs: ["foo.proto"],
 	proto: {
-		canonical_path_from_root: false,
 		export_proto_headers: true,
 	},
 	include_build_directory: false,
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
new file mode 100644
index 0000000..93b0677
--- /dev/null
+++ b/bp2build/java_proto_conversion_test.go
@@ -0,0 +1,124 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func runJavaProtoTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	(&tc).moduleTypeUnderTest = "java_library_static"
+	(&tc).moduleTypeUnderTestFactory = java.LibraryFactory
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestJavaProto(t *testing.T) {
+	testCases := []struct {
+		protoType                string
+		javaLibraryType          string
+		javaLibraryNameExtension string
+	}{
+		{
+			protoType:                "nano",
+			javaLibraryType:          "java_nano_proto_library",
+			javaLibraryNameExtension: "java_proto_nano",
+		},
+		{
+			protoType:                "micro",
+			javaLibraryType:          "java_micro_proto_library",
+			javaLibraryNameExtension: "java_proto_micro",
+		},
+		{
+			protoType:                "lite",
+			javaLibraryType:          "java_lite_proto_library",
+			javaLibraryNameExtension: "java_proto_lite",
+		},
+		{
+			protoType:                "stream",
+			javaLibraryType:          "java_stream_proto_library",
+			javaLibraryNameExtension: "java_proto_stream",
+		},
+		{
+			protoType:                "full",
+			javaLibraryType:          "java_proto_library",
+			javaLibraryNameExtension: "java_proto",
+		},
+	}
+
+	bp := `java_library_static {
+    name: "java-protos",
+    proto: {
+        type: "%s",
+    },
+    srcs: ["a.proto"],
+}`
+
+	protoLibrary := makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
+		"srcs":                `["a.proto"]`,
+		"strip_import_prefix": `""`,
+	})
+
+	for _, tc := range testCases {
+		javaLibraryName := fmt.Sprintf("java-protos_%s", tc.javaLibraryNameExtension)
+
+		runJavaProtoTestCase(t, bp2buildTestCase{
+			description: fmt.Sprintf("java_proto %s", tc.protoType),
+			blueprint:   fmt.Sprintf(bp, tc.protoType),
+			expectedBazelTargets: []string{
+				protoLibrary,
+				makeBazelTarget(
+					tc.javaLibraryType,
+					javaLibraryName,
+					attrNameToString{
+						"deps": `[":java-protos_proto"]`,
+					}),
+				makeBazelTarget("java_library", "java-protos", attrNameToString{
+					"deps": fmt.Sprintf(`[":%s"]`, javaLibraryName),
+				}),
+			},
+		})
+	}
+}
+
+func TestJavaProtoDefault(t *testing.T) {
+	runJavaProtoTestCase(t, bp2buildTestCase{
+		description: "java_proto",
+		blueprint: `java_library_static {
+    name: "java-protos",
+    srcs: ["a.proto"],
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
+				"srcs":                `["a.proto"]`,
+				"strip_import_prefix": `""`,
+			}),
+			makeBazelTarget(
+				"java_lite_proto_library",
+				"java-protos_java_proto_lite",
+				attrNameToString{
+					"deps": `[":java-protos_proto"]`,
+				}),
+			makeBazelTarget("java_library", "java-protos", attrNameToString{
+				"deps": `[":java-protos_java_proto_lite"]`,
+			}),
+		},
+	})
+}
diff --git a/build_test.bash b/build_test.bash
index b6d00e2..1dc6660 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -25,7 +25,9 @@
 
 # Products that are broken or otherwise don't work with multiproduct_kati
 SKIPPED_PRODUCTS=(
+    # Both of these products are for soong-only builds, and will fail the kati stage.
     mainline_sdk
+    ndk
 )
 
 # To track how long we took to startup. %N isn't supported on Darwin, but
diff --git a/cc/afdo.go b/cc/afdo.go
index d7cce77..c888213 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -32,7 +32,7 @@
 
 var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
 
-const afdoCFlagsFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
+const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s"
 
 func getAfdoProfileProjects(config android.DeviceConfig) []string {
 	return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index b56d689..9290272 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -108,6 +108,9 @@
 				if len(c.Properties.AndroidMkHeaderLibs) > 0 {
 					entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
 				}
+				if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
+					entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
+				}
 				entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
 				if c.UseVndk() {
 					entries.SetBool("LOCAL_USE_VNDK", true)
@@ -396,6 +399,9 @@
 		}
 
 		entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
+		if len(test.Properties.Data_bins) > 0 {
+			entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+		}
 	})
 
 	AndroidMkWriteTestData(test.data, entries)
diff --git a/cc/binary.go b/cc/binary.go
index 54fd339..6c7d581 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -70,6 +70,7 @@
 // cc_binary produces a binary that is runnable on a device.
 func BinaryFactory() android.Module {
 	module, _ := newBinary(android.HostAndDeviceSupported, true)
+	module.bazelHandler = &ccBinaryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -556,6 +557,28 @@
 	})
 }
 
+type ccBinaryBazelHandler struct {
+	android.BazelHandler
+
+	module *Module
+}
+
+func (handler *ccBinaryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+	bazelCtx := ctx.Config().BazelContext
+	filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	if ok {
+		if len(filePaths) != 1 {
+			ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths)
+			return false
+		}
+		outputFilePath := android.PathForBazelOut(ctx, filePaths[0])
+		handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+		// TODO(b/220164721): We need to decide if we should return the stripped as the unstripped.
+		handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
+	}
+	return ok
+}
+
 func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {
 	baseAttrs := bp2BuildParseBaseProps(ctx, m)
 	binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
diff --git a/cc/binary_test.go b/cc/binary_test.go
new file mode 100644
index 0000000..8ec3871
--- /dev/null
+++ b/cc/binary_test.go
@@ -0,0 +1,51 @@
+// Copyright 2022 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 (
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestCcBinaryWithBazel(t *testing.T) {
+	bp := `
+cc_binary {
+	name: "foo",
+	srcs: ["foo.cc"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToOutputFiles: map[string][]string{
+			"//foo/bar:bar": []string{"foo"},
+		},
+	}
+	ctx := testCcWithConfig(t, config)
+
+	binMod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module()
+	producer := binMod.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_binary outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+	unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+	expectedUnStrippedFile := "outputbase/execroot/__main__/foo"
+	android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 42fc0e4..379d6f2 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -16,7 +16,6 @@
 import (
 	"fmt"
 	"path/filepath"
-	"regexp"
 	"strings"
 
 	"android/soong/android"
@@ -34,12 +33,6 @@
 	protoSrcPartition = "proto"
 )
 
-var (
-	// ignoring case, checks for proto or protos as an independent word in the name, whether at the
-	// beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
-	filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
-)
-
 // staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
 // properties which apply to either the shared or static version of a cc_library module.
 type staticOrSharedAttributes struct {
@@ -61,46 +54,32 @@
 	Enabled bazel.BoolAttribute
 }
 
+// groupSrcsByExtension partitions `srcs` into groups based on file extension.
 func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
-	// Check that a module is a filegroup type
-	isFilegroup := func(m blueprint.Module) bool {
-		return ctx.OtherModuleType(m) == "filegroup"
-	}
-
 	// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
 	// macro.
 	addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
 		return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
 			m, exists := ctx.ModuleFromName(label.OriginalModuleName)
 			labelStr := label.Label
-			if !exists || !isFilegroup(m) {
+			if !exists || !android.IsFilegroup(ctx, m) {
 				return labelStr, false
 			}
 			return labelStr + suffix, true
 		}
 	}
 
-	isProtoFilegroup := func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
-		m, exists := ctx.ModuleFromName(label.OriginalModuleName)
-		labelStr := label.Label
-		if !exists || !isFilegroup(m) {
-			return labelStr, false
-		}
-		likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
-		return labelStr, likelyProtos
-	}
-
 	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
-	partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
-		protoSrcPartition: bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup},
+	labels := bazel.LabelPartitions{
+		protoSrcPartition: android.ProtoSrcLabelPartition,
 		cSrcPartition:     bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
 		asSrcPartition:    bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
 		// C++ is the "catch-all" group, and comprises generated sources because we don't
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
-	})
+	}
 
-	return partitioned
+	return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
 }
 
 // bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
diff --git a/cc/builder.go b/cc/builder.go
index a5e5406..525b1a1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -203,25 +203,34 @@
 		"clangBin", "format")
 
 	// Rule for invoking clang-tidy (a clang-based linter).
+	clangTidyDep, clangTidyDepRE = pctx.RemoteStaticRules("clangTidyDep",
+		blueprint.RuleParams{
+			Depfile: "$out",
+			Deps:    blueprint.DepsGCC,
+			Command: "${config.CcWrapper}$ccCmd $cFlags -E -o /dev/null $in " +
+				"-MQ $tidyFile -MD -MF $out",
+			CommandDeps: []string{"$ccCmd"},
+		},
+		&remoteexec.REParams{
+			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
+			ExecStrategy: "${config.REClangTidyExecStrategy}",
+			Inputs:       []string{"$in"},
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+		}, []string{"ccCmd", "cFlags", "tidyFile"}, []string{})
+
 	clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
 		blueprint.RuleParams{
 			Depfile: "${out}.d",
 			Deps:    blueprint.DepsGCC,
-			// Pick bash because some machines with old /bin/sh cannot handle arrays.
-			// All $cFlags and $tidyFlags should have single quotes escaped.
-			// Assume no single quotes in other parameters like $in, $out, $ccCmd.
-			Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
-				"rm -f $$TIDYF $${TIDYF}.d && " +
-				"${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
-				"-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
-				"$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
-				"-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
-			CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
+			Command: "cp ${out}.dep ${out}.d && " +
+				"$tidyVars$reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && " +
+				"touch $out",
+			CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
 		},
 		&remoteexec.REParams{
 			Labels:               map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
 			ExecStrategy:         "${config.REClangTidyExecStrategy}",
-			Inputs:               []string{"$in"},
+			Inputs:               []string{"$in", "${out}.dep"},
 			EnvironmentVariables: []string{"TIDY_TIMEOUT"},
 			// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
 			// under parallel compilation and RBE. So we assume no OutputFiles here.
@@ -230,7 +239,7 @@
 			// (1) New timestamps trigger clang and clang-tidy compilations again.
 			// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
 			Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
-		}, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
+		}, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
@@ -449,28 +458,26 @@
 	}
 }
 
-func escapeSingleQuotes(s string) string {
-	// Replace single quotes to work when embedded in a single quoted string for bash.
-	// Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
-	return strings.Replace(s, `'`, `'\''`, -1)
-}
-
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
+func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs, timeoutTidySrcs android.Paths,
 	flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
 	// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
 	objFiles := make(android.Paths, len(srcFiles))
 	var tidyFiles android.Paths
-	noTidySrcsMap := make(map[android.Path]bool)
+	noTidySrcsMap := make(map[string]bool)
 	var tidyVars string
 	if flags.tidy {
 		tidyFiles = make(android.Paths, 0, len(srcFiles))
 		for _, path := range noTidySrcs {
-			noTidySrcsMap[path] = true
+			noTidySrcsMap[path.String()] = true
 		}
 		tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT")
 		if len(tidyTimeout) > 0 {
-			tidyVars += "TIDY_TIMEOUT=" + tidyTimeout
+			tidyVars += "TIDY_TIMEOUT=" + tidyTimeout + " "
+			// add timeoutTidySrcs into noTidySrcsMap if TIDY_TIMEOUT is set
+			for _, path := range timeoutTidySrcs {
+				noTidySrcsMap[path.String()] = true
+			}
 		}
 	}
 	var coverageFiles android.Paths
@@ -672,26 +679,46 @@
 		}
 
 		//  Even with tidy, some src file could be skipped by noTidySrcsMap.
-		if tidy && !noTidySrcsMap[srcFile] {
+		if tidy && !noTidySrcsMap[srcFile.String()] {
 			tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
+			tidyDepFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy.dep")
 			tidyFiles = append(tidyFiles, tidyFile)
 
+			ruleDep := clangTidyDep
 			rule := clangTidy
 			if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
+				ruleDep = clangTidyDepRE
 				rule = clangTidyRE
 			}
 
+			sharedCFlags := shareFlags("cFlags", moduleFlags)
+			srcRelPath := srcFile.Rel()
+
+			// Add the .tidy.d rule
 			ctx.Build(pctx, android.BuildParams{
-				Rule:        rule,
-				Description: "clang-tidy " + srcFile.Rel(),
-				Output:      tidyFile,
+				Rule:        ruleDep,
+				Description: "clang-tidy-dep " + srcRelPath,
+				Output:      tidyDepFile,
 				Input:       srcFile,
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"ccCmd":     ccCmd,
-					"cFlags":    shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
-					"tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+					"ccCmd":    ccCmd,
+					"cFlags":   sharedCFlags,
+					"tidyFile": tidyFile.String(),
+				},
+			})
+			// Add the .tidy rule with order only dependency on the .tidy.d file
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        rule,
+				Description: "clang-tidy " + srcRelPath,
+				Output:      tidyFile,
+				Input:       srcFile,
+				Implicits:   cFlagsDeps,
+				OrderOnly:   append(android.Paths{}, tidyDepFile),
+				Args: map[string]string{
+					"cFlags":    sharedCFlags,
+					"tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
 					"tidyVars":  tidyVars, // short and not shared
 				},
 			})
diff --git a/cc/cc.go b/cc/cc.go
index 2a84f55..a8adb0c 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -505,6 +505,7 @@
 	selectedStl() string
 	baseModuleName() string
 	getVndkExtendsModuleName() string
+	isAfdoCompile() bool
 	isPgoCompile() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
@@ -1259,6 +1260,13 @@
 	return false
 }
 
+func (c *Module) isAfdoCompile() bool {
+	if afdo := c.afdo; afdo != nil {
+		return afdo.Properties.AfdoTarget != nil
+	}
+	return false
+}
+
 func (c *Module) isPgoCompile() bool {
 	if pgo := c.pgo; pgo != nil {
 		return pgo.Properties.PgoCompile
@@ -1536,6 +1544,10 @@
 	return ctx.mod.IsVndk()
 }
 
+func (ctx *moduleContextImpl) isAfdoCompile() bool {
+	return ctx.mod.isAfdoCompile()
+}
+
 func (ctx *moduleContextImpl) isPgoCompile() bool {
 	return ctx.mod.isPgoCompile()
 }
@@ -2795,6 +2807,8 @@
 						// dependency.
 						depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
 					}
+					depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts,
+						staticLibraryInfo.WholeStaticLibsFromPrebuilts...)
 				} else {
 					switch libDepTag.Order {
 					case earlyLibraryDependency:
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 31d91b6..51a6a27 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3943,7 +3943,6 @@
 		"${config.ArmGenericCflags}",
 		"-target",
 		"armv7a-linux-androideabi20",
-		"-B${config.ArmGccRoot}/arm-linux-androideabi/bin",
 	}
 
 	expectedIncludes := []string{
diff --git a/cc/compiler.go b/cc/compiler.go
index 9dbf2d1..eb5458f 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -42,6 +42,9 @@
 	// list of source files that should not be compiled with clang-tidy.
 	Tidy_disabled_srcs []string `android:"path,arch_variant"`
 
+	// list of source files that should not be compiled by clang-tidy when TIDY_TIMEOUT is set.
+	Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
 	// list of source files that should not be used to build the C/C++ module.
 	// This is most useful in the arch/multilib variants to remove non-common files
 	Exclude_srcs []string `android:"path,arch_variant"`
@@ -403,6 +406,10 @@
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RECOVERY__")
 	}
 
+	if ctx.inRecovery() || ctx.inRamdisk() || ctx.inVendorRamdisk() {
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RAMDISK__")
+	}
+
 	if ctx.apexVariationName() != "" {
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
 		if ctx.Device() {
@@ -450,11 +457,9 @@
 		}
 	}
 
-	gccPrefix := "-B" + config.ToolPath(tc)
-
-	flags.Global.CFlags = append(flags.Global.CFlags, target, gccPrefix)
-	flags.Global.AsFlags = append(flags.Global.AsFlags, target, gccPrefix)
-	flags.Global.LdFlags = append(flags.Global.LdFlags, target, gccPrefix)
+	flags.Global.CFlags = append(flags.Global.CFlags, target)
+	flags.Global.AsFlags = append(flags.Global.AsFlags, target)
+	flags.Global.LdFlags = append(flags.Global.LdFlags, target)
 
 	hod := "Host"
 	if ctx.Os().Class == android.Device {
@@ -665,6 +670,7 @@
 	// Compile files listed in c.Properties.Srcs into objects
 	objs := compileObjs(ctx, buildFlags, "", srcs,
 		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs),
+		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_timeout_srcs),
 		pathDeps, compiler.cFlagsDeps)
 
 	if ctx.Failed() {
@@ -676,9 +682,9 @@
 
 // Compile a list of source files into objects a specified subdirectory
 func compileObjs(ctx ModuleContext, flags builderFlags, subdir string,
-	srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+	srcFiles, noTidySrcs, timeoutTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
 
-	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps)
+	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, timeoutTidySrcs, flags, pathDeps, cFlagsDeps)
 }
 
 // Properties for rust_bindgen related to generating rust bindings.
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 206bec1..5e3f7c7 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -258,8 +258,12 @@
 	return darwinAvailableLibraries
 }
 
-func (t *toolchainDarwin) ToolPath() string {
-	return "${config.MacToolPath}"
+func (t *toolchainDarwin) ToolchainCflags() string {
+	return "-B${config.MacToolPath}"
+}
+
+func (t *toolchainDarwin) ToolchainLdflags() string {
+	return "-B${config.MacToolPath}"
 }
 
 var toolchainDarwinArmSingleton Toolchain = &toolchainDarwinArm{}
diff --git a/cc/config/global.go b/cc/config/global.go
index 48a8b48..400be31 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -107,6 +107,9 @@
 		// This macro allows the bionic versioning.h to indirectly determine whether the
 		// option -Wunguarded-availability is on or not.
 		"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
+
+		// Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
+		"-ffp-contract=off",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -238,6 +241,8 @@
 		// http://b/197240255
 		"-Wno-unused-but-set-variable",
 		"-Wno-unused-but-set-parameter",
+		// http://b/215753485
+		"-Wno-bitwise-instead-of-logical",
 	}
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -282,8 +287,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r437112b"
-	ClangDefaultShortVersion = "14.0.1"
+	ClangDefaultVersion      = "clang-r445002"
+	ClangDefaultShortVersion = "14.0.2"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 6320dbb..6cede11 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"path/filepath"
 
 	"android/soong/android"
 )
@@ -77,7 +76,6 @@
 	GccTriple() string
 	// GccVersion should return a real value, not a ninja reference
 	GccVersion() string
-	ToolPath() string
 
 	IncludeFlags() string
 
@@ -198,10 +196,6 @@
 	return false
 }
 
-func (t toolchainBase) ToolPath() string {
-	return ""
-}
-
 type toolchain64Bit struct {
 	toolchainBase
 }
@@ -283,11 +277,4 @@
 	return LibclangRuntimeLibrary(t, "fuzzer")
 }
 
-func ToolPath(t Toolchain) string {
-	if p := t.ToolPath(); p != "" {
-		return p
-	}
-	return filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
-}
-
 var inList = android.InList
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 00f07ff..0da51cb 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -36,10 +37,10 @@
 		"": []string{
 			"-march=x86-64",
 		},
+
 		"broadwell": []string{
 			"-march=broadwell",
 		},
-
 		"haswell": []string{
 			"-march=core-avx2",
 		},
@@ -190,6 +191,11 @@
 }
 
 func x86_64ToolchainFactory(arch android.Arch) Toolchain {
+	// Error now rather than having a confusing Ninja error
+	if _, ok := x86_64ArchVariantCflags[arch.ArchVariant]; !ok {
+		panic(fmt.Sprintf("Unknown x86_64 architecture version: %q", arch.ArchVariant))
+	}
+
 	toolchainCflags := []string{
 		"${config.X86_64ToolchainCflags}",
 		"${config.X86_64" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 29f0593..e32e1bd 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -186,6 +187,11 @@
 }
 
 func x86ToolchainFactory(arch android.Arch) Toolchain {
+	// Error now rather than having a confusing Ninja error
+	if _, ok := x86ArchVariantCflags[arch.ArchVariant]; !ok {
+		panic(fmt.Sprintf("Unknown x86 architecture version: %q", arch.ArchVariant))
+	}
+
 	toolchainCflags := []string{
 		"${config.X86ToolchainCflags}",
 		"${config.X86" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 43333fa..ce6836b 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -47,6 +47,7 @@
 		"-D_LIBCPP_HAS_MUSL_LIBC",
 		"-DANDROID_HOST_MUSL",
 		"-nostdlibinc",
+		"--sysroot /dev/null",
 	}
 
 	linuxLdflags = []string{
@@ -65,6 +66,7 @@
 	linuxMuslLdflags = []string{
 		"-nostdlib",
 		"-lgcc", "-lgcc_eh",
+		"--sysroot /dev/null",
 	}
 
 	// Extended cflags
@@ -194,10 +196,6 @@
 	return ""
 }
 
-func (t *toolchainLinuxX86) ClangTriple() string {
-	return "i686-linux-gnu"
-}
-
 func (t *toolchainLinuxX86) Cflags() string {
 	return "${config.LinuxCflags} ${config.LinuxX86Cflags}"
 }
@@ -206,10 +204,6 @@
 	return ""
 }
 
-func (t *toolchainLinuxX8664) ClangTriple() string {
-	return "x86_64-linux-gnu"
-}
-
 func (t *toolchainLinuxX8664) Cflags() string {
 	return "${config.LinuxCflags} ${config.LinuxX8664Cflags}"
 }
@@ -283,6 +277,10 @@
 	toolchainGlibc
 }
 
+func (t *toolchainLinuxGlibcX86) ClangTriple() string {
+	return "i686-linux-gnu"
+}
+
 func (t *toolchainLinuxGlibcX86) Cflags() string {
 	return t.toolchainLinuxX86.Cflags() + " " + t.toolchainGlibc.Cflags()
 }
@@ -295,6 +293,10 @@
 	return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainGlibc.Lldflags()
 }
 
+func (t *toolchainLinuxGlibcX8664) ClangTriple() string {
+	return "x86_64-linux-gnu"
+}
+
 func (t *toolchainLinuxGlibcX8664) Cflags() string {
 	return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainGlibc.Cflags()
 }
@@ -356,6 +358,10 @@
 	toolchainMusl
 }
 
+func (t *toolchainLinuxMuslX86) ClangTriple() string {
+	return "i686-linux-musl"
+}
+
 func (t *toolchainLinuxMuslX86) Cflags() string {
 	return t.toolchainLinuxX86.Cflags() + " " + t.toolchainMusl.Cflags()
 }
@@ -368,6 +374,10 @@
 	return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainMusl.Lldflags()
 }
 
+func (t *toolchainLinuxMuslX8664) ClangTriple() string {
+	return "x86_64-linux-musl"
+}
+
 func (t *toolchainLinuxMuslX8664) Cflags() string {
 	return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainMusl.Cflags()
 }
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 9daf40f..2c83211 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"path/filepath"
 	"strings"
 
 	"android/soong/android"
@@ -180,6 +181,14 @@
 	return "${config.WindowsGccTriple}"
 }
 
+func (t *toolchainWindows) ToolchainCflags() string {
+	return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
+func (t *toolchainWindows) ToolchainLdflags() string {
+	return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
 func (t *toolchainWindows) GccVersion() string {
 	return windowsGccVersion
 }
diff --git a/cc/library.go b/cc/library.go
index 708aa10..5fa3471 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -145,6 +145,8 @@
 
 	Tidy_disabled_srcs []string `android:"path,arch_variant"`
 
+	Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
 	Sanitized Sanitized `android:"arch_variant"`
 
 	Cflags []string `android:"arch_variant"`
@@ -586,7 +588,8 @@
 	stripper         Stripper
 
 	// For whole_static_libs
-	objects Objects
+	objects                      Objects
+	wholeStaticLibsFromPrebuilts android.Paths
 
 	// Uses the module's name if empty, but can be overridden. Does not include
 	// shlib suffix.
@@ -1078,11 +1081,13 @@
 		srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
+			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_srcs),
 			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	} else if library.shared() {
 		srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
+			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
 			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	}
 
@@ -1343,6 +1348,7 @@
 
 	library.objects = deps.WholeStaticLibObjs.Copy()
 	library.objects = library.objects.Append(objs)
+	library.wholeStaticLibsFromPrebuilts = android.CopyOfPaths(deps.WholeStaticLibsFromPrebuilts)
 
 	fileName := ctx.ModuleName() + staticLibraryExtension
 	outputFile := android.PathForModuleOut(ctx, fileName)
@@ -1368,9 +1374,10 @@
 
 	if library.static() {
 		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
-			StaticLibrary: outputFile,
-			ReuseObjects:  library.reuseObjects,
-			Objects:       library.objects,
+			StaticLibrary:                outputFile,
+			ReuseObjects:                 library.reuseObjects,
+			Objects:                      library.objects,
+			WholeStaticLibsFromPrebuilts: library.wholeStaticLibsFromPrebuilts,
 
 			TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
 				Direct(outputFile).
diff --git a/cc/library_test.go b/cc/library_test.go
index d220e19..6d5eda2 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -379,3 +379,77 @@
 	gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
 	android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
 }
+
+func TestWholeStaticLibPrebuilts(t *testing.T) {
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_prebuilt_library_static {
+			name: "libprebuilt",
+			srcs: ["foo.a"],
+		}
+
+		cc_library_static {
+			name: "libdirect",
+			whole_static_libs: ["libprebuilt"],
+		}
+
+		cc_library_static {
+			name: "libtransitive",
+			whole_static_libs: ["libdirect"],
+		}
+
+		cc_library_static {
+			name: "libdirect_with_srcs",
+			srcs: ["bar.c"],
+			whole_static_libs: ["libprebuilt"],
+		}
+
+		cc_library_static {
+			name: "libtransitive_with_srcs",
+			srcs: ["baz.c"],
+			whole_static_libs: ["libdirect_with_srcs"],
+		}
+	`)
+
+	libdirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libtransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+	libdirectWithSrcs := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libtransitiveWithSrcs := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+	barObj := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+	bazObj := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libdirect.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libdirect.Args["arLibs"], "foo.a")
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libtransitive.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libtransitive.Args["arLibs"], "foo.a")
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libdirectWithSrcs.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libdirectWithSrcs.Args["arLibs"], "foo.a")
+	android.AssertStringListContains(t, "missing dependency on bar.o",
+		libdirectWithSrcs.Inputs.Strings(), barObj.Output.String())
+	android.AssertStringDoesContain(t, "missing flag for bar.o",
+		libdirectWithSrcs.Args["arObjs"], barObj.Output.String())
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libtransitiveWithSrcs.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libtransitiveWithSrcs.Args["arLibs"], "foo.a")
+
+	android.AssertStringListContains(t, "missing dependency on bar.o",
+		libtransitiveWithSrcs.Inputs.Strings(), barObj.Output.String())
+	android.AssertStringDoesContain(t, "missing flag for bar.o",
+		libtransitiveWithSrcs.Args["arObjs"], barObj.Output.String())
+
+	android.AssertStringListContains(t, "missing dependency on baz.o",
+		libtransitiveWithSrcs.Inputs.Strings(), bazObj.Output.String())
+	android.AssertStringDoesContain(t, "missing flag for baz.o",
+		libtransitiveWithSrcs.Args["arObjs"], bazObj.Output.String())
+}
diff --git a/cc/linkable.go b/cc/linkable.go
index 02d7047..d4b4770 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -352,6 +352,11 @@
 	Objects       Objects
 	ReuseObjects  Objects
 
+	// A static library may contain prebuilt static libraries included with whole_static_libs
+	// that won't appear in Objects.  They are transitively available in
+	// WholeStaticLibsFromPrebuilts.
+	WholeStaticLibsFromPrebuilts android.Paths
+
 	// This isn't the actual transitive DepSet, shared library dependencies have been
 	// converted into static library analogues.  It is only used to order the static
 	// library dependencies that were specified for the current module.
diff --git a/cc/lto.go b/cc/lto.go
index 6d55579..2c274bd 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -123,7 +123,7 @@
 
 		// 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() {
+		if !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
 			flags.Local.LdFlags = append(flags.Local.LdFlags,
 				"-Wl,-plugin-opt,-import-instr-limit=5")
 		}
diff --git a/cc/makevars.go b/cc/makevars.go
index b7aaaad..6752f8c 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -227,7 +227,6 @@
 	}
 
 	clangPrefix := secondPrefix + "CLANG_" + typePrefix
-	clangExtras := "-B" + config.ToolPath(toolchain)
 
 	ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
 	ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
@@ -235,7 +234,6 @@
 		"${config.CommonGlobalCflags}",
 		fmt.Sprintf("${config.%sGlobalCflags}", hod),
 		toolchain.ToolchainCflags(),
-		clangExtras,
 		productExtraCflags,
 	}, " "))
 	ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
@@ -248,25 +246,52 @@
 		toolchain.Ldflags(),
 		toolchain.ToolchainLdflags(),
 		productExtraLdflags,
-		clangExtras,
 	}, " "))
 	ctx.Strict(clangPrefix+"GLOBAL_LLDFLAGS", strings.Join([]string{
 		fmt.Sprintf("${config.%sGlobalLldflags}", hod),
 		toolchain.Lldflags(),
 		toolchain.ToolchainLdflags(),
 		productExtraLdflags,
-		clangExtras,
 	}, " "))
 
 	if target.Os.Class == android.Device {
-		ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
-		ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
-		ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"SCUDO_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"SCUDO_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoMinimalRuntimeLibrary(toolchain), ".so"))
+		sanitizerVariables := map[string]string{
+			"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(toolchain),
+			"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(toolchain),
+			"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(toolchain),
+			"UBSAN_RUNTIME_LIBRARY":               config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain),
+			"UBSAN_MINIMAL_RUNTIME_LIBRARY":       config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain),
+			"TSAN_RUNTIME_LIBRARY":                config.ThreadSanitizerRuntimeLibrary(toolchain),
+			"SCUDO_RUNTIME_LIBRARY":               config.ScudoRuntimeLibrary(toolchain),
+			"SCUDO_MINIMAL_RUNTIME_LIBRARY":       config.ScudoMinimalRuntimeLibrary(toolchain),
+		}
+
+		for variable, value := range sanitizerVariables {
+			ctx.Strict(secondPrefix+variable, value)
+		}
+
+		sanitizerLibs := android.SortedStringValues(sanitizerVariables)
+		var sanitizerLibStems []string
+		ctx.VisitAllModules(func(m android.Module) {
+			if !m.Enabled() {
+				return
+			}
+
+			ccModule, _ := m.(*Module)
+			if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
+				return
+			}
+
+			if android.InList(strings.TrimPrefix(ctx.ModuleName(m), "prebuilt_"), sanitizerLibs) &&
+				m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
+				outputFile := ccModule.outputFile
+				if outputFile.Valid() {
+					sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
+				}
+			}
+		})
+		sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
+		ctx.Strict(secondPrefix+"SANITIZER_STEMS", strings.Join(sanitizerLibStems, " "))
 	}
 
 	// This is used by external/gentoo/...
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 7efe134..fc682ff 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -279,7 +279,7 @@
 
 func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
 	return compileObjs(ctx, flagsToBuilderFlags(flags), "",
-		android.Paths{src}, nil, nil, nil)
+		android.Paths{src}, nil, nil, nil, nil)
 }
 
 func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 339a16d..5980319 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"strings"
 
 	"android/soong/android"
 	"android/soong/bazel"
@@ -188,6 +189,16 @@
 				TableOfContents: p.tocFile,
 			})
 
+			// TODO(b/220898484): Mainline module sdk prebuilts of stub libraries use a stub
+			// library as their source and must not be installed, but libclang_rt.* libraries
+			// have stubs because they are LLNDK libraries, but use an implementation library
+			// as their source and need to be installed.  This discrepancy should be resolved
+			// without the prefix hack below.
+			if p.hasStubsVariants() && !p.buildStubs() && !ctx.Host() &&
+				!strings.HasPrefix(ctx.baseModuleName(), "libclang_rt.") {
+				ctx.Module().MakeUninstallable()
+			}
+
 			return outputFile
 		}
 	}
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 94f75fe..901f458 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel/cquery"
+
 	"github.com/google/blueprint"
 )
 
@@ -29,6 +30,7 @@
 )
 
 func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext {
+	t.Helper()
 	result := android.GroupFixturePreparers(
 		prepareForPrebuiltTest,
 		fs.AddToFixture(),
@@ -449,3 +451,72 @@
 	expectedOutputFiles := []string{pathPrefix + "foo.so"}
 	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
 }
+
+func TestPrebuiltStubNoinstall(t *testing.T) {
+	testFunc := func(t *testing.T, bp string) {
+		result := android.GroupFixturePreparers(
+			prepareForPrebuiltTest,
+			android.PrepareForTestWithMakevars,
+		).RunTestWithBp(t, bp)
+
+		installRules := result.InstallMakeRulesForTesting(t)
+		var installedlibRule *android.InstallMakeRule
+		for i, rule := range installRules {
+			if rule.Target == "out/target/product/test_device/system/lib/installedlib.so" {
+				if installedlibRule != nil {
+					t.Errorf("Duplicate install rules for %s", rule.Target)
+				}
+				installedlibRule = &installRules[i]
+			}
+		}
+		if installedlibRule == nil {
+			t.Errorf("No install rule found for installedlib")
+			return
+		}
+
+		android.AssertStringListDoesNotContain(t,
+			"installedlib has install dependency on stub",
+			installedlibRule.Deps,
+			"out/target/product/test_device/system/lib/stublib.so")
+		android.AssertStringListDoesNotContain(t,
+			"installedlib has order-only install dependency on stub",
+			installedlibRule.OrderOnlyDeps,
+			"out/target/product/test_device/system/lib/stublib.so")
+	}
+
+	const prebuiltStublibBp = `
+		cc_prebuilt_library {
+			name: "stublib",
+			prefer: true,
+			srcs: ["foo.so"],
+			stubs: {
+				versions: ["1"],
+			},
+		}
+	`
+
+	const installedlibBp = `
+		cc_library {
+			name: "installedlib",
+			shared_libs: ["stublib"],
+		}
+	`
+
+	t.Run("prebuilt without source", func(t *testing.T) {
+		testFunc(t, prebuiltStublibBp+installedlibBp)
+	})
+
+	const disabledSourceStublibBp = `
+		cc_library {
+			name: "stublib",
+			enabled: false,
+			stubs: {
+				versions: ["1"],
+			},
+		}
+	`
+
+	t.Run("prebuilt with disabled source", func(t *testing.T) {
+		testFunc(t, disabledSourceStublibBp+prebuiltStublibBp+installedlibBp)
+	})
+}
diff --git a/cc/proto.go b/cc/proto.go
index 3cf1453..8e6d5ed 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -177,7 +177,7 @@
 func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) bp2buildProtoDeps {
 	var ret bp2buildProtoDeps
 
-	protoInfo, ok := android.Bp2buildProtoProperties(ctx, m, protoSrcs)
+	protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
 	if !ok {
 		return ret
 	}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index b8e1468..f8661a6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -42,6 +42,7 @@
 		"-fno-omit-frame-pointer",
 		"-Wno-frame-larger-than=",
 		"-fsanitize-hwaddress-abi=platform",
+		"-mllvm", "-hwasan-use-after-scope=1",
 	}
 
 	// ThinLTO performs codegen during link time, thus these flags need to
@@ -582,20 +583,12 @@
 
 func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
 	minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
-	minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
-	builtinsRuntimeLib := config.BuiltinsRuntimeLibrary(ctx.toolchain()) + ".a"
-	builtinsRuntimePath := "${config.ClangAsanLibDir}/" + builtinsRuntimeLib
 
 	if sanitize.Properties.MinimalRuntimeDep {
 		flags.Local.LdFlags = append(flags.Local.LdFlags,
-			minimalRuntimePath,
 			"-Wl,--exclude-libs,"+minimalRuntimeLib)
 	}
 
-	if sanitize.Properties.BuiltinsDep {
-		flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
-	}
-
 	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
 		return flags
 	}
@@ -725,11 +718,7 @@
 
 		if enableMinimalRuntime(sanitize) {
 			flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
-			flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
-			if !ctx.toolchain().Bionic() {
-				flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
-			}
 		}
 
 		if Bool(sanitize.Properties.Sanitize.Fuzzer) {
@@ -1198,6 +1187,36 @@
 			}
 		}
 
+		addStaticDeps := func(deps ...string) {
+			// If we're using snapshots, redirect to snapshot whenever possible
+			snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+			for idx, dep := range deps {
+				if lib, ok := snapshot.StaticLibs[dep]; ok {
+					deps[idx] = lib
+				}
+			}
+
+			// static executable gets static runtime libs
+			depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+			variations := append(mctx.Target().Variations(),
+				blueprint.Variation{Mutator: "link", Variation: "static"})
+			if c.Device() {
+				variations = append(variations, c.ImageVariation())
+			}
+			if c.UseSdk() {
+				variations = append(variations,
+					blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+			}
+			mctx.AddFarVariationDependencies(variations, depTag, deps...)
+
+		}
+		if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
+			addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain))
+		}
+		if c.sanitize.Properties.BuiltinsDep {
+			addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain))
+		}
+
 		if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
 			// UBSan is supported on non-bionic linux host builds as well
 
@@ -1209,23 +1228,8 @@
 			// Note that by adding dependency with {static|shared}DepTag, the lib is
 			// added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
 			if c.staticBinary() {
-				deps := append(extraStaticDeps, runtimeLibrary)
-				// If we're using snapshots, redirect to snapshot whenever possible
-				snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
-				for idx, dep := range deps {
-					if lib, ok := snapshot.StaticLibs[dep]; ok {
-						deps[idx] = lib
-					}
-				}
-
-				// static executable gets static runtime libs
-				depTag := libraryDependencyTag{Kind: staticLibraryDependency}
-				variations := append(mctx.Target().Variations(),
-					blueprint.Variation{Mutator: "link", Variation: "static"})
-				if c.Device() {
-					variations = append(variations, c.ImageVariation())
-				}
-				mctx.AddFarVariationDependencies(variations, depTag, deps...)
+				addStaticDeps(runtimeLibrary)
+				addStaticDeps(extraStaticDeps...)
 			} else if !c.static() && !c.Header() {
 				// If we're using snapshots, redirect to snapshot whenever possible
 				snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
@@ -1250,6 +1254,10 @@
 				if c.Device() {
 					variations = append(variations, c.ImageVariation())
 				}
+				if c.UseSdk() {
+					variations = append(variations,
+						blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+				}
 				AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
 			}
 			// static lib does not have dependency to the runtime library. The
diff --git a/cc/testing.go b/cc/testing.go
index 3d0c10a..a03d147 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -99,6 +99,18 @@
 			vendor_ramdisk_available: true,
 		}
 
+		cc_prebuilt_library_static {
+			name: "libclang_rt.builtins-x86_64",
+			defaults: ["toolchain_libs_defaults"],
+			host_supported: true,
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.builtins-i386",
+			defaults: ["toolchain_libs_defaults"],
+			host_supported: true,
+		}
+
 		cc_prebuilt_library_shared {
 			name: "libclang_rt.hwasan-aarch64-android",
 			defaults: ["toolchain_libs_defaults"],
@@ -168,6 +180,16 @@
 			defaults: ["toolchain_libs_defaults"],
 		}
 
+		cc_prebuilt_library_static {
+			name: "libclang_rt.ubsan_minimal-aarch64-android",
+			defaults: ["toolchain_libs_defaults"],
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.ubsan_minimal-arm-android",
+			defaults: ["toolchain_libs_defaults"],
+		}
+
 		cc_library {
 			name: "libc",
 			defaults: ["linux_bionic_supported"],
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index 1280553..5603b41 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -114,6 +114,10 @@
 
 	fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
 
+	// Discard the PT_INTERP section so that the linker doesn't need to be passed the
+	// --no-dynamic-linker flag.
+	fmt.Println(script, "    /DISCARD/ : { *(.interp) }")
+
 	fmt.Fprintln(script, "}")
 	fmt.Fprintln(script, "INSERT BEFORE .note.android.embedded_linker;")
 
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 0577c86..7cb8ab7 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -407,10 +407,10 @@
 
 	s.Finish()
 
-	if failures == 1 {
+	if failures.count == 1 {
 		log.Fatal("1 failure")
-	} else if failures > 1 {
-		log.Fatalf("%d failures", failures)
+	} else if failures.count > 1 {
+		log.Fatalf("%d failures %q", failures.count, failures.fails)
 	} else {
 		fmt.Fprintln(output, "Success")
 	}
@@ -522,19 +522,23 @@
 	})
 }
 
-type failureCount int
+type failureCount struct {
+	count int
+	fails []string
+}
 
 func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
 
 func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
 	if result.Error != nil {
-		*f += 1
+		f.count += 1
+		f.fails = append(f.fails, result.Action.Description)
 	}
 }
 
 func (f *failureCount) Message(level status.MsgLevel, message string) {
 	if level >= status.ErrorLvl {
-		*f += 1
+		f.count += 1
 	}
 }
 
diff --git a/docs/tidy.md b/docs/tidy.md
index 3140198..890c3a0 100644
--- a/docs/tidy.md
+++ b/docs/tidy.md
@@ -225,6 +225,16 @@
 in several Android continuous builds where `WITH_TIDY=1` and
 `CLANG_ANALYZER_CHECKS=1` are set.
 
+Similar to `tidy_disabled_srcs` a `tidy_timeout_srcs` list
+can be used to include all source files that took too much time to compile
+with clang-tidy. Files listed in `tidy_timeout_srcs` will not
+be compiled by clang-tidy when `TIDY_TIMEOUT` is defined.
+This can save global build time, when it is necessary to set some
+time limit globally to finish in an acceptable time.
+For developers who want to find all clang-tidy warnings and
+are willing to spend more time on all files in a project,
+they should not define `TIDY_TIMEOUT` and build only the wanted project directories.
+
 ## Capabilities for Android.bp and Android.mk
 
 Some of the previously mentioned features are defined only
diff --git a/example_config.json b/example_config.json
new file mode 100644
index 0000000..7489840
--- /dev/null
+++ b/example_config.json
@@ -0,0 +1,6 @@
+{
+    "env": {
+	"ENV_VAR_1": "Value1",
+	"ENV_VAR_2": "Value2"
+    }
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 1679a57..2ddd70e 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -294,7 +294,7 @@
 		if _, exists := locationLabels[label]; !exists {
 			locationLabels[label] = loc
 		} else {
-			ctx.ModuleErrorf("multiple labels for %q, %q and %q",
+			ctx.ModuleErrorf("multiple locations for label %q: %q and %q (do you have duplicate srcs entries?)",
 				label, locationLabels[label], loc)
 		}
 	}
@@ -496,7 +496,7 @@
 						}
 						return paths[0], nil
 					} else {
-						return reportError("unknown location label %q", label)
+						return reportError("unknown location label %q is not in srcs, out, tools or tool_files.", label)
 					}
 				} else if strings.HasPrefix(name, "locations ") {
 					label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
@@ -507,7 +507,7 @@
 						}
 						return strings.Join(paths, " "), nil
 					} else {
-						return reportError("unknown locations label %q", label)
+						return reportError("unknown locations label %q is not in srcs, out, tools or tool_files.", label)
 					}
 				} else {
 					return reportError("unknown variable '$(%s)'", name)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 04c97fd..1b5cef2 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -341,7 +341,7 @@
 				out: ["out"],
 				cmd: "echo foo > $(location missing)",
 			`,
-			err: `unknown location label "missing"`,
+			err: `unknown location label "missing" is not in srcs, out, tools or tool_files.`,
 		},
 		{
 			name: "error locations",
@@ -349,7 +349,7 @@
 					out: ["out"],
 					cmd: "echo foo > $(locations missing)",
 			`,
-			err: `unknown locations label "missing"`,
+			err: `unknown locations label "missing" is not in srcs, out, tools or tool_files`,
 		},
 		{
 			name: "error location no files",
diff --git a/java/base.go b/java/base.go
index 42d7733..6ff2d03 100644
--- a/java/base.go
+++ b/java/base.go
@@ -227,6 +227,12 @@
 		// whether to generate Binder#GetTransaction name method.
 		Generate_get_transaction_name *bool
 
+		// whether all interfaces should be annotated with required permissions.
+		Enforce_permissions *bool
+
+		// allowlist for interfaces that (temporarily) do not require annotation for permissions.
+		Enforce_permissions_exceptions []string `android:"path"`
+
 		// list of flags that will be passed to the AIDL compiler
 		Flags []string
 	}
@@ -418,7 +424,8 @@
 	outputFile       android.Path
 	extraOutputFiles android.Paths
 
-	exportAidlIncludeDirs android.Paths
+	exportAidlIncludeDirs     android.Paths
+	ignoredAidlPermissionList android.Paths
 
 	logtagsSrcs android.Paths
 
@@ -772,6 +779,17 @@
 	return hasSrcExt(j.properties.Srcs, ext)
 }
 
+func (j *Module) individualAidlFlags(ctx android.ModuleContext, aidlFile android.Path) string {
+	var flags string
+
+	if Bool(j.deviceProperties.Aidl.Enforce_permissions) {
+		if !android.InList(aidlFile.String(), j.ignoredAidlPermissionList.Strings()) {
+			flags = "-Wmissing-permission-annotation -Werror"
+		}
+	}
+	return flags
+}
+
 func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
 	aidlIncludeDirs android.Paths) (string, android.Paths) {
 
@@ -814,6 +832,11 @@
 		flags = append(flags, "--transaction_names")
 	}
 
+	if Bool(j.deviceProperties.Aidl.Enforce_permissions) {
+		exceptions := j.deviceProperties.Aidl.Enforce_permissions_exceptions
+		j.ignoredAidlPermissionList = android.PathsForModuleSrcExcludes(ctx, exceptions, nil)
+	}
+
 	aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String()
 	flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion)
 
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index df8d8c8..21e1d12 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -59,7 +59,7 @@
 			name:                     artBootImageName,
 			stem:                     "boot",
 			installDirOnHost:         "apex/art_boot_images/javalib",
-			installDirOnDevice:       "apex/com.android.art/javalib",
+			installDirOnDevice:       "system/framework",
 			profileInstallPathInApex: "etc/boot-image.prof",
 			modules:                  artModules,
 		}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index c84a15c..023d619 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -330,7 +330,7 @@
 
 	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
 	if len(aidlSrcs) > 0 {
-		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, nil, flags.aidlDeps)
 		outSrcFiles = append(outSrcFiles, srcJarFiles...)
 	}
 
diff --git a/java/droidstubs.go b/java/droidstubs.go
index f9dcfd6..5dc7bc9 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -334,11 +334,7 @@
 		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
 		cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
 			FlagWithArg("--hide ", "SuperfluousPrefix").
-			FlagWithArg("--hide ", "AnnotationExtraction").
-			// (b/217545629)
-			FlagWithArg("--hide ", "ChangedThrows").
-			// (b/217552813)
-			FlagWithArg("--hide ", "ChangedAbstract")
+			FlagWithArg("--hide ", "AnnotationExtraction")
 	}
 }
 
@@ -476,7 +472,8 @@
 		Flag("--quiet").
 		Flag("--format=v2").
 		FlagWithArg("--repeat-errors-max ", "10").
-		FlagWithArg("--hide ", "UnresolvedImport")
+		FlagWithArg("--hide ", "UnresolvedImport").
+		FlagWithArg("--hide ", "InvalidNullability")
 
 	return cmd
 }
diff --git a/java/gen.go b/java/gen.go
index 445a2d8..1572bf0 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -44,7 +44,7 @@
 		})
 )
 
-func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags string, deps android.Paths) android.Paths {
+func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlGlobalFlags string, aidlIndividualFlags map[string]string, deps android.Paths) android.Paths {
 	// Shard aidl files into groups of 50 to avoid having to recompile all of them if one changes and to avoid
 	// hitting command line length limits.
 	shards := android.ShardPaths(aidlFiles, 50)
@@ -61,15 +61,17 @@
 
 		rule.Command().Text("rm -rf").Flag(outDir.String())
 		rule.Command().Text("mkdir -p").Flag(outDir.String())
-		rule.Command().Text("FLAGS=' " + aidlFlags + "'")
+		rule.Command().Text("FLAGS=' " + aidlGlobalFlags + "'")
 
 		for _, aidlFile := range shard {
+			localFlag := aidlIndividualFlags[aidlFile.String()]
 			depFile := srcJarFile.InSameDir(ctx, aidlFile.String()+".d")
 			javaFile := outDir.Join(ctx, pathtools.ReplaceExtension(aidlFile.String(), "java"))
 			rule.Command().
 				Tool(ctx.Config().HostToolPath(ctx, "aidl")).
 				FlagWithDepFile("-d", depFile).
 				Flag("$FLAGS").
+				Flag(localFlag).
 				Input(aidlFile).
 				Output(javaFile).
 				Implicits(deps)
@@ -159,7 +161,14 @@
 
 	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
 	if len(aidlSrcs) > 0 {
-		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+		individualFlags := make(map[string]string)
+		for _, aidlSrc := range aidlSrcs {
+			flags := j.individualAidlFlags(ctx, aidlSrc)
+			if flags != "" {
+				individualFlags[aidlSrc.String()] = flags
+			}
+		}
+		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, individualFlags, flags.aidlDeps)
 		outSrcFiles = append(outSrcFiles, srcJarFiles...)
 	}
 
diff --git a/java/java.go b/java/java.go
index e55f045..d0f0abc 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2013,8 +2013,16 @@
 func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) *javaLibraryAttributes {
 	//TODO(b/209577426): Support multiple arch variants
 	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
+
+	javaSrcPartition := "java"
+	protoSrcPartition := "proto"
+	srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
+		javaSrcPartition:  bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true},
+		protoSrcPartition: android.ProtoSrcLabelPartition,
+	})
+
 	attrs := &javaLibraryAttributes{
-		Srcs: srcs,
+		Srcs: srcPartitions[javaSrcPartition],
 	}
 
 	if m.properties.Javacflags != nil {
@@ -2029,6 +2037,12 @@
 		//TODO(b/217236083) handle static libs similarly to Soong
 		deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs))
 	}
+
+	protoDeps := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition])
+	if protoDeps != nil {
+		deps.Add(protoDeps)
+	}
+
 	attrs.Deps = bazel.MakeLabelListAttribute(deps)
 
 	return attrs
diff --git a/java/java_test.go b/java/java_test.go
index 21c76b6..f095c5e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1333,6 +1333,42 @@
 	}
 }
 
+func TestAidlEnforcePermissions(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["aidl/foo/IFoo.aidl"],
+			aidl: { enforce_permissions: true },
+		}
+	`)
+
+	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	expectedAidlFlag := "-Wmissing-permission-annotation -Werror"
+	if !strings.Contains(aidlCommand, expectedAidlFlag) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+	}
+}
+
+func TestAidlEnforcePermissionsException(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["aidl/foo/IFoo.aidl", "aidl/foo/IFoo2.aidl"],
+			aidl: { enforce_permissions: true, enforce_permissions_exceptions: ["aidl/foo/IFoo2.aidl"] },
+		}
+	`)
+
+	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	expectedAidlFlag := "$$FLAGS -Wmissing-permission-annotation -Werror aidl/foo/IFoo.aidl"
+	if !strings.Contains(aidlCommand, expectedAidlFlag) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+	}
+	expectedAidlFlag = "$$FLAGS  aidl/foo/IFoo2.aidl"
+	if !strings.Contains(aidlCommand, expectedAidlFlag) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+	}
+}
+
 func TestDataNativeBinaries(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_test_host {
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c67e2bd..44650a6 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path"
 	"strconv"
 	"strings"
 
@@ -37,6 +38,11 @@
 	// list of api version directories
 	Api_dirs []string
 
+	// Directory containing finalized api txt files for extension versions.
+	// Extension versions higher than the base sdk extension version will
+	// be assumed to be finalized later than all Api_dirs.
+	Extensions_dir *string
+
 	// The next API directory can optionally point to a directory where
 	// files incompatibility-tracking files are stored for the current
 	// "in progress" API. Each module present in one of the api_dirs will have
@@ -60,36 +66,45 @@
 	// no need to implement
 }
 
-func parseJarPath(path string) (module string, apiver string, scope string) {
-	elements := strings.Split(path, "/")
+// parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g.
+// <version>/<scope>/<module>.jar
+// <version>/<scope>/api/<module>.txt
+// extensions/<version>/<scope>/<module>.jar
+// extensions/<version>/<scope>/api/<module>.txt
+func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) {
+	elements := strings.Split(p, "/")
 
-	apiver = elements[0]
-	scope = elements[1]
-
-	module = strings.TrimSuffix(elements[2], ".jar")
-	return
-}
-
-func parseApiFilePath(ctx android.LoadHookContext, path string) (module string, apiver string, scope string) {
-	elements := strings.Split(path, "/")
-	apiver = elements[0]
-
-	scope = elements[1]
-	if scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" {
-		ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
+	scopeIdx := len(elements) - 2
+	if elements[scopeIdx] == "api" {
+		scopeIdx--
+	}
+	scope = elements[scopeIdx]
+	if scope != "core" && scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" {
+		ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, p)
 		return
 	}
+	version = elements[scopeIdx-1]
 
-	// elements[2] is string literal "api". skipping.
-	module = strings.TrimSuffix(elements[3], ".txt")
+	module = strings.TrimSuffix(path.Base(p), path.Ext(p))
 	return
 }
 
-func prebuiltApiModuleName(mctx android.LoadHookContext, module string, scope string, apiver string) string {
-	return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module
+// parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version).
+func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module string, version int, scope string) {
+	module, v, scope := parsePrebuiltPath(ctx, p)
+	version, err := strconv.Atoi(v)
+	if err != nil {
+		ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v)
+		return
+	}
+	return
 }
 
-func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdkVersion string, compileDex bool) {
+func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string {
+	return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
+}
+
+func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
 	props := struct {
 		Name        *string
 		Jars        []string
@@ -97,7 +112,7 @@
 		Installable *bool
 		Compile_dex *bool
 	}{}
-	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
+	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version))
 	props.Jars = append(props.Jars, path)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
 	props.Installable = proptools.BoolPtr(false)
@@ -132,111 +147,125 @@
 	mctx.CreateModule(genrule.GenRuleFactory, &props)
 }
 
-func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string {
+// globApiDirs collects all the files in all api_dirs and all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'.
+// <api-dir>/<scope>/<glob> for all api-dir and scope.
+func globApiDirs(mctx android.LoadHookContext, p *prebuiltApis, api_dir_glob string) []string {
 	var files []string
 	for _, apiver := range p.properties.Api_dirs {
-		files = append(files, getPrebuiltFilesInSubdir(mctx, apiver, name)...)
+		files = append(files, globScopeDir(mctx, apiver, api_dir_glob)...)
 	}
 	return files
 }
 
-func getPrebuiltFilesInSubdir(mctx android.LoadHookContext, subdir string, name string) []string {
+// globExtensionDirs collects all the files under the extension dir (for all versions and scopes) that match the given glob
+// <extension-dir>/<version>/<scope>/<glob> for all version and scope.
+func globExtensionDirs(mctx android.LoadHookContext, p *prebuiltApis, extension_dir_glob string) []string {
+	// <extensions-dir>/<num>/<extension-dir-glob>
+	return globScopeDir(mctx, *p.properties.Extensions_dir+"/*", extension_dir_glob)
+}
+
+// globScopeDir collects all the files in the given subdir across all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'.
+// <subdir>/<scope>/<glob> for all scope.
+func globScopeDir(mctx android.LoadHookContext, subdir string, subdir_glob string) []string {
 	var files []string
 	dir := mctx.ModuleDir() + "/" + subdir
 	for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
-		glob := fmt.Sprintf("%s/%s/%s", dir, scope, name)
+		glob := fmt.Sprintf("%s/%s/%s", dir, scope, subdir_glob)
 		vfiles, err := mctx.GlobWithDeps(glob, nil)
 		if err != nil {
-			mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, dir+"/"+scope, err)
+			mctx.ModuleErrorf("failed to glob %s files under %q: %s", subdir_glob, dir+"/"+scope, err)
 		}
 		files = append(files, vfiles...)
 	}
+	for i, f := range files {
+		files[i] = strings.TrimPrefix(f, mctx.ModuleDir()+"/")
+	}
 	return files
 }
 
 func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) {
-	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/<module>.jar
-	files := getPrebuiltFiles(mctx, p, "*.jar")
+	files := globApiDirs(mctx, p, "*.jar")
 
 	sdkVersion := proptools.StringDefault(p.properties.Imports_sdk_version, "current")
 	compileDex := proptools.BoolDefault(p.properties.Imports_compile_dex, false)
 
 	for _, f := range files {
 		// create a Import module for each jar file
-		localPath := strings.TrimPrefix(f, mydir)
-		module, apiver, scope := parseJarPath(localPath)
-		createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex)
+		module, version, scope := parsePrebuiltPath(mctx, f)
+		createImport(mctx, module, scope, version, f, sdkVersion, compileDex)
 
 		if module == "core-for-system-modules" {
-			createSystemModules(mctx, apiver, scope)
+			createSystemModules(mctx, version, scope)
 		}
 	}
 }
 
-func createSystemModules(mctx android.LoadHookContext, apiver string, scope string) {
+func createSystemModules(mctx android.LoadHookContext, version, scope string) {
 	props := struct {
 		Name *string
 		Libs []string
 	}{}
-	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, apiver))
-	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, apiver))
+	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version))
+	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version))
 
 	mctx.CreateModule(systemModulesImportFactory, &props)
 }
 
 func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
-	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/api/<module>.txt
-	files := getPrebuiltFiles(mctx, p, "api/*.txt")
-
-	if len(files) == 0 {
-		mctx.ModuleErrorf("no api file found under %q", mydir)
-	}
-
-	// construct a map to find out the latest api file path
-	// for each (<module>, <scope>) pair.
-	type latestApiInfo struct {
-		module  string
-		scope   string
-		version int
-		path    string
+	apiLevelFiles := globApiDirs(mctx, p, "api/*.txt")
+	if len(apiLevelFiles) == 0 {
+		mctx.ModuleErrorf("no api file found under %q", mctx.ModuleDir())
 	}
 
 	// Create modules for all (<module>, <scope, <version>) triplets,
-	// and a "latest" module variant for each (<module>, <scope>) pair
 	apiModuleName := func(module, scope, version string) string {
 		return module + ".api." + scope + "." + version
 	}
-	m := make(map[string]latestApiInfo)
-	for _, f := range files {
-		localPath := strings.TrimPrefix(f, mydir)
-		module, apiver, scope := parseApiFilePath(mctx, localPath)
-		createApiModule(mctx, apiModuleName(module, scope, apiver), localPath)
+	for _, f := range apiLevelFiles {
+		module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
+		createApiModule(mctx, apiModuleName(module, scope, strconv.Itoa(version)), f)
+	}
 
-		version, err := strconv.Atoi(apiver)
-		if err != nil {
-			mctx.ModuleErrorf("Found finalized API files in non-numeric dir %v", apiver)
-			return
-		}
+	// Figure out the latest version of each module/scope
+	type latestApiInfo struct {
+		module, scope, path string
+		version             int
+	}
 
-		// Track latest version of each module/scope, except for incompatibilities
-		if !strings.HasSuffix(module, "incompatibilities") {
+	getLatest := func(files []string) map[string]latestApiInfo {
+		m := make(map[string]latestApiInfo)
+		for _, f := range files {
+			module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
+			if strings.HasSuffix(module, "incompatibilities") {
+				continue
+			}
 			key := module + "." + scope
-			info, ok := m[key]
-			if !ok {
-				m[key] = latestApiInfo{module, scope, version, localPath}
-			} else if version > info.version {
-				info.version = version
-				info.path = localPath
-				m[key] = info
+			info, exists := m[key]
+			if !exists || version > info.version {
+				m[key] = latestApiInfo{module, scope, f, version}
+			}
+		}
+		return m
+	}
+
+	latest := getLatest(apiLevelFiles)
+	if p.properties.Extensions_dir != nil {
+		extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
+		for k, v := range getLatest(extensionApiFiles) {
+			if v.version > mctx.Config().PlatformBaseSdkExtensionVersion() {
+				if _, exists := latest[k]; !exists {
+					mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
+				}
+				latest[k] = v
 			}
 		}
 	}
 
 	// Sort the keys in order to make build.ninja stable
-	for _, k := range android.SortedStringKeys(m) {
-		info := m[k]
+	for _, k := range android.SortedStringKeys(latest) {
+		info := latest[k]
 		name := apiModuleName(info.module, info.scope, "latest")
 		createApiModule(mctx, name, info.path)
 	}
@@ -244,21 +273,20 @@
 	// Create incompatibilities tracking files for all modules, if we have a "next" api.
 	incompatibilities := make(map[string]bool)
 	if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" {
-		files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
+		files := globScopeDir(mctx, nextApiDir, "api/*incompatibilities.txt")
 		for _, f := range files {
-			localPath := strings.TrimPrefix(f, mydir)
-			filename, _, scope := parseApiFilePath(mctx, localPath)
+			filename, _, scope := parsePrebuiltPath(mctx, f)
 			referencedModule := strings.TrimSuffix(filename, "-incompatibilities")
 
-			createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
+			createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f)
 
 			incompatibilities[referencedModule+"."+scope] = true
 		}
 	}
 	// Create empty incompatibilities files for remaining modules
-	for _, k := range android.SortedStringKeys(m) {
+	for _, k := range android.SortedStringKeys(latest) {
 		if _, ok := incompatibilities[k]; !ok {
-			createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
+			createEmptyFile(mctx, apiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest"))
 		}
 	}
 }
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
index 79f4225..75422ad 100644
--- a/java/prebuilt_apis_test.go
+++ b/java/prebuilt_apis_test.go
@@ -20,9 +20,14 @@
 	"testing"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
+func intPtr(v int) *int {
+	return &v
+}
+
 func TestPrebuiltApis_SystemModulesCreation(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
@@ -54,3 +59,34 @@
 	sort.Strings(expected)
 	android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules)
 }
+
+func TestPrebuiltApis_WithExtensions(t *testing.T) {
+	runTestWithBaseExtensionLevel := func(v int) (foo_input string, bar_input string) {
+		result := android.GroupFixturePreparers(
+			prepareForJavaTest,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.Platform_base_sdk_extension_version = intPtr(v)
+			}),
+			FixtureWithPrebuiltApisAndExtensions(map[string][]string{
+				"31":      {"foo"},
+				"32":      {"foo", "bar"},
+				"current": {"foo", "bar"},
+			}, map[string][]string{
+				"1": {"foo"},
+				"2": {"foo", "bar"},
+			}),
+		).RunTest(t)
+		foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
+		bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+		return
+	}
+	// Here, the base extension level is 1, so extension level 2 is the latest
+	foo_input, bar_input := runTestWithBaseExtensionLevel(1)
+	android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+	android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+
+	// Here, the base extension level is 2, so 2 is not later than 32.
+	foo_input, bar_input = runTestWithBaseExtensionLevel(2)
+	android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/foo.txt", foo_input)
+	android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/bar.txt", bar_input)
+}
diff --git a/java/proto.go b/java/proto.go
index ab913d8..5ba486f 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -19,6 +19,13 @@
 	"strconv"
 
 	"android/soong/android"
+	"android/soong/bazel"
+
+	"github.com/google/blueprint/proptools"
+)
+
+const (
+	protoTypeDefault = "lite"
 )
 
 func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths {
@@ -73,13 +80,15 @@
 }
 
 func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
+	const unspecifiedProtobufPluginType = ""
 	if String(p.Proto.Plugin) == "" {
 		switch String(p.Proto.Type) {
+		case "stream": // does not require additional dependencies
 		case "micro":
 			ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
 		case "nano":
 			ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
-		case "lite", "":
+		case "lite", unspecifiedProtobufPluginType:
 			ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
 		case "full":
 			if ctx.Host() || ctx.BazelConversionMode() {
@@ -132,3 +141,52 @@
 
 	return flags
 }
+
+type protoAttributes struct {
+	Deps bazel.LabelListAttribute
+}
+
+func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) *bazel.Label {
+	protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
+	if !ok {
+		return nil
+	}
+
+	typ := proptools.StringDefault(protoInfo.Type, protoTypeDefault)
+	var rule_class string
+	suffix := "_java_proto"
+	switch typ {
+	case "nano":
+		suffix += "_nano"
+		rule_class = "java_nano_proto_library"
+	case "micro":
+		suffix += "_micro"
+		rule_class = "java_micro_proto_library"
+	case "lite":
+		suffix += "_lite"
+		rule_class = "java_lite_proto_library"
+	case "stream":
+		suffix += "_stream"
+		rule_class = "java_stream_proto_library"
+	case "full":
+		rule_class = "java_proto_library"
+	default:
+		ctx.PropertyErrorf("proto.type", "cannot handle conversion at this time: %q", typ)
+	}
+
+	protoLabel := bazel.Label{Label: ":" + m.Name() + "_proto"}
+	var protoAttrs protoAttributes
+	protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+
+	name := m.Name() + suffix
+
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        rule_class,
+			Bzl_load_location: "//build/bazel/rules/java:proto.bzl",
+		},
+		android.CommonAttributes{Name: name},
+		&protoAttrs)
+
+	return &bazel.Label{Label: ":" + name}
+}
diff --git a/java/proto_test.go b/java/proto_test.go
new file mode 100644
index 0000000..d1cb714
--- /dev/null
+++ b/java/proto_test.go
@@ -0,0 +1,53 @@
+// Copyright 2022 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 (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+const protoModules = `
+java_library_static {
+    name: "libprotobuf-java-lite",
+}
+`
+
+func TestProtoStream(t *testing.T) {
+	bp := `
+		java_library {
+			name: "java-stream-protos",
+			proto: {
+				type: "stream",
+			},
+			srcs: [
+				"a.proto",
+				"b.proto",
+			],
+		}
+	`
+
+	ctx := android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithJava,
+	).RunTestWithBp(t, protoModules+bp)
+
+	proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar")
+
+	if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") {
+		t.Errorf("expected '--javastream_out' in %q", cmd)
+	}
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 7362cfb..bfdfffc 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -21,7 +21,6 @@
 	"reflect"
 	"regexp"
 	"sort"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -1512,15 +1511,15 @@
 	}
 	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
 	disabledWarnings := []string{
-		"MissingPermission",
 		"BroadcastBehavior",
-		"HiddenSuperclass",
 		"DeprecationMismatch",
-		"UnavailableSymbol",
-		"SdkConstant",
+		"HiddenSuperclass",
 		"HiddenTypeParameter",
+		"MissingPermission",
+		"SdkConstant",
 		"Todo",
 		"Typo",
+		"UnavailableSymbol",
 	}
 	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
 
@@ -2551,8 +2550,14 @@
 		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
 		return ""
 	}
-	intStr := strconv.Itoa(apiLevel.FinalOrPreviewInt())
-	return formattedOptionalAttribute(attrName, &intStr)
+	if apiLevel.IsCurrent() {
+		// passing "current" would always mean a future release, never the current (or the current in
+		// progress) which means some conditions would never be triggered.
+		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
+			`"current" is not an allowed value for this attribute`)
+		return ""
+	}
+	return formattedOptionalAttribute(attrName, value)
 }
 
 // formats an attribute for the xml permissions file if the value is not null
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index e0e5b56..3500c84 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -182,7 +182,7 @@
 			"30": {"foo", "fooUpdatable", "fooUpdatableErr"},
 		}),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W"}
+			variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W", "X"}
 		}),
 	).RunTestWithBp(t,
 		`
@@ -193,7 +193,7 @@
 			on_bootclasspath_since: "U",
 			on_bootclasspath_before: "V",
 			min_device_sdk: "W",
-			max_device_sdk: "current",
+			max_device_sdk: "X",
 			min_sdk_version: "S",
 		}
 		java_sdk_library {
@@ -202,12 +202,13 @@
 			api_packages: ["foo"],
 		}
 `)
+
 	// test that updatability attributes are passed on correctly
 	fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml")
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"9001\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"9002\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"9003\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"10000\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"U\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"V\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"W\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"X\"`)
 
 	// double check that updatability attributes are not written if they don't exist in the bp file
 	// the permissions file for the foo library defined above
@@ -230,7 +231,7 @@
 			`on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`,
 			`on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`,
 			`min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`,
-			`max_device_sdk: "ddd" could not be parsed as an integer and is not a recognized codename`,
+			`max_device_sdk: "current" is not an allowed value for this attribute`,
 		})).RunTestWithBp(t,
 		`
 	java_sdk_library {
@@ -240,7 +241,7 @@
 			on_bootclasspath_since: "aaa",
 			on_bootclasspath_before: "bbc",
 			min_device_sdk: "ccc",
-			max_device_sdk: "ddd",
+			max_device_sdk: "current",
 		}
 `)
 }
diff --git a/java/testing.go b/java/testing.go
index 6c49bc8..82aa29b 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -146,6 +146,10 @@
 // This defines a file in the mock file system in a predefined location (prebuilts/sdk/Android.bp)
 // and so only one instance of this can be used in each fixture.
 func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.FixturePreparer {
+	return FixtureWithPrebuiltApisAndExtensions(release2Modules, nil)
+}
+
+func FixtureWithPrebuiltApisAndExtensions(apiLevel2Modules map[string][]string, extensionLevel2Modules map[string][]string) android.FixturePreparer {
 	mockFS := android.MockFS{}
 	path := "prebuilts/sdk/Android.bp"
 
@@ -153,14 +157,20 @@
 			prebuilt_apis {
 				name: "sdk",
 				api_dirs: ["%s"],
+				extensions_dir: "extensions",
 				imports_sdk_version: "none",
 				imports_compile_dex: true,
 			}
-		`, strings.Join(android.SortedStringKeys(release2Modules), `", "`))
+		`, strings.Join(android.SortedStringKeys(apiLevel2Modules), `", "`))
 
-	for release, modules := range release2Modules {
+	for release, modules := range apiLevel2Modules {
 		mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
 	}
+	if extensionLevel2Modules != nil {
+		for release, modules := range extensionLevel2Modules {
+			mockFS.Merge(prebuiltExtensionApiFiles([]string{release}, modules))
+		}
+	}
 	return android.GroupFixturePreparers(
 		android.FixtureAddTextFile(path, bp),
 		android.FixtureMergeMockFs(mockFS),
@@ -198,6 +208,19 @@
 	return fs
 }
 
+func prebuiltExtensionApiFiles(extensionLevels []string, modules []string) map[string][]byte {
+	fs := make(map[string][]byte)
+	for _, level := range extensionLevels {
+		for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer} {
+			for _, lib := range modules {
+				fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil
+				fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil
+			}
+		}
+	}
+	return fs
+}
+
 // FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and
 // Config.productVariables structs. As a side effect that enables dexpreopt.
 func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index b8fe162..2b46c2e 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -430,7 +430,6 @@
 		{"TARGET_COPY_OUT_RECOVERY", "recovery"},
 		{"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"},
 		// TODO(asmundak): to process internal config files, we need the following variables:
-		//    BOARD_CONFIG_VENDOR_PATH
 		//    TARGET_VENDOR
 		//    target_base_product
 		//
@@ -1092,7 +1091,7 @@
 
 // Given an if statement's directive and the left/right starlarkExprs,
 // check if the starlarkExprs are one of a few hardcoded special cases
-// that can be converted to a simpler equalify expression than simply comparing
+// that can be converted to a simpler equality expression than simply comparing
 // the two.
 func (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr,
 	right starlarkExpr) (starlarkExpr, bool) {
@@ -1121,8 +1120,8 @@
 	}
 
 	switch call.name {
-	case baseName + ".filter", baseName + ".filter-out":
-		return ctx.parseCompareFilterFuncResult(directive, call, value, isEq), true
+	case baseName + ".filter":
+		return ctx.parseCompareFilterFuncResult(directive, call, value, isEq)
 	case baseName + ".expand_wildcard":
 		return ctx.parseCompareWildcardFuncResult(directive, call, value, !isEq), true
 	case baseName + ".findstring":
@@ -1134,68 +1133,39 @@
 }
 
 func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,
-	filterFuncCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+	filterFuncCall *callExpr, xValue starlarkExpr, negate bool) (starlarkExpr, bool) {
 	// We handle:
 	// *  ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...]
 	// *  ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...]
-	// *  ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...) becomes if VAR in/not in ["v1", "v2"]
-	// TODO(Asmundak): check the last case works for filter-out, too.
+	if x, ok := xValue.(*stringLiteralExpr); !ok || x.literal != "" {
+		return nil, false
+	}
 	xPattern := filterFuncCall.args[0]
 	xText := filterFuncCall.args[1]
 	var xInList *stringLiteralExpr
 	var expr starlarkExpr
 	var ok bool
-	switch x := xValue.(type) {
-	case *stringLiteralExpr:
-		if x.literal != "" {
-			return ctx.newBadExpr(cond, "filter comparison to non-empty value: %s", xValue)
-		}
-		// Either pattern or text should be const, and the
-		// non-const one should be varRefExpr
-		if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
-			expr = xText
-		} else if xInList, ok = xText.(*stringLiteralExpr); ok {
-			expr = xPattern
-		} else {
-			expr = &callExpr{
-				object:     nil,
-				name:       filterFuncCall.name,
-				args:       filterFuncCall.args,
-				returnType: starlarkTypeBool,
-			}
-			if negate {
-				expr = &notExpr{expr: expr}
-			}
-			return expr
-		}
-	case *variableRefExpr:
-		if v, ok := xPattern.(*variableRefExpr); ok {
-			if xInList, ok = xText.(*stringLiteralExpr); ok && v.ref.name() == x.ref.name() {
-				// ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...), flip negate,
-				// it's the opposite to what is done when comparing to empty.
-				expr = xPattern
-				negate = !negate
-			}
-		}
+	if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
+		expr = xText
+	} else if xInList, ok = xText.(*stringLiteralExpr); ok {
+		expr = xPattern
+	} else {
+		return nil, false
 	}
-	if expr != nil && xInList != nil {
-		slExpr := newStringListExpr(strings.Fields(xInList.literal))
-		// Generate simpler code for the common cases:
-		if expr.typ() == starlarkTypeList {
-			if len(slExpr.items) == 1 {
-				// Checking that a string belongs to list
-				return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}
-			} else {
-				// TODO(asmundak):
-				panic("TBD")
-			}
-		} else if len(slExpr.items) == 1 {
-			return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}
+	slExpr := newStringListExpr(strings.Fields(xInList.literal))
+	// Generate simpler code for the common cases:
+	if expr.typ() == starlarkTypeList {
+		if len(slExpr.items) == 1 {
+			// Checking that a string belongs to list
+			return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}, true
 		} else {
-			return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}
+			return nil, false
 		}
+	} else if len(slExpr.items) == 1 {
+		return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}, true
+	} else {
+		return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}, true
 	}
-	return ctx.newBadExpr(cond, "filter arguments are too complex: %s", cond.Dump())
 }
 
 func (ctx *parseContext) parseCompareWildcardFuncResult(directive *mkparser.Directive,
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index a56815f..447f658 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -389,6 +389,10 @@
 endif
 ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
 endif
+ifneq (, $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
+endif
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+endif
 ifneq (,$(filter true, $(v1)$(v2)))
 endif
 ifeq (,$(filter barbet coral%,$(TARGET_PRODUCT)))
@@ -407,8 +411,12 @@
     pass
   if "plaf" in g.get("PLATFORM_LIST", []):
     pass
+  if g["TARGET_BUILD_VARIANT"] == " ".join(rblf.filter(g["TARGET_BUILD_VARIANT"], "userdebug eng")):
+    pass
   if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]:
     pass
+  if rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]):
+    pass
   if rblf.filter("true", "%s%s" % (_v1, _v2)):
     pass
   if not rblf.filter("barbet coral%", g["TARGET_PRODUCT"]):
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 4e58632..2361e03 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -106,6 +106,9 @@
 			}
 			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
 			entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
+			if test.Properties.Data_bins != nil {
+				entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+			}
 		})
 
 	cc.AndroidMkWriteTestData(test.data, ret)
diff --git a/rust/bindgen.go b/rust/bindgen.go
index f4c337d..c2b0512 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -30,7 +30,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r437112b"
+	bindgenClangVersion = "clang-r445002"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/rust.go b/rust/rust.go
index 018d1dd..f40f1a8 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -1192,6 +1192,10 @@
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 				directStaticLibDeps = append(directStaticLibDeps, ccDep)
+
+				// Record baseLibName for snapshots.
+				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
+
 				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
 			case cc.IsSharedDepTag(depTag):
 				// For the shared lib dependencies, we may link to the stub variant
@@ -1214,7 +1218,6 @@
 
 				// Record baseLibName for snapshots.
 				mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
-				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
 
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
 				exportDep = true
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index 4783037..b57963b 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -19,54 +19,9 @@
     exit 1
 fi
 
-TOP=$(pwd)
-
-source build/envsetup.sh
-PLATFORM_SDK_VERSION=$(get_build_var PLATFORM_SDK_VERSION)
-PLATFORM_VERSION_ALL_CODENAMES=$(get_build_var PLATFORM_VERSION_ALL_CODENAMES)
-
-# PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to
-# turn this into ["O","P"].
-PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}
-PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
-
-# Get the list of missing <uses-library> modules and convert it to a JSON array
-# (quote module names, add comma separator and wrap in brackets).
-MISSING_USES_LIBRARIES="$(get_build_var INTERNAL_PLATFORM_MISSING_USES_LIBRARIES)"
-MISSING_USES_LIBRARIES="[$(echo $MISSING_USES_LIBRARIES | sed -e 's/\([^ ]\+\)/\"\1\"/g' -e 's/[ ]\+/, /g')]"
-
-SOONG_OUT=${OUT_DIR}/soong
-SOONG_NDK_OUT=${OUT_DIR}/soong/ndk
-rm -rf ${SOONG_OUT}
-mkdir -p ${SOONG_OUT}
-
-# We only really need to set some of these variables, but soong won't merge this
-# with the defaults, so we need to write out all the defaults with our values
-# added.
-cat > ${SOONG_OUT}/soong.variables << EOF
-{
-    "Platform_sdk_version": ${PLATFORM_SDK_VERSION},
-    "Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
-
-    "DeviceName": "generic_arm64",
-    "HostArch": "x86_64",
-    "Malloc_not_svelte": false,
-    "Safestack": false,
-
-    "Ndk_abis": true,
-
-    "VendorVars": {
-        "art_module": {
-            "source_build": "true"
-        }
-    },
-
-    "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
-}
-EOF
-m --soong-only --skip-config ${SOONG_OUT}/ndk.timestamp
+TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
 
 if [ -n "${DIST_DIR}" ]; then
     mkdir -p ${DIST_DIR} || true
-    tar cjf ${DIST_DIR}/ndk_platform.tar.bz2 -C ${SOONG_OUT} ndk
+    tar cjf ${DIST_DIR}/ndk_platform.tar.bz2 -C ${OUT_DIR}/soong ndk
 fi
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 2ab784d..d1beaba 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -149,6 +149,9 @@
 	// Only available for host sh_test modules.
 	Data_device_libs []string `android:"path,arch_variant"`
 
+	// Install the test into a folder named for the module in all test suites.
+	Per_testcase_directory *bool
+
 	// Test options.
 	Test_options TestOptions
 }
@@ -458,9 +461,13 @@
 					dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
 					entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
 				}
+				if s.testProperties.Data_bins != nil {
+					entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
+				}
 				if Bool(s.testProperties.Test_options.Unit_test) {
 					entries.SetBool("LOCAL_IS_UNIT_TEST", true)
 				}
+				entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
 			},
 		},
 	}}
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 7fe1d85..89b8126 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -37,6 +37,8 @@
 //
 // deprecated
 func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+
 	result := prepareForShTest.RunTestWithBp(t, bp)
 
 	return result.TestContext, result.Config