Merge "Surface module properties as Bazel BUILD target attributes in the Bazel overlay"
diff --git a/android/Android.bp b/android/Android.bp
index ff14a70..a1b5159 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -41,6 +41,7 @@
         "paths.go",
         "phony.go",
         "prebuilt.go",
+        "prebuilt_build_tool.go",
         "proto.go",
         "register.go",
         "rule_builder.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 7e86140..fafbfd6 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -183,12 +183,15 @@
 	name := amod.BaseModuleName()
 
 	var ret []string
+	var availableTaggedDists TaggedDistFiles
 
-	availableTaggedDists := TaggedDistFiles{}
 	if a.DistFiles != nil {
 		availableTaggedDists = a.DistFiles
 	} else if a.OutputFile.Valid() {
 		availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
+	} else {
+		// Nothing dist-able for this module.
+		return nil
 	}
 
 	// Iterate over this module's dist structs, merged from the dist and dists properties.
diff --git a/android/apex.go b/android/apex.go
index cd84f8a..f857ec6 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -29,11 +29,22 @@
 )
 
 type ApexInfo struct {
-	// Name of the apex variant that this module is mutated into
-	ApexName string
+	// Name of the apex variation that this module is mutated into
+	ApexVariationName string
 
 	MinSdkVersion int
 	Updatable     bool
+	RequiredSdks  SdkRefs
+
+	InApexes []string
+}
+
+func (i ApexInfo) mergedName() string {
+	name := "apex" + strconv.Itoa(i.MinSdkVersion)
+	for _, sdk := range i.RequiredSdks {
+		name += "_" + sdk.Name + "_" + sdk.Version
+	}
+	return name
 }
 
 // Extracted from ApexModule to make it easier to define custom subsets of the
@@ -69,19 +80,22 @@
 	// Call this before apex.apexMutator is run.
 	BuildForApex(apex ApexInfo)
 
-	// Returns the APEXes that this module will be built for
-	ApexVariations() []ApexInfo
-
-	// Returns the name of APEX that this module will be built for. Empty string
-	// is returned when 'IsForPlatform() == true'. Note that a module can be
-	// included in multiple APEXes, in which case, the module is mutated into
-	// multiple modules each of which for an APEX. This method returns the
-	// name of the APEX that a variant module is for.
+	// Returns the name of APEX variation that this module will be built for.
+	// Empty string is returned when 'IsForPlatform() == true'. Note that a
+	// module can beincluded in multiple APEXes, in which case, the module
+	// is mutated into one or more variants, each of which is for one or
+	// more APEXes.  This method returns the name of the APEX variation of
+	// the module.
 	// Call this after apex.apexMutator is run.
-	ApexName() string
+	ApexVariationName() string
+
+	// Returns the name of the APEX modules that this variant of this module
+	// is present in.
+	// Call this after apex.apexMutator is run.
+	InApexes() []string
 
 	// Tests whether this module will be built for the platform or not.
-	// This is a shortcut for ApexName() == ""
+	// This is a shortcut for ApexVariationName() == ""
 	IsForPlatform() bool
 
 	// Tests if this module could have APEX variants. APEX variants are
@@ -128,6 +142,15 @@
 	// Returns nil if this module supports sdkVersion
 	// Otherwise, returns error with reason
 	ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion int) error
+
+	// Returns true if this module needs a unique variation per apex, for example if
+	// use_apex_name_macro is set.
+	UniqueApexVariations() bool
+
+	// UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies
+	// that are in the same APEX have unique APEX variations so that the module can link against
+	// the right variant.
+	UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext)
 }
 
 type ApexProperties struct {
@@ -144,6 +167,8 @@
 	Info ApexInfo `blueprint:"mutated"`
 
 	NotAvailableForPlatform bool `blueprint:"mutated"`
+
+	UniqueApexVariationsForDeps bool `blueprint:"mutated"`
 }
 
 // Marker interface that identifies dependencies that are excluded from APEX
@@ -179,27 +204,68 @@
 	return nil
 }
 
+func (m *ApexModuleBase) UniqueApexVariations() bool {
+	return false
+}
+
+func (m *ApexModuleBase) UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext) {
+	// anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes list
+	// in common.  It is used instead of DepIsInSameApex because it needs to determine if the dep
+	// is in the same APEX due to being directly included, not only if it is included _because_ it
+	// is a dependency.
+	anyInSameApex := func(a, b []ApexInfo) bool {
+		collectApexes := func(infos []ApexInfo) []string {
+			var ret []string
+			for _, info := range infos {
+				ret = append(ret, info.InApexes...)
+			}
+			return ret
+		}
+
+		aApexes := collectApexes(a)
+		bApexes := collectApexes(b)
+		sort.Strings(bApexes)
+		for _, aApex := range aApexes {
+			index := sort.SearchStrings(bApexes, aApex)
+			if index < len(bApexes) && bApexes[index] == aApex {
+				return true
+			}
+		}
+		return false
+	}
+
+	mctx.VisitDirectDeps(func(dep Module) {
+		if depApexModule, ok := dep.(ApexModule); ok {
+			if anyInSameApex(depApexModule.apexModuleBase().apexVariations, m.apexVariations) &&
+				(depApexModule.UniqueApexVariations() ||
+					depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
+				m.ApexProperties.UniqueApexVariationsForDeps = true
+			}
+		}
+	})
+}
+
 func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
 	m.apexVariationsLock.Lock()
 	defer m.apexVariationsLock.Unlock()
 	for _, v := range m.apexVariations {
-		if v.ApexName == apex.ApexName {
+		if v.ApexVariationName == apex.ApexVariationName {
 			return
 		}
 	}
 	m.apexVariations = append(m.apexVariations, apex)
 }
 
-func (m *ApexModuleBase) ApexVariations() []ApexInfo {
-	return m.apexVariations
+func (m *ApexModuleBase) ApexVariationName() string {
+	return m.ApexProperties.Info.ApexVariationName
 }
 
-func (m *ApexModuleBase) ApexName() string {
-	return m.ApexProperties.Info.ApexName
+func (m *ApexModuleBase) InApexes() []string {
+	return m.ApexProperties.Info.InApexes
 }
 
 func (m *ApexModuleBase) IsForPlatform() bool {
-	return m.ApexProperties.Info.ApexName == ""
+	return m.ApexProperties.Info.ApexVariationName == ""
 }
 
 func (m *ApexModuleBase) CanHaveApexVariants() bool {
@@ -276,17 +342,48 @@
 
 func (a byApexName) Len() int           { return len(a) }
 func (a byApexName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a byApexName) Less(i, j int) bool { return a[i].ApexName < a[j].ApexName }
+func (a byApexName) Less(i, j int) bool { return a[i].ApexVariationName < a[j].ApexVariationName }
+
+// mergeApexVariations deduplicates APEX variations that would build identically into a common
+// variation.  It returns the reduced list of variations and a list of aliases from the original
+// variation names to the new variation names.
+func mergeApexVariations(apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
+	sort.Sort(byApexName(apexVariations))
+	seen := make(map[string]int)
+	for _, apexInfo := range apexVariations {
+		apexName := apexInfo.ApexVariationName
+		mergedName := apexInfo.mergedName()
+		if index, exists := seen[mergedName]; exists {
+			merged[index].InApexes = append(merged[index].InApexes, apexName)
+			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
+		} else {
+			seen[mergedName] = len(merged)
+			apexInfo.ApexVariationName = apexInfo.mergedName()
+			apexInfo.InApexes = CopyOf(apexInfo.InApexes)
+			merged = append(merged, apexInfo)
+		}
+		aliases = append(aliases, [2]string{apexName, mergedName})
+	}
+	return merged, aliases
+}
 
 func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module {
 	if len(m.apexVariations) > 0 {
 		m.checkApexAvailableProperty(mctx)
 
-		sort.Sort(byApexName(m.apexVariations))
+		var apexVariations []ApexInfo
+		var aliases [][2]string
+		if !mctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps {
+			apexVariations, aliases = mergeApexVariations(m.apexVariations)
+		} else {
+			apexVariations = m.apexVariations
+		}
+
+		sort.Sort(byApexName(apexVariations))
 		variations := []string{}
 		variations = append(variations, "") // Original variation for platform
-		for _, apex := range m.apexVariations {
-			variations = append(variations, apex.ApexName)
+		for _, apex := range apexVariations {
+			variations = append(variations, apex.ApexVariationName)
 		}
 
 		defaultVariation := ""
@@ -296,12 +393,20 @@
 		for i, mod := range modules {
 			platformVariation := i == 0
 			if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
-				mod.SkipInstall()
+				// Do not install the module for platform, but still allow it to output
+				// uninstallable AndroidMk entries in certain cases when they have
+				// side effects.
+				mod.MakeUninstallable()
 			}
 			if !platformVariation {
-				mod.(ApexModule).apexModuleBase().ApexProperties.Info = m.apexVariations[i-1]
+				mod.(ApexModule).apexModuleBase().ApexProperties.Info = apexVariations[i-1]
 			}
 		}
+
+		for _, alias := range aliases {
+			mctx.CreateAliasVariation(alias[0], alias[1])
+		}
+
 		return modules
 	}
 	return nil
@@ -335,7 +440,10 @@
 		apexesForModule = make(map[string]bool)
 		apexNamesMap()[moduleName] = apexesForModule
 	}
-	apexesForModule[apex.ApexName] = apexesForModule[apex.ApexName] || directDep
+	apexesForModule[apex.ApexVariationName] = apexesForModule[apex.ApexVariationName] || directDep
+	for _, apexName := range apex.InApexes {
+		apexesForModule[apexName] = apexesForModule[apex.ApexVariationName] || directDep
+	}
 }
 
 // TODO(b/146393795): remove this when b/146393795 is fixed
@@ -351,12 +459,26 @@
 func DirectlyInApex(apexName string, moduleName string) bool {
 	apexNamesMapMutex.Lock()
 	defer apexNamesMapMutex.Unlock()
-	if apexNames, ok := apexNamesMap()[moduleName]; ok {
-		return apexNames[apexName]
+	if apexNamesForModule, ok := apexNamesMap()[moduleName]; ok {
+		return apexNamesForModule[apexName]
 	}
 	return false
 }
 
+// Tests whether a module named moduleName is directly depended on by all APEXes
+// in a list of apexNames.
+func DirectlyInAllApexes(apexNames []string, moduleName string) bool {
+	apexNamesMapMutex.Lock()
+	defer apexNamesMapMutex.Unlock()
+	for _, apexName := range apexNames {
+		apexNamesForModule := apexNamesMap()[moduleName]
+		if !apexNamesForModule[apexName] {
+			return false
+		}
+	}
+	return true
+}
+
 type hostContext interface {
 	Host() bool
 }
diff --git a/android/apex_test.go b/android/apex_test.go
new file mode 100644
index 0000000..db02833
--- /dev/null
+++ b/android/apex_test.go
@@ -0,0 +1,111 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"reflect"
+	"testing"
+)
+
+func Test_mergeApexVariations(t *testing.T) {
+	tests := []struct {
+		name        string
+		in          []ApexInfo
+		wantMerged  []ApexInfo
+		wantAliases [][2]string
+	}{
+		{
+			name: "single",
+			in: []ApexInfo{
+				{"foo", 10000, false, nil, []string{"foo"}},
+			},
+			wantMerged: []ApexInfo{
+				{"apex10000", 10000, false, nil, []string{"foo"}},
+			},
+			wantAliases: [][2]string{
+				{"foo", "apex10000"},
+			},
+		},
+		{
+			name: "merge",
+			in: []ApexInfo{
+				{"foo", 10000, false, SdkRefs{{"baz", "1"}}, []string{"foo"}},
+				{"bar", 10000, false, SdkRefs{{"baz", "1"}}, []string{"bar"}},
+			},
+			wantMerged: []ApexInfo{
+				{"apex10000_baz_1", 10000, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}},
+			},
+			wantAliases: [][2]string{
+				{"bar", "apex10000_baz_1"},
+				{"foo", "apex10000_baz_1"},
+			},
+		},
+		{
+			name: "don't merge version",
+			in: []ApexInfo{
+				{"foo", 10000, false, nil, []string{"foo"}},
+				{"bar", 30, false, nil, []string{"bar"}},
+			},
+			wantMerged: []ApexInfo{
+				{"apex30", 30, false, nil, []string{"bar"}},
+				{"apex10000", 10000, false, nil, []string{"foo"}},
+			},
+			wantAliases: [][2]string{
+				{"bar", "apex30"},
+				{"foo", "apex10000"},
+			},
+		},
+		{
+			name: "merge updatable",
+			in: []ApexInfo{
+				{"foo", 10000, false, nil, []string{"foo"}},
+				{"bar", 10000, true, nil, []string{"bar"}},
+			},
+			wantMerged: []ApexInfo{
+				{"apex10000", 10000, true, nil, []string{"bar", "foo"}},
+			},
+			wantAliases: [][2]string{
+				{"bar", "apex10000"},
+				{"foo", "apex10000"},
+			},
+		},
+		{
+			name: "don't merge sdks",
+			in: []ApexInfo{
+				{"foo", 10000, false, SdkRefs{{"baz", "1"}}, []string{"foo"}},
+				{"bar", 10000, false, SdkRefs{{"baz", "2"}}, []string{"bar"}},
+			},
+			wantMerged: []ApexInfo{
+				{"apex10000_baz_2", 10000, false, SdkRefs{{"baz", "2"}}, []string{"bar"}},
+				{"apex10000_baz_1", 10000, false, SdkRefs{{"baz", "1"}}, []string{"foo"}},
+			},
+			wantAliases: [][2]string{
+				{"bar", "apex10000_baz_2"},
+				{"foo", "apex10000_baz_1"},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotMerged, gotAliases := mergeApexVariations(tt.in)
+			if !reflect.DeepEqual(gotMerged, tt.wantMerged) {
+				t.Errorf("mergeApexVariations() gotMerged = %v, want %v", gotMerged, tt.wantMerged)
+			}
+			if !reflect.DeepEqual(gotAliases, tt.wantAliases) {
+				t.Errorf("mergeApexVariations() gotAliases = %v, want %v", gotAliases, tt.wantAliases)
+			}
+		})
+	}
+}
diff --git a/android/config.go b/android/config.go
index 92a21a7..a1e97c9 100644
--- a/android/config.go
+++ b/android/config.go
@@ -722,7 +722,7 @@
 	return Bool(c.productVariables.Allow_missing_dependencies)
 }
 
-// Returns true if building without full platform sources.
+// Returns true if a full platform source tree cannot be assumed.
 func (c *config) UnbundledBuild() bool {
 	return Bool(c.productVariables.Unbundled_build)
 }
@@ -733,18 +733,15 @@
 	return Bool(c.productVariables.Unbundled_build_apps)
 }
 
-func (c *config) UnbundledBuildUsePrebuiltSdks() bool {
-	return Bool(c.productVariables.Unbundled_build) && !Bool(c.productVariables.Unbundled_build_sdks_from_source)
+// Returns true if building modules against prebuilt SDKs.
+func (c *config) AlwaysUsePrebuiltSdks() bool {
+	return Bool(c.productVariables.Always_use_prebuilt_sdks)
 }
 
 func (c *config) Fuchsia() bool {
 	return Bool(c.productVariables.Fuchsia)
 }
 
-func (c *config) IsPdkBuild() bool {
-	return Bool(c.productVariables.Pdk)
-}
-
 func (c *config) MinimizeJavaDebugInfo() bool {
 	return Bool(c.productVariables.MinimizeJavaDebugInfo) && !Bool(c.productVariables.Eng)
 }
@@ -1248,6 +1245,10 @@
 	return c.config.productVariables.BoardKernelBinaries
 }
 
+func (c *deviceConfig) BoardKernelModuleInterfaceVersions() []string {
+	return c.config.productVariables.BoardKernelModuleInterfaceVersions
+}
+
 // The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
 // Such lists are used in the build system for things like bootclasspath jars or system server jars.
 // The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
diff --git a/android/defs.go b/android/defs.go
index 4552224..83daa03 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -69,7 +69,7 @@
 	// A symlink rule.
 	Symlink = pctx.AndroidStaticRule("Symlink",
 		blueprint.RuleParams{
-			Command:     "ln -f -s $fromPath $out",
+			Command:     "rm -f $out && ln -f -s $fromPath $out",
 			Description: "symlink $out",
 		},
 		"fromPath")
diff --git a/android/module.go b/android/module.go
index 8605954..b689a87 100644
--- a/android/module.go
+++ b/android/module.go
@@ -97,6 +97,8 @@
 	GlobFiles(globPattern string, excludes []string) Paths
 	IsSymlink(path Path) bool
 	Readlink(path Path) string
+
+	Namespace() *Namespace
 }
 
 // BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -219,7 +221,6 @@
 	VisitAllModuleVariants(visit func(Module))
 
 	GetMissingDependencies() []string
-	Namespace() blueprint.Namespace
 }
 
 type Module interface {
@@ -256,6 +257,7 @@
 	InstallForceOS() *OsType
 	SkipInstall()
 	IsSkipInstall() bool
+	MakeUninstallable()
 	ExportedToMake() bool
 	InitRc() Paths
 	VintfFragments() Paths
@@ -568,6 +570,12 @@
 type TaggedDistFiles map[string]Paths
 
 func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
+	for _, path := range paths {
+		if path == nil {
+			panic("The path to a dist file cannot be nil.")
+		}
+	}
+
 	// The default OutputFile tag is the empty "" string.
 	return TaggedDistFiles{"": paths}
 }
@@ -1046,6 +1054,15 @@
 	return m.commonProperties.SkipInstall == true
 }
 
+// Similar to SkipInstall, but if the AndroidMk entry would set
+// LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
+// rather than leaving it out altogether. That happens in cases where it would
+// have other side effects, in particular when it adds a NOTICE file target,
+// which other install targets might depend on.
+func (m *ModuleBase) MakeUninstallable() {
+	m.SkipInstall()
+}
+
 func (m *ModuleBase) ExportedToMake() bool {
 	return m.commonProperties.NamespaceExportedToMake
 }
@@ -1171,7 +1188,7 @@
 
 	var deps Paths
 
-	namespacePrefix := ctx.Namespace().(*Namespace).id
+	namespacePrefix := ctx.Namespace().id
 	if namespacePrefix != "" {
 		namespacePrefix = namespacePrefix + "-"
 	}
@@ -1315,7 +1332,7 @@
 		suffix = append(suffix, ctx.Arch().ArchType.String())
 	}
 	if apex, ok := m.module.(ApexModule); ok && !apex.IsForPlatform() {
-		suffix = append(suffix, apex.ApexName())
+		suffix = append(suffix, apex.ApexVariationName())
 	}
 
 	ctx.Variable(pctx, "moduleDesc", desc)
@@ -1482,6 +1499,10 @@
 	return e.kind == systemExtSpecificModule
 }
 
+func (e *earlyModuleContext) Namespace() *Namespace {
+	return e.EarlyModuleContext.Namespace().(*Namespace)
+}
+
 type baseModuleContext struct {
 	bp blueprint.BaseModuleContext
 	earlyModuleContext
diff --git a/android/mutator.go b/android/mutator.go
index b70c4ff..40e61de 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -216,6 +216,7 @@
 	ReplaceDependencies(string)
 	ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate)
 	AliasVariation(variationName string)
+	CreateAliasVariation(fromVariationName, toVariationName string)
 }
 
 type bottomUpMutatorContext struct {
@@ -436,3 +437,7 @@
 func (b *bottomUpMutatorContext) AliasVariation(variationName string) {
 	b.bp.AliasVariation(variationName)
 }
+
+func (b *bottomUpMutatorContext) CreateAliasVariation(fromVariationName, toVariationName string) {
+	b.bp.CreateAliasVariation(fromVariationName, toVariationName)
+}
diff --git a/android/neverallow.go b/android/neverallow.go
index 73829f1..aaea920 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -194,7 +194,7 @@
 		// This sometimes works because the APEX modules that contain derive_sdk and
 		// derive_sdk_prefer32 suppress the platform installation rules, but fails when
 		// the APEX modules contain the SDK variant and the platform variant still exists.
-		"frameworks/base/apex/sdkextensions/derive_sdk",
+		"packages/modules/SdkExtensions/derive_sdk",
 		// These are for apps and shouldn't be used by non-SDK variant modules.
 		"prebuilts/ndk",
 		"tools/test/graphicsbenchmark/apps/sample_app",
diff --git a/android/override_module.go b/android/override_module.go
index 3994084..f8342d5 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -313,3 +313,15 @@
 		}
 	}
 }
+
+// ModuleNameWithPossibleOverride returns the name of the OverrideModule that overrides the current
+// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
+// or if this variant is not overridden.
+func ModuleNameWithPossibleOverride(ctx ModuleContext) string {
+	if overridable, ok := ctx.Module().(OverridableModule); ok {
+		if o := overridable.GetOverriddenBy(); o != "" {
+			return o
+		}
+	}
+	return ctx.ModuleName()
+}
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
new file mode 100644
index 0000000..1dcf199
--- /dev/null
+++ b/android/prebuilt_build_tool.go
@@ -0,0 +1,94 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+func init() {
+	RegisterModuleType("prebuilt_build_tool", prebuiltBuildToolFactory)
+}
+
+type prebuiltBuildToolProperties struct {
+	// Source file to be executed for this build tool
+	Src *string `android:"path,arch_variant"`
+
+	// Extra files that should trigger rules using this tool to rebuild
+	Deps []string `android:"path,arch_variant"`
+
+	// Create a make variable with the specified name that contains the path to
+	// this prebuilt built tool, relative to the root of the source tree.
+	Export_to_make_var *string
+}
+
+type prebuiltBuildTool struct {
+	ModuleBase
+	prebuilt Prebuilt
+
+	properties prebuiltBuildToolProperties
+
+	toolPath OptionalPath
+}
+
+func (t *prebuiltBuildTool) Name() string {
+	return t.prebuilt.Name(t.ModuleBase.Name())
+}
+
+func (t *prebuiltBuildTool) Prebuilt() *Prebuilt {
+	return &t.prebuilt
+}
+
+func (t *prebuiltBuildTool) DepsMutator(ctx BottomUpMutatorContext) {
+	if t.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+	}
+}
+
+func (t *prebuiltBuildTool) GenerateAndroidBuildActions(ctx ModuleContext) {
+	sourcePath := t.prebuilt.SingleSourcePath(ctx)
+	installedPath := PathForModuleOut(ctx, t.ModuleBase.Name())
+	deps := PathsForModuleSrc(ctx, t.properties.Deps)
+
+	ctx.Build(pctx, BuildParams{
+		Rule:      Symlink,
+		Output:    installedPath,
+		Input:     sourcePath,
+		Implicits: deps,
+		Args: map[string]string{
+			"fromPath": "$$PWD/" + sourcePath.String(),
+		},
+	})
+
+	t.toolPath = OptionalPathForPath(installedPath)
+}
+
+func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
+	if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
+		ctx.StrictRaw(makeVar, t.toolPath.String())
+	}
+}
+
+func (t *prebuiltBuildTool) HostToolPath() OptionalPath {
+	return t.toolPath
+}
+
+var _ HostToolProvider = &prebuiltBuildTool{}
+
+// prebuilt_build_tool is to declare prebuilts to be used during the build, particularly for use
+// in genrules with the "tools" property.
+func prebuiltBuildToolFactory() Module {
+	module := &prebuiltBuildTool{}
+	module.AddProperties(&module.properties)
+	InitSingleSourcePrebuiltModule(module, &module.properties, "Src")
+	InitAndroidArchModule(module, HostSupportedNoCross, MultilibFirst)
+	return module
+}
diff --git a/android/test_suites.go b/android/test_suites.go
index 7b2d7dc..79d0fbc 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -68,7 +68,7 @@
 		FlagWithOutput("-o ", outputFile).
 		FlagWithArg("-P ", "host/testcases").
 		FlagWithArg("-C ", testCasesDir.String()).
-		FlagWithRspFileInputList("-l ", installedPaths.Paths())
+		FlagWithRspFileInputList("-r ", installedPaths.Paths())
 	rule.Build(pctx, ctx, "robolectric_tests_zip", "robolectric-tests.zip")
 
 	return outputFile
diff --git a/android/testing.go b/android/testing.go
index 696efb6..8a9134c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -119,14 +119,24 @@
 
 	if module == nil {
 		// find all the modules that do exist
-		allModuleNames := []string{}
+		var allModuleNames []string
+		var allVariants []string
 		ctx.VisitAllModules(func(m blueprint.Module) {
-			allModuleNames = append(allModuleNames, m.(Module).Name()+"("+ctx.ModuleSubDir(m)+")")
+			allModuleNames = append(allModuleNames, ctx.ModuleName(m))
+			if ctx.ModuleName(m) == name {
+				allVariants = append(allVariants, ctx.ModuleSubDir(m))
+			}
 		})
 		sort.Strings(allModuleNames)
+		sort.Strings(allVariants)
 
-		panic(fmt.Errorf("failed to find module %q variant %q. All modules:\n  %s",
-			name, variant, strings.Join(allModuleNames, "\n  ")))
+		if len(allVariants) == 0 {
+			panic(fmt.Errorf("failed to find module %q. All modules:\n  %s",
+				name, strings.Join(allModuleNames, "\n  ")))
+		} else {
+			panic(fmt.Errorf("failed to find module %q variant %q. All variants:\n  %s",
+				name, variant, strings.Join(allVariants, "\n  ")))
+		}
 	}
 
 	return TestingModule{module}
@@ -384,7 +394,7 @@
 	if !found {
 		t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
 		for i, err := range errs {
-			t.Errorf("errs[%d] = %s", i, err)
+			t.Errorf("errs[%d] = %q", i, err)
 		}
 	}
 }
diff --git a/android/util.go b/android/util.go
index 8dbf214..65c5f1b 100644
--- a/android/util.go
+++ b/android/util.go
@@ -79,6 +79,20 @@
 	return string(ret)
 }
 
+func SortedIntKeys(m interface{}) []int {
+	v := reflect.ValueOf(m)
+	if v.Kind() != reflect.Map {
+		panic(fmt.Sprintf("%#v is not a map", m))
+	}
+	keys := v.MapKeys()
+	s := make([]int, 0, len(keys))
+	for _, key := range keys {
+		s = append(s, int(key.Int()))
+	}
+	sort.Ints(s)
+	return s
+}
+
 func SortedStringKeys(m interface{}) []string {
 	v := reflect.ValueOf(m)
 	if v.Kind() != reflect.Map {
diff --git a/android/variable.go b/android/variable.go
index c1e1b42..1f21f34 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -214,30 +214,29 @@
 
 	AppsDefaultVersionName *string `json:",omitempty"`
 
-	Allow_missing_dependencies       *bool `json:",omitempty"`
-	Unbundled_build                  *bool `json:",omitempty"`
-	Unbundled_build_apps             *bool `json:",omitempty"`
-	Unbundled_build_sdks_from_source *bool `json:",omitempty"`
-	Malloc_not_svelte                *bool `json:",omitempty"`
-	Malloc_zero_contents             *bool `json:",omitempty"`
-	Malloc_pattern_fill_contents     *bool `json:",omitempty"`
-	Safestack                        *bool `json:",omitempty"`
-	HostStaticBinaries               *bool `json:",omitempty"`
-	Binder32bit                      *bool `json:",omitempty"`
-	UseGoma                          *bool `json:",omitempty"`
-	UseRBE                           *bool `json:",omitempty"`
-	UseRBEJAVAC                      *bool `json:",omitempty"`
-	UseRBER8                         *bool `json:",omitempty"`
-	UseRBED8                         *bool `json:",omitempty"`
-	Debuggable                       *bool `json:",omitempty"`
-	Eng                              *bool `json:",omitempty"`
-	Treble_linker_namespaces         *bool `json:",omitempty"`
-	Enforce_vintf_manifest           *bool `json:",omitempty"`
-	Pdk                              *bool `json:",omitempty"`
-	Uml                              *bool `json:",omitempty"`
-	Use_lmkd_stats_log               *bool `json:",omitempty"`
-	Arc                              *bool `json:",omitempty"`
-	MinimizeJavaDebugInfo            *bool `json:",omitempty"`
+	Allow_missing_dependencies   *bool `json:",omitempty"`
+	Unbundled_build              *bool `json:",omitempty"`
+	Unbundled_build_apps         *bool `json:",omitempty"`
+	Always_use_prebuilt_sdks     *bool `json:",omitempty"`
+	Malloc_not_svelte            *bool `json:",omitempty"`
+	Malloc_zero_contents         *bool `json:",omitempty"`
+	Malloc_pattern_fill_contents *bool `json:",omitempty"`
+	Safestack                    *bool `json:",omitempty"`
+	HostStaticBinaries           *bool `json:",omitempty"`
+	Binder32bit                  *bool `json:",omitempty"`
+	UseGoma                      *bool `json:",omitempty"`
+	UseRBE                       *bool `json:",omitempty"`
+	UseRBEJAVAC                  *bool `json:",omitempty"`
+	UseRBER8                     *bool `json:",omitempty"`
+	UseRBED8                     *bool `json:",omitempty"`
+	Debuggable                   *bool `json:",omitempty"`
+	Eng                          *bool `json:",omitempty"`
+	Treble_linker_namespaces     *bool `json:",omitempty"`
+	Enforce_vintf_manifest       *bool `json:",omitempty"`
+	Uml                          *bool `json:",omitempty"`
+	Use_lmkd_stats_log           *bool `json:",omitempty"`
+	Arc                          *bool `json:",omitempty"`
+	MinimizeJavaDebugInfo        *bool `json:",omitempty"`
 
 	Check_elf_files *bool `json:",omitempty"`
 
@@ -345,7 +344,8 @@
 
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
 
-	BoardKernelBinaries []string `json:",omitempty"`
+	BoardKernelBinaries                []string `json:",omitempty"`
+	BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex.go b/apex/apex.go
index 9905b79..84a1e75 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -549,7 +549,6 @@
 		"android.hardware.tetheroffload.config-V1.0-java",
 		"android.hardware.tetheroffload.control-V1.0-java",
 		"android.hidl.base-V1.0-java",
-		"ipmemorystore-aidl-interfaces-java",
 		"libcgrouprc",
 		"libcgrouprc_format",
 		"libtetherutilsjni",
@@ -670,6 +669,7 @@
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
 	ctx.TopDown("apex_deps", apexDepsMutator).Parallel()
+	ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
 	ctx.BottomUp("apex", apexMutator).Parallel()
 	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
 	ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
@@ -687,9 +687,11 @@
 		return
 	}
 	apexInfo := android.ApexInfo{
-		ApexName:      mctx.ModuleName(),
-		MinSdkVersion: a.minSdkVersion(mctx),
-		Updatable:     a.Updatable(),
+		ApexVariationName: mctx.ModuleName(),
+		MinSdkVersion:     a.minSdkVersion(mctx),
+		RequiredSdks:      a.RequiredSdks(),
+		Updatable:         a.Updatable(),
+		InApexes:          []string{mctx.ModuleName()},
 	}
 
 	useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
@@ -722,6 +724,17 @@
 	})
 }
 
+func apexUniqueVariationsMutator(mctx android.BottomUpMutatorContext) {
+	if !mctx.Module().Enabled() {
+		return
+	}
+	if am, ok := mctx.Module().(android.ApexModule); ok {
+		// Check if any dependencies use unique apex variations.  If so, use unique apex variations
+		// for this module.
+		am.UpdateUniqueApexVariationsForDeps(mctx)
+	}
+}
+
 // mark if a module cannot be available to platform. A module cannot be available
 // to platform if 1) it is explicitly marked as not available (i.e. "//apex_available:platform"
 // is absent) or 2) it depends on another module that isn't (or can't be) available to platform
@@ -1798,7 +1811,7 @@
 		}
 
 		// Check for the indirect dependencies if it is considered as part of the APEX
-		if am.ApexName() != "" {
+		if android.InList(ctx.ModuleName(), am.InApexes()) {
 			return do(ctx, parent, am, false /* externalDep */)
 		}
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index d13ec5f..70464fc 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -528,13 +528,13 @@
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("myjar_dex"), "android_common_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myjar_dex"), "android_common_apex10000")
 
 	// Ensure that apex variant is created for the indirect dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_apex10000")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
@@ -723,10 +723,10 @@
 	ensureContains(t, zipApexRule.Output.String(), "myapex.zipapex.unsigned")
 
 	// Ensure that APEX variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000")
 
 	// Ensure that APEX variant is created for the indirect dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.zipapex/lib64/mylib.so")
@@ -800,7 +800,7 @@
 	// Ensure that direct stubs dep is included
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with the latest version of stubs for mylib2
 	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3/mylib2.so")
@@ -808,9 +808,9 @@
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
 	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
-	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_myapex/mylib3.so")
+	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex10000/mylib3.so")
 	// .. and not linking to the stubs variant of mylib3
-	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12_myapex/mylib3.so")
+	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12/mylib3.so")
 
 	// Ensure that stubs libs are built without -include flags
 	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
@@ -890,7 +890,7 @@
 	// Ensure that dependency of stubs is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex2").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with version 10 of libfoo
 	ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_10/libfoo.so")
@@ -1110,18 +1110,21 @@
 	testcases := []struct {
 		name          string
 		minSdkVersion string
+		apexVariant   string
 		shouldLink    string
 		shouldNotLink []string
 	}{
 		{
 			name:          "should link to the latest",
 			minSdkVersion: "",
+			apexVariant:   "apex10000",
 			shouldLink:    "30",
 			shouldNotLink: []string{"29"},
 		},
 		{
 			name:          "should link to llndk#29",
 			minSdkVersion: "min_sdk_version: \"29\",",
+			apexVariant:   "apex29",
 			shouldLink:    "29",
 			shouldNotLink: []string{"30"},
 		},
@@ -1180,13 +1183,13 @@
 			ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
 			ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
 
-			mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
+			mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
 			ensureContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
 			for _, ver := range tc.shouldNotLink {
 				ensureNotContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
 			}
 
-			mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
+			mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
 			ensureContains(t, mylibCFlags, "__LIBBAR_API__="+tc.shouldLink)
 		})
 	}
@@ -1241,9 +1244,9 @@
 	// Ensure that libc is not included (since it has stubs and not listed in native_shared_libs)
 	ensureNotContains(t, copyCmds, "image.apex/lib64/bionic/libc.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
-	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
-	mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_myapex").Rule("cc").Args["cFlags"]
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
+	mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_apex10000").Rule("cc").Args["cFlags"]
 
 	// For dependency to libc
 	// Ensure that mylib is linking with the latest version of stubs
@@ -1256,7 +1259,7 @@
 
 	// For dependency to libm
 	// Ensure that mylib is linking with the non-stub (impl) variant
-	ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_myapex/libm.so")
+	ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_apex10000/libm.so")
 	// ... and not linking to the stub variant
 	ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_29/libm.so")
 	// ... and is not compiling with the stub
@@ -1270,7 +1273,7 @@
 	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_28/libdl.so")
 	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_29/libdl.so")
 	// ... and not linking to the non-stub (impl) variant
-	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_myapex/libdl.so")
+	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_apex10000/libdl.so")
 	// ... Cflags from stub is correctly exported to mylib
 	ensureContains(t, mylibCFlags, "__LIBDL_API__=27")
 	ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27")
@@ -1359,13 +1362,13 @@
 	// platform liba is linked to non-stub version
 	expectLink("liba", "shared", "libz", "shared")
 	// liba in myapex is linked to #28
-	expectLink("liba", "shared_myapex", "libz", "shared_28")
-	expectNoLink("liba", "shared_myapex", "libz", "shared_30")
-	expectNoLink("liba", "shared_myapex", "libz", "shared")
+	expectLink("liba", "shared_apex29", "libz", "shared_28")
+	expectNoLink("liba", "shared_apex29", "libz", "shared_30")
+	expectNoLink("liba", "shared_apex29", "libz", "shared")
 	// liba in otherapex is linked to #30
-	expectLink("liba", "shared_otherapex", "libz", "shared_30")
-	expectNoLink("liba", "shared_otherapex", "libz", "shared_28")
-	expectNoLink("liba", "shared_otherapex", "libz", "shared")
+	expectLink("liba", "shared_apex30", "libz", "shared_30")
+	expectNoLink("liba", "shared_apex30", "libz", "shared_28")
+	expectNoLink("liba", "shared_apex30", "libz", "shared")
 }
 
 func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) {
@@ -1418,9 +1421,9 @@
 	// to distinguish them from finalized and future_api(10000)
 	// In this test, "R" is assumed not finalized yet( listed in Platform_version_active_codenames) and translated into 9000
 	// (refer android/api_levels.go)
-	expectLink("libx", "shared_myapex", "libz", "shared_9000")
-	expectNoLink("libx", "shared_myapex", "libz", "shared_29")
-	expectNoLink("libx", "shared_myapex", "libz", "shared")
+	expectLink("libx", "shared_apex10000", "libz", "shared_9000")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared_29")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
 func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) {
@@ -1463,9 +1466,9 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_myapex", "libz", "shared_2")
-	expectNoLink("libx", "shared_myapex", "libz", "shared_1")
-	expectNoLink("libx", "shared_myapex", "libz", "shared")
+	expectLink("libx", "shared_apex10000", "libz", "shared_2")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared_1")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
 func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
@@ -1549,7 +1552,7 @@
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_hwasan_myapex", "libbar", "shared_30")
+	expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_30")
 }
 
 func TestQTargetApexUsesStaticUnwinder(t *testing.T) {
@@ -1575,7 +1578,7 @@
 	`)
 
 	// ensure apex variant of c++ is linked with static unwinder
-	cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module)
+	cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_apex29").Module().(*cc.Module)
 	ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
 	// note that platform variant is not.
 	cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module)
@@ -1937,8 +1940,8 @@
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("mylib", "shared_myapex", "mylib2", "shared_29")
-	expectLink("mylib", "shared_otherapex", "mylib2", "shared_otherapex")
+	expectLink("mylib", "shared_apex29", "mylib2", "shared_29")
+	expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30")
 }
 
 func TestFilesInSubDir(t *testing.T) {
@@ -2107,12 +2110,12 @@
 	inputsString := strings.Join(inputsList, " ")
 
 	// ensure that the apex includes vendor variants of the direct and indirect deps
-	ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_myapex/mylib.so")
-	ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_myapex/mylib2.so")
+	ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_apex10000/mylib.so")
+	ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_apex10000/mylib2.so")
 
 	// ensure that the apex does not include core variants
-	ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_myapex/mylib.so")
-	ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_myapex/mylib2.so")
+	ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_apex10000/mylib.so")
+	ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_apex10000/mylib2.so")
 }
 
 func TestUseVendorNotAllowedForSystemApexes(t *testing.T) {
@@ -2250,13 +2253,13 @@
 
 	vendorVariant := "android_vendor.VER_arm64_armv8-a"
 
-	ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_myapex").Rule("ld")
+	ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
 	libs := names(ldRule.Args["libFlags"])
 	// VNDK libs(libvndk/libc++) as they are
 	ensureListContains(t, libs, buildDir+"/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
 	ensureListContains(t, libs, buildDir+"/.intermediates/libc++/"+vendorVariant+"_shared/libc++.so")
 	// non-stable Vendor libs as APEX variants
-	ensureListContains(t, libs, buildDir+"/.intermediates/libvendor/"+vendorVariant+"_shared_myapex/libvendor.so")
+	ensureListContains(t, libs, buildDir+"/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
 
 	// VNDK libs are not included when use_vndk_as_stable: true
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
@@ -2633,7 +2636,21 @@
 				"myapex",
 				"otherapex",
 			],
+			static_libs: ["mylib3"],
+			recovery_available: true,
+			min_sdk_version: "29",
+		}
+		cc_library {
+			name: "mylib3",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"otherapex",
+			],
 			use_apex_name_macro: true,
+			recovery_available: true,
 			min_sdk_version: "29",
 		}
 	`)
@@ -2644,19 +2661,43 @@
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__")
 
 	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__=10000")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
 
 	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__=29")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
-	// When cc_library sets use_apex_name_macro: true
-	// apex variants define additional macro to distinguish which apex variant it is built for
+	// When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and
+	// each variant defines additional macros to distinguish which apex variant it is built for
+
+	// non-APEX variant does not have __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+
+	// APEX variant has __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
+
+	// APEX variant has __ANDROID_APEX__ defined
+	mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
+
+	// recovery variant does not set __ANDROID_SDK_VERSION__
+	mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__")
+
+	// When a dependency of a cc_library sets use_apex_name_macro: true each apex gets a unique
+	// variant.
 
 	// non-APEX variant does not have __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
@@ -2665,17 +2706,17 @@
 	// APEX variant has __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
 	// APEX variant has __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
 	// recovery variant does not set __ANDROID_SDK_VERSION__
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__")
 }
@@ -3455,7 +3496,7 @@
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_apex10000")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib_common.so")
@@ -3511,7 +3552,7 @@
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared_apex10000")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib_common_test.so")
@@ -3595,9 +3636,9 @@
 	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
 
 	// Ensure that apex variant is created for the direct dep
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_myapex")
-	ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_apex10000")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
@@ -4062,8 +4103,8 @@
 	apexRule2 := module2.Rule("apexRule")
 	copyCmds2 := apexRule2.Args["copy_commands"]
 
-	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
-	ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_shared_commonapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_shared_apex10000")
 	ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so")
 	ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so")
 	ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so")
@@ -4243,14 +4284,14 @@
 	ensureContains(t, copyCmds, "image.apex/app/AppFoo/AppFoo.apk")
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv/AppFooPriv.apk")
 
-	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").Description("zip jni libs")
+	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs")
 	// JNI libraries are uncompressed
 	if args := appZipRule.Args["jarArgs"]; !strings.Contains(args, "-L 0") {
 		t.Errorf("jni libs are not uncompressed for AppFoo")
 	}
 	// JNI libraries including transitive deps are
 	for _, jni := range []string{"libjni", "libfoo"} {
-		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_myapex").Module().(*cc.Module).OutputFile()
+		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile()
 		// ... embedded inside APK (jnilibs.zip)
 		ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String())
 		// ... and not directly inside the APEX
@@ -4764,7 +4805,7 @@
 	// the dependency names directly here but for some reason the names are blank in
 	// this test.
 	for _, lib := range []string{"libc++", "mylib"} {
-		apexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared_myapex").Rule("ld").Implicits
+		apexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared_apex29").Rule("ld").Implicits
 		nonApexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld").Implicits
 		if len(apexImplicits) != len(nonApexImplicits)+1 {
 			t.Errorf("%q missing unwinder dep", lib)
@@ -5616,7 +5657,7 @@
 	})
 
 	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
-		err = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
+		err = `module "some-art-lib" from updatable apexes \["com.android.art.something"\] is not allowed in the framework boot image`
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"com.android.art.something:some-art-lib"})
 		}
@@ -5624,7 +5665,7 @@
 	})
 
 	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
+		err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"some-updatable-apex:some-updatable-apex-lib"})
 		}
@@ -5632,7 +5673,7 @@
 	})
 
 	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
+		err = `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
 		}
@@ -5640,7 +5681,7 @@
 	})
 
 	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
-		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
+		err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"some-updatable-apex:some-updatable-apex-lib"})
 		}
@@ -5671,7 +5712,7 @@
 	})
 
 	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
-		err = "module 'some-platform-lib' is not allowed in the ART boot image"
+		err = `module "some-platform-lib" is not allowed in the ART boot image`
 		transform = func(config *dexpreopt.GlobalConfig) {
 			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"platform:some-platform-lib"})
 		}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 689cbd1..faec473 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -128,6 +128,10 @@
 		Name: "removeSoongConfigBoolVariable",
 		Fix:  removeSoongConfigBoolVariable,
 	},
+	{
+		Name: "removePdkProperty",
+		Fix:  runPatchListMod(removePdkProperty),
+	},
 }
 
 func NewFixRequest() FixRequest {
@@ -993,6 +997,25 @@
 	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
 }
 
+func removePdkProperty(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+	prop, ok := mod.GetProperty("product_variables")
+	if !ok {
+		return nil
+	}
+	propMap, ok := prop.Value.(*parser.Map)
+	if !ok {
+		return nil
+	}
+	pdkProp, ok := propMap.GetProperty("pdk")
+	if !ok {
+		return nil
+	}
+	if len(propMap.Properties) > 1 {
+		return patchlist.Add(pdkProp.Pos().Offset, pdkProp.End().Offset+2, "")
+	}
+	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
+}
+
 func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
 	return mergeMatchingProperties(&mod.Properties, buf, patchlist)
 }
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 8988177..ef9814f 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -998,3 +998,61 @@
 		})
 	}
 }
+
+func TestRemovePdkProperty(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "remove property",
+			in: `
+				cc_library_shared {
+					name: "foo",
+					product_variables: {
+						other: {
+							bar: true,
+						},
+						pdk: {
+							enabled: false,
+						},
+					},
+				}
+			`,
+			out: `
+				cc_library_shared {
+					name: "foo",
+					product_variables: {
+						other: {
+							bar: true,
+						},
+					},
+				}
+			`,
+		},
+		{
+			name: "remove property and empty product_variables",
+			in: `
+				cc_library_shared {
+					name: "foo",
+					product_variables: {
+						pdk: {
+							enabled: false,
+						},
+					},
+				}
+			`,
+			out: `
+				cc_library_shared {
+					name: "foo",
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, runPatchListMod(removePdkProperty))
+		})
+	}
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index d106c0b..c899cdd 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -167,6 +167,14 @@
 	return testFiles
 }
 
+func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *android.AndroidMkEntries) {
+	if len(extraTestConfigs) > 0 {
+		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+			entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
+		})
+	}
+}
+
 func androidMkWriteTestData(data []android.DataPath, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	testFiles := AndroidMkDataPaths(data)
 	if len(testFiles) > 0 {
@@ -206,29 +214,15 @@
 }
 
 func (library *libraryDecorator) androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries *android.AndroidMkEntries) {
-	if library.sAbiOutputFile.Valid() {
-		entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES",
-			"$(LOCAL_ADDITIONAL_DEPENDENCIES) "+library.sAbiOutputFile.String())
-		if library.sAbiDiff.Valid() && !library.static() {
-			entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES",
-				"$(LOCAL_ADDITIONAL_DEPENDENCIES) "+library.sAbiDiff.String())
-			entries.SetString("HEADER_ABI_DIFFS",
-				"$(HEADER_ABI_DIFFS) "+library.sAbiDiff.String())
-		}
+	if library.sAbiDiff.Valid() && !library.static() {
+		entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", library.sAbiDiff.String())
 	}
 }
 
 // TODO(ccross): remove this once apex/androidmk.go is converted to AndroidMkEntries
 func (library *libraryDecorator) androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer) {
-	if library.sAbiOutputFile.Valid() {
-		fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_ADDITIONAL_DEPENDENCIES) ",
-			library.sAbiOutputFile.String())
-		if library.sAbiDiff.Valid() && !library.static() {
-			fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_ADDITIONAL_DEPENDENCIES) ",
-				library.sAbiDiff.String())
-			fmt.Fprintln(w, "HEADER_ABI_DIFFS := $(HEADER_ABI_DIFFS) ",
-				library.sAbiDiff.String())
-		}
+	if library.sAbiDiff.Valid() && !library.static() {
+		fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", library.sAbiDiff.String())
 	}
 }
 
@@ -286,10 +280,8 @@
 			entries.SubName = "." + library.stubsVersion()
 		}
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-			// Note library.skipInstall() has a special case to get here for static
-			// libraries that otherwise would have skipped installation and hence not
-			// have executed AndroidMkEntries at all. The reason is to ensure they get
-			// a NOTICE file make target which other libraries might depend on.
+			// library.makeUninstallable() depends on this to bypass SkipInstall() for
+			// static libraries.
 			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 			if library.buildStubs() {
 				entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
@@ -388,6 +380,7 @@
 	})
 
 	androidMkWriteTestData(test.data, ctx, entries)
+	androidMkWriteExtraTestConfigs(test.extraTestConfigs, entries)
 }
 
 func (fuzz *fuzzBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
diff --git a/cc/binary.go b/cc/binary.go
index 565cb8a..6769fa7 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -130,34 +130,12 @@
 	deps = binary.baseLinker.linkerDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
 		if !Bool(binary.baseLinker.Properties.Nocrt) {
-			if !ctx.useSdk() {
-				if binary.static() {
-					deps.CrtBegin = "crtbegin_static"
-				} else {
-					deps.CrtBegin = "crtbegin_dynamic"
-				}
-				deps.CrtEnd = "crtend_android"
+			if binary.static() {
+				deps.CrtBegin = "crtbegin_static"
 			} else {
-				// TODO(danalbert): Add generation of crt objects.
-				// For `sdk_version: "current"`, we don't actually have a
-				// freshly generated set of CRT objects. Use the last stable
-				// version.
-				version := ctx.sdkVersion()
-				if version == "current" {
-					version = getCurrentNdkPrebuiltVersion(ctx)
-				}
-
-				if binary.static() {
-					deps.CrtBegin = "ndk_crtbegin_static." + version
-				} else {
-					if binary.static() {
-						deps.CrtBegin = "ndk_crtbegin_static." + version
-					} else {
-						deps.CrtBegin = "ndk_crtbegin_dynamic." + version
-					}
-					deps.CrtEnd = "ndk_crtend_android." + version
-				}
+				deps.CrtBegin = "crtbegin_dynamic"
 			}
+			deps.CrtEnd = "crtend_android"
 		}
 
 		if binary.static() {
@@ -467,7 +445,7 @@
 	// The original path becomes a symlink to the corresponding file in the
 	// runtime APEX.
 	translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
-	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRamdisk() && !ctx.inRecovery() {
+	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexVariationName() == "" && !ctx.inRamdisk() && !ctx.inRecovery() {
 		if ctx.Device() && isBionic(ctx.baseModuleName()) {
 			binary.installSymlinkToRuntimeApex(ctx, file)
 		}
diff --git a/cc/builder.go b/cc/builder.go
index b4f9947..f2bab8c 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -174,12 +174,21 @@
 		},
 		"crossCompile", "format")
 
-	clangTidy = pctx.AndroidStaticRule("clangTidy",
+	clangTidy, clangTidyRE = remoteexec.StaticRules(pctx, "clangTidy",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && ${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
+			Command:     "rm -f $out && $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
 			CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
 		},
-		"cFlags", "tidyFlags")
+		&remoteexec.REParams{
+			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
+			ExecStrategy: "${config.REClangTidyExecStrategy}",
+			Inputs:       []string{"$in"},
+			// OutputFile here is $in for remote-execution since its possible that
+			// clang-tidy modifies the given input file itself and $out refers to the
+			// ".tidy" file generated for ninja-dependency reasons.
+			OutputFiles: []string{"$in"},
+			Platform:    map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+		}, []string{"cFlags", "tidyFlags"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
@@ -256,9 +265,9 @@
 
 	zip = pctx.AndroidStaticRule("zip",
 		blueprint.RuleParams{
-			Command:        "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
+			Command:        "${SoongZipCmd} -o ${out} -C $$OUT_DIR -r ${out}.rsp",
 			CommandDeps:    []string{"${SoongZipCmd}"},
-			Rspfile:        "$out.rsp",
+			Rspfile:        "${out}.rsp",
 			RspfileContent: "$in",
 		})
 
@@ -569,8 +578,13 @@
 			tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
 			tidyFiles = append(tidyFiles, tidyFile)
 
+			rule := clangTidy
+			if ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
+				rule = clangTidyRE
+			}
+
 			ctx.Build(pctx, android.BuildParams{
-				Rule:        clangTidy,
+				Rule:        rule,
 				Description: "clang-tidy " + srcFile.Rel(),
 				Output:      tidyFile,
 				Input:       srcFile,
diff --git a/cc/cc.go b/cc/cc.go
index 4b36218..9bf9c84 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -344,7 +344,7 @@
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
 	isForPlatform() bool
-	apexName() string
+	apexVariationName() string
 	apexSdkVersion() int
 	hasStubsVariants() bool
 	isStubs() bool
@@ -418,7 +418,7 @@
 	inSanitizerDir() bool
 	hostToolPath() android.OptionalPath
 	relativeInstallPath() string
-	skipInstall(mod *Module)
+	makeUninstallable(mod *Module)
 }
 
 type xref interface {
@@ -681,6 +681,13 @@
 	return String(c.Properties.Min_sdk_version)
 }
 
+func (c *Module) SplitPerApiLevel() bool {
+	if linker, ok := c.linker.(*objectLinker); ok {
+		return linker.isCrt()
+	}
+	return false
+}
+
 func (c *Module) AlwaysSdk() bool {
 	return c.Properties.AlwaysSdk || Bool(c.Properties.Sdk_variant_only)
 }
@@ -952,7 +959,7 @@
 
 func (c *Module) UseSdk() bool {
 	if c.canUseSdk() {
-		return String(c.Properties.Sdk_version) != ""
+		return String(c.Properties.Sdk_version) != "" || c.SplitPerApiLevel()
 	}
 	return false
 }
@@ -962,7 +969,7 @@
 }
 
 func (c *Module) IsNdk() bool {
-	return inList(c.Name(), ndkKnownLibs)
+	return inList(c.BaseModuleName(), ndkKnownLibs)
 }
 
 func (c *Module) isLlndk(config android.Config) bool {
@@ -1283,8 +1290,8 @@
 	return ctx.mod.IsForPlatform()
 }
 
-func (ctx *moduleContextImpl) apexName() string {
-	return ctx.mod.ApexName()
+func (ctx *moduleContextImpl) apexVariationName() string {
+	return ctx.mod.ApexVariationName()
 }
 
 func (ctx *moduleContextImpl) apexSdkVersion() int {
@@ -1485,8 +1492,11 @@
 		c.Properties.SubName += ramdiskSuffix
 	} else if c.InRecovery() && !c.OnlyInRecovery() {
 		c.Properties.SubName += recoverySuffix
-	} else if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
+	} else if c.IsSdkVariant() && (c.Properties.SdkAndPlatformVariantVisibleToMake || c.SplitPerApiLevel()) {
 		c.Properties.SubName += sdkSuffix
+		if c.SplitPerApiLevel() {
+			c.Properties.SubName += "." + c.SdkVersion()
+		}
 	}
 
 	ctx := &moduleContext{
@@ -1659,7 +1669,7 @@
 	for _, feature := range c.features {
 		feature.begin(ctx)
 	}
-	if ctx.useSdk() {
+	if ctx.useSdk() && c.IsSdkVariant() {
 		version, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch())
 		if err != nil {
 			ctx.PropertyErrorf("sdk_version", err.Error())
@@ -1762,6 +1772,22 @@
 	return name, ""
 }
 
+func GetCrtVariations(ctx android.BottomUpMutatorContext,
+	m LinkableInterface) []blueprint.Variation {
+	if ctx.Os() != android.Android {
+		return nil
+	}
+	if m.UseSdk() {
+		return []blueprint.Variation{
+			{Mutator: "sdk", Variation: "sdk"},
+			{Mutator: "ndk_api", Variation: m.SdkVersion()},
+		}
+	}
+	return []blueprint.Variation{
+		{Mutator: "sdk", Variation: ""},
+	}
+}
+
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	if !c.Enabled() {
 		return
@@ -2024,11 +2050,14 @@
 
 	vendorSnapshotObjects := vendorSnapshotObjects(actx.Config())
 
+	crtVariations := GetCrtVariations(ctx, c)
 	if deps.CrtBegin != "" {
-		actx.AddVariationDependencies(nil, CrtBeginDepTag, rewriteSnapshotLibs(deps.CrtBegin, vendorSnapshotObjects))
+		actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
+			rewriteSnapshotLibs(deps.CrtBegin, vendorSnapshotObjects))
 	}
 	if deps.CrtEnd != "" {
-		actx.AddVariationDependencies(nil, CrtEndDepTag, rewriteSnapshotLibs(deps.CrtEnd, vendorSnapshotObjects))
+		actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
+			rewriteSnapshotLibs(deps.CrtEnd, vendorSnapshotObjects))
 	}
 	if deps.LinkerFlagsFile != "" {
 		actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -2361,7 +2390,7 @@
 			if ccDep.CcLibrary() && !libDepTag.static() {
 				depIsStubs := ccDep.BuildStubs()
 				depHasStubs := VersionVariantAvailable(c) && ccDep.HasStubsVariants()
-				depInSameApex := android.DirectlyInApex(c.ApexName(), depName)
+				depInSameApexes := android.DirectlyInAllApexes(c.InApexes(), depName)
 				depInPlatform := !android.DirectlyInAnyApex(ctx, depName)
 
 				var useThisDep bool
@@ -2391,9 +2420,9 @@
 						}
 					}
 				} else {
-					// If building for APEX, use stubs only when it is not from
-					// the same APEX
-					useThisDep = (depInSameApex != depIsStubs)
+					// If building for APEX, use stubs when the parent is in any APEX that
+					// the child is not in.
+					useThisDep = (depInSameApexes != depIsStubs)
 				}
 
 				// when to use (unspecified) stubs, check min_sdk_version and choose the right one
@@ -2417,7 +2446,7 @@
 					// by default, use current version of LLNDK
 					versionToUse := ""
 					versions := stubsVersionsFor(ctx.Config())[depName]
-					if c.ApexName() != "" && len(versions) > 0 {
+					if c.ApexVariationName() != "" && len(versions) > 0 {
 						// if this is for use_vendor apex && dep has stubsVersions
 						// apply the same rule of apex sdk enforcement to choose right version
 						var err error
@@ -2572,12 +2601,8 @@
 			makeLibName := c.makeLibName(ctx, ccDep, depName) + libDepTag.makeSuffix
 			switch {
 			case libDepTag.header():
-				// TODO(ccross): The reexportFlags check is there to maintain previous
-				//   behavior when adding libraryDependencyTag and should be removed.
-				if !libDepTag.reexportFlags {
-					c.Properties.AndroidMkHeaderLibs = append(
-						c.Properties.AndroidMkHeaderLibs, makeLibName)
-				}
+				c.Properties.AndroidMkHeaderLibs = append(
+					c.Properties.AndroidMkHeaderLibs, makeLibName)
 			case libDepTag.shared():
 				if ccDep.CcLibrary() {
 					if ccDep.BuildStubs() && android.InAnyApex(depName) {
@@ -2592,13 +2617,8 @@
 
 				// Note: the order of libs in this list is not important because
 				// they merely serve as Make dependencies and do not affect this lib itself.
-				// TODO(ccross): The reexportFlags, order and ndk checks are there to
-				//   maintain previous behavior when adding libraryDependencyTag and
-				//   should be removed.
-				if !c.static() || libDepTag.reexportFlags || libDepTag.Order == lateLibraryDependency || libDepTag.ndk {
-					c.Properties.AndroidMkSharedLibs = append(
-						c.Properties.AndroidMkSharedLibs, makeLibName)
-				}
+				c.Properties.AndroidMkSharedLibs = append(
+					c.Properties.AndroidMkSharedLibs, makeLibName)
 				// Record baseLibName for snapshots.
 				c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, baseLibName(depName))
 			case libDepTag.static():
@@ -2730,12 +2750,12 @@
 	return c.InRecovery()
 }
 
-func (c *Module) SkipInstall() {
+func (c *Module) MakeUninstallable() {
 	if c.installer == nil {
-		c.ModuleBase.SkipInstall()
+		c.ModuleBase.MakeUninstallable()
 		return
 	}
-	c.installer.skipInstall(c)
+	c.installer.makeUninstallable(c)
 }
 
 func (c *Module) HostToolPath() android.OptionalPath {
@@ -2875,6 +2895,16 @@
 	}
 }
 
+func (c *Module) UniqueApexVariations() bool {
+	if u, ok := c.compiler.(interface {
+		uniqueApexVariations() bool
+	}); ok {
+		return u.uniqueApexVariations()
+	} else {
+		return false
+	}
+}
+
 // Return true if the module is ever installable.
 func (c *Module) EverInstallable() bool {
 	return c.installer != nil &&
@@ -2930,9 +2960,7 @@
 				return false
 			}
 		}
-		// TODO(ccross): The libDepTag.reexportFlags is there to maintain previous behavior
-		//   when adding libraryDependencyTag and should be removed.
-		if isLibDepTag && c.static() && libDepTag.shared() && !libDepTag.reexportFlags {
+		if isLibDepTag && c.static() && libDepTag.shared() {
 			// shared_lib dependency from a static lib is considered as crossing
 			// the APEX boundary because the dependency doesn't actually is
 			// linked; the dependency is used only during the compilation phase.
@@ -3076,7 +3104,7 @@
 }
 
 func (c *Module) IsSdkVariant() bool {
-	return c.Properties.IsSdkVariant
+	return c.Properties.IsSdkVariant || c.AlwaysSdk()
 }
 
 func getCurrentNdkPrebuiltVersion(ctx DepsContext) string {
diff --git a/cc/compiler.go b/cc/compiler.go
index d5ea2c3..e06243b 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -335,10 +335,10 @@
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RECOVERY__")
 	}
 
-	if ctx.apexName() != "" {
+	if ctx.apexVariationName() != "" {
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
 		if Bool(compiler.Properties.Use_apex_name_macro) {
-			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_"+makeDefineString(ctx.apexName())+"__")
+			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_"+makeDefineString(ctx.apexVariationName())+"__")
 		}
 		if ctx.Device() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_SDK_VERSION__="+strconv.Itoa(ctx.apexSdkVersion()))
@@ -556,6 +556,10 @@
 	return false
 }
 
+func (compiler *baseCompiler) uniqueApexVariations() bool {
+	return Bool(compiler.Properties.Use_apex_name_macro)
+}
+
 // makeDefineString transforms a name of an APEX module into a value to be used as value for C define
 // For example, com.android.foo => COM_ANDROID_FOO
 func makeDefineString(name string) string {
@@ -565,7 +569,7 @@
 var gnuToCReplacer = strings.NewReplacer("gnu", "c")
 
 func ndkPathDeps(ctx ModuleContext) android.Paths {
-	if ctx.useSdk() {
+	if ctx.Module().(*Module).IsSdkVariant() {
 		// The NDK sysroot timestamp file depends on all the NDK sysroot files
 		// (headers and libraries).
 		return android.Paths{getNdkBaseTimestampFile(ctx)}
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 24dc6b9..7db405c 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"android/soong/android"
 	"sort"
 	"strings"
 )
@@ -88,6 +89,12 @@
 
 var ClangLibToolingUnknownCflags = sorted([]string{})
 
+// List of tidy checks that should be disabled globally. When the compiler is
+// updated, some checks enabled by this module may be disabled if they have
+// become more strict, or if they are a new match for a wildcard group like
+// `modernize-*`.
+var ClangTidyDisableChecks = []string{}
+
 func init() {
 	pctx.StaticVariable("ClangExtraCflags", strings.Join([]string{
 		"-D__compiler_offsetof=__builtin_offsetof",
@@ -202,25 +209,34 @@
 }
 
 func ClangFilterUnknownCflags(cflags []string) []string {
-	ret := make([]string, 0, len(cflags))
-	for _, f := range cflags {
-		if !inListSorted(f, ClangUnknownCflags) {
-			ret = append(ret, f)
+	result, _ := android.FilterList(cflags, ClangUnknownCflags)
+	return result
+}
+
+func clangTidyNegateChecks(checks []string) []string {
+	ret := make([]string, 0, len(checks))
+	for _, c := range checks {
+		if strings.HasPrefix(c, "-") {
+			ret = append(ret, c)
+		} else {
+			ret = append(ret, "-"+c)
 		}
 	}
-
 	return ret
 }
 
-func ClangFilterUnknownLldflags(lldflags []string) []string {
-	ret := make([]string, 0, len(lldflags))
-	for _, f := range lldflags {
-		if !inListSorted(f, ClangUnknownLldflags) {
-			ret = append(ret, f)
-		}
-	}
+func ClangRewriteTidyChecks(checks []string) []string {
+	checks = append(checks, clangTidyNegateChecks(ClangTidyDisableChecks)...)
+	// clang-tidy does not allow later arguments to override earlier arguments,
+	// so if we just disabled an argument that was explicitly enabled we must
+	// remove the enabling argument from the list.
+	result, _ := android.FilterList(checks, ClangTidyDisableChecks)
+	return result
+}
 
-	return ret
+func ClangFilterUnknownLldflags(lldflags []string) []string {
+	result, _ := android.FilterList(lldflags, ClangUnknownLldflags)
+	return result
 }
 
 func inListSorted(s string, list []string) bool {
diff --git a/cc/config/global.go b/cc/config/global.go
index 373fc77..32f163d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -261,7 +261,9 @@
 
 	pctx.VariableFunc("RECXXPool", remoteexec.EnvOverrideFunc("RBE_CXX_POOL", remoteexec.DefaultPool))
 	pctx.VariableFunc("RECXXLinksPool", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_POOL", remoteexec.DefaultPool))
+	pctx.VariableFunc("REClangTidyPool", remoteexec.EnvOverrideFunc("RBE_CLANG_TIDY_POOL", remoteexec.DefaultPool))
 	pctx.VariableFunc("RECXXLinksExecStrategy", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REClangTidyExecStrategy", remoteexec.EnvOverrideFunc("RBE_CLANG_TIDY_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("REAbiDumperExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_DUMPER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("REAbiLinkerExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 }
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 58c1888..5295418 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -402,7 +402,7 @@
 			command := builder.Command().BuiltTool(ctx, "soong_zip").
 				Flag("-j").
 				FlagWithOutput("-o ", corpusZip)
-			command.FlagWithRspFileInputList("-l ", fuzzModule.corpus)
+			command.FlagWithRspFileInputList("-r ", fuzzModule.corpus)
 			files = append(files, fileToZip{corpusZip, ""})
 		}
 
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index d38cf27..a366f76 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -76,3 +76,42 @@
 		t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Inputs.Strings())
 	}
 }
+
+func TestLibraryGenruleCmd(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libboth",
+		}
+
+		cc_library_shared {
+			name: "libshared",
+		}
+
+		cc_library_static {
+			name: "libstatic",
+		}
+
+		cc_genrule {
+			name: "gen",
+			tool_files: ["tool"],
+			srcs: [
+				":libboth",
+				":libshared",
+				":libstatic",
+			],
+			cmd: "$(location tool) $(in) $(out)",
+			out: ["out"],
+		}
+		`
+	ctx := testCc(t, bp)
+
+	gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out")
+	expected := []string{"libboth.so", "libshared.so", "libstatic.a"}
+	var got []string
+	for _, input := range gen.Inputs {
+		got = append(got, input.Base())
+	}
+	if !reflect.DeepEqual(expected, got) {
+		t.Errorf(`want inputs %v, got %v`, expected, got)
+	}
+}
diff --git a/cc/installer.go b/cc/installer.go
index 0b4a68c..e551c63 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -107,6 +107,6 @@
 	return String(installer.Properties.Relative_install_path)
 }
 
-func (installer *baseInstaller) skipInstall(mod *Module) {
-	mod.ModuleBase.SkipInstall()
+func (installer *baseInstaller) makeUninstallable(mod *Module) {
+	mod.ModuleBase.MakeUninstallable()
 }
diff --git a/cc/library.go b/cc/library.go
index 2a329ac..1c2b1ee 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -28,7 +28,6 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
-	"android/soong/genrule"
 )
 
 type LibraryProperties struct {
@@ -801,21 +800,8 @@
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
 	} else if library.shared() {
 		if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
-			if !ctx.useSdk() {
-				deps.CrtBegin = "crtbegin_so"
-				deps.CrtEnd = "crtend_so"
-			} else {
-				// TODO(danalbert): Add generation of crt objects.
-				// For `sdk_version: "current"`, we don't actually have a
-				// freshly generated set of CRT objects. Use the last stable
-				// version.
-				version := ctx.sdkVersion()
-				if version == "current" {
-					version = getCurrentNdkPrebuiltVersion(ctx)
-				}
-				deps.CrtBegin = "ndk_crtbegin_so." + version
-				deps.CrtEnd = "ndk_crtend_so." + version
-			}
+			deps.CrtBegin = "crtbegin_so"
+			deps.CrtEnd = "crtend_so"
 		}
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
 		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
@@ -1365,16 +1351,15 @@
 	return android.CheckAvailableForApex(what, list)
 }
 
-func (library *libraryDecorator) skipInstall(mod *Module) {
+func (library *libraryDecorator) makeUninstallable(mod *Module) {
 	if library.static() && library.buildStatic() && !library.buildStubs() {
-		// If we're asked to skip installation of a static library (in particular
-		// when it's not //apex_available:platform) we still want an AndroidMk entry
-		// for it to ensure we get the relevant NOTICE file targets (cf.
-		// notice_files.mk) that other libraries might depend on. AndroidMkEntries
-		// always sets LOCAL_UNINSTALLABLE_MODULE for these entries.
+		// If we're asked to make a static library uninstallable we don't do
+		// anything since AndroidMkEntries always sets LOCAL_UNINSTALLABLE_MODULE
+		// for these entries. This is done to still get the make targets for NOTICE
+		// files from notice_files.mk, which other libraries might depend on.
 		return
 	}
-	mod.ModuleBase.SkipInstall()
+	mod.ModuleBase.MakeUninstallable()
 }
 
 var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
@@ -1470,6 +1455,12 @@
 			static.linker.(prebuiltLibraryInterface).setStatic()
 			shared.linker.(prebuiltLibraryInterface).setShared()
 
+			if library.buildShared() {
+				mctx.AliasVariation("shared")
+			} else if library.buildStatic() {
+				mctx.AliasVariation("static")
+			}
+
 			if !library.buildStatic() {
 				static.linker.(prebuiltLibraryInterface).disablePrebuilt()
 			}
@@ -1501,18 +1492,22 @@
 			if _, ok := library.(*Module); ok {
 				reuseStaticLibrary(mctx, static.(*Module), shared.(*Module))
 			}
+			mctx.AliasVariation("shared")
 		} else if library.BuildStaticVariant() {
 			variations := append([]string{"static"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
 			modules[0].(LinkableInterface).SetStatic()
+			mctx.AliasVariation("static")
 		} else if library.BuildSharedVariant() {
 			variations := append([]string{"shared"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
 			modules[0].(LinkableInterface).SetShared()
+			mctx.AliasVariation("shared")
 		} else if len(variations) > 0 {
 			mctx.CreateLocalVariations(variations...)
+			mctx.AliasVariation(variations[0])
 		}
 	}
 }
@@ -1559,13 +1554,14 @@
 	// "" is for the non-stubs variant
 	versions = append([]string{""}, versions...)
 
-	modules := mctx.CreateVariations(versions...)
+	modules := mctx.CreateLocalVariations(versions...)
 	for i, m := range modules {
 		if versions[i] != "" {
 			m.(LinkableInterface).SetBuildStubs()
 			m.(LinkableInterface).SetStubsVersions(versions[i])
 		}
 	}
+	mctx.AliasVariation("")
 }
 
 func VersionVariantAvailable(module interface {
@@ -1600,7 +1596,7 @@
 		if c, ok := library.(*Module); ok && c.IsStubs() {
 			stubsVersionsLock.Lock()
 			defer stubsVersionsLock.Unlock()
-			// For LLNDK llndk_library, we borrow vstubs.ersions from its implementation library.
+			// For LLNDK llndk_library, we borrow stubs.versions from its implementation library.
 			// Since llndk_library has dependency to its implementation library,
 			// we can safely access stubsVersionsFor() with its baseModuleName.
 			versions := stubsVersionsFor(mctx.Config())[c.BaseModuleName()]
@@ -1611,17 +1607,10 @@
 			return
 		}
 
-		mctx.CreateVariations("")
+		mctx.CreateLocalVariations("")
+		mctx.AliasVariation("")
 		return
 	}
-	if genrule, ok := mctx.Module().(*genrule.Module); ok {
-		if _, ok := genrule.Extra.(*GenruleExtraProperties); ok {
-			if VersionVariantAvailable(genrule) {
-				mctx.CreateVariations("")
-				return
-			}
-		}
-	}
 }
 
 // maybeInjectBoringSSLHash adds a rule to run bssl_inject_hash on the output file if the module has the
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index cff00b6..9328a25 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -212,6 +212,11 @@
 // Add properties that may, or may not, be arch specific.
 func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, libInfo *nativeLibInfoProperties, outputProperties android.BpPropertySet) {
 
+	if libInfo.SanitizeNever {
+		sanitizeSet := outputProperties.AddPropertySet("sanitize")
+		sanitizeSet.AddProperty("never", true)
+	}
+
 	// Copy the generated library to the snapshot and add a reference to it in the .bp module.
 	if libInfo.outputFile != nil {
 		nativeLibraryPath := nativeLibraryPathFor(libInfo)
@@ -359,6 +364,9 @@
 	// not vary by arch so cannot be android specific.
 	StubsVersion string `sdk:"ignored-on-host"`
 
+	// Value of SanitizeProperties.Sanitize.Never. Needs to be propagated for CRT objects.
+	SanitizeNever bool `android:"arch_variant"`
+
 	// outputFile is not exported as it is always arch specific.
 	outputFile android.Path
 }
@@ -405,6 +413,10 @@
 	if ccModule.HasStubsVariants() {
 		p.StubsVersion = ccModule.StubsVersion()
 	}
+
+	if ccModule.sanitize != nil && proptools.Bool(ccModule.sanitize.Properties.Sanitize.Never) {
+		p.SanitizeNever = true
+	}
 }
 
 func getRequiredMemberOutputFile(ctx android.SdkMemberContext, ccModule *Module) android.Path {
diff --git a/cc/lto.go b/cc/lto.go
index ed3abe7..9868cdf 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -152,9 +152,7 @@
 
 			// Do not recurse down non-static dependencies
 			if isLibTag {
-				// TODO(ccross): the staticUnwinder check is there to maintain existing behavior
-				//   when adding libraryDependencyTag and should be removed.
-				if !libTag.static() || libTag.staticUnwinder {
+				if !libTag.static() {
 					return false
 				}
 			} else {
diff --git a/cc/makevars.go b/cc/makevars.go
index 968eeb5..dcfd6d8 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -148,8 +148,6 @@
 	ctx.Strict("AIDL_CPP", "${aidlCmd}")
 	ctx.Strict("ALLOWED_MANUAL_INTERFACE_PATHS", strings.Join(allowedManualInterfacePaths, " "))
 
-	ctx.Strict("M4", "${m4Cmd}")
-
 	ctx.Strict("RS_GLOBAL_INCLUDES", "${config.RsGlobalIncludes}")
 
 	ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 22e3ec3..58e742e 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -110,6 +110,10 @@
 func normalizeNdkApiLevel(ctx android.BaseModuleContext, apiLevel string,
 	arch android.Arch) (string, error) {
 
+	if apiLevel == "" {
+		panic("empty apiLevel not allowed")
+	}
+
 	if apiLevel == "current" {
 		return apiLevel, nil
 	}
@@ -136,7 +140,8 @@
 	// supported version here instead.
 	version, err := strconv.Atoi(apiLevel)
 	if err != nil {
-		return "", fmt.Errorf("API level must be an integer (is %q)", apiLevel)
+		// Non-integer API levels are codenames.
+		return apiLevel, nil
 	}
 	version = intMax(version, minVersion)
 
@@ -182,40 +187,61 @@
 	return version >= unversionedUntil, nil
 }
 
-func generateStubApiVariants(mctx android.BottomUpMutatorContext, c *stubDecorator) {
-	platformVersion := mctx.Config().PlatformSdkVersionInt()
+func generatePerApiVariants(ctx android.BottomUpMutatorContext, m *Module,
+	propName string, propValue string, perSplit func(*Module, string)) {
+	platformVersion := ctx.Config().PlatformSdkVersionInt()
 
-	firstSupportedVersion, err := normalizeNdkApiLevel(mctx, String(c.properties.First_version),
-		mctx.Arch())
+	firstSupportedVersion, err := normalizeNdkApiLevel(ctx, propValue,
+		ctx.Arch())
 	if err != nil {
-		mctx.PropertyErrorf("first_version", err.Error())
+		ctx.PropertyErrorf(propName, err.Error())
 	}
 
-	firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion, platformVersion)
+	firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion,
+		platformVersion)
 	if err != nil {
 		// In theory this is impossible because we've already run this through
 		// normalizeNdkApiLevel above.
-		mctx.PropertyErrorf("first_version", err.Error())
+		ctx.PropertyErrorf(propName, err.Error())
 	}
 
 	var versionStrs []string
 	for version := firstGenVersion; version <= platformVersion; version++ {
 		versionStrs = append(versionStrs, strconv.Itoa(version))
 	}
-	versionStrs = append(versionStrs, mctx.Config().PlatformVersionActiveCodenames()...)
+	versionStrs = append(versionStrs, ctx.Config().PlatformVersionActiveCodenames()...)
 	versionStrs = append(versionStrs, "current")
 
-	modules := mctx.CreateVariations(versionStrs...)
+	modules := ctx.CreateVariations(versionStrs...)
 	for i, module := range modules {
-		module.(*Module).compiler.(*stubDecorator).properties.ApiLevel = versionStrs[i]
+		perSplit(module.(*Module), versionStrs[i])
 	}
 }
 
-func NdkApiMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok {
+func NdkApiMutator(ctx android.BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(*Module); ok {
 		if m.Enabled() {
 			if compiler, ok := m.compiler.(*stubDecorator); ok {
-				generateStubApiVariants(mctx, compiler)
+				if ctx.Os() != android.Android {
+					// These modules are always android.DeviceEnabled only, but
+					// those include Fuchsia devices, which we don't support.
+					ctx.Module().Disable()
+					return
+				}
+				generatePerApiVariants(ctx, m, "first_version",
+					String(compiler.properties.First_version),
+					func(m *Module, version string) {
+						m.compiler.(*stubDecorator).properties.ApiLevel =
+							version
+					})
+			} else if m.SplitPerApiLevel() && m.IsSdkVariant() {
+				if ctx.Os() != android.Android {
+					return
+				}
+				generatePerApiVariants(ctx, m, "min_sdk_version",
+					m.MinSdkVersion(), func(m *Module, version string) {
+						m.Properties.Sdk_version = &version
+					})
 			}
 		}
 	}
diff --git a/cc/object.go b/cc/object.go
index 15a529e..778d131 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -55,6 +55,10 @@
 
 	// if set, the path to a linker script to pass to ld -r when combining multiple object files.
 	Linker_script *string `android:"path,arch_variant"`
+
+	// Indicates that this module is a CRT object. CRT objects will be split
+	// into a variant per-API level between min_sdk_version and current.
+	Crt *bool
 }
 
 func newObject() *Module {
@@ -162,3 +166,7 @@
 func (object *objectLinker) object() bool {
 	return true
 }
+
+func (object *objectLinker) isCrt() bool {
+	return Bool(object.Properties.Crt)
+}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 653b43e..baf43ce 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -199,10 +199,6 @@
 	p.properties.Srcs = nil
 }
 
-func (p *prebuiltLibraryLinker) skipInstall(mod *Module) {
-	mod.ModuleBase.SkipInstall()
-}
-
 func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewLibrary(hod)
 	module.compiler = nil
@@ -211,7 +207,6 @@
 		libraryDecorator: library,
 	}
 	module.linker = prebuilt
-	module.installer = prebuilt
 
 	module.AddProperties(&prebuilt.properties)
 
diff --git a/cc/rs.go b/cc/rs.go
index 9149e17..ba69f23 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -25,8 +25,8 @@
 
 func init() {
 	pctx.VariableFunc("rsCmd", func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() {
-			// Use RenderScript prebuilts for unbundled builds but not PDK builds
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
+			// Use RenderScript prebuilts for unbundled builds
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin/llvm-rs-cc")
 		} else {
 			return ctx.Config().HostToolPath(ctx, "llvm-rs-cc").String()
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 6db6348..2243082 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -174,8 +174,6 @@
 		Recover []string
 
 		// value to pass to -fsanitize-blacklist
-		Blacklist *string
-		// value to pass to -fsanitize-blacklist
 		Blocklist *string
 	} `android:"arch_variant"`
 
@@ -610,12 +608,6 @@
 			strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ","))
 	}
 
-	blacklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blacklist)
-	if blacklist.Valid() {
-		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-blacklist="+blacklist.String())
-		flags.CFlagsDeps = append(flags.CFlagsDeps, blacklist.Path())
-	}
-
 	blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist)
 	if blocklist.Valid() {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-blacklist="+blocklist.String())
diff --git a/cc/test.go b/cc/test.go
index 9b6864a..a805647 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -40,8 +40,12 @@
 type TestOptions struct {
 	// The UID that you want to run the test as on a device.
 	Run_test_as *string
+
 	// A list of free-formed strings without spaces that categorize the test.
 	Test_suite_tag []string
+
+	// a list of extra test configuration files that should be installed with the module.
+	Extra_test_configs []string `android:"path,arch_variant"`
 }
 
 type TestBinaryProperties struct {
@@ -309,9 +313,10 @@
 	testDecorator
 	*binaryDecorator
 	*baseCompiler
-	Properties TestBinaryProperties
-	data       []android.DataPath
-	testConfig android.Path
+	Properties       TestBinaryProperties
+	data             []android.DataPath
+	testConfig       android.Path
+	extraTestConfigs android.Paths
 }
 
 func (test *testBinary) linkerProps() []interface{} {
@@ -408,6 +413,8 @@
 	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
 		test.Properties.Test_config_template, test.Properties.Test_suites, configs, test.Properties.Auto_gen_config)
 
+	test.extraTestConfigs = android.PathsForModuleSrc(ctx, test.Properties.Test_options.Extra_test_configs)
+
 	test.binaryDecorator.baseInstaller.dir = "nativetest"
 	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
 
diff --git a/cc/testing.go b/cc/testing.go
index 4113fd2..06e5f83 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -338,6 +338,8 @@
 			vendor_available: true,
 			native_bridge_supported: true,
 			stl: "none",
+			min_sdk_version: "16",
+			crt: true,
 			apex_available: [
 				"//apex_available:platform",
 				"//apex_available:anyapex",
@@ -347,48 +349,26 @@
 		cc_object {
 			name: "crtbegin_so",
 			defaults: ["crt_defaults"],
-			recovery_available: true,
-			vendor_available: true,
-			native_bridge_supported: true,
-			min_sdk_version: "29",
-			stl: "none",
 		}
 
 		cc_object {
 			name: "crtbegin_dynamic",
 			defaults: ["crt_defaults"],
-			recovery_available: true,
-			vendor_available: true,
-			native_bridge_supported: true,
-			stl: "none",
 		}
 
 		cc_object {
 			name: "crtbegin_static",
 			defaults: ["crt_defaults"],
-			recovery_available: true,
-			vendor_available: true,
-			native_bridge_supported: true,
-			stl: "none",
 		}
 
 		cc_object {
 			name: "crtend_so",
 			defaults: ["crt_defaults"],
-			recovery_available: true,
-			vendor_available: true,
-			native_bridge_supported: true,
-			min_sdk_version: "29",
-			stl: "none",
 		}
 
 		cc_object {
 			name: "crtend_android",
 			defaults: ["crt_defaults"],
-			recovery_available: true,
-			vendor_available: true,
-			native_bridge_supported: true,
-			stl: "none",
 		}
 
 		cc_library {
@@ -420,26 +400,6 @@
 			symbol_file: "libdl.map.txt",
 		}
 
-		ndk_prebuilt_object {
-			name: "ndk_crtbegin_so.27",
-			sdk_version: "27",
-		}
-
-		ndk_prebuilt_object {
-			name: "ndk_crtend_so.27",
-			sdk_version: "27",
-		}
-
-		ndk_prebuilt_object {
-			name: "ndk_crtbegin_dynamic.27",
-			sdk_version: "27",
-		}
-
-		ndk_prebuilt_object {
-			name: "ndk_crtend_android.27",
-			sdk_version: "27",
-		}
-
 		ndk_prebuilt_shared_stl {
 			name: "ndk_libc++_shared",
 		}
diff --git a/cc/tidy.go b/cc/tidy.go
index 364e56c..17471e6 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -109,7 +109,8 @@
 		tidyChecks += config.TidyChecksForDir(ctx.ModuleDir())
 	}
 	if len(tidy.Properties.Tidy_checks) > 0 {
-		tidyChecks = tidyChecks + "," + strings.Join(esc(tidy.Properties.Tidy_checks), ",")
+		tidyChecks = tidyChecks + "," + strings.Join(esc(
+			config.ClangRewriteTidyChecks(tidy.Properties.Tidy_checks)), ",")
 	}
 	if ctx.Windows() {
 		// https://b.corp.google.com/issues/120614316
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 69e4f69..4aa62be 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -173,6 +173,7 @@
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
 	defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+	defer build.PrintGomaDeprecation(buildCtx, config)
 
 	os.MkdirAll(logsDir, 0777)
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index db5e97a..78f91df 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -100,6 +100,8 @@
 	ConstructContext android.Path
 }
 
+const UnknownInstallLibraryPath = "error"
+
 // LibraryPath contains paths to the library DEX jar on host and on device.
 type LibraryPath struct {
 	Host   android.Path
@@ -109,6 +111,36 @@
 // LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
 type LibraryPaths map[string]*LibraryPath
 
+// Add a new path to the map of library paths, unless a path for this library already exists.
+func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib *string, hostPath, installPath android.Path) {
+	if lib == nil {
+		return
+	}
+	if _, present := libPaths[*lib]; !present {
+		var devicePath string
+		if installPath != nil {
+			devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
+		} else {
+			// For some stub libraries the only known thing is the name of their implementation
+			// library, but the library itself is unavailable (missing or part of a prebuilt). In
+			// such cases we still need to add the library to <uses-library> tags in the manifest,
+			// but we cannot use if for dexpreopt.
+			devicePath = UnknownInstallLibraryPath
+		}
+		libPaths[*lib] = &LibraryPath{hostPath, devicePath}
+	}
+	return
+}
+
+// Add library paths from the second map to the first map (do not override existing entries).
+func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) {
+	for lib, path := range otherPaths {
+		if _, present := libPaths[lib]; !present {
+			libPaths[lib] = path
+		}
+	}
+}
+
 type ModuleConfig struct {
 	Name            string
 	DexLocation     string // dex location on device
@@ -360,7 +392,33 @@
 func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
 	dex2oatBin := dex2oatModuleName(ctx.Config())
 
-	dex2oatModule := ctx.GetDirectDepWithTag(dex2oatBin, dex2oatDepTag)
+	// Find the right dex2oat module, trying to follow PrebuiltDepTag from source
+	// to prebuilt if there is one. We wouldn't have to do this if the
+	// prebuilt_postdeps mutator that replaces source deps with prebuilt deps was
+	// run after RegisterToolDeps above, but changing that leads to ordering
+	// problems between mutators (RegisterToolDeps needs to run late to act on
+	// final variants, while prebuilt_postdeps needs to run before many of the
+	// PostDeps mutators, like the APEX mutators). Hence we need to dig out the
+	// prebuilt explicitly here instead.
+	var dex2oatModule android.Module
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == dex2oatDepTag {
+			// Found the source module, or prebuilt module that has replaced the source.
+			dex2oatModule = child
+			if p, ok := child.(android.PrebuiltInterface); ok && p.Prebuilt() != nil {
+				return false // If it's the prebuilt we're done.
+			} else {
+				return true // Recurse to check if the source has a prebuilt dependency.
+			}
+		}
+		if parent == dex2oatModule && ctx.OtherModuleDependencyTag(child) == android.PrebuiltDepTag {
+			if p, ok := child.(android.PrebuiltInterface); ok && p.Prebuilt() != nil && p.Prebuilt().UsePrebuilt() {
+				dex2oatModule = child // Found a prebuilt that should be used.
+			}
+		}
+		return false
+	})
+
 	if dex2oatModule == nil {
 		// If this happens there's probably a missing call to AddToolDeps in DepsMutator.
 		panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin))
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 8c9f0a2..8deb0a3 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -37,7 +37,6 @@
 	"fmt"
 	"path/filepath"
 	"runtime"
-	"sort"
 	"strings"
 
 	"android/soong/android"
@@ -208,15 +207,6 @@
 
 const anySdkVersion int = 9999 // should go last in class loader context
 
-func (m classLoaderContextMap) getSortedKeys() []int {
-	keys := make([]int, 0, len(m))
-	for k := range m {
-		keys = append(keys, k)
-	}
-	sort.Ints(keys)
-	return keys
-}
-
 func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
 	if _, ok := m[sdkVer]; !ok {
 		m[sdkVer] = &classLoaderContext{}
@@ -342,7 +332,7 @@
 		cmd := rule.Command().
 			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
 			Text(` --target-sdk-version ${target_sdk_version}`)
-		for _, ver := range classLoaderContexts.getSortedKeys() {
+		for _, ver := range android.SortedIntKeys(classLoaderContexts) {
 			clc := classLoaderContexts.getValue(ver)
 			verString := fmt.Sprintf("%d", ver)
 			if ver == anySdkVersion {
diff --git a/go.mod b/go.mod
index 1483a31..7297dea 100644
--- a/go.mod
+++ b/go.mod
@@ -8,4 +8,4 @@
 
 replace github.com/google/blueprint v0.0.0 => ../blueprint
 
-go 1.13
+go 1.15
diff --git a/java/aar.go b/java/aar.go
index ad9b5e7..5c19a9c 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -20,6 +20,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -99,7 +100,7 @@
 	useEmbeddedNativeLibs   bool
 	useEmbeddedDex          bool
 	usesNonSdkApis          bool
-	sdkLibraries            []string
+	sdkLibraries            dexpreopt.LibraryPaths
 	hasNoCode               bool
 	LoggingParent           string
 	resourceFiles           android.Paths
@@ -231,6 +232,8 @@
 	transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags, sdkLibraries :=
 		aaptLibs(ctx, sdkContext)
 
+	a.sdkLibraries = sdkLibraries
+
 	// App manifest file
 	manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
 	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
@@ -357,7 +360,7 @@
 
 // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
 func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, transitiveStaticLibManifests android.Paths,
-	staticRRODirs []rroDir, assets, deps android.Paths, flags []string, sdkLibraries []string) {
+	staticRRODirs []rroDir, assets, deps android.Paths, flags []string, sdkLibraries dexpreopt.LibraryPaths) {
 
 	var sharedLibs android.Paths
 
@@ -366,6 +369,8 @@
 		sharedLibs = append(sharedLibs, sdkDep.jars...)
 	}
 
+	sdkLibraries = make(dexpreopt.LibraryPaths)
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		var exportPackage android.Path
 		aarDep, _ := module.(AndroidLibraryDependency)
@@ -385,7 +390,8 @@
 			// (including the java_sdk_library) itself then append any implicit sdk library
 			// names to the list of sdk libraries to be added to the manifest.
 			if component, ok := module.(SdkLibraryComponentDependency); ok {
-				sdkLibraries = append(sdkLibraries, component.OptionalImplicitSdkLibrary()...)
+				sdkLibraries.AddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(),
+					component.DexJarBuildPath(), component.DexJarInstallPath())
 			}
 
 		case frameworkResTag:
@@ -393,11 +399,14 @@
 				sharedLibs = append(sharedLibs, exportPackage)
 			}
 		case staticLibTag:
+			if dep, ok := module.(Dependency); ok {
+				sdkLibraries.AddLibraryPaths(dep.ExportedSdkLibs())
+			}
 			if exportPackage != nil {
 				transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
 				transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
 				transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...)
-				sdkLibraries = append(sdkLibraries, aarDep.ExportedSdkLibs()...)
+				sdkLibraries.AddLibraryPaths(aarDep.ExportedSdkLibs())
 				if aarDep.ExportedAssets().Valid() {
 					assets = append(assets, aarDep.ExportedAssets().Path())
 				}
@@ -428,7 +437,6 @@
 
 	transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs)
 	transitiveStaticLibManifests = android.FirstUniquePaths(transitiveStaticLibManifests)
-	sdkLibraries = android.FirstUniqueStrings(sdkLibraries)
 
 	return transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assets, deps, flags, sdkLibraries
 }
@@ -465,8 +473,8 @@
 
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.aapt.isLibrary = true
-	a.aapt.sdkLibraries = a.exportedSdkLibs
 	a.aapt.buildActions(ctx, sdkContext(a))
+	a.exportedSdkLibs = a.aapt.sdkLibraries
 
 	ctx.CheckbuildFile(a.proguardOptionsFile)
 	ctx.CheckbuildFile(a.exportPackage)
@@ -625,7 +633,7 @@
 }
 
 func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if !ctx.Config().AlwaysUsePrebuiltSdks() {
 		sdkDep := decodeSdkDep(ctx, sdkContext(a))
 		if sdkDep.useModule && sdkDep.frameworkResModule != "" {
 			ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
@@ -641,9 +649,11 @@
 var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
 	blueprint.RuleParams{
 		Command: `rm -rf $outDir && mkdir -p $outDir && ` +
-			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out`,
+			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` +
+			`${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`,
+		CommandDeps: []string{"${config.MergeZipsCmd}"},
 	},
-	"outDir")
+	"outDir", "combinedClassesJar")
 
 func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(a.properties.Aars) != 1 {
@@ -661,7 +671,7 @@
 	}
 
 	extractedAARDir := android.PathForModuleOut(ctx, "aar")
-	a.classpathFile = extractedAARDir.Join(ctx, "classes.jar")
+	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
 	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
 	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
 
@@ -671,7 +681,8 @@
 		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
 		Description: "unzip AAR",
 		Args: map[string]string{
-			"outDir": extractedAARDir.String(),
+			"outDir":             extractedAARDir.String(),
+			"combinedClassesJar": a.classpathFile.String(),
 		},
 	})
 
@@ -746,7 +757,7 @@
 	return nil
 }
 
-func (a *AARImport) ExportedSdkLibs() []string {
+func (a *AARImport) ExportedSdkLibs() dexpreopt.LibraryPaths {
 	return nil
 }
 
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 84dee16..f45ebe8 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -21,6 +21,7 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 )
 
 var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
@@ -52,7 +53,7 @@
 }
 
 // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
-func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries []string,
+func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries dexpreopt.LibraryPaths,
 	isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
 
 	var args []string
@@ -79,7 +80,7 @@
 		args = append(args, "--use-embedded-dex")
 	}
 
-	for _, usesLib := range sdkLibraries {
+	for usesLib, _ := range sdkLibraries {
 		if inList(usesLib, optionalUsesLibs) {
 			args = append(args, "--optional-uses-library", usesLib)
 		} else {
diff --git a/java/androidmk.go b/java/androidmk.go
index 650d126..2c02e5f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -121,15 +121,14 @@
 						entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
 					}
 
-					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.exportedSdkLibs...)
+					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", android.SortedStringKeys(library.exportedSdkLibs)...)
 
 					if len(library.additionalCheckedModules) != 0 {
 						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
 					}
 
-					if library.dexer.proguardDictionary.Valid() {
-						entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.dexer.proguardDictionary.Path())
-					}
+					entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_DICT", library.dexer.proguardDictionary)
+					entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_USAGE_ZIP", library.dexer.proguardUsageZip)
 					entries.SetString("LOCAL_MODULE_STEM", library.Stem())
 
 					entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", library.linter.reports)
@@ -162,6 +161,7 @@
 		if j.testConfig != nil {
 			entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig)
 		}
+		androidMkWriteExtraTestConfigs(j.extraTestConfigs, entries)
 		androidMkWriteTestData(j.data, entries)
 		if !BoolDefault(j.testProperties.Auto_gen_config, true) {
 			entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true")
@@ -171,6 +171,12 @@
 	return entriesList
 }
 
+func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *android.AndroidMkEntries) {
+	if len(extraTestConfigs) > 0 {
+		entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
+	}
+}
+
 func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := j.Library.AndroidMkEntries()
 	entries := &entriesList[0]
@@ -196,7 +202,7 @@
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion().raw)
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.makeSdkVersion())
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
@@ -332,9 +338,8 @@
 				if app.jacocoReportClassesFile != nil {
 					entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", app.jacocoReportClassesFile)
 				}
-				if app.dexer.proguardDictionary.Valid() {
-					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", app.dexer.proguardDictionary.Path())
-				}
+				entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_DICT", app.dexer.proguardDictionary)
+				entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_USAGE_ZIP", app.dexer.proguardUsageZip)
 
 				if app.Name() == "framework-res" {
 					entries.SetString("LOCAL_MODULE_PATH", "$(TARGET_OUT_JAVA_LIBRARIES)")
@@ -433,6 +438,7 @@
 		if a.testConfig != nil {
 			entries.SetPath("LOCAL_FULL_TEST_CONFIG", a.testConfig)
 		}
+		androidMkWriteExtraTestConfigs(a.extraTestConfigs, entries)
 		androidMkWriteTestData(a.data, entries)
 	})
 
@@ -559,15 +565,21 @@
 	// are created in make if only the api txt file is being generated. This is
 	// needed because an invalid output file would prevent the make entries from
 	// being written.
+	//
+	// Note that dstubs.apiFile can be also be nil if WITHOUT_CHECKS_API is true.
 	// TODO(b/146727827): Revert when we do not need to generate stubs and API separately.
-	distFile := dstubs.apiFile
+
+	var distFiles android.TaggedDistFiles
+	if dstubs.apiFile != nil {
+		distFiles = android.MakeDefaultDistFiles(dstubs.apiFile)
+	}
 	outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar)
 	if !outputFile.Valid() {
-		outputFile = android.OptionalPathForPath(distFile)
+		outputFile = android.OptionalPathForPath(dstubs.apiFile)
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		DistFiles:  android.MakeDefaultDistFiles(distFile),
+		DistFiles:  distFiles,
 		OutputFile: outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
diff --git a/java/app.go b/java/app.go
index 8a0b3db..5e3a9d9 100755
--- a/java/app.go
+++ b/java/app.go
@@ -598,6 +598,7 @@
 	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
 	a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
 	a.dexpreopter.manifestFile = a.mergedManifestFile
+	a.exportedSdkLibs = make(dexpreopt.LibraryPaths)
 
 	if ctx.ModuleName() != "framework-res" {
 		a.Module.compile(ctx, a.aaptSrcJar)
@@ -845,9 +846,7 @@
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
-		// TODO(ccross): The tag == cc.SharedDepTag() check should be cc.IsSharedDepTag(tag) but
-		//   was left to maintain behavior when adding libraryDependencyTag.
-		if IsJniDepTag(tag) || tag == cc.SharedDepTag() {
+		if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
 			if dep, ok := module.(*cc.Module); ok {
 				if dep.IsNdk() || dep.IsStubs() {
 					return false
@@ -965,6 +964,8 @@
 	switch tag {
 	case ".aapt.srcjar":
 		return []android.Path{a.aaptSrcJar}, nil
+	case ".export-package.apk":
+		return []android.Path{a.exportPackage}, nil
 	}
 	return a.Library.OutputFiles(tag)
 }
@@ -1033,8 +1034,9 @@
 
 	testProperties testProperties
 
-	testConfig android.Path
-	data       android.Paths
+	testConfig       android.Path
+	extraTestConfigs android.Paths
+	data             android.Paths
 }
 
 func (a *AndroidTest) InstallInTestcases() bool {
@@ -1062,6 +1064,7 @@
 	testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
 		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config, configs)
 	a.testConfig = a.FixTestConfig(ctx, testConfig)
+	a.extraTestConfigs = android.PathsForModuleSrc(ctx, a.testProperties.Test_options.Extra_test_configs)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
 
diff --git a/java/app_test.go b/java/app_test.go
index efb4fd2..6b27124 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -531,16 +531,6 @@
 			system_shared_libs: [],
 			sdk_version: "29",
 		}
-
-		ndk_prebuilt_object {
-			name: "ndk_crtbegin_so.29",
-			sdk_version: "29",
-		}
-
-		ndk_prebuilt_object {
-			name: "ndk_crtend_so.29",
-			sdk_version: "29",
-		}
 	`
 	fs := map[string][]byte{
 		"prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o": nil,
@@ -553,16 +543,28 @@
 
 	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
 	var crtbeginFound, crtendFound bool
+	expectedCrtBegin := ctx.ModuleForTests("crtbegin_so",
+		"android_arm64_armv8-a_sdk_29").Rule("partialLd").Output
+	expectedCrtEnd := ctx.ModuleForTests("crtend_so",
+		"android_arm64_armv8-a_sdk_29").Rule("partialLd").Output
+	implicits := []string{}
 	for _, input := range inputs {
-		switch input.String() {
-		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o":
+		implicits = append(implicits, input.String())
+		if strings.HasSuffix(input.String(), expectedCrtBegin.String()) {
 			crtbeginFound = true
-		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":
+		} else if strings.HasSuffix(input.String(), expectedCrtEnd.String()) {
 			crtendFound = true
 		}
 	}
-	if !crtbeginFound || !crtendFound {
-		t.Error("should link with ndk_crtbegin_so.29 and ndk_crtend_so.29")
+	if !crtbeginFound {
+		t.Error(fmt.Sprintf(
+			"expected implicit with suffix %q, have the following implicits:\n%s",
+			expectedCrtBegin, strings.Join(implicits, "\n")))
+	}
+	if !crtendFound {
+		t.Error(fmt.Sprintf(
+			"expected implicit with suffix %q, have the following implicits:\n%s",
+			expectedCrtEnd, strings.Join(implicits, "\n")))
 	}
 }
 
@@ -2524,10 +2526,24 @@
 			sdk_version: "current",
 		}
 
+		java_sdk_library {
+			name: "runtime-library",
+			srcs: ["a.java"],
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "static-runtime-helper",
+			srcs: ["a.java"],
+			libs: ["runtime-library"],
+			sdk_version: "current",
+		}
+
 		android_app {
 			name: "app",
 			srcs: ["a.java"],
 			libs: ["qux", "quuz.stubs"],
+			static_libs: ["static-runtime-helper"],
 			uses_libs: ["foo"],
 			sdk_version: "current",
 			optional_uses_libs: [
@@ -2560,11 +2576,10 @@
 
 	// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
 	manifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-	if w := "--uses-library qux"; !strings.Contains(manifestFixerArgs, w) {
-		t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs)
-	}
-	if w := "--uses-library quuz"; !strings.Contains(manifestFixerArgs, w) {
-		t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs)
+	for _, w := range []string{"qux", "quuz", "runtime-library"} {
+		if !strings.Contains(manifestFixerArgs, "--uses-library "+w) {
+			t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs)
+		}
 	}
 
 	// Test that all libraries are verified
@@ -2866,6 +2881,7 @@
 		config := testAppConfig(nil, bp, nil)
 		if unbundled {
 			config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+			config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 		}
 
 		ctx := testContext()
diff --git a/java/config/config.go b/java/config/config.go
index d2f4513..05da3b5 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -128,7 +128,7 @@
 	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
 	pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
 		turbine := "turbine.jar"
-		if ctx.Config().UnbundledBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return "prebuilts/build-tools/common/framework/" + turbine
 		} else {
 			return ctx.Config().HostJavaToolPath(ctx, turbine).String()
@@ -148,9 +148,9 @@
 	pctx.HostBinToolVariable("DexpreoptGen", "dexpreopt_gen")
 
 	pctx.VariableFunc("REJavaPool", remoteexec.EnvOverrideFunc("RBE_JAVA_POOL", "java16"))
-	pctx.VariableFunc("REJavacExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAVAC_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
-	pctx.VariableFunc("RED8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_D8_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
-	pctx.VariableFunc("RER8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_R8_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REJavacExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAVAC_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
+	pctx.VariableFunc("RED8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_D8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
+	pctx.VariableFunc("RER8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_R8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
 	pctx.VariableFunc("RETurbineExecStrategy", remoteexec.EnvOverrideFunc("RBE_TURBINE_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("RESignApkExecStrategy", remoteexec.EnvOverrideFunc("RBE_SIGNAPK_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("REJarExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAR_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
@@ -163,7 +163,7 @@
 
 	pctx.HostBinToolVariable("ManifestMergerCmd", "manifest-merger")
 
-	pctx.HostBinToolVariable("Class2Greylist", "class2greylist")
+	pctx.HostBinToolVariable("Class2NonSdkList", "class2nonsdklist")
 	pctx.HostBinToolVariable("HiddenAPI", "hiddenapi")
 
 	hostBinToolVariableWithSdkToolsPrebuilt("Aapt2Cmd", "aapt2")
@@ -178,7 +178,7 @@
 
 func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin", tool)
 		} else {
 			return ctx.Config().HostToolPath(ctx, tool).String()
@@ -188,7 +188,7 @@
 
 func hostJavaToolVariableWithSdkToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return filepath.Join("prebuilts/sdk/tools/lib", tool+".jar")
 		} else {
 			return ctx.Config().HostJavaToolPath(ctx, tool+".jar").String()
@@ -198,7 +198,7 @@
 
 func hostJNIToolVariableWithSdkToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			ext := ".so"
 			if runtime.GOOS == "darwin" {
 				ext = ".dylib"
@@ -212,7 +212,7 @@
 
 func hostBinToolVariableWithBuildToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
-		if ctx.Config().UnbundledBuild() || ctx.Config().IsPdkBuild() {
+		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return filepath.Join("prebuilts/build-tools", ctx.Config().PrebuiltOS(), "bin", tool)
 		} else {
 			return ctx.Config().HostToolPath(ctx, tool).String()
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 708a72a..df447a1 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -75,7 +75,7 @@
 
 	ctx.Strict("ANDROID_MANIFEST_MERGER", "${ManifestMergerCmd}")
 
-	ctx.Strict("CLASS2GREYLIST", "${Class2Greylist}")
+	ctx.Strict("CLASS2NONSDKLIST", "${Class2NonSdkList}")
 	ctx.Strict("HIDDENAPI", "${HiddenAPI}")
 
 	ctx.Strict("DEX_FLAGS", "${DexFlags}")
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 9191a83..40a2280 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -19,6 +19,7 @@
 	"io"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 )
 
 type DeviceHostConverter struct {
@@ -162,7 +163,7 @@
 	return nil
 }
 
-func (d *DeviceHostConverter) ExportedSdkLibs() []string {
+func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.LibraryPaths {
 	return nil
 }
 
diff --git a/java/dex.go b/java/dex.go
index cd45a93..c85914c 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -72,6 +72,7 @@
 	// list of extra proguard flag files
 	extraProguardFlagFiles android.Paths
 	proguardDictionary     android.OptionalPath
+	proguardUsageZip       android.OptionalPath
 }
 
 func (d *dexer) effectiveOptimizeEnabled() bool {
@@ -109,13 +110,17 @@
 var r8, r8RE = remoteexec.MultiCommandStaticRules(pctx, "r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`rm -f "$outDict" && ` +
+			`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
+			`mkdir -p $$(dirname ${outUsage}) && ` +
 			`$r8Template${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
 			`--force-proguard-compatibility ` +
 			`--no-data-resources ` +
-			`-printmapping $outDict ` +
+			`-printmapping ${outDict} ` +
+			`-printusage ${outUsage} ` +
 			`$r8Flags && ` +
-			`touch "$outDict" && ` +
+			`touch "${outDict}" "${outUsage}" && ` +
+			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
+			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
@@ -138,7 +143,15 @@
 			ExecStrategy: "${config.RER8ExecStrategy}",
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-	}, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
+		"$zipUsageTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "${outUsage}"},
+			OutputFiles:  []string{"${outUsageZip}"},
+			ExecStrategy: "${config.RER8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
+		"r8Flags", "zipFlags"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion sdkSpec) []string {
 	flags := d.dexProperties.Dxflags
@@ -259,26 +272,34 @@
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+		proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
+		proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
+			android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
+		proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
+		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		r8Flags, r8Deps := d.r8Flags(ctx, flags)
 		rule := r8
 		args := map[string]string{
-			"r8Flags":  strings.Join(append(commonFlags, r8Flags...), " "),
-			"zipFlags": zipFlags,
-			"outDict":  proguardDictionary.String(),
-			"outDir":   outDir.String(),
+			"r8Flags":     strings.Join(append(commonFlags, r8Flags...), " "),
+			"zipFlags":    zipFlags,
+			"outDict":     proguardDictionary.String(),
+			"outUsageDir": proguardUsageDir.String(),
+			"outUsage":    proguardUsage.String(),
+			"outUsageZip": proguardUsageZip.String(),
+			"outDir":      outDir.String(),
 		}
 		if ctx.Config().IsEnvTrue("RBE_R8") {
 			rule = r8RE
 			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
 		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:           rule,
-			Description:    "r8",
-			Output:         javalibJar,
-			ImplicitOutput: proguardDictionary,
-			Input:          classesJar,
-			Implicits:      r8Deps,
-			Args:           args,
+			Rule:            rule,
+			Description:     "r8",
+			Output:          javalibJar,
+			ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip},
+			Input:           classesJar,
+			Implicits:       r8Deps,
+			Args:            args,
 		})
 	} else {
 		d8Flags, d8Deps := d8Flags(flags)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 3abe782..3addc1a 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -262,7 +262,7 @@
 	apex, isApexModule := module.(android.ApexModule)
 	fromUpdatableApex := isApexModule && apex.Updatable()
 	if image.name == artBootImageName {
-		if isApexModule && strings.HasPrefix(apex.ApexName(), "com.android.art.") {
+		if isApexModule && len(apex.InApexes()) > 0 && allHavePrefix(apex.InApexes(), "com.android.art.") {
 			// ok: found the jar in the ART apex
 		} else if isApexModule && apex.IsForPlatform() && Bool(module.(*Library).deviceProperties.Hostdex) {
 			// exception (skip and continue): special "hostdex" platform variant
@@ -272,17 +272,17 @@
 			return -1, nil
 		} else if fromUpdatableApex {
 			// error: this jar is part of an updatable apex other than ART
-			ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the ART boot image", name, apex.ApexName())
+			ctx.Errorf("module %q from updatable apexes %q is not allowed in the ART boot image", name, apex.InApexes())
 		} else {
 			// error: this jar is part of the platform or a non-updatable apex
-			ctx.Errorf("module '%s' is not allowed in the ART boot image", name)
+			ctx.Errorf("module %q is not allowed in the ART boot image", name)
 		}
 	} else if image.name == frameworkBootImageName {
 		if !fromUpdatableApex {
 			// ok: this jar is part of the platform or a non-updatable apex
 		} else {
 			// error: this jar is part of an updatable apex
-			ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the framework boot image", name, apex.ApexName())
+			ctx.Errorf("module %q from updatable apexes %q is not allowed in the framework boot image", name, apex.InApexes())
 		}
 	} else {
 		panic("unknown boot image: " + image.name)
@@ -291,6 +291,15 @@
 	return index, jar.DexJarBuildPath()
 }
 
+func allHavePrefix(list []string, prefix string) bool {
+	for _, s := range list {
+		if !strings.HasPrefix(s, prefix) {
+			return false
+		}
+	}
+	return true
+}
+
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
 func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
 	// Collect dex jar paths for the boot image modules.
@@ -505,7 +514,7 @@
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
 		return nil
 	}
 	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
@@ -560,7 +569,7 @@
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
 		return nil
 	}
 	return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
@@ -602,7 +611,7 @@
 var bootFrameworkProfileRuleKey = android.NewOnceKey("bootFrameworkProfileRule")
 
 func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConfig, missingDeps []string) android.WritablePath {
-	if ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+	if ctx.Config().UnbundledBuild() {
 		return nil
 	}
 
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 0840d50..4c5f66c 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1081,9 +1081,7 @@
 
 	rule.Build(pctx, ctx, "javadoc", desc)
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
-		!ctx.Config().IsPdkBuild() {
-
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
 		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
 		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
 
@@ -1150,9 +1148,7 @@
 		rule.Build(pctx, ctx, "doclavaCurrentApiUpdate", "update current API")
 	}
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
-		!ctx.Config().IsPdkBuild() {
-
+	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
 		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
 		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
 
@@ -1444,7 +1440,7 @@
 }
 
 func (d *Droidstubs) apiToXmlFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() && d.apiFile != nil {
+	if Bool(d.properties.Jdiff_enabled) && d.apiFile != nil {
 		if d.apiFile.String() == "" {
 			ctx.ModuleErrorf("API signature file has to be specified in Metalava when jdiff is enabled.")
 		}
@@ -1592,7 +1588,7 @@
 
 	// Add API lint options.
 
-	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().IsPdkBuild() {
+	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
 		doApiLint = true
 
 		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
@@ -1650,8 +1646,7 @@
 
 	// Add "check released" options. (Detect incompatible API changes from the last public release)
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
-		!ctx.Config().IsPdkBuild() {
+	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
 		doCheckReleased = true
 
 		if len(d.Javadoc.properties.Out) > 0 {
@@ -1728,8 +1723,7 @@
 
 	rule.Build(pctx, ctx, "metalava", "metalava merged")
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
-		!ctx.Config().IsPdkBuild() {
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
 
 		if len(d.Javadoc.properties.Out) > 0 {
 			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
@@ -1843,7 +1837,7 @@
 		rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
 	}
 
-	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
+	if Bool(d.properties.Jdiff_enabled) {
 		if len(d.Javadoc.properties.Out) > 0 {
 			ctx.PropertyErrorf("out", "out property may not be combined with jdiff")
 		}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index b5a0217..63b801a 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -23,8 +23,8 @@
 )
 
 var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", blueprint.RuleParams{
-	Command:     "${config.Class2Greylist} --stub-api-flags ${stubAPIFlags} $in $outFlag $out",
-	CommandDeps: []string{"${config.Class2Greylist}"},
+	Command:     "${config.Class2NonSdkList} --stub-api-flags ${stubAPIFlags} $in $outFlag $out",
+	CommandDeps: []string{"${config.Class2NonSdkList}"},
 }, "outFlag", "stubAPIFlags")
 
 type hiddenAPI struct {
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 1e6becb..ea3fbda 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -43,7 +43,7 @@
 		return hiddenAPISingletonPathsStruct{
 			flags:     android.PathForOutput(ctx, "hiddenapi", "hiddenapi-flags.csv"),
 			index:     android.PathForOutput(ctx, "hiddenapi", "hiddenapi-index.csv"),
-			metadata:  android.PathForOutput(ctx, "hiddenapi", "hiddenapi-greylist.csv"),
+			metadata:  android.PathForOutput(ctx, "hiddenapi", "hiddenapi-unsupported.csv"),
 			stubFlags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-stub-flags.txt"),
 		}
 	}).(hiddenAPISingletonPathsStruct)
@@ -100,7 +100,7 @@
 	// Add the android.test.base to the set of stubs only if the android.test.base module is on
 	// the boot jars list as the runtime will only enforce hiddenapi access against modules on
 	// that list.
-	if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().AlwaysUsePrebuiltSdks() {
 		publicStubModules = append(publicStubModules, "android.test.base.stubs")
 	}
 
@@ -284,7 +284,7 @@
 	return outputPath
 }
 
-// metadataRule creates a rule to build hiddenapi-greylist.csv out of the metadata.csv files generated for boot image
+// metadataRule creates a rule to build hiddenapi-unsupported.csv out of the metadata.csv files generated for boot image
 // modules.
 func metadataRule(ctx android.SingletonContext) android.Path {
 	var metadataCSV android.Paths
diff --git a/java/java.go b/java/java.go
index d5375a5..4d7d568 100644
--- a/java/java.go
+++ b/java/java.go
@@ -29,6 +29,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java/config"
 	"android/soong/tradefed"
 )
@@ -411,8 +412,8 @@
 	// manifest file to use instead of properties.Manifest
 	overrideManifest android.OptionalPath
 
-	// list of SDK lib names that this java module is exporting
-	exportedSdkLibs []string
+	// map of SDK libs exported by this java module to their build and install paths
+	exportedSdkLibs dexpreopt.LibraryPaths
 
 	// list of plugins that this java module is exporting
 	exportedPluginJars android.Paths
@@ -488,14 +489,19 @@
 	ImplementationAndResourcesJars() android.Paths
 }
 
-type Dependency interface {
-	ApexDependency
-	ImplementationJars() android.Paths
-	ResourceJars() android.Paths
+// Provides build path and install path to DEX jars.
+type UsesLibraryDependency interface {
 	DexJarBuildPath() android.Path
 	DexJarInstallPath() android.Path
+}
+
+type Dependency interface {
+	ApexDependency
+	UsesLibraryDependency
+	ImplementationJars() android.Paths
+	ResourceJars() android.Paths
 	AidlIncludeDirs() android.Paths
-	ExportedSdkLibs() []string
+	ExportedSdkLibs() dexpreopt.LibraryPaths
 	ExportedPlugins() (android.Paths, []string)
 	SrcJarArgs() ([]string, android.Paths)
 	BaseModuleName() string
@@ -552,7 +558,6 @@
 	bootClasspathTag      = dependencyTag{name: "bootclasspath"}
 	systemModulesTag      = dependencyTag{name: "system modules"}
 	frameworkResTag       = dependencyTag{name: "framework-res"}
-	frameworkApkTag       = dependencyTag{name: "framework-apk"}
 	kotlinStdlibTag       = dependencyTag{name: "kotlin-stdlib"}
 	kotlinAnnotationsTag  = dependencyTag{name: "kotlin-annotations"}
 	proguardRaiseTag      = dependencyTag{name: "proguard-raise"}
@@ -693,12 +698,6 @@
 		if sdkDep.systemModules != "" {
 			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
 		}
-
-		if ctx.ModuleName() == "android_stubs_current" ||
-			ctx.ModuleName() == "android_system_stubs_current" ||
-			ctx.ModuleName() == "android_test_stubs_current" {
-			ctx.AddVariationDependencies(nil, frameworkApkTag, "framework-res")
-		}
 	}
 
 	syspropPublicStubs := syspropPublicStubs(ctx.Config())
@@ -973,12 +972,6 @@
 		}
 	}
 
-	// If this is a component library (stubs, etc.) for a java_sdk_library then
-	// add the name of that java_sdk_library to the exported sdk libs to make sure
-	// that, if necessary, a <uses-library> element for that java_sdk_library is
-	// added to the Android manifest.
-	j.exportedSdkLibs = append(j.exportedSdkLibs, j.OptionalImplicitSdkLibrary()...)
-
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -998,7 +991,7 @@
 			case libTag:
 				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
 				// names of sdk libs that are directly depended are exported
-				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.OptionalImplicitSdkLibrary()...)
+				j.exportedSdkLibs.AddLibraryPath(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath())
 			case staticLibTag:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
@@ -1009,7 +1002,7 @@
 			case libTag, instrumentationForTag:
 				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
 				// sdk lib names from dependencies are re-exported
-				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+				j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs())
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
 				pluginJars, pluginClasses := dep.ExportedPlugins()
 				addPlugins(&deps, pluginJars, pluginClasses...)
@@ -1021,7 +1014,7 @@
 				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
 				deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...)
 				// sdk lib names from dependencies are re-exported
-				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+				j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs())
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
 				pluginJars, pluginClasses := dep.ExportedPlugins()
 				addPlugins(&deps, pluginJars, pluginClasses...)
@@ -1048,18 +1041,6 @@
 				} else {
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
-			case frameworkApkTag:
-				if ctx.ModuleName() == "android_stubs_current" ||
-					ctx.ModuleName() == "android_system_stubs_current" ||
-					ctx.ModuleName() == "android_test_stubs_current" {
-					// framework stubs.jar need to depend on framework-res.apk, in order to pull the
-					// resource files out of there for aapt.
-					//
-					// Normally the package rule runs aapt, which includes the resource,
-					// but we're not running that in our package rule so just copy in the
-					// resource files here.
-					deps.staticResourceJars = append(deps.staticResourceJars, dep.(*AndroidApp).exportPackage)
-				}
 			case kotlinStdlibTag:
 				deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars()...)
 			case kotlinAnnotationsTag:
@@ -1096,8 +1077,6 @@
 		}
 	})
 
-	j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
-
 	return deps
 }
 
@@ -1680,7 +1659,7 @@
 		j.linter.compileSdkVersion = lintSDKVersionString(j.sdkVersion())
 		j.linter.javaLanguageLevel = flags.javaVersion.String()
 		j.linter.kotlinLanguageLevel = "1.3"
-		if j.ApexName() != "" && ctx.Config().UnbundledBuildApps() {
+		if j.ApexVariationName() != "" && ctx.Config().UnbundledBuildApps() {
 			j.linter.buildModuleReportZip = true
 		}
 		j.linter.lint(ctx)
@@ -1838,8 +1817,7 @@
 	return j.exportAidlIncludeDirs
 }
 
-func (j *Module) ExportedSdkLibs() []string {
-	// exportedSdkLibs is type []string
+func (j *Module) ExportedSdkLibs() dexpreopt.LibraryPaths {
 	return j.exportedSdkLibs
 }
 
@@ -1972,6 +1950,7 @@
 		j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
 	}
 	j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+	j.exportedSdkLibs = make(dexpreopt.LibraryPaths)
 	j.compile(ctx, nil)
 
 	// Collect the module directory for IDE info in java/jdeps.go.
@@ -1987,6 +1966,12 @@
 			j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
 
+	// If this is a component library (stubs, etc.) for a java_sdk_library then
+	// add the name of that java_sdk_library to the exported sdk libs to make sure
+	// that, if necessary, a <uses-library> element for that java_sdk_library is
+	// added to the Android manifest.
+	j.exportedSdkLibs.AddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath())
+
 	j.distFiles = j.GenerateTaggedDistFiles(ctx)
 }
 
@@ -2139,6 +2124,12 @@
 // Java Tests
 //
 
+// Test option struct.
+type TestOptions struct {
+	// a list of extra test configuration files that should be installed with the module.
+	Extra_test_configs []string `android:"path,arch_variant"`
+}
+
 type testProperties struct {
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
@@ -2164,6 +2155,9 @@
 	// Add parameterized mainline modules to auto generated test config. The options will be
 	// handled by TradeFed to do downloading and installing the specified modules on the device.
 	Test_mainline_modules []string
+
+	// Test options.
+	Test_options TestOptions
 }
 
 type hostTestProperties struct {
@@ -2192,8 +2186,9 @@
 
 	testProperties testProperties
 
-	testConfig android.Path
-	data       android.Paths
+	testConfig       android.Path
+	extraTestConfigs android.Paths
+	data             android.Paths
 }
 
 type TestHost struct {
@@ -2232,6 +2227,8 @@
 
 	j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
 
+	j.extraTestConfigs = android.PathsForModuleSrc(ctx, j.testProperties.Test_options.Extra_test_configs)
+
 	ctx.VisitDirectDepsWithTag(dataNativeBinsTag, func(dep android.Module) {
 		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
 	})
@@ -2537,7 +2534,7 @@
 	properties ImportProperties
 
 	combinedClasspathFile android.Path
-	exportedSdkLibs       []string
+	exportedSdkLibs       dexpreopt.LibraryPaths
 	exportAidlIncludeDirs android.Paths
 }
 
@@ -2545,6 +2542,10 @@
 	return sdkSpecFrom(String(j.properties.Sdk_version))
 }
 
+func (j *Import) makeSdkVersion() string {
+	return j.sdkVersion().raw
+}
+
 func (j *Import) minSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
@@ -2590,12 +2591,7 @@
 		TransformJetifier(ctx, outputFile, inputFile)
 	}
 	j.combinedClasspathFile = outputFile
-
-	// If this is a component library (impl, stubs, etc.) for a java_sdk_library then
-	// add the name of that java_sdk_library to the exported sdk libs to make sure
-	// that, if necessary, a <uses-library> element for that java_sdk_library is
-	// added to the Android manifest.
-	j.exportedSdkLibs = append(j.exportedSdkLibs, j.OptionalImplicitSdkLibrary()...)
+	j.exportedSdkLibs = make(dexpreopt.LibraryPaths)
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
@@ -2606,23 +2602,29 @@
 			switch tag {
 			case libTag, staticLibTag:
 				// sdk lib names from dependencies are re-exported
-				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+				j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs())
 			}
 		case SdkLibraryDependency:
 			switch tag {
 			case libTag:
 				// names of sdk libs that are directly depended are exported
-				j.exportedSdkLibs = append(j.exportedSdkLibs, otherName)
+				j.exportedSdkLibs.AddLibraryPath(ctx, &otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath())
 			}
 		}
 	})
 
-	j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
+	var installFile android.Path
 	if Bool(j.properties.Installable) {
-		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
+		installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			jarName, outputFile)
 	}
 
+	// If this is a component library (impl, stubs, etc.) for a java_sdk_library then
+	// add the name of that java_sdk_library to the exported sdk libs to make sure
+	// that, if necessary, a <uses-library> element for that java_sdk_library is
+	// added to the Android manifest.
+	j.exportedSdkLibs.AddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile)
+
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 }
 
@@ -2665,7 +2667,7 @@
 	return j.exportAidlIncludeDirs
 }
 
-func (j *Import) ExportedSdkLibs() []string {
+func (j *Import) ExportedSdkLibs() dexpreopt.LibraryPaths {
 	return j.exportedSdkLibs
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index 50c40c3..0e93611 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -20,7 +20,6 @@
 	"path/filepath"
 	"reflect"
 	"regexp"
-	"sort"
 	"strconv"
 	"strings"
 	"testing"
@@ -1496,8 +1495,7 @@
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := ctx.ModuleForTests("qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
-		sdkLibs := quxLib.ExportedSdkLibs()
-		sort.Strings(sdkLibs)
+		sdkLibs := android.SortedStringKeys(quxLib.ExportedSdkLibs())
 		if w := []string{"bar", "foo", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) {
 			t.Errorf("qux should export %q but exports %q", w, sdkLibs)
 		}
diff --git a/java/lint.go b/java/lint.go
index 1bf7f69..3a210cc 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -309,7 +309,7 @@
 	rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
 
 	var annotationsZipPath, apiVersionsXMLPath android.Path
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
 		apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
 	} else {
@@ -319,7 +319,7 @@
 
 	cmd := rule.Command().
 		Text("(").
-		Flag("JAVA_OPTS=-Xmx2048m").
+		Flag("JAVA_OPTS=-Xmx3072m").
 		FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
 		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
 		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath).
@@ -395,7 +395,7 @@
 }
 
 func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		return
 	}
 
@@ -513,7 +513,7 @@
 	rule.Command().BuiltTool(ctx, "soong_zip").
 		FlagWithOutput("-o ", outputPath).
 		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
-		FlagWithRspFileInputList("-l ", paths)
+		FlagWithRspFileInputList("-r ", paths)
 
 	rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
 }
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index b10e6c7..ac8337d 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -34,6 +34,10 @@
 type prebuiltApisProperties struct {
 	// list of api version directories
 	Api_dirs []string
+
+	// The sdk_version of java_import modules generated based on jar files.
+	// Defaults to "current"
+	Imports_sdk_version *string
 }
 
 type prebuiltApis struct {
@@ -74,7 +78,7 @@
 	return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module
 }
 
-func createImport(mctx android.LoadHookContext, module string, scope string, apiver string, path string) {
+func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdk_version string) {
 	props := struct {
 		Name        *string
 		Jars        []string
@@ -83,7 +87,7 @@
 	}{}
 	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
 	props.Jars = append(props.Jars, path)
-	props.Sdk_version = proptools.StringPtr(scope)
+	props.Sdk_version = proptools.StringPtr(sdk_version)
 	props.Installable = proptools.BoolPtr(false)
 
 	mctx.CreateModule(ImportFactory, &props)
@@ -100,10 +104,10 @@
 	mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
 }
 
-func getPrebuiltFiles(mctx android.LoadHookContext, name string) []string {
+func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string {
 	mydir := mctx.ModuleDir() + "/"
 	var files []string
-	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
+	for _, apiver := range p.properties.Api_dirs {
 		for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
 			vfiles, err := mctx.GlobWithDeps(mydir+apiver+"/"+scope+"/"+name, nil)
 			if err != nil {
@@ -115,16 +119,18 @@
 	return files
 }
 
-func prebuiltSdkStubs(mctx android.LoadHookContext) {
+func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/<module>.jar
-	files := getPrebuiltFiles(mctx, "*.jar")
+	files := getPrebuiltFiles(mctx, p, "*.jar")
+
+	sdk_version := proptools.StringDefault(p.properties.Imports_sdk_version, "current")
 
 	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)
+		createImport(mctx, module, scope, apiver, localPath, sdk_version)
 	}
 }
 
@@ -139,8 +145,8 @@
 	mctx.CreateModule(SystemModulesFactory, &props)
 }
 
-func prebuiltSdkSystemModules(mctx android.LoadHookContext) {
-	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
+func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) {
+	for _, apiver := range p.properties.Api_dirs {
 		jar := android.ExistentPathForSource(mctx,
 			mctx.ModuleDir(), apiver, "public", "core-for-system-modules.jar")
 		if jar.Valid() {
@@ -149,10 +155,10 @@
 	}
 }
 
-func prebuiltApiFiles(mctx android.LoadHookContext) {
+func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/api/<module>.txt
-	files := getPrebuiltFiles(mctx, "api/*.txt")
+	files := getPrebuiltFiles(mctx, p, "api/*.txt")
 
 	if len(files) == 0 {
 		mctx.ModuleErrorf("no api file found under %q", mydir)
@@ -200,10 +206,10 @@
 }
 
 func createPrebuiltApiModules(mctx android.LoadHookContext) {
-	if _, ok := mctx.Module().(*prebuiltApis); ok {
-		prebuiltApiFiles(mctx)
-		prebuiltSdkStubs(mctx)
-		prebuiltSdkSystemModules(mctx)
+	if p, ok := mctx.Module().(*prebuiltApis); ok {
+		prebuiltApiFiles(mctx, p)
+		prebuiltSdkStubs(mctx, p)
+		prebuiltSdkSystemModules(mctx, p)
 	}
 }
 
diff --git a/java/robolectric.go b/java/robolectric.go
index 4d68fd9..3fe6626 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -357,7 +357,7 @@
 var _ android.TestSuiteModule = (*robolectricRuntimes)(nil)
 
 func (r *robolectricRuntimes) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() && r.props.Lib != nil {
+	if !ctx.Config().AlwaysUsePrebuiltSdks() && r.props.Lib != nil {
 		ctx.AddVariationDependencies(nil, libTag, String(r.props.Lib))
 	}
 }
@@ -371,8 +371,16 @@
 		r.runtimes = append(r.runtimes, installedRuntime)
 	}
 
-	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() && r.props.Lib != nil {
+	if !ctx.Config().AlwaysUsePrebuiltSdks() && r.props.Lib != nil {
 		runtimeFromSourceModule := ctx.GetDirectDepWithTag(String(r.props.Lib), libTag)
+		if runtimeFromSourceModule == nil {
+			if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{String(r.props.Lib)})
+			} else {
+				ctx.PropertyErrorf("lib", "missing dependency %q", String(r.props.Lib))
+			}
+			return
+		}
 		runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "")
 
 		runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar",
diff --git a/java/sdk.go b/java/sdk.go
index 5d79d1d..b44cd8e1 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -53,7 +53,7 @@
 
 func UseApiFingerprint(ctx android.BaseModuleContext) bool {
 	if ctx.Config().UnbundledBuild() &&
-		!ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
+		!ctx.Config().AlwaysUsePrebuiltSdks() &&
 		ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
 		return true
 	}
@@ -191,28 +191,11 @@
 	return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
 }
 
-// forPdkBuild converts this sdkSpec into another sdkSpec that is for the PDK builds.
-func (s sdkSpec) forPdkBuild(ctx android.EarlyModuleContext) sdkSpec {
-	// For PDK builds, use the latest SDK version instead of "current" or ""
-	if s.kind == sdkPrivate || s.kind == sdkPublic {
-		kind := s.kind
-		if kind == sdkPrivate {
-			// We don't have prebuilt SDK for private APIs, so use the public SDK
-			// instead. This looks odd, but that's how it has been done.
-			// TODO(b/148271073): investigate the need for this.
-			kind = sdkPublic
-		}
-		version := sdkVersion(LatestSdkVersionInt(ctx))
-		return sdkSpec{kind, version, s.raw}
-	}
-	return s
-}
-
 // usePrebuilt determines whether prebuilt SDK should be used for this sdkSpec with the given context.
 func (s sdkSpec) usePrebuilt(ctx android.EarlyModuleContext) bool {
 	if s.version.isCurrent() {
 		// "current" can be built from source and be from prebuilt SDK
-		return ctx.Config().UnbundledBuildUsePrebuiltSdks()
+		return ctx.Config().AlwaysUsePrebuiltSdks()
 	} else if s.version.isNumbered() {
 		// validation check
 		if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest {
@@ -233,9 +216,6 @@
 	if !s.valid() {
 		return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
 	}
-	if ctx.Config().IsPdkBuild() {
-		s = s.forPdkBuild(ctx)
-	}
 	if s.version.isNumbered() {
 		return s.version, nil
 	}
@@ -350,9 +330,6 @@
 		return sdkDep{}
 	}
 
-	if ctx.Config().IsPdkBuild() {
-		sdkVersion = sdkVersion.forPdkBuild(ctx)
-	}
 	if !sdkVersion.validateSystemSdk(ctx) {
 		return sdkDep{}
 	}
@@ -511,7 +488,7 @@
 type sdkSingleton struct{}
 
 func (sdkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		return
 	}
 
@@ -631,10 +608,7 @@
 
 	if ctx.Config().PlatformSdkCodename() == "REL" {
 		cmd.Text("echo REL >").Output(out)
-	} else if ctx.Config().IsPdkBuild() {
-		// TODO: get this from the PDK artifacts?
-		cmd.Text("echo PDK >").Output(out)
-	} else if !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	} else if !ctx.Config().AlwaysUsePrebuiltSdks() {
 		in, err := ctx.GlobWithDeps("frameworks/base/api/*current.txt", nil)
 		if err != nil {
 			ctx.Errorf("error globbing API files: %s", err)
@@ -663,7 +637,7 @@
 }
 
 func sdkMakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		return
 	}
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 0379a31..a5db56c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -849,22 +849,20 @@
 }
 
 // to satisfy SdkLibraryComponentDependency
-func (e *EmbeddableSdkLibraryComponent) OptionalImplicitSdkLibrary() []string {
-	if e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack != nil {
-		return []string{*e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack}
-	}
-	return nil
+func (e *EmbeddableSdkLibraryComponent) OptionalImplicitSdkLibrary() *string {
+	return e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack
 }
 
 // Implemented by modules that are (or possibly could be) a component of a java_sdk_library
 // (including the java_sdk_library) itself.
 type SdkLibraryComponentDependency interface {
+	UsesLibraryDependency
+
 	// The optional name of the sdk library that should be implicitly added to the
 	// AndroidManifest of an app that contains code which references the sdk library.
 	//
-	// Returns an array containing 0 or 1 items rather than a *string to make it easier
-	// to append this to the list of exported sdk libraries.
-	OptionalImplicitSdkLibrary() []string
+	// Returns the name of the optional implicit SDK library or nil, if there isn't one.
+	OptionalImplicitSdkLibrary() *string
 }
 
 // Make sure that all the module types that are components of java_sdk_library/_import
@@ -1124,22 +1122,17 @@
 // Creates a static java library that has API stubs
 func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
 	props := struct {
-		Name              *string
-		Visibility        []string
-		Srcs              []string
-		Installable       *bool
-		Sdk_version       *string
-		System_modules    *string
-		Patch_module      *string
-		Libs              []string
-		Compile_dex       *bool
-		Java_version      *string
-		Product_variables struct {
-			Pdk struct {
-				Enabled *bool
-			}
-		}
-		Openjdk9 struct {
+		Name           *string
+		Visibility     []string
+		Srcs           []string
+		Installable    *bool
+		Sdk_version    *string
+		System_modules *string
+		Patch_module   *string
+		Libs           []string
+		Compile_dex    *bool
+		Java_version   *string
+		Openjdk9       struct {
 			Srcs       []string
 			Javacflags []string
 		}
@@ -1166,7 +1159,6 @@
 	props.Patch_module = module.properties.Patch_module
 	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
-	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
 	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
 	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
 	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
@@ -1382,22 +1374,22 @@
 	return android.Paths{jarPath.Path()}
 }
 
-// Get the apex name for module, "" if it is for platform.
-func getApexNameForModule(module android.Module) string {
+// Get the apex names for module, nil if it is for platform.
+func getApexNamesForModule(module android.Module) []string {
 	if apex, ok := module.(android.ApexModule); ok {
-		return apex.ApexName()
+		return apex.InApexes()
 	}
 
-	return ""
+	return nil
 }
 
-// Check to see if the other module is within the same named APEX as this module.
+// Check to see if the other module is within the same set of named APEXes as this module.
 //
 // If either this or the other module are on the platform then this will return
 // false.
-func withinSameApexAs(module android.ApexModule, other android.Module) bool {
-	name := module.ApexName()
-	return name != "" && getApexNameForModule(other) == name
+func withinSameApexesAs(module android.ApexModule, other android.Module) bool {
+	names := module.InApexes()
+	return len(names) > 0 && reflect.DeepEqual(names, getApexNamesForModule(other))
 }
 
 func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths {
@@ -1416,7 +1408,7 @@
 		// Only allow access to the implementation library in the following condition:
 		// * No sdk_version specified on the referencing module.
 		// * The referencing module is in the same apex as this.
-		if sdkVersion.kind == sdkPrivate || withinSameApexAs(module, ctx.Module()) {
+		if sdkVersion.kind == sdkPrivate || withinSameApexesAs(module, ctx.Module()) {
 			if headerJars {
 				return module.HeaderJars()
 			} else {
@@ -1799,7 +1791,7 @@
 func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHookContext) {
 
 	// If the build is configured to use prebuilts then force this to be preferred.
-	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
+	if mctx.Config().AlwaysUsePrebuiltSdks() {
 		module.prebuilt.ForcePrefer()
 	}
 
@@ -1955,7 +1947,7 @@
 	// For consistency with SdkLibrary make the implementation jar available to libraries that
 	// are within the same APEX.
 	implLibraryModule := module.implLibraryModule
-	if implLibraryModule != nil && withinSameApexAs(module, ctx.Module()) {
+	if implLibraryModule != nil && withinSameApexesAs(module, ctx.Module()) {
 		if headerJars {
 			return implLibraryModule.HeaderJars()
 		} else {
@@ -1978,7 +1970,7 @@
 	return module.sdkJars(ctx, sdkVersion, false)
 }
 
-// to satisfy apex.javaDependency interface
+// to satisfy SdkLibraryDependency interface
 func (module *SdkLibraryImport) DexJarBuildPath() android.Path {
 	if module.implLibraryModule == nil {
 		return nil
@@ -1987,6 +1979,15 @@
 	}
 }
 
+// to satisfy SdkLibraryDependency interface
+func (module *SdkLibraryImport) DexJarInstallPath() android.Path {
+	if module.implLibraryModule == nil {
+		return nil
+	} else {
+		return module.implLibraryModule.DexJarInstallPath()
+	}
+}
+
 // to satisfy apex.javaDependency interface
 func (module *SdkLibraryImport) JacocoReportClassesFile() android.Path {
 	if module.implLibraryModule == nil {
@@ -2062,6 +2063,12 @@
 	return module
 }
 
+func (module *sdkLibraryXml) UniqueApexVariations() bool {
+	// sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the
+	// mounted APEX, which contains the name of the APEX.
+	return true
+}
+
 // from android.PrebuiltEtcModule
 func (module *sdkLibraryXml) SubDir() string {
 	return "permissions"
@@ -2089,8 +2096,8 @@
 // File path to the runtime implementation library
 func (module *sdkLibraryXml) implPath() string {
 	implName := proptools.String(module.properties.Lib_name)
-	if apexName := module.ApexName(); apexName != "" {
-		// TODO(b/146468504): ApexName() is only a soong module name, not apex name.
+	if apexName := module.ApexVariationName(); apexName != "" {
+		// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
 		// In most cases, this works fine. But when apex_name is set or override_apex is used
 		// this can be wrong.
 		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, implName)
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 395da79..776069d 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -30,7 +30,6 @@
 	var classpathTestcases = []struct {
 		name       string
 		unbundled  bool
-		pdk        bool
 		moduleType string
 		host       android.OsClass
 		properties string
@@ -217,35 +216,6 @@
 		},
 
 		{
-			name:           "pdk default",
-			pdk:            true,
-			bootclasspath:  []string{`""`},
-			system:         "sdk_public_30_system_modules",
-			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
-		},
-		{
-			name:           "pdk current",
-			pdk:            true,
-			properties:     `sdk_version: "current",`,
-			bootclasspath:  []string{`""`},
-			system:         "sdk_public_30_system_modules",
-			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
-		},
-		{
-			name:           "pdk 29",
-			pdk:            true,
-			properties:     `sdk_version: "29",`,
-			bootclasspath:  []string{`""`},
-			system:         "sdk_public_30_system_modules",
-			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
-		},
-		{
 			name:           "module_current",
 			properties:     `sdk_version: "module_current",`,
 			bootclasspath:  []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
@@ -384,9 +354,7 @@
 				config := testConfig(nil, bpJava8, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
@@ -407,9 +375,7 @@
 				config := testConfig(nil, bp, nil)
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
@@ -433,9 +399,7 @@
 
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
@@ -451,9 +415,7 @@
 
 				if testcase.unbundled {
 					config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-				}
-				if testcase.pdk {
-					config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+					config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				}
 				ctx := testContext()
 				run(t, ctx, config)
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 5806017..fda0a25 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -26,18 +26,18 @@
 type AndroidMkContext interface {
 	Name() string
 	Target() android.Target
-	subAndroidMk(*android.AndroidMkData, interface{})
+	SubAndroidMk(*android.AndroidMkData, interface{})
 }
 
-type subAndroidMkProvider interface {
+type SubAndroidMkProvider interface {
 	AndroidMk(AndroidMkContext, *android.AndroidMkData)
 }
 
-func (mod *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
+func (mod *Module) SubAndroidMk(data *android.AndroidMkData, obj interface{}) {
 	if mod.subAndroidMkOnce == nil {
-		mod.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
+		mod.subAndroidMkOnce = make(map[SubAndroidMkProvider]bool)
 	}
-	if androidmk, ok := obj.(subAndroidMkProvider); ok {
+	if androidmk, ok := obj.(SubAndroidMkProvider); ok {
 		if !mod.subAndroidMkOnce[androidmk] {
 			mod.subAndroidMkOnce[androidmk] = true
 			androidmk.AndroidMk(mod, data)
@@ -77,10 +77,10 @@
 	}
 
 	if mod.compiler != nil && !mod.compiler.Disabled() {
-		mod.subAndroidMk(&ret, mod.compiler)
+		mod.SubAndroidMk(&ret, mod.compiler)
 	} else if mod.sourceProvider != nil {
 		// If the compiler is disabled, this is a SourceProvider.
-		mod.subAndroidMk(&ret, mod.sourceProvider)
+		mod.SubAndroidMk(&ret, mod.sourceProvider)
 	}
 	ret.SubName += mod.Properties.SubName
 
@@ -88,7 +88,7 @@
 }
 
 func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, binary.baseCompiler)
+	ctx.SubAndroidMk(ret, binary.baseCompiler)
 
 	if binary.distFile.Valid() {
 		ret.DistFiles = android.MakeDefaultDistFiles(binary.distFile.Path())
@@ -122,7 +122,7 @@
 }
 
 func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, library.baseCompiler)
+	ctx.SubAndroidMk(ret, library.baseCompiler)
 
 	if library.rlib() {
 		ret.Class = "RLIB_LIBRARIES"
@@ -150,7 +150,7 @@
 }
 
 func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, procMacro.baseCompiler)
+	ctx.SubAndroidMk(ret, procMacro.baseCompiler)
 
 	ret.Class = "PROC_MACRO_LIBRARIES"
 	if procMacro.distFile.Valid() {
@@ -159,8 +159,8 @@
 
 }
 
-func (sourceProvider *baseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	outFile := sourceProvider.outputFile
+func (sourceProvider *BaseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	outFile := sourceProvider.OutputFile
 	ret.Class = "ETC"
 	ret.OutputFile = android.OptionalPathForPath(outFile)
 	ret.SubName += sourceProvider.subName
@@ -173,7 +173,7 @@
 }
 
 func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	ctx.subAndroidMk(ret, bindgen.baseSourceProvider)
+	ctx.SubAndroidMk(ret, bindgen.BaseSourceProvider)
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 	})
diff --git a/rust/binary_test.go b/rust/binary_test.go
index ab2dae1..b9c8698 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -36,6 +36,11 @@
 	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Output("fizz-buzz")
 	fizzBuzzDynamic := ctx.ModuleForTests("fizz-buzz-dynamic", "linux_glibc_x86_64").Output("fizz-buzz-dynamic")
 
+	path := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module).HostToolPath()
+	if g, w := path.String(), "/host/linux-x86/bin/fizz-buzz"; !strings.Contains(g, w) {
+		t.Errorf("wrong host tool path, expected %q got %q", w, g)
+	}
+
 	// Do not compile binary modules with the --test flag.
 	flags := fizzBuzzDynamic.Args["rustcFlags"]
 	if strings.Contains(flags, "--test") {
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 74b2777..9b09e61 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -87,12 +87,12 @@
 }
 
 type bindgenDecorator struct {
-	*baseSourceProvider
+	*BaseSourceProvider
 
 	Properties BindgenProperties
 }
 
-func (b *bindgenDecorator) generateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
+func (b *bindgenDecorator) GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
 	ccToolchain := ccConfig.FindToolchain(ctx.Os(), ctx.Arch())
 
 	var cflags []string
@@ -137,7 +137,7 @@
 		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
 	}
 
-	outputFile := android.PathForModuleOut(ctx, b.baseSourceProvider.getStem(ctx)+".rs")
+	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
 	var cmd, cmdDesc string
 	if b.Properties.Custom_bindgen != "" {
@@ -161,12 +161,12 @@
 		},
 	})
 
-	b.baseSourceProvider.outputFile = outputFile
+	b.BaseSourceProvider.OutputFile = outputFile
 	return outputFile
 }
 
-func (b *bindgenDecorator) sourceProviderProps() []interface{} {
-	return append(b.baseSourceProvider.sourceProviderProps(),
+func (b *bindgenDecorator) SourceProviderProps() []interface{} {
+	return append(b.BaseSourceProvider.SourceProviderProps(),
 		&b.Properties)
 }
 
@@ -184,27 +184,18 @@
 }
 
 func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) {
-	module := newModule(hod, android.MultilibBoth)
-
 	bindgen := &bindgenDecorator{
-		baseSourceProvider: NewSourceProvider(),
+		BaseSourceProvider: NewSourceProvider(),
 		Properties:         BindgenProperties{},
 	}
 
-	_, library := NewRustLibrary(hod)
-	library.BuildOnlyRust()
-	library.setNoLint()
-	library.sourceProvider = bindgen
-
-	module.sourceProvider = bindgen
-	module.compiler = library
-	module.setClippy(false)
+	module := NewSourceProviderModule(hod, bindgen, false)
 
 	return module, bindgen
 }
 
-func (b *bindgenDecorator) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
-	deps = b.baseSourceProvider.sourceProviderDeps(ctx, deps)
+func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
+	deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
 		deps = bionicDeps(deps)
 	}
diff --git a/rust/clippy.go b/rust/clippy.go
index e1f567d..6f0ed7f 100644
--- a/rust/clippy.go
+++ b/rust/clippy.go
@@ -19,8 +19,14 @@
 )
 
 type ClippyProperties struct {
-	// whether to run clippy.
-	Clippy *bool
+	// name of the lint set that should be used to validate this module.
+	//
+	// Possible values are "default" (for using a sensible set of lints
+	// depending on the module's location), "android" (for the strictest
+	// lint set that applies to all Android platform code), "vendor" (for a
+	// relaxed set) and "none" (to disable the execution of clippy).  The
+	// default value is "default". See also the `lints` property.
+	Clippy_lints *string
 }
 
 type clippy struct {
@@ -32,10 +38,10 @@
 }
 
 func (c *clippy) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
-	if c.Properties.Clippy != nil && !*c.Properties.Clippy {
-		return flags, deps
+	enabled, lints, err := config.ClippyLintsForDir(ctx.ModuleDir(), c.Properties.Clippy_lints)
+	if err != nil {
+		ctx.PropertyErrorf("clippy_lints", err.Error())
 	}
-	enabled, lints := config.ClippyLintsForDir(ctx.ModuleDir())
 	flags.Clippy = enabled
 	flags.ClippyFlags = append(flags.ClippyFlags, lints)
 	return flags, deps
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index 3144173..7815aab 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -16,31 +16,77 @@
 
 import (
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestClippy(t *testing.T) {
-	ctx := testRust(t, `
+
+	bp := `
+		// foo uses the default value of clippy_lints
 		rust_library {
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
 		}
+		// bar forces the use of the "android" lint set
+		rust_library {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			clippy_lints: "android",
+		}
+		// foobar explicitly disable clippy
 		rust_library {
 			name: "libfoobar",
 			srcs: ["foo.rs"],
 			crate_name: "foobar",
-			clippy: false,
-		}`)
+			clippy_lints: "none",
+		}`
 
-	ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Output("libfoo.dylib.so")
-	fooClippy := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-	if fooClippy.Rule.String() != "android/soong/rust.clippy" {
-		t.Errorf("Clippy output (default) for libfoo was not generated: %+v", fooClippy)
+	bp = bp + GatherRequiredDepsForTest()
+
+	fs := map[string][]byte{
+		// Reuse the same blueprint file for subdirectories.
+		"external/Android.bp": []byte(bp),
+		"hardware/Android.bp": []byte(bp),
 	}
 
-	ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").Output("libfoobar.dylib.so")
-	foobarClippy := ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-	if foobarClippy.Rule != nil {
-		t.Errorf("Clippy output for libfoobar is not empty")
+	var clippyLintTests = []struct {
+		modulePath string
+		fooFlags   string
+	}{
+		{"", "${config.ClippyDefaultLints}"},
+		{"external/", ""},
+		{"hardware/", "${config.ClippyVendorLints}"},
+	}
+
+	for _, tc := range clippyLintTests {
+		t.Run("path="+tc.modulePath, func(t *testing.T) {
+
+			config := android.TestArchConfig(buildDir, nil, bp, fs)
+			ctx := CreateTestContext()
+			ctx.Register(config)
+			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
+			android.FailIfErrored(t, errs)
+			_, errs = ctx.PrepareBuildActions(config)
+			android.FailIfErrored(t, errs)
+
+			r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			if r.Args["clippyFlags"] != tc.fooFlags {
+				t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["clippyFlags"], tc.fooFlags)
+			}
+
+			r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			if r.Args["clippyFlags"] != "${config.ClippyDefaultLints}" {
+				t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["clippyFlags"], "${config.ClippyDefaultLints}")
+			}
+
+			r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			if r.Rule != nil {
+				t.Errorf("libfoobar is setup to use clippy when explicitly disabled: clippyFlags=%q", r.Args["clippyFlags"])
+			}
+
+		})
 	}
 }
diff --git a/rust/compiler.go b/rust/compiler.go
index 0274015..ef7fb8c 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -32,8 +32,8 @@
 	compiler.Properties.No_stdlibs = proptools.BoolPtr(true)
 }
 
-func (compiler *baseCompiler) setNoLint() {
-	compiler.Properties.No_lint = proptools.BoolPtr(true)
+func (compiler *baseCompiler) disableLints() {
+	compiler.Properties.Lints = proptools.StringPtr("none")
 }
 
 func NewBaseCompiler(dir, dir64 string, location installLocation) *baseCompiler {
@@ -58,8 +58,14 @@
 	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs)
 	Srcs []string `android:"path,arch_variant"`
 
-	// whether to suppress the standard lint flags - default to false
-	No_lint *bool
+	// name of the lint set that should be used to validate this module.
+	//
+	// Possible values are "default" (for using a sensible set of lints
+	// depending on the module's location), "android" (for the strictest
+	// lint set that applies to all Android platform code), "vendor" (for
+	// a relaxed set) and "none" (for ignoring all lint warnings and
+	// errors). The default value is "default".
+	Lints *string
 
 	// flags to pass to rustc
 	Flags []string `android:"path,arch_variant"`
@@ -159,9 +165,11 @@
 
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 
-	if !Bool(compiler.Properties.No_lint) {
-		flags.RustFlags = append(flags.RustFlags, config.RustcLintsForDir(ctx.ModuleDir()))
+	lintFlags, err := config.RustcLintsForDir(ctx.ModuleDir(), compiler.Properties.Lints)
+	if err != nil {
+		ctx.PropertyErrorf("lints", err.Error())
 	}
+	flags.RustFlags = append(flags.RustFlags, lintFlags)
 	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
 	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
 	flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index b853196..8b9fccc 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 // Test that feature flags are being correctly generated.
@@ -104,3 +106,74 @@
 		t.Fatalf("unexpected install path for binary: %#v", install_path_bin)
 	}
 }
+
+func TestLints(t *testing.T) {
+
+	bp := `
+		// foo uses the default value of lints
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		// bar forces the use of the "android" lint set
+		rust_library {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			lints: "android",
+		}
+		// foobar explicitly disable all lints
+		rust_library {
+			name: "libfoobar",
+			srcs: ["foo.rs"],
+			crate_name: "foobar",
+			lints: "none",
+		}`
+
+	bp = bp + GatherRequiredDepsForTest()
+
+	fs := map[string][]byte{
+		// Reuse the same blueprint file for subdirectories.
+		"external/Android.bp": []byte(bp),
+		"hardware/Android.bp": []byte(bp),
+	}
+
+	var lintTests = []struct {
+		modulePath string
+		fooFlags   string
+	}{
+		{"", "${config.RustDefaultLints}"},
+		{"external/", "${config.RustAllowAllLints}"},
+		{"hardware/", "${config.RustVendorLints}"},
+	}
+
+	for _, tc := range lintTests {
+		t.Run("path="+tc.modulePath, func(t *testing.T) {
+
+			config := android.TestArchConfig(buildDir, nil, bp, fs)
+			ctx := CreateTestContext()
+			ctx.Register(config)
+			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
+			android.FailIfErrored(t, errs)
+			_, errs = ctx.PrepareBuildActions(config)
+			android.FailIfErrored(t, errs)
+
+			r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			if !strings.Contains(r.Args["rustcFlags"], tc.fooFlags) {
+				t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["rustcFlags"], tc.fooFlags)
+			}
+
+			r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			if !strings.Contains(r.Args["rustcFlags"], "${config.RustDefaultLints}") {
+				t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["rustcFlags"], "${config.RustDefaultLints}")
+			}
+
+			r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			if !strings.Contains(r.Args["rustcFlags"], "${config.RustAllowAllLints}") {
+				t.Errorf("Incorrect flags for libfoobar: %q, want %q", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
+			}
+
+		})
+	}
+}
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 9c9a136..62d469e 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -6,8 +6,11 @@
 		"external/rust",
 		"external/crosvm",
 		"external/adhd",
+		"frameworks/native/libs/binder/rust",
 		"prebuilts/rust",
+		"system/extras/profcollectd",
 		"system/security",
+		"system/tools/aidl",
 	}
 
 	RustModuleTypes = []string{
diff --git a/rust/config/global.go b/rust/config/global.go
index 2020f46..97de676 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.44.0"
+	RustDefaultVersion = "1.45.2"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 529d094..06bb668 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -24,18 +25,21 @@
 // The Android build system tries to avoid reporting warnings during the build.
 // Therefore, by default, we upgrade warnings to denials. For some of these
 // lints, an allow exception is setup, using the variables below.
-// There are different default lints depending on the repository location (see
-// DefaultLocalClippyChecks).
+//
 // The lints are split into two categories. The first one contains the built-in
 // lints (https://doc.rust-lang.org/rustc/lints/index.html). The second is
 // specific to Clippy lints (https://rust-lang.github.io/rust-clippy/master/).
 //
-// When developing a module, it is possible to use the `no_lint` property to
-// disable the built-in lints configuration. It is also possible to set
-// `clippy` to false to disable the clippy verification. Expect some
-// questioning during review if you enable one of these options. For external/
-// code, if you need to use them, it is likely a bug. Otherwise, it may be
-// useful to add an exception (that is, move a lint from deny to allow).
+// For both categories, there are 3 levels of linting possible:
+// - "android", for the strictest lints that applies to all Android platform code.
+// - "vendor", for relaxed rules.
+// - "none", to disable the linting.
+// There is a fourth option ("default") which automatically selects the linting level
+// based on the module's location. See defaultLintSetForPath.
+//
+// When developing a module, you may set `lints = "none"` and `clippy_lints =
+// "none"` to disable all the linting. Expect some questioning during code review
+// if you enable one of these options.
 var (
 	// Default Rust lints that applies to Google-authored modules.
 	defaultRustcLints = []string{
@@ -102,13 +106,6 @@
 	pctx.StaticVariable("RustAllowAllLints", strings.Join(allowAllLints, " "))
 }
 
-type PathBasedClippyConfig struct {
-	PathPrefix    string
-	RustcConfig   string
-	ClippyEnabled bool
-	ClippyConfig  string
-}
-
 const noLint = ""
 const rustcDefault = "${config.RustDefaultLints}"
 const rustcVendor = "${config.RustVendorLints}"
@@ -116,35 +113,78 @@
 const clippyDefault = "${config.ClippyDefaultLints}"
 const clippyVendor = "${config.ClippyVendorLints}"
 
-// This is a map of local path prefixes to a set of parameters for the linting:
-// - a string for the lints to apply to rustc.
-// - a boolean to indicate if clippy should be executed.
-// - a string for the lints to apply to clippy.
-// The first entry matching will be used.
-var DefaultLocalClippyChecks = []PathBasedClippyConfig{
-	{"external/", rustcAllowAll, false, noLint},
-	{"hardware/", rustcVendor, true, clippyVendor},
-	{"prebuilts/", rustcAllowAll, false, noLint},
-	{"vendor/google", rustcDefault, true, clippyDefault},
-	{"vendor/", rustcVendor, true, clippyVendor},
+// lintConfig defines a set of lints and clippy configuration.
+type lintConfig struct {
+	rustcConfig   string // for the lints to apply to rustc.
+	clippyEnabled bool   // to indicate if clippy should be executed.
+	clippyConfig  string // for the lints to apply to clippy.
+}
+
+const (
+	androidLints = "android"
+	vendorLints  = "vendor"
+	noneLints    = "none"
+)
+
+// lintSets defines the categories of linting for Android and their mapping to lintConfigs.
+var lintSets = map[string]lintConfig{
+	androidLints: {rustcDefault, true, clippyDefault},
+	vendorLints:  {rustcVendor, true, clippyVendor},
+	noneLints:    {rustcAllowAll, false, noLint},
+}
+
+type pathLintSet struct {
+	prefix string
+	set    string
+}
+
+// This is a map of local path prefixes to a lint set.  The first entry
+// matching will be used. If no entry matches, androidLints ("android") will be
+// used.
+var defaultLintSetForPath = []pathLintSet{
+	{"external", noneLints},
+	{"hardware", vendorLints},
+	{"prebuilts", noneLints},
+	{"vendor/google", androidLints},
+	{"vendor", vendorLints},
 }
 
 // ClippyLintsForDir returns a boolean if Clippy should be executed and if so, the lints to be used.
-func ClippyLintsForDir(dir string) (bool, string) {
-	for _, pathCheck := range DefaultLocalClippyChecks {
-		if strings.HasPrefix(dir, pathCheck.PathPrefix) {
-			return pathCheck.ClippyEnabled, pathCheck.ClippyConfig
+func ClippyLintsForDir(dir string, clippyLintsProperty *string) (bool, string, error) {
+	if clippyLintsProperty != nil {
+		set, ok := lintSets[*clippyLintsProperty]
+		if ok {
+			return set.clippyEnabled, set.clippyConfig, nil
+		}
+		if *clippyLintsProperty != "default" {
+			return false, "", fmt.Errorf("unknown value for `clippy_lints`: %v, valid options are: default, android, vendor or none", *clippyLintsProperty)
 		}
 	}
-	return true, clippyDefault
+	for _, p := range defaultLintSetForPath {
+		if strings.HasPrefix(dir, p.prefix) {
+			setConfig := lintSets[p.set]
+			return setConfig.clippyEnabled, setConfig.clippyConfig, nil
+		}
+	}
+	return true, clippyDefault, nil
 }
 
 // RustcLintsForDir returns the standard lints to be used for a repository.
-func RustcLintsForDir(dir string) string {
-	for _, pathCheck := range DefaultLocalClippyChecks {
-		if strings.HasPrefix(dir, pathCheck.PathPrefix) {
-			return pathCheck.RustcConfig
+func RustcLintsForDir(dir string, lintProperty *string) (string, error) {
+	if lintProperty != nil {
+		set, ok := lintSets[*lintProperty]
+		if ok {
+			return set.rustcConfig, nil
+		}
+		if *lintProperty != "default" {
+			return "", fmt.Errorf("unknown value for `lints`: %v, valid options are: default, android, vendor or none", *lintProperty)
+		}
+
+	}
+	for _, p := range defaultLintSetForPath {
+		if strings.HasPrefix(dir, p.prefix) {
+			return lintSets[p.set].rustcConfig, nil
 		}
 	}
-	return rustcDefault
+	return rustcDefault, nil
 }
diff --git a/rust/project_json.go b/rust/project_json.go
index 41dd194..8310479 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -75,17 +75,16 @@
 	knownCrates map[string]crateInfo, module android.Module,
 	crate *rustProjectCrate, deps map[string]int) {
 
-	//TODO(tweek): The stdlib dependencies do not appear here. We need to manually add them.
 	ctx.VisitDirectDeps(module, func(child android.Module) {
-		childId, childName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
+		childId, childCrateName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
 		if !ok {
 			return
 		}
-		if _, ok = deps[childName]; ok {
+		if _, ok = deps[ctx.ModuleName(child)]; ok {
 			return
 		}
-		crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childName})
-		deps[childName] = childId
+		crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childCrateName})
+		deps[ctx.ModuleName(child)] = childId
 	})
 }
 
@@ -106,8 +105,9 @@
 	if !ok {
 		return 0, "", false
 	}
+	moduleName := ctx.ModuleName(module)
 	crateName := rModule.CrateName()
-	if cInfo, ok := knownCrates[crateName]; ok {
+	if cInfo, ok := knownCrates[moduleName]; ok {
 		// We have seen this crate already; merge any new dependencies.
 		crate := project.Crates[cInfo.ID]
 		mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps)
@@ -115,15 +115,18 @@
 		return cInfo.ID, crateName, true
 	}
 	crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)}
-	src := rustLib.baseCompiler.Properties.Srcs[0]
-	crate.RootModule = path.Join(ctx.ModuleDir(rModule), src)
+	srcs := rustLib.baseCompiler.Properties.Srcs
+	if len(srcs) == 0 {
+		return 0, "", false
+	}
+	crate.RootModule = path.Join(ctx.ModuleDir(rModule), srcs[0])
 	crate.Edition = rustLib.baseCompiler.edition()
 
 	deps := make(map[string]int)
 	mergeDependencies(ctx, project, knownCrates, module, &crate, deps)
 
 	id := len(project.Crates)
-	knownCrates[crateName] = crateInfo{ID: id, Deps: deps}
+	knownCrates[moduleName] = crateInfo{ID: id, Deps: deps}
 	project.Crates = append(project.Crates, crate)
 	// rust-analyzer requires that all crates belong to at least one root:
 	// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 6786e72..8521940 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -15,6 +15,7 @@
 package rust
 
 import (
+	"encoding/json"
 	"io/ioutil"
 	"path/filepath"
 	"testing"
@@ -23,20 +24,12 @@
 	"android/soong/cc"
 )
 
-func TestProjectJson(t *testing.T) {
-	bp := `rust_library {
-		  name: "liba",
-		  srcs: ["src/lib.rs"],
-		  crate_name: "a"
-		}` + GatherRequiredDepsForTest()
-	env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
-	fs := map[string][]byte{
-		"foo.rs":     nil,
-		"src/lib.rs": nil,
-	}
-
+// testProjectJson run the generation of rust-project.json. It returns the raw
+// content of the generated file.
+func testProjectJson(t *testing.T, bp string, fs map[string][]byte) []byte {
 	cc.GatherRequiredFilesForTest(fs)
 
+	env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
 	config := android.TestArchConfig(buildDir, env, bp, fs)
 	ctx := CreateTestContext()
 	ctx.Register(config)
@@ -48,8 +41,131 @@
 	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
 	// won't appear in the Output of the TestingSingleton. Manually verify
 	// it exists.
-	_, err := ioutil.ReadFile(filepath.Join(buildDir, "rust-project.json"))
+	content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
 	if err != nil {
 		t.Errorf("rust-project.json has not been generated")
 	}
+	return content
+}
+
+// validateJsonCrates validates that content follows the basic structure of
+// rust-project.json. It returns the crates attribute if the validation
+// succeeded.
+// It uses an empty interface instead of relying on a defined structure to
+// avoid a strong dependency on our implementation.
+func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} {
+	var content interface{}
+	err := json.Unmarshal(rawContent, &content)
+	if err != nil {
+		t.Errorf("Unable to parse the rust-project.json as JSON: %v", err)
+	}
+	root, ok := content.(map[string]interface{})
+	if !ok {
+		t.Errorf("Unexpected JSON format: %v", content)
+	}
+	if _, ok = root["crates"]; !ok {
+		t.Errorf("No crates attribute in rust-project.json: %v", root)
+	}
+	crates, ok := root["crates"].([]interface{})
+	if !ok {
+		t.Errorf("Unexpected crates format: %v", root["crates"])
+	}
+	return crates
+}
+
+func TestProjectJsonDep(t *testing.T) {
+	bp := `
+	rust_library {
+		name: "liba",
+		srcs: ["a/src/lib.rs"],
+		crate_name: "a"
+	}
+	rust_library {
+		name: "libb",
+		srcs: ["b/src/lib.rs"],
+		crate_name: "b",
+		rlibs: ["liba"],
+	}
+	` + GatherRequiredDepsForTest()
+	fs := map[string][]byte{
+		"a/src/lib.rs": nil,
+		"b/src/lib.rs": nil,
+	}
+	jsonContent := testProjectJson(t, bp, fs)
+	validateJsonCrates(t, jsonContent)
+}
+
+func TestProjectJsonBindGen(t *testing.T) {
+	bp := `
+	rust_library {
+		name: "liba",
+		srcs: ["src/lib.rs"],
+		rlibs: ["libbindings"],
+		crate_name: "a"
+	}
+	rust_bindgen {
+		name: "libbindings",
+		crate_name: "bindings",
+		source_stem: "bindings",
+		host_supported: true,
+		wrapper_src: "src/any.h",
+	}
+	` + GatherRequiredDepsForTest()
+	fs := map[string][]byte{
+		"src/lib.rs": nil,
+	}
+	jsonContent := testProjectJson(t, bp, fs)
+	validateJsonCrates(t, jsonContent)
+}
+
+func TestProjectJsonMultiVersion(t *testing.T) {
+	bp := `
+	rust_library {
+		name: "liba1",
+		srcs: ["a1/src/lib.rs"],
+		crate_name: "a"
+	}
+	rust_library {
+		name: "liba2",
+		srcs: ["a2/src/lib.rs"],
+		crate_name: "a",
+	}
+	rust_library {
+		name: "libb",
+		srcs: ["b/src/lib.rs"],
+		crate_name: "b",
+		rustlibs: ["liba1", "liba2"],
+	}
+	` + GatherRequiredDepsForTest()
+	fs := map[string][]byte{
+		"a1/src/lib.rs": nil,
+		"a2/src/lib.rs": nil,
+		"b/src/lib.rs":  nil,
+	}
+	jsonContent := testProjectJson(t, bp, fs)
+	crates := validateJsonCrates(t, jsonContent)
+	for _, crate := range crates {
+		c := crate.(map[string]interface{})
+		if c["root_module"] == "b/src/lib.rs" {
+			deps, ok := c["deps"].([]interface{})
+			if !ok {
+				t.Errorf("Unexpected format for deps: %v", c["deps"])
+			}
+			aCount := 0
+			for _, dep := range deps {
+				d, ok := dep.(map[string]interface{})
+				if !ok {
+					t.Errorf("Unexpected format for dep: %v", dep)
+				}
+				if d["name"] == "a" {
+					aCount++
+				}
+			}
+			if aCount != 2 {
+				t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
+			}
+			return
+		}
+	}
+	t.Errorf("libb crate has not been found: %v", crates)
 }
diff --git a/rust/rust.go b/rust/rust.go
index edfa5d8..b697869 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -83,7 +83,7 @@
 	clippy           *clippy
 	cachedToolchain  config.Toolchain
 	sourceProvider   SourceProvider
-	subAndroidMkOnce map[subAndroidMkProvider]bool
+	subAndroidMkOnce map[SubAndroidMkProvider]bool
 
 	outputFile    android.OptionalPath
 	generatedFile android.OptionalPath
@@ -92,7 +92,7 @@
 func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
 	case "":
-		if mod.sourceProvider != nil {
+		if mod.sourceProvider != nil && (mod.compiler == nil || mod.compiler.Disabled()) {
 			return mod.sourceProvider.Srcs(), nil
 		} else {
 			if mod.outputFile.Valid() {
@@ -537,7 +537,7 @@
 		mod.AddProperties(mod.clippy.props()...)
 	}
 	if mod.sourceProvider != nil {
-		mod.AddProperties(mod.sourceProvider.sourceProviderProps()...)
+		mod.AddProperties(mod.sourceProvider.SourceProviderProps()...)
 	}
 
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
@@ -671,10 +671,10 @@
 		flags, deps = mod.clippy.flags(ctx, flags, deps)
 	}
 
-	// SourceProvider needs to call generateSource() before compiler calls compile() so it can provide the source.
+	// SourceProvider needs to call GenerateSource() before compiler calls compile() so it can provide the source.
 	// TODO(b/162588681) This shouldn't have to run for every variant.
 	if mod.sourceProvider != nil {
-		generatedFile := mod.sourceProvider.generateSource(ctx, deps)
+		generatedFile := mod.sourceProvider.GenerateSource(ctx, deps)
 		mod.generatedFile = android.OptionalPathForPath(generatedFile)
 		mod.sourceProvider.setSubName(ctx.ModuleSubDir())
 	}
@@ -696,7 +696,7 @@
 		deps = mod.compiler.compilerDeps(ctx, deps)
 	}
 	if mod.sourceProvider != nil {
-		deps = mod.sourceProvider.sourceProviderDeps(ctx, deps)
+		deps = mod.sourceProvider.SourceProviderDeps(ctx, deps)
 	}
 
 	if mod.coverage != nil {
@@ -807,8 +807,8 @@
 				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
 			}
 
-			//Append the dependencies exportedDirs
-			if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok {
+			//Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
+			if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok && depTag != procMacroDepTag {
 				depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedLinkDirs()...)
 				depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
 			}
@@ -1003,11 +1003,12 @@
 		blueprint.Variation{Mutator: "link", Variation: "static"}),
 		cc.StaticDepTag(), deps.StaticLibs...)
 
+	crtVariations := append(cc.GetCrtVariations(ctx, mod), commonDepVariations...)
 	if deps.CrtBegin != "" {
-		actx.AddVariationDependencies(commonDepVariations, cc.CrtBeginDepTag, deps.CrtBegin)
+		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag, deps.CrtBegin)
 	}
 	if deps.CrtEnd != "" {
-		actx.AddVariationDependencies(commonDepVariations, cc.CrtEndDepTag, deps.CrtEnd)
+		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag, deps.CrtEnd)
 	}
 
 	if mod.sourceProvider != nil {
@@ -1045,9 +1046,9 @@
 	return name
 }
 
-func (mod *Module) setClippy(clippy bool) {
+func (mod *Module) disableClippy() {
 	if mod.clippy != nil {
-		mod.clippy.Properties.Clippy = proptools.BoolPtr(clippy)
+		mod.clippy.Properties.Clippy_lints = proptools.StringPtr("none")
 	}
 }
 
@@ -1057,8 +1058,8 @@
 	if !mod.Host() {
 		return android.OptionalPath{}
 	}
-	if _, ok := mod.compiler.(*binaryDecorator); ok {
-		return mod.outputFile
+	if binary, ok := mod.compiler.(*binaryDecorator); ok {
+		return android.OptionalPathForPath(binary.baseCompiler.path)
 	}
 	return android.OptionalPath{}
 }
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 503880f..e168718 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -27,43 +27,60 @@
 	Crate_name string `android:"arch_variant"`
 }
 
-type baseSourceProvider struct {
+type BaseSourceProvider struct {
 	Properties SourceProviderProperties
 
-	outputFile       android.Path
-	subAndroidMkOnce map[subAndroidMkProvider]bool
+	OutputFile       android.Path
+	subAndroidMkOnce map[SubAndroidMkProvider]bool
 	subName          string
 }
 
-var _ SourceProvider = (*baseSourceProvider)(nil)
+var _ SourceProvider = (*BaseSourceProvider)(nil)
 
 type SourceProvider interface {
-	generateSource(ctx android.ModuleContext, deps PathDeps) android.Path
+	GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path
 	Srcs() android.Paths
-	sourceProviderProps() []interface{}
-	sourceProviderDeps(ctx DepsContext, deps Deps) Deps
+	SourceProviderProps() []interface{}
+	SourceProviderDeps(ctx DepsContext, deps Deps) Deps
 	setSubName(subName string)
 }
 
-func (sp *baseSourceProvider) Srcs() android.Paths {
-	return android.Paths{sp.outputFile}
+func (sp *BaseSourceProvider) Srcs() android.Paths {
+	return android.Paths{sp.OutputFile}
 }
 
-func (sp *baseSourceProvider) generateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
-	panic("baseSourceProviderModule does not implement generateSource()")
+func (sp *BaseSourceProvider) GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
+	panic("BaseSourceProviderModule does not implement GenerateSource()")
 }
 
-func (sp *baseSourceProvider) sourceProviderProps() []interface{} {
+func (sp *BaseSourceProvider) SourceProviderProps() []interface{} {
 	return []interface{}{&sp.Properties}
 }
 
-func NewSourceProvider() *baseSourceProvider {
-	return &baseSourceProvider{
+func NewSourceProvider() *BaseSourceProvider {
+	return &BaseSourceProvider{
 		Properties: SourceProviderProperties{},
 	}
 }
 
-func (sp *baseSourceProvider) getStem(ctx android.ModuleContext) string {
+func NewSourceProviderModule(hod android.HostOrDeviceSupported, sourceProvider SourceProvider, enableLints bool) *Module {
+	_, library := NewRustLibrary(hod)
+	library.BuildOnlyRust()
+	library.sourceProvider = sourceProvider
+
+	module := newModule(hod, android.MultilibBoth)
+	module.sourceProvider = sourceProvider
+	module.compiler = library
+
+	if !enableLints {
+		library.disableLints()
+		module.disableClippy()
+	}
+
+	return module
+}
+
+func (sp *BaseSourceProvider) getStem(ctx android.ModuleContext) string {
 	if String(sp.Properties.Source_stem) == "" {
 		ctx.PropertyErrorf("source_stem",
 			"source_stem property is undefined but required for rust_bindgen modules")
@@ -71,10 +88,10 @@
 	return String(sp.Properties.Source_stem)
 }
 
-func (sp *baseSourceProvider) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
+func (sp *BaseSourceProvider) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	return deps
 }
 
-func (sp *baseSourceProvider) setSubName(subName string) {
+func (sp *BaseSourceProvider) setSubName(subName string) {
 	sp.subName = subName
 }
diff --git a/rust/testing.go b/rust/testing.go
index 83b2828..80e4148 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -44,7 +44,28 @@
                                 },
 				host_supported: true,
 		}
-
+		rust_prebuilt_library {
+				name: "libstd_x86_64-apple-darwin",
+                                crate_name: "std",
+                                rlib: {
+                                    srcs: ["libstd.rlib"],
+                                },
+                                dylib: {
+                                    srcs: ["libstd.so"],
+                                },
+				host_supported: true,
+		}
+		rust_prebuilt_library {
+				name: "libtest_x86_64-apple-darwin",
+                                crate_name: "test",
+                                rlib: {
+                                    srcs: ["libtest.rlib"],
+                                },
+                                dylib: {
+                                    srcs: ["libtest.so"],
+                                },
+				host_supported: true,
+		}
 		//////////////////////////////
 		// Device module requirements
 
@@ -78,6 +99,7 @@
 func CreateTestContext() *android.TestContext {
 	ctx := android.NewTestArchContext()
 	android.RegisterPrebuiltMutators(ctx)
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index c60eaa1..ef5565d 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -84,6 +84,8 @@
     "CrossHostArch": "x86_64",
     "Aml_abis": true,
 
+    "Allow_missing_dependencies": ${SOONG_ALLOW_MISSING_DEPENDENCIES:-false},
+    "Unbundled_build": ${TARGET_BUILD_UNBUNDLED:-false},
     "UseGoma": ${USE_GOMA}
 }
 EOF
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 9501d88..84b905c 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -265,11 +265,11 @@
 		}
 	`)
 
-	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_shared_myapex").Rule("toc").Output
-	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_shared_myapex2").Rule("toc").Output
+	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_shared_apex10000_mysdk_1").Rule("toc").Output
+	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_shared_apex10000_mysdk_2").Rule("toc").Output
 
-	cpplibForMyApex := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_myapex")
-	cpplibForMyApex2 := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_myapex2")
+	cpplibForMyApex := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_apex10000_mysdk_1")
+	cpplibForMyApex2 := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_apex10000_mysdk_2")
 
 	// Depending on the uses_sdks value, different libs are linked
 	ensureListContains(t, pathsToStrings(cpplibForMyApex.Rule("ld").Implicits), sdkMemberV1.String())
@@ -340,6 +340,9 @@
 		cc_object {
 			name: "crtobj",
 			stl: "none",
+			sanitize: {
+				never: true,
+			},
 		}
 	`)
 
@@ -352,6 +355,9 @@
     sdk_member_name: "crtobj",
     stl: "none",
     compile_multilib: "both",
+    sanitize: {
+        never: true,
+    },
     arch: {
         arm64: {
             srcs: ["arm64/lib/crtobj.o"],
@@ -367,6 +373,9 @@
     prefer: false,
     stl: "none",
     compile_multilib: "both",
+    sanitize: {
+        never: true,
+    },
     arch: {
         arm64: {
             srcs: ["arm64/lib/crtobj.o"],
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 931ca3c..5911c71 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -207,8 +207,8 @@
 	sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common").Rule("combineJar").Output
 	sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common").Rule("combineJar").Output
 
-	javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_myapex")
-	javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_myapex2")
+	javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_1")
+	javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_2")
 
 	// Depending on the uses_sdks value, different libs are linked
 	ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
diff --git a/sdk/update.go b/sdk/update.go
index 936696a..537ab13 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -43,7 +43,7 @@
 
 	zipFiles = pctx.AndroidStaticRule("SnapshotZipFiles",
 		blueprint.RuleParams{
-			Command: `${config.SoongZipCmd} -C $basedir -l $out.rsp -o $out`,
+			Command: `${config.SoongZipCmd} -C $basedir -r $out.rsp -o $out`,
 			CommandDeps: []string{
 				"${config.SoongZipCmd}",
 			},
diff --git a/sh/Android.bp b/sh/Android.bp
index 0f40c5f..e5ffeef 100644
--- a/sh/Android.bp
+++ b/sh/Android.bp
@@ -5,6 +5,7 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-cc",
         "soong-tradefed",
     ],
     srcs: [
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index f28b2b6..217a4e1 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -17,11 +17,14 @@
 import (
 	"fmt"
 	"path/filepath"
+	"sort"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/cc"
 	"android/soong/tradefed"
 )
 
@@ -88,6 +91,20 @@
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
 	Auto_gen_config *bool
+
+	// list of binary modules that should be installed alongside the test
+	Data_bins []string `android:"path,arch_variant"`
+
+	// list of library modules that should be installed alongside the test
+	Data_libs []string `android:"path,arch_variant"`
+
+	// list of device binary modules that should be installed alongside the test.
+	// Only available for host sh_test modules.
+	Data_device_bins []string `android:"path,arch_variant"`
+
+	// list of device library modules that should be installed alongside the test.
+	// Only available for host sh_test modules.
+	Data_device_libs []string `android:"path,arch_variant"`
 }
 
 type ShBinary struct {
@@ -111,6 +128,8 @@
 
 	data       android.Paths
 	testConfig android.Path
+
+	dataModules map[string]android.Path
 }
 
 func (s *ShBinary) HostToolPath() android.OptionalPath {
@@ -192,6 +211,50 @@
 	}
 }
 
+type dependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var (
+	shTestDataBinsTag       = dependencyTag{name: "dataBins"}
+	shTestDataLibsTag       = dependencyTag{name: "dataLibs"}
+	shTestDataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
+	shTestDataDeviceLibsTag = dependencyTag{name: "dataDeviceLibs"}
+)
+
+var sharedLibVariations = []blueprint.Variation{{Mutator: "link", Variation: "shared"}}
+
+func (s *ShTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+	s.ShBinary.DepsMutator(ctx)
+
+	ctx.AddFarVariationDependencies(ctx.Target().Variations(), shTestDataBinsTag, s.testProperties.Data_bins...)
+	ctx.AddFarVariationDependencies(append(ctx.Target().Variations(), sharedLibVariations...),
+		shTestDataLibsTag, s.testProperties.Data_libs...)
+	if ctx.Target().Os.Class == android.Host && len(ctx.Config().Targets[android.Android]) > 0 {
+		deviceVariations := ctx.Config().Targets[android.Android][0].Variations()
+		ctx.AddFarVariationDependencies(deviceVariations, shTestDataDeviceBinsTag, s.testProperties.Data_device_bins...)
+		ctx.AddFarVariationDependencies(append(deviceVariations, sharedLibVariations...),
+			shTestDataDeviceLibsTag, s.testProperties.Data_device_libs...)
+	} else if ctx.Target().Os.Class != android.Host {
+		if len(s.testProperties.Data_device_bins) > 0 {
+			ctx.PropertyErrorf("data_device_bins", "only available for host modules")
+		}
+		if len(s.testProperties.Data_device_libs) > 0 {
+			ctx.PropertyErrorf("data_device_libs", "only available for host modules")
+		}
+	}
+}
+
+func (s *ShTest) addToDataModules(ctx android.ModuleContext, relPath string, path android.Path) {
+	if _, exists := s.dataModules[relPath]; exists {
+		ctx.ModuleErrorf("data modules have a conflicting installation path, %v - %s, %s",
+			relPath, s.dataModules[relPath].String(), path.String())
+		return
+	}
+	s.dataModules[relPath] = path
+}
+
 func (s *ShTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	s.ShBinary.generateAndroidBuildActions(ctx)
 	testDir := "nativetest"
@@ -223,6 +286,50 @@
 	}
 	s.testConfig = tradefed.AutoGenShellTestConfig(ctx, s.testProperties.Test_config,
 		s.testProperties.Test_config_template, s.testProperties.Test_suites, configs, s.testProperties.Auto_gen_config, s.outputFilePath.Base())
+
+	s.dataModules = make(map[string]android.Path)
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		depTag := ctx.OtherModuleDependencyTag(dep)
+		switch depTag {
+		case shTestDataBinsTag, shTestDataDeviceBinsTag:
+			if cc, isCc := dep.(*cc.Module); isCc {
+				s.addToDataModules(ctx, cc.OutputFile().Path().Base(), cc.OutputFile().Path())
+				return
+			}
+			property := "data_bins"
+			if depTag == shTestDataDeviceBinsTag {
+				property = "data_device_bins"
+			}
+			ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+		case shTestDataLibsTag, shTestDataDeviceLibsTag:
+			if cc, isCc := dep.(*cc.Module); isCc {
+				// Copy to an intermediate output directory to append "lib[64]" to the path,
+				// so that it's compatible with the default rpath values.
+				var relPath string
+				if cc.Arch().ArchType.Multilib == "lib64" {
+					relPath = filepath.Join("lib64", cc.OutputFile().Path().Base())
+				} else {
+					relPath = filepath.Join("lib", cc.OutputFile().Path().Base())
+				}
+				if _, exist := s.dataModules[relPath]; exist {
+					return
+				}
+				relocatedLib := android.PathForModuleOut(ctx, "relocated", relPath)
+				ctx.Build(pctx, android.BuildParams{
+					Rule:   android.Cp,
+					Input:  cc.OutputFile().Path(),
+					Output: relocatedLib,
+				})
+				s.addToDataModules(ctx, relPath, relocatedLib)
+				return
+			}
+			property := "data_libs"
+			if depTag == shTestDataDeviceBinsTag {
+				property = "data_device_libs"
+			}
+			ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+		}
+	})
 }
 
 func (s *ShTest) InstallInData() bool {
@@ -251,6 +358,15 @@
 					path = strings.TrimSuffix(path, rel)
 					entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
 				}
+				relPaths := make([]string, 0)
+				for relPath, _ := range s.dataModules {
+					relPaths = append(relPaths, relPath)
+				}
+				sort.Strings(relPaths)
+				for _, relPath := range relPaths {
+					dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
+					entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
+				}
 			},
 		},
 	}}
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 3bfe611..0aa607b 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -3,10 +3,12 @@
 import (
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"reflect"
 	"testing"
 
 	"android/soong/android"
+	"android/soong/cc"
 )
 
 var buildDir string
@@ -46,6 +48,9 @@
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("sh_test", ShTestFactory)
 	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
+
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+
 	ctx.Register(config)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
@@ -105,6 +110,65 @@
 	}
 }
 
+func TestShTest_dataModules(t *testing.T) {
+	ctx, config := testShBinary(t, `
+		sh_test {
+			name: "foo",
+			src: "test.sh",
+			host_supported: true,
+			data_bins: ["bar"],
+			data_libs: ["libbar"],
+		}
+
+		cc_binary {
+			name: "bar",
+			host_supported: true,
+			shared_libs: ["libbar"],
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			host_supported: true,
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	buildOS := android.BuildOs.String()
+	arches := []string{"android_arm64_armv8-a", buildOS + "_x86_64"}
+	for _, arch := range arches {
+		variant := ctx.ModuleForTests("foo", arch)
+
+		libExt := ".so"
+		if arch == "darwin_x86_64" {
+			libExt = ".dylib"
+		}
+		relocated := variant.Output("relocated/lib64/libbar" + libExt)
+		expectedInput := filepath.Join(buildDir, ".intermediates/libbar/"+arch+"_shared/libbar"+libExt)
+		if relocated.Input.String() != expectedInput {
+			t.Errorf("Unexpected relocation input, expected: %q, actual: %q",
+				expectedInput, relocated.Input.String())
+		}
+
+		mod := variant.Module().(*ShTest)
+		entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+		expectedData := []string{
+			filepath.Join(buildDir, ".intermediates/bar", arch, ":bar"),
+			filepath.Join(buildDir, ".intermediates/foo", arch, "relocated/:lib64/libbar"+libExt),
+		}
+		actualData := entries.EntryMap["LOCAL_TEST_DATA"]
+		if !reflect.DeepEqual(expectedData, actualData) {
+			t.Errorf("Unexpected test data, expected: %q, actual: %q", expectedData, actualData)
+		}
+	}
+}
+
 func TestShTestHost(t *testing.T) {
 	ctx, _ := testShBinary(t, `
 		sh_test_host {
@@ -124,3 +188,53 @@
 		t.Errorf("host bit is not set for a sh_test_host module.")
 	}
 }
+
+func TestShTestHost_dataDeviceModules(t *testing.T) {
+	ctx, config := testShBinary(t, `
+		sh_test_host {
+			name: "foo",
+			src: "test.sh",
+			data_device_bins: ["bar"],
+			data_device_libs: ["libbar"],
+		}
+
+		cc_binary {
+			name: "bar",
+			shared_libs: ["libbar"],
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	buildOS := android.BuildOs.String()
+	variant := ctx.ModuleForTests("foo", buildOS+"_x86_64")
+
+	relocated := variant.Output("relocated/lib64/libbar.so")
+	expectedInput := filepath.Join(buildDir, ".intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
+	if relocated.Input.String() != expectedInput {
+		t.Errorf("Unexpected relocation input, expected: %q, actual: %q",
+			expectedInput, relocated.Input.String())
+	}
+
+	mod := variant.Module().(*ShTest)
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	expectedData := []string{
+		filepath.Join(buildDir, ".intermediates/bar/android_arm64_armv8-a/:bar"),
+		// libbar has been relocated, and so has a variant that matches the host arch.
+		filepath.Join(buildDir, ".intermediates/foo/"+buildOS+"_x86_64/relocated/:lib64/libbar.so"),
+	}
+	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
+	if !reflect.DeepEqual(expectedData, actualData) {
+		t.Errorf("Unexpected test data, expected: %q, actual: %q", expectedData, actualData)
+	}
+}
diff --git a/third_party/zip/android.go b/third_party/zip/android.go
index 8d387cc..f8e45c5 100644
--- a/third_party/zip/android.go
+++ b/third_party/zip/android.go
@@ -43,6 +43,15 @@
 		offset:     uint64(w.cw.count),
 	}
 	w.dir = append(w.dir, h)
+	if !fh.isZip64() {
+		// Some writers will generate 64 bit sizes and set 32 bit fields to
+		// uint32max even if the actual size fits in 32 bit. So we should
+		// make sure CompressedSize contains the correct value in such
+		// cases. With out the two lines below we would be writing invalid(-1)
+		// sizes in such case.
+		fh.CompressedSize = uint32(fh.CompressedSize64)
+		fh.UncompressedSize = uint32(fh.UncompressedSize64)
+	}
 
 	if err := writeHeader(w.cw, fh); err != nil {
 		return err
diff --git a/ui/build/config.go b/ui/build/config.go
index 3fa0479..e9a8fc9 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,6 +15,7 @@
 package build
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -23,6 +24,9 @@
 	"time"
 
 	"android/soong/shared"
+	"github.com/golang/protobuf/proto"
+
+	smpb "android/soong/ui/metrics/metrics_proto"
 )
 
 type Config struct{ *configImpl }
@@ -53,8 +57,6 @@
 	// Autodetected
 	totalRAM uint64
 
-	pdkBuild bool
-
 	brokenDupRules     bool
 	brokenUsesNetwork  bool
 	brokenNinjaEnvVars []string
@@ -260,12 +262,14 @@
 	ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
 
 	if ret.UseRBE() {
-		for k, v := range getRBEVars(ctx, tmpDir) {
+		for k, v := range getRBEVars(ctx, Config{ret}) {
 			ret.environ.Set(k, v)
 		}
 	}
 
-	return Config{ret}
+	c := Config{ret}
+	storeConfigMetrics(ctx, c)
+	return c
 }
 
 // NewBuildActionConfig returns a build configuration based on the build action. The arguments are
@@ -274,6 +278,20 @@
 	return NewConfig(ctx, getConfigArgs(action, dir, ctx, args)...)
 }
 
+// storeConfigMetrics selects a set of configuration information and store in
+// the metrics system for further analysis.
+func storeConfigMetrics(ctx Context, config Config) {
+	if ctx.Metrics == nil {
+		return
+	}
+
+	b := &smpb.BuildConfig{
+		UseGoma: proto.Bool(config.UseGoma()),
+		UseRbe:  proto.Bool(config.UseRBE()),
+	}
+	ctx.Metrics.BuildConfig(b)
+}
+
 // getConfigArgs processes the command arguments based on the build action and creates a set of new
 // arguments to be accepted by Config.
 func getConfigArgs(action BuildAction, dir string, ctx Context, args []string) []string {
@@ -808,13 +826,73 @@
 	return true
 }
 
-func (c *configImpl) RBEStatsOutputDir() string {
+func (c *configImpl) logDir() string {
+	if c.Dist() {
+		return filepath.Join(c.DistDir(), "logs")
+	}
+	return c.OutDir()
+}
+
+func (c *configImpl) rbeStatsOutputDir() string {
 	for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} {
 		if v, ok := c.environ.Get(f); ok {
 			return v
 		}
 	}
-	return ""
+	return c.logDir()
+}
+
+func (c *configImpl) rbeLogPath() string {
+	for _, f := range []string{"RBE_log_path", "FLAG_log_path"} {
+		if v, ok := c.environ.Get(f); ok {
+			return v
+		}
+	}
+	return fmt.Sprintf("text://%v/reproxy_log.txt", c.logDir())
+}
+
+func (c *configImpl) rbeExecRoot() string {
+	for _, f := range []string{"RBE_exec_root", "FLAG_exec_root"} {
+		if v, ok := c.environ.Get(f); ok {
+			return v
+		}
+	}
+	wd, err := os.Getwd()
+	if err != nil {
+		return ""
+	}
+	return wd
+}
+
+func (c *configImpl) rbeDir() string {
+	if v, ok := c.environ.Get("RBE_DIR"); ok {
+		return v
+	}
+	return "prebuilts/remoteexecution-client/live/"
+}
+
+func (c *configImpl) rbeReproxy() string {
+	for _, f := range []string{"RBE_re_proxy", "FLAG_re_proxy"} {
+		if v, ok := c.environ.Get(f); ok {
+			return v
+		}
+	}
+	return filepath.Join(c.rbeDir(), "reproxy")
+}
+
+func (c *configImpl) rbeAuth() (string, string) {
+	credFlags := []string{"use_application_default_credentials", "use_gce_credentials", "credential_file"}
+	for _, cf := range credFlags {
+		for _, f := range []string{"RBE_" + cf, "FLAG_" + cf} {
+			if v, ok := c.environ.Get(f); ok {
+				v = strings.TrimSpace(v)
+				if v != "" && v != "false" && v != "0" {
+					return "RBE_" + cf, v
+				}
+			}
+		}
+	}
+	return "RBE_use_application_default_credentials", "true"
 }
 
 func (c *configImpl) UseRemoteBuild() bool {
@@ -968,14 +1046,6 @@
 	return c.targetDeviceDir
 }
 
-func (c *configImpl) SetPdkBuild(pdk bool) {
-	c.pdkBuild = pdk
-}
-
-func (c *configImpl) IsPdkBuild() bool {
-	return c.pdkBuild
-}
-
 func (c *configImpl) BuildDateTime() string {
 	return c.buildDateTime
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index e229856..999af07 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -161,8 +161,6 @@
 	"BUILD_ID",
 	"OUT_DIR",
 	"AUX_OS_VARIANT_LIST",
-	"TARGET_BUILD_PDK",
-	"PDK_FUSION_PLATFORM_ZIP",
 	"PRODUCT_SOONG_NAMESPACES",
 }
 
@@ -281,7 +279,6 @@
 	config.SetTargetDevice(make_vars["TARGET_DEVICE"])
 	config.SetTargetDeviceDir(make_vars["TARGET_DEVICE_DIR"])
 
-	config.SetPdkBuild(make_vars["TARGET_BUILD_PDK"] == "true")
 	config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(make_vars["BUILD_BROKEN_USES_NETWORK"] == "true")
 	config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(make_vars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 1cd5fea..f6d3a57 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -134,14 +134,10 @@
 
 	args := []string{
 		"--writable", config.OutDir() + "/",
+		"--werror_implicit_rules",
 		"-f", "build/make/core/main.mk",
 	}
 
-	// PDK builds still uses a few implicit rules
-	if !config.IsPdkBuild() {
-		args = append(args, "--werror_implicit_rules")
-	}
-
 	if !config.BuildBrokenDupRules() {
 		args = append(args, "--werror_overriding_commands")
 	}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index fd3b7ab..67bcebb 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -37,10 +37,8 @@
 
 func rbeCommand(ctx Context, config Config, rbeCmd string) string {
 	var cmdPath string
-	if rbeDir, ok := config.Environment().Get("RBE_DIR"); ok {
+	if rbeDir := config.rbeDir(); rbeDir != "" {
 		cmdPath = filepath.Join(rbeDir, rbeCmd)
-	} else if home, ok := config.Environment().Get("HOME"); ok {
-		cmdPath = filepath.Join(home, "rbe", rbeCmd)
 	} else {
 		ctx.Fatalf("rbe command path not found")
 	}
@@ -52,9 +50,21 @@
 	return cmdPath
 }
 
-func getRBEVars(ctx Context, tmpDir string) map[string]string {
+func getRBEVars(ctx Context, config Config) map[string]string {
 	rand.Seed(time.Now().UnixNano())
-	return map[string]string{"RBE_server_address": fmt.Sprintf("unix://%v/reproxy_%v.sock", tmpDir, rand.Intn(1000))}
+	vars := map[string]string{
+		"RBE_log_path":   config.rbeLogPath(),
+		"RBE_log_dir":    config.logDir(),
+		"RBE_re_proxy":   config.rbeReproxy(),
+		"RBE_exec_root":  config.rbeExecRoot(),
+		"RBE_output_dir": config.rbeStatsOutputDir(),
+	}
+	if config.StartRBE() {
+		vars["RBE_server_address"] = fmt.Sprintf("unix://%v/reproxy_%v.sock", absPath(ctx, config.TempDir()), rand.Intn(1000))
+	}
+	k, v := config.rbeAuth()
+	vars[k] = v
+	return vars
 }
 
 func startRBE(ctx Context, config Config) {
@@ -102,7 +112,7 @@
 		return
 	}
 
-	outputDir := config.RBEStatsOutputDir()
+	outputDir := config.rbeStatsOutputDir()
 	if outputDir == "" {
 		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
 	}
@@ -111,7 +121,20 @@
 	// Stop the proxy first in order to generate the RBE metrics protobuf file.
 	stopRBE(ctx, config)
 
+	if metricsFile == filename {
+		return
+	}
 	if _, err := copyFile(metricsFile, filename); err != nil {
 		ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err)
 	}
 }
+
+// PrintGomaDeprecation prints a PSA on the deprecation of Goma if it is set for the build.
+func PrintGomaDeprecation(ctx Context, config Config) {
+	if config.UseGoma() {
+		fmt.Fprintln(ctx.Writer, "")
+		fmt.Fprintln(ctx.Writer, "Goma for Android is being deprecated and replaced with RBE.")
+		fmt.Fprintln(ctx.Writer, "See go/goma_android_deprecation for more details.")
+		fmt.Fprintln(ctx.Writer, "")
+	}
+}
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
index 23a53b4..8ff96bc 100644
--- a/ui/build/rbe_test.go
+++ b/ui/build/rbe_test.go
@@ -83,24 +83,13 @@
 func TestDumpRBEMetricsErrors(t *testing.T) {
 	ctx := testContext()
 	tests := []struct {
-		description         string
-		rbeOutputDirDefined bool
-		bootstrapProgram    string
-		expectedErr         string
+		description      string
+		bootstrapProgram string
+		expectedErr      string
 	}{{
-		description:      "output_dir not defined",
-		bootstrapProgram: rbeBootstrapProgram,
-		expectedErr:      "RBE output dir variable not defined",
-	}, {
-		description:         "stopRBE failed",
-		rbeOutputDirDefined: true,
-		bootstrapProgram:    "#!/bin/bash\nexit 1\n",
-		expectedErr:         "shutdown failed",
-	}, {
-		description:         "failed to copy metrics file",
-		rbeOutputDirDefined: true,
-		bootstrapProgram:    "#!/bin/bash\n",
-		expectedErr:         "failed to copy",
+		description:      "stopRBE failed",
+		bootstrapProgram: "#!/bin/bash\nexit 1\n",
+		expectedErr:      "shutdown failed",
 	}}
 
 	for _, tt := range tests {
@@ -124,10 +113,6 @@
 			env.Set("OUT_DIR", tmpDir)
 			env.Set("RBE_DIR", tmpDir)
 
-			if tt.rbeOutputDirDefined {
-				env.Set("RBE_output_dir", t.TempDir())
-			}
-
 			config := Config{&configImpl{
 				environ: env,
 			}}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 2b5c4c3..f5552a3 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -17,6 +17,7 @@
 import (
 	"io/ioutil"
 	"os"
+	"runtime"
 	"time"
 
 	"github.com/golang/protobuf/proto"
@@ -65,6 +66,10 @@
 	}
 }
 
+func (m *Metrics) BuildConfig(b *soong_metrics_proto.BuildConfig) {
+	m.metrics.BuildConfig = b
+}
+
 func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
 	for k, v := range metadata {
 		switch k {
@@ -98,8 +103,6 @@
 			m.metrics.HostArch = m.getArch(v)
 		case "HOST_2ND_ARCH":
 			m.metrics.Host_2NdArch = m.getArch(v)
-		case "HOST_OS":
-			m.metrics.HostOs = proto.String(v)
 		case "HOST_OS_EXTRA":
 			m.metrics.HostOsExtra = proto.String(v)
 		case "HOST_CROSS_OS":
@@ -137,6 +140,7 @@
 
 // exports the output to the file at outputPath
 func (m *Metrics) Dump(outputPath string) (err error) {
+	m.metrics.HostOs = proto.String(runtime.GOOS)
 	return writeMessageToFile(&m.metrics, outputPath)
 }
 
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index a39d1a8..d7c53ec 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -152,7 +152,7 @@
 }
 
 func (ModuleTypeInfo_BuildSystem) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{2, 0}
+	return fileDescriptor_6039342a2ba47b72, []int{3, 0}
 }
 
 type MetricsBase struct {
@@ -199,6 +199,7 @@
 	// The metrics for the whole build
 	Total                *PerfInfo          `protobuf:"bytes,21,opt,name=total" json:"total,omitempty"`
 	SoongBuildMetrics    *SoongBuildMetrics `protobuf:"bytes,22,opt,name=soong_build_metrics,json=soongBuildMetrics" json:"soong_build_metrics,omitempty"`
+	BuildConfig          *BuildConfig       `protobuf:"bytes,23,opt,name=build_config,json=buildConfig" json:"build_config,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
 	XXX_unrecognized     []byte             `json:"-"`
 	XXX_sizecache        int32              `json:"-"`
@@ -388,6 +389,60 @@
 	return nil
 }
 
+func (m *MetricsBase) GetBuildConfig() *BuildConfig {
+	if m != nil {
+		return m.BuildConfig
+	}
+	return nil
+}
+
+type BuildConfig struct {
+	UseGoma              *bool    `protobuf:"varint,1,opt,name=use_goma,json=useGoma" json:"use_goma,omitempty"`
+	UseRbe               *bool    `protobuf:"varint,2,opt,name=use_rbe,json=useRbe" json:"use_rbe,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *BuildConfig) Reset()         { *m = BuildConfig{} }
+func (m *BuildConfig) String() string { return proto.CompactTextString(m) }
+func (*BuildConfig) ProtoMessage()    {}
+func (*BuildConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6039342a2ba47b72, []int{1}
+}
+
+func (m *BuildConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_BuildConfig.Unmarshal(m, b)
+}
+func (m *BuildConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_BuildConfig.Marshal(b, m, deterministic)
+}
+func (m *BuildConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_BuildConfig.Merge(m, src)
+}
+func (m *BuildConfig) XXX_Size() int {
+	return xxx_messageInfo_BuildConfig.Size(m)
+}
+func (m *BuildConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_BuildConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildConfig proto.InternalMessageInfo
+
+func (m *BuildConfig) GetUseGoma() bool {
+	if m != nil && m.UseGoma != nil {
+		return *m.UseGoma
+	}
+	return false
+}
+
+func (m *BuildConfig) GetUseRbe() bool {
+	if m != nil && m.UseRbe != nil {
+		return *m.UseRbe
+	}
+	return false
+}
+
 type PerfInfo struct {
 	// The description for the phase/action/part while the tool running.
 	Desc *string `protobuf:"bytes,1,opt,name=desc" json:"desc,omitempty"`
@@ -410,7 +465,7 @@
 func (m *PerfInfo) String() string { return proto.CompactTextString(m) }
 func (*PerfInfo) ProtoMessage()    {}
 func (*PerfInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{1}
+	return fileDescriptor_6039342a2ba47b72, []int{2}
 }
 
 func (m *PerfInfo) XXX_Unmarshal(b []byte) error {
@@ -482,7 +537,7 @@
 func (m *ModuleTypeInfo) String() string { return proto.CompactTextString(m) }
 func (*ModuleTypeInfo) ProtoMessage()    {}
 func (*ModuleTypeInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{2}
+	return fileDescriptor_6039342a2ba47b72, []int{3}
 }
 
 func (m *ModuleTypeInfo) XXX_Unmarshal(b []byte) error {
@@ -540,7 +595,7 @@
 func (m *CriticalUserJourneyMetrics) String() string { return proto.CompactTextString(m) }
 func (*CriticalUserJourneyMetrics) ProtoMessage()    {}
 func (*CriticalUserJourneyMetrics) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{3}
+	return fileDescriptor_6039342a2ba47b72, []int{4}
 }
 
 func (m *CriticalUserJourneyMetrics) XXX_Unmarshal(b []byte) error {
@@ -587,7 +642,7 @@
 func (m *CriticalUserJourneysMetrics) String() string { return proto.CompactTextString(m) }
 func (*CriticalUserJourneysMetrics) ProtoMessage()    {}
 func (*CriticalUserJourneysMetrics) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{4}
+	return fileDescriptor_6039342a2ba47b72, []int{5}
 }
 
 func (m *CriticalUserJourneysMetrics) XXX_Unmarshal(b []byte) error {
@@ -635,7 +690,7 @@
 func (m *SoongBuildMetrics) String() string { return proto.CompactTextString(m) }
 func (*SoongBuildMetrics) ProtoMessage()    {}
 func (*SoongBuildMetrics) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{5}
+	return fileDescriptor_6039342a2ba47b72, []int{6}
 }
 
 func (m *SoongBuildMetrics) XXX_Unmarshal(b []byte) error {
@@ -696,6 +751,7 @@
 	proto.RegisterEnum("soong_build_metrics.MetricsBase_Arch", MetricsBase_Arch_name, MetricsBase_Arch_value)
 	proto.RegisterEnum("soong_build_metrics.ModuleTypeInfo_BuildSystem", ModuleTypeInfo_BuildSystem_name, ModuleTypeInfo_BuildSystem_value)
 	proto.RegisterType((*MetricsBase)(nil), "soong_build_metrics.MetricsBase")
+	proto.RegisterType((*BuildConfig)(nil), "soong_build_metrics.BuildConfig")
 	proto.RegisterType((*PerfInfo)(nil), "soong_build_metrics.PerfInfo")
 	proto.RegisterType((*ModuleTypeInfo)(nil), "soong_build_metrics.ModuleTypeInfo")
 	proto.RegisterType((*CriticalUserJourneyMetrics)(nil), "soong_build_metrics.CriticalUserJourneyMetrics")
@@ -703,69 +759,74 @@
 	proto.RegisterType((*SoongBuildMetrics)(nil), "soong_build_metrics.SoongBuildMetrics")
 }
 
-func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
+func init() {
+	proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72)
+}
 
 var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 962 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xef, 0x4e, 0xdc, 0x46,
-	0x10, 0x8f, 0xe1, 0xe0, 0xce, 0x63, 0xee, 0x30, 0x0b, 0x69, 0x9c, 0x44, 0xa8, 0x27, 0xab, 0x89,
-	0x50, 0xd5, 0x90, 0x88, 0x46, 0x28, 0x42, 0x51, 0x25, 0x38, 0x50, 0x9a, 0x22, 0xb8, 0xc8, 0xfc,
-	0x69, 0xd4, 0x7e, 0x58, 0x2d, 0xf6, 0x12, 0x9c, 0xda, 0x5e, 0x6b, 0x77, 0x1d, 0x41, 0xde, 0xa1,
-	0x0f, 0xd4, 0xcf, 0x7d, 0x96, 0xbe, 0x47, 0xb5, 0xb3, 0xf6, 0x61, 0xda, 0x8b, 0x40, 0xf9, 0x66,
-	0xcf, 0xef, 0xcf, 0xce, 0xac, 0x67, 0xe6, 0x0e, 0xfa, 0x39, 0xd7, 0x32, 0x8d, 0xd5, 0x7a, 0x29,
-	0x85, 0x16, 0x64, 0x59, 0x09, 0x51, 0x7c, 0xa0, 0x67, 0x55, 0x9a, 0x25, 0xb4, 0x86, 0xc2, 0xbf,
-	0x00, 0xbc, 0x03, 0xfb, 0xbc, 0xc3, 0x14, 0x27, 0x2f, 0x60, 0xc5, 0x12, 0x12, 0xa6, 0x39, 0xd5,
-	0x69, 0xce, 0x95, 0x66, 0x79, 0x19, 0x38, 0x43, 0x67, 0x6d, 0x36, 0x22, 0x88, 0xed, 0x32, 0xcd,
-	0x8f, 0x1b, 0x84, 0x3c, 0x84, 0x9e, 0x55, 0xa4, 0x49, 0x30, 0x33, 0x74, 0xd6, 0xdc, 0xa8, 0x8b,
-	0xef, 0x6f, 0x13, 0xb2, 0x05, 0x0f, 0xcb, 0x8c, 0xe9, 0x73, 0x21, 0x73, 0xfa, 0x89, 0x4b, 0x95,
-	0x8a, 0x82, 0xc6, 0x22, 0xe1, 0x05, 0xcb, 0x79, 0x30, 0x8b, 0xdc, 0x07, 0x0d, 0xe1, 0xd4, 0xe2,
-	0xa3, 0x1a, 0x26, 0x4f, 0x60, 0xa0, 0x99, 0xfc, 0xc0, 0x35, 0x2d, 0xa5, 0x48, 0xaa, 0x58, 0x07,
-	0x1d, 0x14, 0xf4, 0x6d, 0xf4, 0x9d, 0x0d, 0x92, 0x04, 0x56, 0x6a, 0x9a, 0x4d, 0xe2, 0x13, 0x93,
-	0x29, 0x2b, 0x74, 0x30, 0x37, 0x74, 0xd6, 0x06, 0x1b, 0xcf, 0xd6, 0xa7, 0xd4, 0xbc, 0xde, 0xaa,
-	0x77, 0x7d, 0xc7, 0x20, 0xa7, 0x56, 0xb4, 0x35, 0xbb, 0x77, 0xf8, 0x26, 0x22, 0xd6, 0xaf, 0x0d,
-	0x90, 0x31, 0x78, 0xf5, 0x29, 0x4c, 0xc6, 0x17, 0xc1, 0x3c, 0x9a, 0x3f, 0xb9, 0xd5, 0x7c, 0x5b,
-	0xc6, 0x17, 0x5b, 0xdd, 0x93, 0xc3, 0xfd, 0xc3, 0xf1, 0xaf, 0x87, 0x11, 0x58, 0x0b, 0x13, 0x24,
-	0xeb, 0xb0, 0xdc, 0x32, 0x9c, 0x64, 0xdd, 0xc5, 0x12, 0x97, 0xae, 0x89, 0x4d, 0x02, 0x3f, 0x40,
-	0x9d, 0x16, 0x8d, 0xcb, 0x6a, 0x42, 0xef, 0x21, 0xdd, 0xb7, 0xc8, 0xa8, 0xac, 0x1a, 0xf6, 0x3e,
-	0xb8, 0x17, 0x42, 0xd5, 0xc9, 0xba, 0x5f, 0x95, 0x6c, 0xcf, 0x18, 0x60, 0xaa, 0x11, 0xf4, 0xd1,
-	0x6c, 0xa3, 0x48, 0xac, 0x21, 0x7c, 0x95, 0xa1, 0x67, 0x4c, 0x36, 0x8a, 0x04, 0x3d, 0x1f, 0x40,
-	0x17, 0x3d, 0x85, 0x0a, 0x3c, 0xac, 0x61, 0xde, 0xbc, 0x8e, 0x15, 0x09, 0xeb, 0xc3, 0x84, 0xa2,
-	0xfc, 0x52, 0x4b, 0x16, 0x2c, 0x20, 0xec, 0x59, 0x78, 0xcf, 0x84, 0x26, 0x9c, 0x58, 0x0a, 0xa5,
-	0x8c, 0x45, 0xff, 0x9a, 0x33, 0x32, 0xb1, 0xb1, 0x22, 0x4f, 0x61, 0xb1, 0xc5, 0xc1, 0xb4, 0x07,
-	0xb6, 0x7d, 0x26, 0x2c, 0x4c, 0xe4, 0x19, 0x2c, 0xb7, 0x78, 0x93, 0x12, 0x17, 0xed, 0xc5, 0x4e,
-	0xb8, 0xad, 0xbc, 0x45, 0xa5, 0x69, 0x92, 0xca, 0xc0, 0xb7, 0x79, 0x8b, 0x4a, 0xef, 0xa6, 0x92,
-	0xfc, 0x04, 0x9e, 0xe2, 0xba, 0x2a, 0xa9, 0x16, 0x22, 0x53, 0xc1, 0xd2, 0x70, 0x76, 0xcd, 0xdb,
-	0x58, 0x9d, 0x7a, 0x45, 0xef, 0xb8, 0x3c, 0x7f, 0x5b, 0x9c, 0x8b, 0x08, 0x50, 0x71, 0x6c, 0x04,
-	0x64, 0x0b, 0xdc, 0x3f, 0x98, 0x4e, 0xa9, 0xac, 0x0a, 0x15, 0x90, 0xbb, 0xa8, 0x7b, 0x86, 0x1f,
-	0x55, 0x85, 0x22, 0xaf, 0x01, 0x2c, 0x13, 0xc5, 0xcb, 0x77, 0x11, 0xbb, 0x88, 0x36, 0xea, 0x22,
-	0x2d, 0x3e, 0x32, 0xab, 0x5e, 0xb9, 0x93, 0x1a, 0x05, 0xa8, 0xfe, 0x11, 0xe6, 0xb4, 0xd0, 0x2c,
-	0x0b, 0xee, 0x0f, 0x9d, 0xdb, 0x85, 0x96, 0x4b, 0x4e, 0x61, 0xda, 0x2a, 0x0a, 0xbe, 0x41, 0x8b,
-	0xa7, 0x53, 0x2d, 0x8e, 0x4c, 0x0c, 0x47, 0xb2, 0xee, 0xb0, 0x68, 0x49, 0xfd, 0x37, 0x14, 0xbe,
-	0x80, 0x85, 0x1b, 0x53, 0xdb, 0x83, 0xce, 0xc9, 0xd1, 0x5e, 0xe4, 0xdf, 0x23, 0x7d, 0x70, 0xcd,
-	0xd3, 0xee, 0xde, 0xce, 0xc9, 0x1b, 0xdf, 0x21, 0x5d, 0x30, 0x93, 0xee, 0xcf, 0x84, 0xaf, 0xa1,
-	0x83, 0xdf, 0xd5, 0x83, 0xa6, 0x4f, 0xfd, 0x7b, 0x06, 0xdd, 0x8e, 0x0e, 0x7c, 0x87, 0xb8, 0x30,
-	0xb7, 0x1d, 0x1d, 0x6c, 0xbe, 0xf4, 0x67, 0x4c, 0xec, 0xfd, 0xab, 0x4d, 0x7f, 0x96, 0x00, 0xcc,
-	0xbf, 0x7f, 0xb5, 0x49, 0x37, 0x5f, 0xfa, 0x9d, 0xf0, 0x4f, 0x07, 0x7a, 0x4d, 0x6d, 0x84, 0x40,
-	0x27, 0xe1, 0x2a, 0xc6, 0x45, 0xe9, 0x46, 0xf8, 0x6c, 0x62, 0xb8, 0xea, 0xec, 0x5a, 0xc4, 0x67,
-	0xb2, 0x0a, 0xa0, 0x34, 0x93, 0x1a, 0x77, 0x2b, 0x2e, 0xc1, 0x4e, 0xe4, 0x62, 0xc4, 0xac, 0x54,
-	0xf2, 0x18, 0x5c, 0xc9, 0x59, 0x66, 0xd1, 0x0e, 0xa2, 0x3d, 0x13, 0x40, 0x70, 0x15, 0x20, 0xe7,
-	0xb9, 0x90, 0x57, 0xb4, 0x52, 0x1c, 0x57, 0x5c, 0x27, 0x72, 0x6d, 0xe4, 0x44, 0xf1, 0xf0, 0x1f,
-	0x07, 0x06, 0x07, 0x22, 0xa9, 0x32, 0x7e, 0x7c, 0x55, 0x72, 0xcc, 0xea, 0x77, 0x58, 0xb0, 0x17,
-	0xa9, 0xae, 0x94, 0xe6, 0x39, 0x66, 0x37, 0xd8, 0x78, 0x3e, 0x7d, 0x76, 0x6f, 0x48, 0xed, 0x66,
-	0x3c, 0x42, 0x59, 0x6b, 0x8a, 0xcf, 0xae, 0xa3, 0xe4, 0x5b, 0xf0, 0x72, 0xd4, 0x50, 0x7d, 0x55,
-	0x36, 0x55, 0x42, 0x3e, 0xb1, 0x21, 0xdf, 0xc1, 0xa0, 0xa8, 0x72, 0x2a, 0xce, 0xa9, 0x0d, 0x2a,
-	0xac, 0xb7, 0x1f, 0x2d, 0x14, 0x55, 0x3e, 0x3e, 0xb7, 0xe7, 0xa9, 0xf0, 0x39, 0x78, 0xad, 0xb3,
-	0x6e, 0x7e, 0x0b, 0x17, 0xe6, 0x8e, 0xc6, 0xe3, 0x43, 0xf3, 0xd1, 0x7a, 0xd0, 0x39, 0xd8, 0xde,
-	0xdf, 0xf3, 0x67, 0xc2, 0x0c, 0x1e, 0x8d, 0x64, 0xaa, 0xd3, 0x98, 0x65, 0x27, 0x8a, 0xcb, 0x5f,
-	0x44, 0x25, 0x0b, 0x7e, 0x55, 0x77, 0xc1, 0xe4, 0xd2, 0x9d, 0xd6, 0xa5, 0x6f, 0x41, 0xb7, 0xe9,
-	0xb2, 0x19, 0xec, 0xb2, 0xe1, 0x6d, 0xdb, 0x2b, 0x6a, 0x04, 0xe1, 0x19, 0x3c, 0x9e, 0x72, 0x9a,
-	0x6a, 0x8e, 0x1b, 0x41, 0x27, 0xae, 0x3e, 0xaa, 0xc0, 0xc1, 0xc9, 0x99, 0x7e, 0xb3, 0x5f, 0xce,
-	0x36, 0x42, 0x71, 0xf8, 0xb7, 0x03, 0x4b, 0xff, 0x6b, 0x71, 0x12, 0x40, 0xb7, 0xb9, 0x37, 0x07,
-	0xef, 0xad, 0x79, 0x25, 0x8f, 0xa0, 0x57, 0xff, 0x06, 0xd8, 0x82, 0xfa, 0xd1, 0xe4, 0x9d, 0x7c,
-	0x0f, 0x4b, 0x38, 0x66, 0x94, 0x65, 0x99, 0x88, 0x69, 0x2c, 0xaa, 0x42, 0xd7, 0x7d, 0xb6, 0x88,
-	0xc0, 0xb6, 0x89, 0x8f, 0x4c, 0x98, 0xac, 0x81, 0xdf, 0xe6, 0xaa, 0xf4, 0x73, 0xd3, 0x74, 0x83,
-	0x6b, 0xea, 0x51, 0xfa, 0x99, 0x9b, 0xa5, 0x9b, 0xb3, 0x4b, 0x7a, 0xc1, 0x59, 0x69, 0x69, 0xb6,
-	0xfb, 0xbc, 0x9c, 0x5d, 0xfe, 0xcc, 0x59, 0x69, 0x38, 0x3b, 0xf7, 0x7f, 0xab, 0xe7, 0xba, 0xae,
-	0x9b, 0xe2, 0xff, 0x8e, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x2a, 0x36, 0xe3, 0x87, 0x08,
-	0x00, 0x00,
+	// 1021 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xef, 0x6e, 0xdb, 0x36,
+	0x10, 0xaf, 0x12, 0x27, 0xb6, 0x4e, 0xb1, 0xab, 0x30, 0xed, 0xa2, 0xb6, 0x08, 0x66, 0x18, 0x6b,
+	0x11, 0x0c, 0x6b, 0x5a, 0x64, 0x45, 0x50, 0x04, 0xc5, 0x00, 0xc7, 0x09, 0xb2, 0x2e, 0x48, 0x5c,
+	0x30, 0x7f, 0x56, 0x6c, 0x1f, 0x04, 0x5a, 0xa2, 0x13, 0x75, 0x96, 0x28, 0x90, 0x54, 0x91, 0xf4,
+	0x1d, 0xf6, 0x54, 0x7b, 0x96, 0xbd, 0xc6, 0x30, 0xf0, 0x28, 0xd9, 0xca, 0xe6, 0xad, 0x41, 0xbf,
+	0x89, 0xf7, 0xfb, 0xc3, 0x3b, 0xf2, 0x78, 0x36, 0xb4, 0x53, 0xae, 0x65, 0x12, 0xa9, 0xad, 0x5c,
+	0x0a, 0x2d, 0xc8, 0x9a, 0x12, 0x22, 0xbb, 0x0c, 0x47, 0x45, 0x32, 0x89, 0xc3, 0x12, 0xea, 0xfd,
+	0x05, 0xe0, 0x1d, 0xdb, 0xef, 0x3d, 0xa6, 0x38, 0x79, 0x09, 0x0f, 0x2c, 0x21, 0x66, 0x9a, 0x87,
+	0x3a, 0x49, 0xb9, 0xd2, 0x2c, 0xcd, 0x03, 0xa7, 0xeb, 0x6c, 0x2e, 0x52, 0x82, 0xd8, 0x3e, 0xd3,
+	0xfc, 0xac, 0x42, 0xc8, 0x23, 0x68, 0x59, 0x45, 0x12, 0x07, 0x0b, 0x5d, 0x67, 0xd3, 0xa5, 0x4d,
+	0x5c, 0xbf, 0x8d, 0xc9, 0x2e, 0x3c, 0xca, 0x27, 0x4c, 0x8f, 0x85, 0x4c, 0xc3, 0x8f, 0x5c, 0xaa,
+	0x44, 0x64, 0x61, 0x24, 0x62, 0x9e, 0xb1, 0x94, 0x07, 0x8b, 0xc8, 0x5d, 0xaf, 0x08, 0x17, 0x16,
+	0x1f, 0x94, 0x30, 0x79, 0x0a, 0x1d, 0xcd, 0xe4, 0x25, 0xd7, 0x61, 0x2e, 0x45, 0x5c, 0x44, 0x3a,
+	0x68, 0xa0, 0xa0, 0x6d, 0xa3, 0xef, 0x6c, 0x90, 0xc4, 0xf0, 0xa0, 0xa4, 0xd9, 0x24, 0x3e, 0x32,
+	0x99, 0xb0, 0x4c, 0x07, 0x4b, 0x5d, 0x67, 0xb3, 0xb3, 0xfd, 0x7c, 0x6b, 0x4e, 0xcd, 0x5b, 0xb5,
+	0x7a, 0xb7, 0xf6, 0x0c, 0x72, 0x61, 0x45, 0xbb, 0x8b, 0x07, 0x27, 0x87, 0x94, 0x58, 0xbf, 0x3a,
+	0x40, 0x86, 0xe0, 0x95, 0xbb, 0x30, 0x19, 0x5d, 0x05, 0xcb, 0x68, 0xfe, 0xf4, 0xb3, 0xe6, 0x7d,
+	0x19, 0x5d, 0xed, 0x36, 0xcf, 0x4f, 0x8e, 0x4e, 0x86, 0x3f, 0x9f, 0x50, 0xb0, 0x16, 0x26, 0x48,
+	0xb6, 0x60, 0xad, 0x66, 0x38, 0xcd, 0xba, 0x89, 0x25, 0xae, 0xce, 0x88, 0x55, 0x02, 0xdf, 0x41,
+	0x99, 0x56, 0x18, 0xe5, 0xc5, 0x94, 0xde, 0x42, 0xba, 0x6f, 0x91, 0x41, 0x5e, 0x54, 0xec, 0x23,
+	0x70, 0xaf, 0x84, 0x2a, 0x93, 0x75, 0xbf, 0x28, 0xd9, 0x96, 0x31, 0xc0, 0x54, 0x29, 0xb4, 0xd1,
+	0x6c, 0x3b, 0x8b, 0xad, 0x21, 0x7c, 0x91, 0xa1, 0x67, 0x4c, 0xb6, 0xb3, 0x18, 0x3d, 0xd7, 0xa1,
+	0x89, 0x9e, 0x42, 0x05, 0x1e, 0xd6, 0xb0, 0x6c, 0x96, 0x43, 0x45, 0x7a, 0xe5, 0x66, 0x42, 0x85,
+	0xfc, 0x5a, 0x4b, 0x16, 0xac, 0x20, 0xec, 0x59, 0xf8, 0xc0, 0x84, 0xa6, 0x9c, 0x48, 0x0a, 0xa5,
+	0x8c, 0x45, 0x7b, 0xc6, 0x19, 0x98, 0xd8, 0x50, 0x91, 0x67, 0x70, 0xbf, 0xc6, 0xc1, 0xb4, 0x3b,
+	0xb6, 0x7d, 0xa6, 0x2c, 0x4c, 0xe4, 0x39, 0xac, 0xd5, 0x78, 0xd3, 0x12, 0xef, 0xdb, 0x83, 0x9d,
+	0x72, 0x6b, 0x79, 0x8b, 0x42, 0x87, 0x71, 0x22, 0x03, 0xdf, 0xe6, 0x2d, 0x0a, 0xbd, 0x9f, 0x48,
+	0xf2, 0x03, 0x78, 0x8a, 0xeb, 0x22, 0x0f, 0xb5, 0x10, 0x13, 0x15, 0xac, 0x76, 0x17, 0x37, 0xbd,
+	0xed, 0x8d, 0xb9, 0x47, 0xf4, 0x8e, 0xcb, 0xf1, 0xdb, 0x6c, 0x2c, 0x28, 0xa0, 0xe2, 0xcc, 0x08,
+	0xc8, 0x2e, 0xb8, 0xbf, 0x31, 0x9d, 0x84, 0xb2, 0xc8, 0x54, 0x40, 0xee, 0xa2, 0x6e, 0x19, 0x3e,
+	0x2d, 0x32, 0x45, 0xde, 0x00, 0x58, 0x26, 0x8a, 0xd7, 0xee, 0x22, 0x76, 0x11, 0xad, 0xd4, 0x59,
+	0x92, 0x7d, 0x60, 0x56, 0xfd, 0xe0, 0x4e, 0x6a, 0x14, 0xa0, 0xfa, 0x7b, 0x58, 0xd2, 0x42, 0xb3,
+	0x49, 0xf0, 0xb0, 0xeb, 0x7c, 0x5e, 0x68, 0xb9, 0xe4, 0x02, 0xe6, 0x8d, 0xa2, 0xe0, 0x2b, 0xb4,
+	0x78, 0x36, 0xd7, 0xe2, 0xd4, 0xc4, 0xf0, 0x49, 0x96, 0x1d, 0x46, 0x57, 0xd5, 0x3f, 0x43, 0x64,
+	0x00, 0x2b, 0x56, 0x15, 0x89, 0x6c, 0x9c, 0x5c, 0x06, 0xeb, 0x68, 0xd8, 0x9d, 0x6b, 0x88, 0xc2,
+	0x01, 0xf2, 0xa8, 0x37, 0x9a, 0x2d, 0x7a, 0x2f, 0x61, 0xe5, 0xd6, 0xd3, 0x6f, 0x41, 0xe3, 0xfc,
+	0xf4, 0x80, 0xfa, 0xf7, 0x48, 0x1b, 0x5c, 0xf3, 0xb5, 0x7f, 0xb0, 0x77, 0x7e, 0xe8, 0x3b, 0xa4,
+	0x09, 0x66, 0x5c, 0xf8, 0x0b, 0xbd, 0x37, 0xd0, 0xc0, 0xe6, 0xf0, 0xa0, 0x6a, 0x76, 0xff, 0x9e,
+	0x41, 0xfb, 0xf4, 0xd8, 0x77, 0x88, 0x0b, 0x4b, 0x7d, 0x7a, 0xbc, 0xf3, 0xca, 0x5f, 0x30, 0xb1,
+	0xf7, 0xaf, 0x77, 0xfc, 0x45, 0x02, 0xb0, 0xfc, 0xfe, 0xf5, 0x4e, 0xb8, 0xf3, 0xca, 0x6f, 0xf4,
+	0xfa, 0xe0, 0xd5, 0x72, 0x31, 0xd3, 0xb4, 0x50, 0x3c, 0xbc, 0x14, 0x29, 0xc3, 0x99, 0xdb, 0xa2,
+	0xcd, 0x42, 0xf1, 0x43, 0x91, 0x32, 0xd3, 0x7c, 0x06, 0x92, 0x23, 0x8e, 0x73, 0xb6, 0x45, 0x97,
+	0x0b, 0xc5, 0xe9, 0x88, 0xf7, 0x7e, 0x77, 0xa0, 0x55, 0x9d, 0x31, 0x21, 0xd0, 0x88, 0xb9, 0x8a,
+	0x50, 0xec, 0x52, 0xfc, 0x36, 0x31, 0x1c, 0xb9, 0x76, 0x3c, 0xe3, 0x37, 0xd9, 0x00, 0x50, 0x9a,
+	0x49, 0x8d, 0x33, 0x1e, 0x87, 0x71, 0x83, 0xba, 0x18, 0x31, 0xa3, 0x9d, 0x3c, 0x01, 0x57, 0x72,
+	0x36, 0xb1, 0x68, 0x03, 0xd1, 0x96, 0x09, 0x20, 0xb8, 0x01, 0x90, 0xf2, 0x54, 0xc8, 0x9b, 0xb0,
+	0x50, 0x1c, 0x47, 0x6d, 0x83, 0xba, 0x36, 0x72, 0xae, 0x78, 0xef, 0x4f, 0x07, 0x3a, 0xc7, 0x22,
+	0x2e, 0x26, 0xfc, 0xec, 0x26, 0xe7, 0x98, 0xd5, 0xaf, 0xd5, 0xd5, 0xa8, 0x1b, 0xa5, 0x79, 0x8a,
+	0xd9, 0x75, 0xb6, 0x5f, 0xcc, 0x9f, 0x21, 0xb7, 0xa4, 0xf6, 0xa6, 0x4e, 0x51, 0x56, 0x9b, 0x26,
+	0xa3, 0x59, 0x94, 0x7c, 0x0d, 0x5e, 0x8a, 0x9a, 0x50, 0xdf, 0xe4, 0x55, 0x95, 0x90, 0x4e, 0x6d,
+	0xc8, 0x37, 0xd0, 0xc9, 0x8a, 0x34, 0x14, 0xe3, 0xd0, 0x06, 0x15, 0xd6, 0xdb, 0xa6, 0x2b, 0x59,
+	0x91, 0x0e, 0xc7, 0x76, 0x3f, 0xd5, 0x7b, 0x51, 0xde, 0x44, 0xe9, 0x7a, 0xeb, 0x3a, 0x5d, 0x58,
+	0x3a, 0x1d, 0x0e, 0x4f, 0xcc, 0xbd, 0xb7, 0xa0, 0x71, 0xdc, 0x3f, 0x3a, 0xf0, 0x17, 0x7a, 0x13,
+	0x78, 0x3c, 0x90, 0x89, 0x4e, 0x22, 0x36, 0x39, 0x57, 0x5c, 0xfe, 0x24, 0x0a, 0x99, 0xf1, 0x9b,
+	0xaa, 0x1b, 0xab, 0x43, 0x77, 0x6a, 0x87, 0xbe, 0x0b, 0xcd, 0xaa, 0xdb, 0x17, 0xfe, 0xa7, 0x39,
+	0x6b, 0x53, 0x94, 0x56, 0x82, 0xde, 0x08, 0x9e, 0xcc, 0xd9, 0x4d, 0xcd, 0x9a, 0xbf, 0x11, 0x15,
+	0x1f, 0x54, 0xe0, 0xe0, 0x0b, 0x9e, 0x7f, 0xb2, 0xff, 0x9d, 0x2d, 0x45, 0x71, 0xef, 0x0f, 0x07,
+	0x56, 0xff, 0xf5, 0xd4, 0x48, 0x00, 0xcd, 0xea, 0xdc, 0x1c, 0x3c, 0xb7, 0x6a, 0x49, 0x1e, 0x43,
+	0xab, 0xfc, 0x2d, 0xb2, 0x05, 0xb5, 0xe9, 0x74, 0x4d, 0xbe, 0x85, 0x55, 0x7c, 0xee, 0x21, 0x9b,
+	0x4c, 0x44, 0x14, 0x46, 0xa2, 0xc8, 0x74, 0xd9, 0x67, 0xf7, 0x11, 0xe8, 0x9b, 0xf8, 0xc0, 0x84,
+	0xc9, 0x26, 0xf8, 0x75, 0xae, 0x4a, 0x3e, 0x55, 0x4d, 0xd7, 0x99, 0x51, 0x4f, 0x93, 0x4f, 0xdc,
+	0x0c, 0xff, 0x94, 0x5d, 0x87, 0x57, 0x9c, 0xe5, 0x96, 0x66, 0xbb, 0xcf, 0x4b, 0xd9, 0xf5, 0x8f,
+	0x9c, 0xe5, 0x86, 0xb3, 0xf7, 0xf0, 0x97, 0x72, 0xbe, 0x94, 0x75, 0x87, 0xf8, 0xff, 0xe7, 0xef,
+	0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x03, 0x26, 0x59, 0x0f, 0x09, 0x00, 0x00,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 50810eb..6559ba3 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -94,6 +94,14 @@
   optional PerfInfo total = 21;
 
   optional SoongBuildMetrics soong_build_metrics = 22;
+
+  optional BuildConfig build_config = 23;
+}
+
+message BuildConfig {
+  optional bool use_goma = 1;
+
+  optional bool use_rbe = 2;
 }
 
 message PerfInfo {
@@ -159,4 +167,4 @@
 
   // The approximate maximum size of the heap in soong_build in bytes.
   optional uint64 max_heap_size = 5;
-}
\ No newline at end of file
+}
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index fba2e4b..d603586 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -62,6 +62,15 @@
 	return nil
 }
 
+type rspFiles struct{}
+
+func (rspFiles) String() string { return `""` }
+
+func (rspFiles) Set(s string) error {
+	fileArgsBuilder.RspFile(s)
+	return nil
+}
+
 type dir struct{}
 
 func (dir) String() string { return `""` }
@@ -143,7 +152,8 @@
 	traceFile := flags.String("trace", "", "write trace to file")
 
 	flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
-	flags.Var(&listFiles{}, "l", "file containing list of .class files")
+	flags.Var(&listFiles{}, "l", "file containing list of files to zip")
+	flags.Var(&rspFiles{}, "r", "file containing list of files to zip with Ninja rsp file escaping")
 	flags.Var(&dir{}, "D", "directory to include in zip")
 	flags.Var(&file{}, "f", "file to include in zip")
 	flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
diff --git a/zip/zip.go b/zip/zip.go
index 3c710a7..e27432c 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -150,6 +150,30 @@
 	return b
 }
 
+func (b *FileArgsBuilder) RspFile(name string) *FileArgsBuilder {
+	if b.err != nil {
+		return b
+	}
+
+	f, err := b.fs.Open(name)
+	if err != nil {
+		b.err = err
+		return b
+	}
+	defer f.Close()
+
+	list, err := ioutil.ReadAll(f)
+	if err != nil {
+		b.err = err
+		return b
+	}
+
+	arg := b.state
+	arg.SourceFiles = ReadRespFile(list)
+	b.fileArgs = append(b.fileArgs, arg)
+	return b
+}
+
 func (b *FileArgsBuilder) Error() error {
 	if b == nil {
 		return nil
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 9705d6c..302a749 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -49,6 +49,9 @@
 	"l_nl":                []byte("a/a/a\na/a/b\nc\n"),
 	"l_sp":                []byte("a/a/a a/a/b c"),
 	"l2":                  []byte("missing\n"),
+	"rsp":                 []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'"),
+	"@ -> c":              nil,
+	"foo'bar -> c":        nil,
 	"manifest.txt":        fileCustomManifest,
 })
 
@@ -247,6 +250,19 @@
 			},
 		},
 		{
+			name: "rsp",
+			args: fileArgsBuilder().
+				RspFile("rsp"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("a/a/a", fileA, zip.Deflate),
+				fh("a/a/b", fileB, zip.Deflate),
+				fh("@", fileC, zip.Deflate),
+				fh("foo'bar", fileC, zip.Deflate),
+			},
+		},
+		{
 			name: "prefix in zip",
 			args: fileArgsBuilder().
 				PathPrefixInZip("foo").
@@ -568,6 +584,11 @@
 			in:   `./cmd "\""-C`,
 			out:  []string{"./cmd", `"-C`},
 		},
+		{
+			name: "ninja rsp file",
+			in:   "'a'\nb\n'@'\n'foo'\\''bar'\n'foo\"bar'",
+			out:  []string{"a", "b", "@", "foo'bar", `foo"bar`},
+		},
 	}
 
 	for _, testCase := range testCases {