Merge "Keep only out file dependency rule in output .d files"
diff --git a/android/apex.go b/android/apex.go
index 8c06b63..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 := ""
@@ -302,9 +399,14 @@
 				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
@@ -338,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
@@ -354,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 a1e97c9..dd622e5 100644
--- a/android/config.go
+++ b/android/config.go
@@ -35,6 +35,7 @@
 
 var Bool = proptools.Bool
 var String = proptools.String
+var StringDefault = proptools.StringDefault
 
 const FutureApiLevel = 10000
 
@@ -958,6 +959,10 @@
 	return String(c.config.productVariables.DeviceVndkVersion)
 }
 
+func (c *deviceConfig) CurrentApiLevelForVendorModules() string {
+	return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current")
+}
+
 func (c *deviceConfig) PlatformVndkVersion() string {
 	return String(c.config.productVariables.Platform_vndk_version)
 }
@@ -1393,7 +1398,7 @@
 	if len(pair) == 2 {
 		return pair[0], pair[1]
 	} else {
-		reportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
+		ReportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
 		return "error-apex", "error-jar"
 	}
 }
diff --git a/android/module.go b/android/module.go
index 6956167..17bc205 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 {
@@ -1187,7 +1188,7 @@
 
 	var deps Paths
 
-	namespacePrefix := ctx.Namespace().(*Namespace).id
+	namespacePrefix := ctx.Namespace().id
 	if namespacePrefix != "" {
 		namespacePrefix = namespacePrefix + "-"
 	}
@@ -1331,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)
@@ -1498,6 +1499,10 @@
 	return e.kind == systemExtSpecificModule
 }
 
+func (e *earlyModuleContext) Namespace() *Namespace {
+	return e.EarlyModuleContext.Namespace().(*Namespace)
+}
+
 type baseModuleContext struct {
 	bp blueprint.BaseModuleContext
 	earlyModuleContext
@@ -2241,7 +2246,7 @@
 		return nil
 	}
 	if len(paths) > 1 {
-		reportPathErrorf(ctx, "got multiple output files from module %q, expected exactly one",
+		ReportPathErrorf(ctx, "got multiple output files from module %q, expected exactly one",
 			pathContextName(ctx, module))
 		return nil
 	}
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/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/paths.go b/android/paths.go
index 65f129c..6b603ba 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -86,13 +86,13 @@
 // attempts ctx.ModuleErrorf for a better error message first, then falls
 // back to ctx.Errorf.
 func reportPathError(ctx PathContext, err error) {
-	reportPathErrorf(ctx, "%s", err.Error())
+	ReportPathErrorf(ctx, "%s", err.Error())
 }
 
-// reportPathErrorf will register an error with the attached context. It
+// ReportPathErrorf will register an error with the attached context. It
 // attempts ctx.ModuleErrorf for a better error message first, then falls
 // back to ctx.Errorf.
-func reportPathErrorf(ctx PathContext, format string, args ...interface{}) {
+func ReportPathErrorf(ctx PathContext, format string, args ...interface{}) {
 	if mctx, ok := ctx.(moduleErrorf); ok {
 		mctx.ModuleErrorf(format, args...)
 	} else if ectx, ok := ctx.(errorfContext); ok {
@@ -155,7 +155,7 @@
 	if path, ok := p.(genPathProvider); ok {
 		return path.genPathWithExt(ctx, subdir, ext)
 	}
-	reportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
+	ReportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
 	return PathForModuleGen(ctx)
 }
 
@@ -165,7 +165,7 @@
 	if path, ok := p.(objPathProvider); ok {
 		return path.objPathWithExt(ctx, subdir, ext)
 	}
-	reportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+	ReportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
 	return PathForModuleObj(ctx)
 }
 
@@ -176,7 +176,7 @@
 	if path, ok := p.(resPathProvider); ok {
 		return path.resPathWithName(ctx, name)
 	}
-	reportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+	ReportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
 	return PathForModuleRes(ctx)
 }
 
@@ -416,9 +416,9 @@
 	} else {
 		p := pathForModuleSrc(ctx, s)
 		if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
-			reportPathErrorf(ctx, "%s: %s", p, err.Error())
+			ReportPathErrorf(ctx, "%s: %s", p, err.Error())
 		} else if !exists && !ctx.Config().testAllowNonExistentPaths {
-			reportPathErrorf(ctx, "module source path %q does not exist", p)
+			ReportPathErrorf(ctx, "module source path %q does not exist", p)
 		}
 
 		if InList(p.String(), expandedExcludes) {
@@ -445,7 +445,7 @@
 		}
 		path := filepath.Clean(p)
 		if !strings.HasPrefix(path, prefix) {
-			reportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix)
+			ReportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix)
 			continue
 		}
 
@@ -801,7 +801,7 @@
 	}
 
 	if pathtools.IsGlob(path.String()) {
-		reportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
+		ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
 	}
 
 	if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() {
@@ -813,9 +813,9 @@
 			modCtx.AddMissingDependencies([]string{path.String()})
 		}
 	} else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
-		reportPathErrorf(ctx, "%s: %s", path, err.Error())
+		ReportPathErrorf(ctx, "%s: %s", path, err.Error())
 	} else if !exists && !ctx.Config().testAllowNonExistentPaths {
-		reportPathErrorf(ctx, "source path %q does not exist", path)
+		ReportPathErrorf(ctx, "source path %q does not exist", path)
 	}
 	return path
 }
@@ -831,7 +831,7 @@
 	}
 
 	if pathtools.IsGlob(path.String()) {
-		reportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
+		ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
 		return OptionalPath{}
 	}
 
@@ -876,17 +876,17 @@
 	if srcPath, ok := path.(SourcePath); ok {
 		relDir = srcPath.path
 	} else {
-		reportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
+		ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
 		return OptionalPath{}
 	}
 	dir := filepath.Join(p.config.srcDir, p.path, relDir)
 	// Use Glob so that we are run again if the directory is added.
 	if pathtools.IsGlob(dir) {
-		reportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
+		ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
 	}
 	paths, err := ctx.GlobWithDeps(dir, nil)
 	if err != nil {
-		reportPathErrorf(ctx, "glob: %s", err.Error())
+		ReportPathErrorf(ctx, "glob: %s", err.Error())
 		return OptionalPath{}
 	}
 	if len(paths) == 0 {
@@ -977,7 +977,7 @@
 // ReplaceExtension creates a new OutputPath with the extension replaced with ext.
 func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
 	if strings.Contains(ext, "/") {
-		reportPathErrorf(ctx, "extension %q cannot contain /", ext)
+		ReportPathErrorf(ctx, "extension %q cannot contain /", ext)
 	}
 	ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext))
 	ret.rel = pathtools.ReplaceExtension(p.rel, ext)
@@ -1030,10 +1030,10 @@
 		}
 		return nil
 	} else if len(paths) == 0 {
-		reportPathErrorf(ctx, "%q produced no files, expected exactly one", p)
+		ReportPathErrorf(ctx, "%q produced no files, expected exactly one", p)
 		return nil
 	} else if len(paths) > 1 {
-		reportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths))
+		ReportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths))
 	}
 	return paths[0]
 }
@@ -1447,7 +1447,7 @@
 
 func PathForPhony(ctx PathContext, phony string) WritablePath {
 	if strings.ContainsAny(phony, "$/") {
-		reportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
+		ReportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
 	}
 	return PhonyPath{basePath{phony, ctx.Config(), ""}}
 }
@@ -1513,7 +1513,7 @@
 func Rel(ctx PathContext, basePath string, targetPath string) string {
 	rel, isRel := MaybeRel(ctx, basePath, targetPath)
 	if !isRel {
-		reportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath)
+		ReportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath)
 		return ""
 	}
 	return rel
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..8ea4168 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}
@@ -177,19 +187,21 @@
 	}
 }
 
-func maybeBuildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
+func maybeBuildParamsFromRule(provider testBuildProvider, rule string) (TestingBuildParams, []string) {
+	var searchedRules []string
 	for _, p := range provider.BuildParamsForTests() {
+		searchedRules = append(searchedRules, p.Rule.String())
 		if strings.Contains(p.Rule.String(), rule) {
-			return newTestingBuildParams(provider, p)
+			return newTestingBuildParams(provider, p), searchedRules
 		}
 	}
-	return TestingBuildParams{}
+	return TestingBuildParams{}, searchedRules
 }
 
 func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
-	p := maybeBuildParamsFromRule(provider, rule)
+	p, searchRules := maybeBuildParamsFromRule(provider, rule)
 	if p.Rule == nil {
-		panic(fmt.Errorf("couldn't find rule %q", rule))
+		panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules))
 	}
 	return p
 }
@@ -265,7 +277,8 @@
 // MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
 // BuildParams if no rule is found.
 func (m TestingModule) MaybeRule(rule string) TestingBuildParams {
-	return maybeBuildParamsFromRule(m.module, rule)
+	r, _ := maybeBuildParamsFromRule(m.module, rule)
+	return r
 }
 
 // Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
@@ -318,7 +331,8 @@
 // MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
 // BuildParams if no rule is found.
 func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams {
-	return maybeBuildParamsFromRule(s.provider, rule)
+	r, _ := maybeBuildParamsFromRule(s.provider, rule)
+	return r
 }
 
 // Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
@@ -384,7 +398,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 1f21f34..53f081e 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -166,13 +166,14 @@
 	Platform_min_supported_target_sdk_version *string  `json:",omitempty"`
 	Platform_base_os                          *string  `json:",omitempty"`
 
-	DeviceName              *string  `json:",omitempty"`
-	DeviceArch              *string  `json:",omitempty"`
-	DeviceArchVariant       *string  `json:",omitempty"`
-	DeviceCpuVariant        *string  `json:",omitempty"`
-	DeviceAbi               []string `json:",omitempty"`
-	DeviceVndkVersion       *string  `json:",omitempty"`
-	DeviceSystemSdkVersions []string `json:",omitempty"`
+	DeviceName                            *string  `json:",omitempty"`
+	DeviceArch                            *string  `json:",omitempty"`
+	DeviceArchVariant                     *string  `json:",omitempty"`
+	DeviceCpuVariant                      *string  `json:",omitempty"`
+	DeviceAbi                             []string `json:",omitempty"`
+	DeviceVndkVersion                     *string  `json:",omitempty"`
+	DeviceCurrentApiLevelForVendorModules *string  `json:",omitempty"`
+	DeviceSystemSdkVersions               []string `json:",omitempty"`
 
 	DeviceSecondaryArch        *string  `json:",omitempty"`
 	DeviceSecondaryArchVariant *string  `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index 8d9aa51..1c64328 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -41,6 +41,9 @@
 	imageApexType     = "image"
 	zipApexType       = "zip"
 	flattenedApexType = "flattened"
+
+	ext4FsType = "ext4"
+	f2fsFsType = "f2fs"
 )
 
 type dependencyTag struct {
@@ -669,6 +672,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()
@@ -686,9 +690,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())
@@ -721,6 +727,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
@@ -733,18 +750,6 @@
 	if am, ok := mctx.Module().(android.ApexModule); ok {
 		availableToPlatform := am.AvailableFor(android.AvailableToPlatform)
 
-		// In a rare case when a lib is marked as available only to an apex
-		// but the apex doesn't exist. This can happen in a partial manifest branch
-		// like master-art. Currently, libstatssocket in the stats APEX is causing
-		// this problem.
-		// Include the lib in platform because the module SDK that ought to provide
-		// it doesn't exist, so it would otherwise be left out completely.
-		// TODO(b/154888298) remove this by adding those libraries in module SDKS and skipping
-		// this check for libraries provided by SDKs.
-		if !availableToPlatform && !android.InAnyApex(am.Name()) {
-			availableToPlatform = true
-		}
-
 		// If any of the dep is not available to platform, this module is also considered
 		// as being not available to platform even if it has "//apex_available:platform"
 		mctx.VisitDirectDeps(func(child android.Module) {
@@ -1026,6 +1031,10 @@
 	// Should be only used in non-system apexes (e.g. vendor: true).
 	// Default is false.
 	Use_vndk_as_stable *bool
+
+	// The type of filesystem to use for an image apex. Either 'ext4' or 'f2fs'.
+	// Default 'ext4'.
+	Payload_fs_type *string
 }
 
 type apexTargetBundleProperties struct {
@@ -1233,6 +1242,24 @@
 	return false
 }
 
+type fsType int
+
+const (
+	ext4 fsType = iota
+	f2fs
+)
+
+func (f fsType) string() string {
+	switch f {
+	case ext4:
+		return ext4FsType
+	case f2fs:
+		return f2fsFsType
+	default:
+		panic(fmt.Errorf("unknown APEX payload type %d", f))
+	}
+}
+
 type apexBundle struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -1298,6 +1325,8 @@
 
 	// Optional list of lint report zip files for apexes that contain java or app modules
 	lintReports android.Paths
+
+	payloadFsType fsType
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -1711,7 +1740,7 @@
 }
 
 func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
-	dirInApex := filepath.Join("etc", prebuilt.SubDir())
+	dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
 	fileToCopy := prebuilt.OutputFile()
 	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
 }
@@ -1797,7 +1826,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 */)
 		}
 
@@ -2270,6 +2299,15 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
+	switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+	case ext4FsType:
+		a.payloadFsType = ext4
+	case f2fsFsType:
+		a.payloadFsType = f2fs
+	default:
+		ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs]", *a.properties.Payload_fs_type)
+	}
+
 	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
 	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
 	// the same library in the system partition, thus effectively sharing the same libraries
diff --git a/apex/apex_test.go b/apex/apex_test.go
index d13ec5f..391fca0 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -248,7 +248,7 @@
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
-	ctx.RegisterModuleType("prebuilt_etc", prebuilt_etc.PrebuiltEtcFactory)
+	prebuilt_etc.RegisterPrebuiltEtcBuildComponents(ctx)
 	ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
 	ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
@@ -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{
@@ -2269,6 +2272,32 @@
 	ensureListContains(t, requireNativeLibs, ":vndk")
 }
 
+func TestVendorApex_withPrebuiltFirmware(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			prebuilts: ["myfirmware"],
+			vendor: true,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		prebuilt_firmware {
+			name: "myfirmware",
+			src: "myfirmware.bin",
+			filename_from_src: true,
+			vendor: true,
+		}
+	`)
+
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"firmware/myfirmware.bin",
+	})
+}
+
 func TestAndroidMk_UseVendorRequired(t *testing.T) {
 	ctx, config := testApex(t, `
 		apex {
@@ -2633,7 +2662,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 +2687,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 +2732,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 +3522,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 +3578,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 +3662,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 +4129,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 +4310,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 +4831,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)
@@ -5358,29 +5425,6 @@
 	}
 }
 
-func TestApexWithJniLibs_Errors(t *testing.T) {
-	testApexError(t, `jni_libs: "xxx" is not a cc_library`, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			jni_libs: ["xxx"],
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		prebuilt_etc {
-			name: "xxx",
-			src: "xxx",
-		}
-	`, withFiles(map[string][]byte{
-		"xxx": nil,
-	}))
-}
-
 func TestAppBundle(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -5616,7 +5660,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 +5668,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 +5676,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 +5684,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 +5715,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/apex/builder.go b/apex/builder.go
index 0a1ec3e..22cd69b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -63,6 +63,8 @@
 	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
 	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
 	pctx.HostBinToolVariable("extract_apks", "extract_apks")
+	pctx.HostBinToolVariable("make_f2fs", "make_f2fs")
+	pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs")
 }
 
 var (
@@ -116,12 +118,12 @@
 			`--payload_type image ` +
 			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
 		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
-			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
+			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}",
 			"${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest")
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest", "payload_fs_type")
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
@@ -193,7 +195,7 @@
 	// collect jniLibs. Notice that a.filesInfo is already sorted
 	var jniLibs []string
 	for _, fi := range a.filesInfo {
-		if fi.isJniLib {
+		if fi.isJniLib && !android.InList(fi.Stem(), jniLibs) {
 			jniLibs = append(jniLibs, fi.Stem())
 		}
 	}
@@ -582,6 +584,8 @@
 			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
 		}
 
+		optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string())
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        apexRule,
 			Implicits:   implicitInputs,
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 9ed8d81..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 {
@@ -372,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 63bbd83..b3ce5ff 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -83,7 +83,7 @@
 type binaryDecorator struct {
 	*baseLinker
 	*baseInstaller
-	stripper
+	stripper Stripper
 
 	Properties BinaryLinkerProperties
 
@@ -317,14 +317,14 @@
 	}
 
 	builderFlags := flagsToBuilderFlags(flags)
-
-	if binary.stripper.needsStrip(ctx) {
+	stripFlags := flagsToStripFlags(flags)
+	if binary.stripper.NeedsStrip(ctx) {
 		if ctx.Darwin() {
-			builderFlags.stripUseGnuStrip = true
+			stripFlags.StripUseGnuStrip = true
 		}
 		strippedOutputFile := outputFile
 		outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
-		binary.stripper.stripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile, builderFlags)
+		binary.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile, stripFlags)
 	}
 
 	binary.unstrippedOutputFile = outputFile
@@ -333,7 +333,7 @@
 		afterPrefixSymbols := outputFile
 		outputFile = android.PathForModuleOut(ctx, "unprefixed", fileName)
 		TransformBinaryPrefixSymbols(ctx, String(binary.Properties.Prefix_symbols), outputFile,
-			flagsToBuilderFlags(flags), afterPrefixSymbols)
+			builderFlags, afterPrefixSymbols)
 	}
 
 	outputFile = maybeInjectBoringSSLHash(ctx, outputFile, binary.Properties.Inject_bssl_hash, fileName)
@@ -347,10 +347,10 @@
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
 			binary.distFiles = android.MakeDefaultDistFiles(versionedOutputFile)
 
-			if binary.stripper.needsStrip(ctx) {
+			if binary.stripper.NeedsStrip(ctx) {
 				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
 				binary.distFiles = android.MakeDefaultDistFiles(out)
-				binary.stripper.stripExecutableOrSharedLib(ctx, versionedOutputFile, out, builderFlags)
+				binary.stripper.StripExecutableOrSharedLib(ctx, versionedOutputFile, out, stripFlags)
 			}
 
 			binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
@@ -445,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 android.DirectlyInAnyApex(ctx, ctx.ModuleName()) && 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 28a573f..81d2f1e 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -69,12 +69,12 @@
 		&remoteexec.REParams{
 			Labels:          map[string]string{"type": "link", "tool": "clang"},
 			ExecStrategy:    "${config.RECXXLinksExecStrategy}",
-			Inputs:          []string{"${out}.rsp"},
+			Inputs:          []string{"${out}.rsp", "$implicitInputs"},
 			RSPFile:         "${out}.rsp",
 			OutputFiles:     []string{"${out}", "$implicitOutputs"},
 			ToolchainInputs: []string{"$ldCmd"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
-		}, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags"}, []string{"implicitOutputs"})
+		}, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags"}, []string{"implicitInputs", "implicitOutputs"})
 
 	partialLd, partialLdRE = remoteexec.StaticRules(pctx, "partialLd",
 		blueprint.RuleParams{
@@ -83,12 +83,13 @@
 			Command:     "$reTemplate$ldCmd -fuse-ld=lld -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
 			CommandDeps: []string{"$ldCmd"},
 		}, &remoteexec.REParams{
-			Labels:       map[string]string{"type": "link", "tool": "clang"},
-			ExecStrategy: "${config.RECXXLinksExecStrategy}", Inputs: []string{"$inCommaList"},
+			Labels:          map[string]string{"type": "link", "tool": "clang"},
+			ExecStrategy:    "${config.RECXXLinksExecStrategy}",
+			Inputs:          []string{"$inCommaList", "$implicitInputs"},
 			OutputFiles:     []string{"${out}", "$implicitOutputs"},
 			ToolchainInputs: []string{"$ldCmd"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
-		}, []string{"ldCmd", "ldFlags"}, []string{"inCommaList", "implicitOutputs"})
+		}, []string{"ldCmd", "ldFlags"}, []string{"implicitInputs", "inCommaList", "implicitOutputs"})
 
 	ar = pctx.AndroidStaticRule("ar",
 		blueprint.RuleParams{
@@ -186,8 +187,8 @@
 			// 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}"},
+			OutputFiles: []string{"$in"},
+			Platform:    map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
 		}, []string{"cFlags", "tidyFlags"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -236,12 +237,12 @@
 		}, &remoteexec.REParams{
 			Labels:          map[string]string{"type": "tool", "name": "abi-linker"},
 			ExecStrategy:    "${config.REAbiLinkerExecStrategy}",
-			Inputs:          []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicits"},
+			Inputs:          []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicitInputs"},
 			RSPFile:         "${out}.rsp",
 			OutputFiles:     []string{"$out"},
 			ToolchainInputs: []string{"$sAbiLinker"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXPool}"},
-		}, []string{"symbolFilter", "arch", "exportedHeaderFlags"}, []string{"implicits"})
+		}, []string{"symbolFilter", "arch", "exportedHeaderFlags"}, []string{"implicitInputs"})
 
 	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
 
@@ -265,9 +266,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",
 		})
 
@@ -349,18 +350,22 @@
 
 	groupStaticLibs bool
 
-	stripKeepSymbols              bool
-	stripKeepSymbolsList          string
-	stripKeepSymbolsAndDebugFrame bool
-	stripKeepMiniDebugInfo        bool
-	stripAddGnuDebuglink          bool
-	stripUseGnuStrip              bool
-
 	proto            android.ProtoFlags
 	protoC           bool
 	protoOptionsFile bool
 
 	yacc *YaccProperties
+	lex  *LexProperties
+}
+
+type StripFlags struct {
+	Toolchain                     config.Toolchain
+	StripKeepSymbols              bool
+	StripKeepSymbolsList          string
+	StripKeepSymbolsAndDebugFrame bool
+	StripKeepMiniDebugInfo        bool
+	StripAddGnuDebuglink          bool
+	StripUseGnuStrip              bool
 }
 
 type Objects struct {
@@ -743,6 +748,7 @@
 	if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = ldRE
 		args["implicitOutputs"] = strings.Join(implicitOutputs.Strings(), ",")
+		args["implicitInputs"] = strings.Join(deps.Strings(), ",")
 	}
 
 	ctx.Build(pctx, android.BuildParams{
@@ -792,7 +798,7 @@
 				rbeImplicits = append(rbeImplicits, p[2:])
 			}
 		}
-		args["implicits"] = strings.Join(rbeImplicits, ",")
+		args["implicitInputs"] = strings.Join(rbeImplicits, ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
@@ -909,6 +915,7 @@
 	if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = partialLdRE
 		args["inCommaList"] = strings.Join(objFiles.Strings(), ",")
+		args["implicitInputs"] = strings.Join(deps.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
@@ -939,26 +946,26 @@
 }
 
 func TransformStrip(ctx android.ModuleContext, inputFile android.Path,
-	outputFile android.WritablePath, flags builderFlags) {
+	outputFile android.WritablePath, flags StripFlags) {
 
-	crossCompile := gccCmd(flags.toolchain, "")
+	crossCompile := gccCmd(flags.Toolchain, "")
 	args := ""
-	if flags.stripAddGnuDebuglink {
+	if flags.StripAddGnuDebuglink {
 		args += " --add-gnu-debuglink"
 	}
-	if flags.stripKeepMiniDebugInfo {
+	if flags.StripKeepMiniDebugInfo {
 		args += " --keep-mini-debug-info"
 	}
-	if flags.stripKeepSymbols {
+	if flags.StripKeepSymbols {
 		args += " --keep-symbols"
 	}
-	if flags.stripKeepSymbolsList != "" {
-		args += " -k" + flags.stripKeepSymbolsList
+	if flags.StripKeepSymbolsList != "" {
+		args += " -k" + flags.StripKeepSymbolsList
 	}
-	if flags.stripKeepSymbolsAndDebugFrame {
+	if flags.StripKeepSymbolsAndDebugFrame {
 		args += " --keep-symbols-and-debug-frame"
 	}
-	if flags.stripUseGnuStrip {
+	if flags.StripUseGnuStrip {
 		args += " --use-gnu-strip"
 	}
 
diff --git a/cc/cc.go b/cc/cc.go
index abe003e..9196d47 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -210,6 +210,7 @@
 	protoOptionsFile bool // Whether to look for a .options file next to the .proto
 
 	Yacc *YaccProperties
+	Lex  *LexProperties
 }
 
 // Properties used to compile all C or C++ modules
@@ -344,7 +345,7 @@
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
 	isForPlatform() bool
-	apexName() string
+	apexVariationName() string
 	apexSdkVersion() int
 	hasStubsVariants() bool
 	isStubs() bool
@@ -1290,8 +1291,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 {
@@ -2390,7 +2391,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
@@ -2420,9 +2421,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
@@ -2446,7 +2447,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
@@ -2895,6 +2896,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 &&
diff --git a/cc/compiler.go b/cc/compiler.go
index d0b5b46..f504c38 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -111,6 +111,7 @@
 	Gnu_extensions *bool
 
 	Yacc *YaccProperties
+	Lex  *LexProperties
 
 	Aidl struct {
 		// list of directories that will be added to the aidl include paths.
@@ -189,8 +190,14 @@
 	// Build and link with OpenMP
 	Openmp *bool `android:"arch_variant"`
 
+	// Deprecated.
 	// Adds __ANDROID_APEX_<APEX_MODULE_NAME>__ macro defined for apex variants in addition to __ANDROID_APEX__
 	Use_apex_name_macro *bool
+
+	// Adds two macros for apex variants in addition to __ANDROID_APEX__
+	// * __ANDROID_APEX_COM_ANDROID_FOO__
+	// * __ANDROID_APEX_NAME__="com.android.foo"
+	UseApexNameMacro bool `blueprint:"mutated"`
 }
 
 func NewBaseCompiler() *baseCompiler {
@@ -254,6 +261,10 @@
 	return deps
 }
 
+func (compiler *baseCompiler) useApexNameMacro() bool {
+	return Bool(compiler.Properties.Use_apex_name_macro) || compiler.Properties.UseApexNameMacro
+}
+
 // Return true if the module is in the WarningAllowedProjects.
 func warningsAreAllowed(subdir string) bool {
 	subdir += "/"
@@ -289,6 +300,7 @@
 	flags.Local.YasmFlags = append(flags.Local.YasmFlags, esc(compiler.Properties.Asflags)...)
 
 	flags.Yacc = compiler.Properties.Yacc
+	flags.Lex = compiler.Properties.Lex
 
 	// Include dir cflags
 	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
@@ -335,10 +347,11 @@
 		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())+"__")
+		if compiler.useApexNameMacro() {
+			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_"+makeDefineString(ctx.apexVariationName())+"__")
+			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX_NAME__='\""+ctx.apexVariationName()+"\"'")
 		}
 		if ctx.Device() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_SDK_VERSION__="+strconv.Itoa(ctx.apexSdkVersion()))
@@ -556,6 +569,10 @@
 	return false
 }
 
+func (compiler *baseCompiler) uniqueApexVariations() bool {
+	return compiler.useApexNameMacro()
+}
+
 // 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 {
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/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/gen.go b/cc/gen.go
index b0aadc6..ccc3d0e 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -34,9 +34,9 @@
 var (
 	lex = pctx.AndroidStaticRule("lex",
 		blueprint.RuleParams{
-			Command:     "M4=$m4Cmd $lexCmd -o$out $in",
+			Command:     "M4=$m4Cmd $lexCmd $flags -o$out $in",
 			CommandDeps: []string{"$lexCmd", "$m4Cmd"},
-		})
+		}, "flags")
 
 	sysprop = pctx.AndroidStaticRule("sysprop",
 		blueprint.RuleParams{
@@ -153,12 +153,23 @@
 	}
 }
 
-func genLex(ctx android.ModuleContext, lexFile android.Path, outFile android.ModuleGenPath) {
+type LexProperties struct {
+	// list of module-specific flags that will be used for .l and .ll compiles
+	Flags []string
+}
+
+func genLex(ctx android.ModuleContext, lexFile android.Path, outFile android.ModuleGenPath, props *LexProperties) {
+	var flags []string
+	if props != nil {
+		flags = props.Flags
+	}
+	flagsString := strings.Join(flags[:], " ")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        lex,
 		Description: "lex " + lexFile.Rel(),
 		Output:      outFile,
 		Input:       lexFile,
+		Args:        map[string]string{"flags": flagsString},
 	})
 }
 
@@ -235,11 +246,11 @@
 		case ".l":
 			cFile := android.GenPathWithExt(ctx, "lex", srcFile, "c")
 			srcFiles[i] = cFile
-			genLex(ctx, srcFile, cFile)
+			genLex(ctx, srcFile, cFile, buildFlags.lex)
 		case ".ll":
 			cppFile := android.GenPathWithExt(ctx, "lex", srcFile, "cpp")
 			srcFiles[i] = cppFile
-			genLex(ctx, srcFile, cppFile)
+			genLex(ctx, srcFile, cppFile, buildFlags.lex)
 		case ".proto":
 			ccFile, headerFile := genProto(ctx, srcFile, buildFlags)
 			srcFiles[i] = ccFile
diff --git a/cc/library.go b/cc/library.go
index 1c2b1ee..92853b5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -336,7 +336,7 @@
 	tocFile android.OptionalPath
 
 	flagExporter
-	stripper
+	stripper Stripper
 
 	// If we're used as a whole_static_lib, our missing dependencies need
 	// to be given
@@ -955,13 +955,14 @@
 	library.tocFile = android.OptionalPathForPath(tocFile)
 	TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
 
-	if library.stripper.needsStrip(ctx) {
+	stripFlags := flagsToStripFlags(flags)
+	if library.stripper.NeedsStrip(ctx) {
 		if ctx.Darwin() {
-			builderFlags.stripUseGnuStrip = true
+			stripFlags.StripUseGnuStrip = true
 		}
 		strippedOutputFile := outputFile
 		outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
-		library.stripper.stripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile, builderFlags)
+		library.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile, stripFlags)
 	}
 	library.unstrippedOutputFile = outputFile
 
@@ -976,10 +977,10 @@
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
 			library.distFile = versionedOutputFile
 
-			if library.stripper.needsStrip(ctx) {
+			if library.stripper.NeedsStrip(ctx) {
 				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
 				library.distFile = out
-				library.stripper.stripExecutableOrSharedLib(ctx, versionedOutputFile, out, builderFlags)
+				library.stripper.StripExecutableOrSharedLib(ctx, versionedOutputFile, out, stripFlags)
 			}
 
 			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
@@ -1027,6 +1028,10 @@
 	return library.unstrippedOutputFile
 }
 
+func (library *libraryDecorator) disableStripping() {
+	library.stripper.StripProperties.Strip.None = BoolPtr(true)
+}
+
 func (library *libraryDecorator) nativeCoverage() bool {
 	if library.header() || library.buildStubs() {
 		return false
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/llndk_library.go b/cc/llndk_library.go
index 71c9204..b3f9d61 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -179,7 +179,7 @@
 	library.BuildOnlyShared()
 	module.stl = nil
 	module.sanitize = nil
-	library.StripProperties.Strip.None = BoolPtr(true)
+	library.disableStripping()
 
 	stub := &llndkStubDecorator{
 		libraryDecorator: library,
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 58e742e..fe3efc0 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -405,7 +405,7 @@
 	library.BuildOnlyShared()
 	module.stl = nil
 	module.sanitize = nil
-	library.StripProperties.Strip.None = BoolPtr(true)
+	library.disableStripping()
 
 	stub := &stubDecorator{
 		libraryDecorator: library,
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index baf43ce..3af65d6 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -125,9 +125,10 @@
 			outputFile := android.PathForModuleOut(ctx, libName)
 			var implicits android.Paths
 
-			if p.needsStrip(ctx) {
+			if p.stripper.NeedsStrip(ctx) {
+				stripFlags := flagsToStripFlags(flags)
 				stripped := android.PathForModuleOut(ctx, "stripped", libName)
-				p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
+				p.stripper.StripExecutableOrSharedLib(ctx, in, stripped, stripFlags)
 				in = stripped
 			}
 
@@ -331,16 +332,16 @@
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 	// TODO(ccross): verify shared library dependencies
 	if len(p.properties.Srcs) > 0 {
-		builderFlags := flagsToBuilderFlags(flags)
+		stripFlags := flagsToStripFlags(flags)
 
 		fileName := p.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
 		in := p.Prebuilt.SingleSourcePath(ctx)
 
 		p.unstrippedOutputFile = in
 
-		if p.needsStrip(ctx) {
+		if p.stripper.NeedsStrip(ctx) {
 			stripped := android.PathForModuleOut(ctx, "stripped", fileName)
-			p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
+			p.stripper.StripExecutableOrSharedLib(ctx, in, stripped, stripFlags)
 			in = stripped
 		}
 
diff --git a/cc/strip.go b/cc/strip.go
index 7e560ec..18150dc 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -30,42 +30,42 @@
 	} `android:"arch_variant"`
 }
 
-type stripper struct {
+type Stripper struct {
 	StripProperties StripProperties
 }
 
-func (stripper *stripper) needsStrip(ctx ModuleContext) bool {
+func (stripper *Stripper) NeedsStrip(actx android.ModuleContext) bool {
 	// TODO(ccross): enable host stripping when embedded in make?  Make never had support for stripping host binaries.
-	return (!ctx.Config().EmbeddedInMake() || ctx.Device()) && !Bool(stripper.StripProperties.Strip.None)
+	return (!actx.Config().EmbeddedInMake() || actx.Device()) && !Bool(stripper.StripProperties.Strip.None)
 }
 
-func (stripper *stripper) strip(ctx ModuleContext, in android.Path, out android.ModuleOutPath,
-	flags builderFlags, isStaticLib bool) {
-	if ctx.Darwin() {
-		TransformDarwinStrip(ctx, in, out)
+func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
+	flags StripFlags, isStaticLib bool) {
+	if actx.Darwin() {
+		TransformDarwinStrip(actx, in, out)
 	} else {
 		if Bool(stripper.StripProperties.Strip.Keep_symbols) {
-			flags.stripKeepSymbols = true
+			flags.StripKeepSymbols = true
 		} else if Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame) {
-			flags.stripKeepSymbolsAndDebugFrame = true
+			flags.StripKeepSymbolsAndDebugFrame = true
 		} else if len(stripper.StripProperties.Strip.Keep_symbols_list) > 0 {
-			flags.stripKeepSymbolsList = strings.Join(stripper.StripProperties.Strip.Keep_symbols_list, ",")
+			flags.StripKeepSymbolsList = strings.Join(stripper.StripProperties.Strip.Keep_symbols_list, ",")
 		} else if !Bool(stripper.StripProperties.Strip.All) {
-			flags.stripKeepMiniDebugInfo = true
+			flags.StripKeepMiniDebugInfo = true
 		}
-		if ctx.Config().Debuggable() && !flags.stripKeepMiniDebugInfo && !isStaticLib {
-			flags.stripAddGnuDebuglink = true
+		if actx.Config().Debuggable() && !flags.StripKeepMiniDebugInfo && !isStaticLib {
+			flags.StripAddGnuDebuglink = true
 		}
-		TransformStrip(ctx, in, out, flags)
+		TransformStrip(actx, in, out, flags)
 	}
 }
 
-func (stripper *stripper) stripExecutableOrSharedLib(ctx ModuleContext, in android.Path,
-	out android.ModuleOutPath, flags builderFlags) {
-	stripper.strip(ctx, in, out, flags, false)
+func (stripper *Stripper) StripExecutableOrSharedLib(actx android.ModuleContext, in android.Path,
+	out android.ModuleOutPath, flags StripFlags) {
+	stripper.strip(actx, in, out, flags, false)
 }
 
-func (stripper *stripper) stripStaticLib(ctx ModuleContext, in android.Path, out android.ModuleOutPath,
-	flags builderFlags) {
-	stripper.strip(ctx, in, out, flags, true)
+func (stripper *Stripper) StripStaticLib(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
+	flags StripFlags) {
+	stripper.strip(actx, in, out, flags, true)
 }
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/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/cc/toolchain_library.go b/cc/toolchain_library.go
index 042e012..19f5ea4 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -36,8 +36,7 @@
 
 type toolchainLibraryDecorator struct {
 	*libraryDecorator
-
-	stripper
+	stripper Stripper
 
 	Properties toolchainLibraryProperties
 }
@@ -89,8 +88,8 @@
 	if library.stripper.StripProperties.Strip.Keep_symbols_list != nil {
 		fileName := ctx.ModuleName() + staticLibraryExtension
 		outputFile := android.PathForModuleOut(ctx, fileName)
-		buildFlags := flagsToBuilderFlags(flags)
-		library.stripper.stripStaticLib(ctx, srcPath, outputFile, buildFlags)
+		stripFlags := flagsToStripFlags(flags)
+		library.stripper.StripStaticLib(ctx, srcPath, outputFile, stripFlags)
 		return outputFile
 	}
 
diff --git a/cc/util.go b/cc/util.go
index af26268..40374bf 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -97,9 +97,14 @@
 		protoOptionsFile: in.protoOptionsFile,
 
 		yacc: in.Yacc,
+		lex:  in.Lex,
 	}
 }
 
+func flagsToStripFlags(in Flags) StripFlags {
+	return StripFlags{Toolchain: in.Toolchain}
+}
+
 func addPrefix(list []string, prefix string) []string {
 	for i := range list {
 		list[i] = prefix + list[i]
diff --git a/cc/vendor_public_library.go b/cc/vendor_public_library.go
index e9d1c73..85f514c 100644
--- a/cc/vendor_public_library.go
+++ b/cc/vendor_public_library.go
@@ -144,7 +144,7 @@
 	library.BuildOnlyShared()
 	module.stl = nil
 	module.sanitize = nil
-	library.StripProperties.Strip.None = BoolPtr(true)
+	library.disableStripping()
 
 	stub := &vendorPublicLibraryStubDecorator{
 		libraryDecorator: library,
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index e17a6d0..93aece4 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -264,7 +264,7 @@
 
 	module.stl = nil
 	module.sanitize = nil
-	library.StripProperties.Strip.None = BoolPtr(true)
+	library.disableStripping()
 
 	prebuilt := &vendorSnapshotLibraryDecorator{
 		libraryDecorator: library,
@@ -340,12 +340,12 @@
 	}
 
 	in := android.PathForModuleSrc(ctx, *p.properties.Src)
-	builderFlags := flagsToBuilderFlags(flags)
+	stripFlags := flagsToStripFlags(flags)
 	p.unstrippedOutputFile = in
 	binName := in.Base()
-	if p.needsStrip(ctx) {
+	if p.stripper.NeedsStrip(ctx) {
 		stripped := android.PathForModuleOut(ctx, "stripped", binName)
-		p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
+		p.stripper.StripExecutableOrSharedLib(ctx, in, stripped, stripFlags)
 		in = stripped
 	}
 
diff --git a/cc/vndk.go b/cc/vndk.go
index 23bb095..9a2fa09 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -505,18 +505,25 @@
 	}}
 }
 
+// PrebuiltEtcModule interface
 func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
 	return txt.outputFile
 }
 
-func (txt *vndkLibrariesTxt) OutputFiles(tag string) (android.Paths, error) {
-	return android.Paths{txt.outputFile}, nil
+// PrebuiltEtcModule interface
+func (txt *vndkLibrariesTxt) BaseDir() string {
+	return "etc"
 }
 
+// PrebuiltEtcModule interface
 func (txt *vndkLibrariesTxt) SubDir() string {
 	return ""
 }
 
+func (txt *vndkLibrariesTxt) OutputFiles(tag string) (android.Paths, error) {
+	return android.Paths{txt.outputFile}, nil
+}
+
 func VndkSnapshotSingleton() android.Singleton {
 	return &vndkSnapshotSingleton{}
 }
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 5a44c46..9484760 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -142,9 +142,10 @@
 		builderFlags := flagsToBuilderFlags(flags)
 		p.unstrippedOutputFile = in
 		libName := in.Base()
-		if p.needsStrip(ctx) {
+		if p.stripper.NeedsStrip(ctx) {
+			stripFlags := flagsToStripFlags(flags)
 			stripped := android.PathForModuleOut(ctx, "stripped", libName)
-			p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
+			p.stripper.StripExecutableOrSharedLib(ctx, in, stripped, stripFlags)
 			in = stripped
 		}
 
@@ -213,7 +214,7 @@
 	library.BuildOnlyShared()
 	module.stl = nil
 	module.sanitize = nil
-	library.StripProperties.Strip.None = BoolPtr(true)
+	library.disableStripping()
 
 	prebuilt := &vndkPrebuiltLibraryDecorator{
 		libraryDecorator: library,
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 7b8352b..4ebbe68 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -28,5 +28,8 @@
         "writedocs.go",
         "bazel_overlay.go",
     ],
+    testSrcs: [
+        "bazel_overlay_test.go",
+    ],
     primaryBuilder: true,
 }
diff --git a/cmd/soong_build/bazel_overlay.go b/cmd/soong_build/bazel_overlay.go
index e37c163..308076d 100644
--- a/cmd/soong_build/bazel_overlay.go
+++ b/cmd/soong_build/bazel_overlay.go
@@ -20,14 +20,17 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 const (
 	soongModuleLoad = `package(default_visibility = ["//visibility:public"])
 load("//:soong_module.bzl", "soong_module")
+
 `
 
 	// A BUILD file target snippet representing a Soong module
@@ -36,15 +39,11 @@
     module_name = "%s",
     module_type = "%s",
     module_variant = "%s",
-    deps = [
-        %s
-    ],
-)
-`
+    module_deps = %s,
+%s)`
 
 	// The soong_module rule implementation in a .bzl file
-	soongModuleBzl = `
-SoongModuleInfo = provider(
+	soongModuleBzl = `SoongModuleInfo = provider(
     fields = {
         "name": "Name of module",
         "type": "Type of module",
@@ -52,7 +51,17 @@
     },
 )
 
-def _soong_module_impl(ctx):
+def _merge_dicts(*dicts):
+    """Adds a list of dictionaries into a single dictionary."""
+
+    # If keys are repeated in multiple dictionaries, the latter one "wins".
+    result = {}
+    for d in dicts:
+        result.update(d)
+
+    return result
+
+def _generic_soong_module_impl(ctx):
     return [
         SoongModuleInfo(
             name = ctx.attr.module_name,
@@ -61,21 +70,66 @@
         ),
     ]
 
-soong_module = rule(
-    implementation = _soong_module_impl,
-    attrs = {
-        "module_name": attr.string(mandatory = True),
-        "module_type": attr.string(mandatory = True),
-        "module_variant": attr.string(),
-        "deps": attr.label_list(providers = [SoongModuleInfo]),
-    },
+_COMMON_ATTRS = {
+    "module_name": attr.string(mandatory = True),
+    "module_type": attr.string(mandatory = True),
+    "module_variant": attr.string(),
+    "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+}
+
+
+generic_soong_module = rule(
+    implementation = _generic_soong_module_impl,
+    attrs = _COMMON_ATTRS,
 )
+
+# TODO(jingwen): auto generate Soong module shims
+def _soong_filegroup_impl(ctx):
+    return [SoongModuleInfo(),]
+
+soong_filegroup = rule(
+    implementation = _soong_filegroup_impl,
+    # Matches https://cs.android.com/android/platform/superproject/+/master:build/soong/android/filegroup.go;l=25-40;drc=6a6478d49e78703ba22a432c41d819c8df79ef6c
+    attrs = _merge_dicts(_COMMON_ATTRS, {
+        "srcs": attr.string_list(doc = "srcs lists files that will be included in this filegroup"),
+        "exclude_srcs": attr.string_list(),
+        "path": attr.string(doc = "The base path to the files. May be used by other modules to determine which portion of the path to use. For example, when a filegroup is used as data in a cc_test rule, the base path is stripped off the path and the remaining path is used as the installation directory."),
+        "export_to_make_var": attr.string(doc = "Create a make variable with the specified name that contains the list of files in the filegroup, relative to the root of the source tree."),
+    })
+)
+
+soong_module_rule_map = {
+    "filegroup": soong_filegroup,
+}
+
+# soong_module is a macro that supports arbitrary kwargs, and uses module_type to
+# expand to the right underlying shim.
+def soong_module(name, module_type, **kwargs):
+    soong_module_rule = soong_module_rule_map.get(module_type)
+
+    if soong_module_rule == None:
+        # This module type does not have an existing rule to map to, so use the
+        # generic_soong_module rule instead.
+        generic_soong_module(
+            name = name,
+            module_type = module_type,
+            module_name = kwargs.pop("module_name", ""),
+            module_variant = kwargs.pop("module_variant", ""),
+            module_deps = kwargs.pop("module_deps", []),
+        )
+    else:
+        soong_module_rule(
+            name = name,
+            module_type = module_type,
+            **kwargs,
+        )
 `
 )
 
 func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
 	name := ""
 	if c.ModuleSubDir(logicModule) != "" {
+		// TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
 		name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
 	} else {
 		name = c.ModuleName(logicModule)
@@ -95,6 +149,153 @@
 	return filepath.Dir(c.BlueprintFile(logicModule))
 }
 
+func escapeString(s string) string {
+	s = strings.ReplaceAll(s, "\\", "\\\\")
+	return strings.ReplaceAll(s, "\"", "\\\"")
+}
+
+func makeIndent(indent int) string {
+	if indent < 0 {
+		panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
+	}
+	return strings.Repeat("    ", indent)
+}
+
+// prettyPrint a property value into the equivalent Starlark representation
+// recursively.
+func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
+	if isZero(propertyValue) {
+		// A property value being set or unset actually matters -- Soong does set default
+		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
+		// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
+		//
+		// In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
+		// value of unset attributes.
+		return "", nil
+	}
+
+	var ret string
+	switch propertyValue.Kind() {
+	case reflect.String:
+		ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+	case reflect.Bool:
+		ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+	case reflect.Int, reflect.Uint, reflect.Int64:
+		ret = fmt.Sprintf("%v", propertyValue.Interface())
+	case reflect.Ptr:
+		return prettyPrint(propertyValue.Elem(), indent)
+	case reflect.Slice:
+		ret = "[\n"
+		for i := 0; i < propertyValue.Len(); i++ {
+			indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+			if err != nil {
+				return "", err
+			}
+
+			if indexedValue != "" {
+				ret += makeIndent(indent + 1)
+				ret += indexedValue
+				ret += ",\n"
+			}
+		}
+		ret += makeIndent(indent)
+		ret += "]"
+	case reflect.Struct:
+		ret = "{\n"
+		// Sort and print the struct props by the key.
+		structProps := extractStructProperties(propertyValue, indent)
+		for _, k := range android.SortedStringKeys(structProps) {
+			ret += makeIndent(indent + 1)
+			ret += "\"" + k + "\": "
+			ret += structProps[k]
+			ret += ",\n"
+		}
+		ret += makeIndent(indent)
+		ret += "}"
+	case reflect.Interface:
+		// TODO(b/164227191): implement pretty print for interfaces.
+		// Interfaces are used for for arch, multilib and target properties.
+		return "", nil
+	default:
+		return "", fmt.Errorf(
+			"unexpected kind for property struct field: %s", propertyValue.Kind())
+	}
+	return ret, nil
+}
+
+func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+	if structValue.Kind() != reflect.Struct {
+		panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+	}
+
+	ret := map[string]string{}
+	structType := structValue.Type()
+	for i := 0; i < structValue.NumField(); i++ {
+		field := structType.Field(i)
+		if field.PkgPath != "" {
+			// Skip unexported fields. Some properties are
+			// internal to Soong only, and these fields do not have PkgPath.
+			continue
+		}
+		if proptools.HasTag(field, "blueprint", "mutated") {
+			continue
+		}
+
+		fieldValue := structValue.Field(i)
+		if isZero(fieldValue) {
+			// Ignore zero-valued fields
+			continue
+		}
+
+		propertyName := proptools.PropertyNameForField(field.Name)
+		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
+		if err != nil {
+			panic(
+				fmt.Errorf(
+					"Error while parsing property: %q. %s",
+					propertyName,
+					err))
+		}
+		if prettyPrintedValue != "" {
+			ret[propertyName] = prettyPrintedValue
+		}
+	}
+
+	return ret
+}
+
+func isStructPtr(t reflect.Type) bool {
+	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
+// Generically extract module properties and types into a map, keyed by the module property name.
+func extractModuleProperties(aModule android.Module) map[string]string {
+	ret := map[string]string{}
+
+	// Iterate over this android.Module's property structs.
+	for _, properties := range aModule.GetProperties() {
+		propertiesValue := reflect.ValueOf(properties)
+		// Check that propertiesValue is a pointer to the Properties struct, like
+		// *cc.BaseLinkerProperties or *java.CompilerProperties.
+		//
+		// propertiesValue can also be type-asserted to the structs to
+		// manipulate internal props, if needed.
+		if isStructPtr(propertiesValue.Type()) {
+			structValue := propertiesValue.Elem()
+			for k, v := range extractStructProperties(structValue, 0) {
+				ret[k] = v
+			}
+		} else {
+			panic(fmt.Errorf(
+				"properties must be a pointer to a struct, got %T",
+				propertiesValue.Interface()))
+		}
+
+	}
+
+	return ret
+}
+
 func createBazelOverlay(ctx *android.Context, bazelOverlayDir string) error {
 	blueprintCtx := ctx.Context
 	blueprintCtx.VisitAllModules(func(module blueprint.Module) {
@@ -103,27 +304,7 @@
 			panic(err)
 		}
 
-		// TODO(b/163018919): DirectDeps can have duplicate (module, variant)
-		// items, if the modules are added using different DependencyTag. Figure
-		// out the implications of that.
-		depLabels := map[string]bool{}
-		blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
-			depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
-		})
-
-		var depLabelList string
-		for depLabel, _ := range depLabels {
-			depLabelList += "\"" + depLabel + "\",\n        "
-		}
-		buildFile.Write([]byte(
-			fmt.Sprintf(
-				soongModuleTarget,
-				targetNameWithVariant(blueprintCtx, module),
-				blueprintCtx.ModuleName(module),
-				blueprintCtx.ModuleType(module),
-				// misleading name, this actually returns the variant.
-				blueprintCtx.ModuleSubDir(module),
-				depLabelList)))
+		buildFile.Write([]byte(generateSoongModuleTarget(blueprintCtx, module) + "\n\n"))
 		buildFile.Close()
 	})
 
@@ -138,6 +319,70 @@
 	return writeReadOnlyFile(bazelOverlayDir, "soong_module.bzl", soongModuleBzl)
 }
 
+var ignoredProps map[string]bool = map[string]bool{
+	"name":       true, // redundant, since this is explicitly generated for every target
+	"from":       true, // reserved keyword
+	"in":         true, // reserved keyword
+	"arch":       true, // interface prop type is not supported yet.
+	"multilib":   true, // interface prop type is not supported yet.
+	"target":     true, // interface prop type is not supported yet.
+	"visibility": true, // Bazel has native visibility semantics. Handle later.
+}
+
+func shouldGenerateAttribute(prop string) bool {
+	return !ignoredProps[prop]
+}
+
+// props is an unsorted map. This function ensures that
+// the generated attributes are sorted to ensure determinism.
+func propsToAttributes(props map[string]string) string {
+	var attributes string
+	for _, propName := range android.SortedStringKeys(props) {
+		if shouldGenerateAttribute(propName) {
+			attributes += fmt.Sprintf("    %s = %s,\n", propName, props[propName])
+		}
+	}
+	return attributes
+}
+
+// Convert a module and its deps and props into a Bazel macro/rule
+// representation in the BUILD file.
+func generateSoongModuleTarget(
+	blueprintCtx *blueprint.Context,
+	module blueprint.Module) string {
+
+	var props map[string]string
+	if aModule, ok := module.(android.Module); ok {
+		props = extractModuleProperties(aModule)
+	}
+	attributes := propsToAttributes(props)
+
+	// TODO(b/163018919): DirectDeps can have duplicate (module, variant)
+	// items, if the modules are added using different DependencyTag. Figure
+	// out the implications of that.
+	depLabels := map[string]bool{}
+	blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
+		depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
+	})
+
+	depLabelList := "[\n"
+	for depLabel, _ := range depLabels {
+		depLabelList += "        \""
+		depLabelList += depLabel
+		depLabelList += "\",\n"
+	}
+	depLabelList += "    ]"
+
+	return fmt.Sprintf(
+		soongModuleTarget,
+		targetNameWithVariant(blueprintCtx, module),
+		blueprintCtx.ModuleName(module),
+		blueprintCtx.ModuleType(module),
+		blueprintCtx.ModuleSubDir(module),
+		depLabelList,
+		attributes)
+}
+
 func buildFileForModule(ctx *blueprint.Context, module blueprint.Module) (*os.File, error) {
 	// Create nested directories for the BUILD file
 	dirPath := filepath.Join(bazelOverlayDir, packagePath(ctx, module))
@@ -171,3 +416,34 @@
 	// 0444 is read-only
 	return ioutil.WriteFile(workspaceFile, []byte(content), 0444)
 }
+
+func isZero(value reflect.Value) bool {
+	switch value.Kind() {
+	case reflect.Func, reflect.Map, reflect.Slice:
+		return value.IsNil()
+	case reflect.Array:
+		valueIsZero := true
+		for i := 0; i < value.Len(); i++ {
+			valueIsZero = valueIsZero && isZero(value.Index(i))
+		}
+		return valueIsZero
+	case reflect.Struct:
+		valueIsZero := true
+		for i := 0; i < value.NumField(); i++ {
+			if value.Field(i).CanSet() {
+				valueIsZero = valueIsZero && isZero(value.Field(i))
+			}
+		}
+		return valueIsZero
+	case reflect.Ptr:
+		if !value.IsNil() {
+			return isZero(reflect.Indirect(value))
+		} else {
+			return true
+		}
+	default:
+		zeroValue := reflect.Zero(value.Type())
+		result := value.Interface() == zeroValue.Interface()
+		return result
+	}
+}
diff --git a/cmd/soong_build/bazel_overlay_test.go b/cmd/soong_build/bazel_overlay_test.go
new file mode 100644
index 0000000..8db784d
--- /dev/null
+++ b/cmd/soong_build/bazel_overlay_test.go
@@ -0,0 +1,255 @@
+// 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 main
+
+import (
+	"android/soong/android"
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "bazel_overlay_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
+
+type customModule struct {
+	android.ModuleBase
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// nothing for now.
+}
+
+func customModuleFactory() android.Module {
+	module := &customModule{}
+	android.InitAndroidModule(module)
+	return module
+}
+
+func TestGenerateBazelOverlayFromBlueprint(t *testing.T) {
+	testCases := []struct {
+		bp                  string
+		expectedBazelTarget string
+	}{
+		{
+			bp: `custom {
+	name: "foo",
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	ramdisk: true,
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    ramdisk = True,
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	required: ["bar"],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    required = [
+        "bar",
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	target_required: ["qux", "bazqux"],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    target_required = [
+        "qux",
+        "bazqux",
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	dist: {
+		targets: ["goal_foo"],
+		tag: ".foo",
+	},
+	dists: [
+		{
+			targets: ["goal_bar"],
+			tag: ".bar",
+		},
+	],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    dist = {
+        "tag": ".foo",
+        "targets": [
+            "goal_foo",
+        ],
+    },
+    dists = [
+        {
+            "tag": ".bar",
+            "targets": [
+                "goal_bar",
+            ],
+        },
+    ],
+)`,
+		},
+		{
+			bp: `custom {
+	name: "foo",
+	required: ["bar"],
+	target_required: ["qux", "bazqux"],
+	ramdisk: true,
+	owner: "custom_owner",
+	dists: [
+		{
+			tag: ".tag",
+			targets: ["my_goal"],
+		},
+	],
+}
+		`,
+			expectedBazelTarget: `soong_module(
+    name = "foo",
+    module_name = "foo",
+    module_type = "custom",
+    module_variant = "",
+    module_deps = [
+    ],
+    dists = [
+        {
+            "tag": ".tag",
+            "targets": [
+                "my_goal",
+            ],
+        },
+    ],
+    owner = "custom_owner",
+    ramdisk = True,
+    required = [
+        "bar",
+    ],
+    target_required = [
+        "qux",
+        "bazqux",
+    ],
+)`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		ctx := android.NewTestContext()
+		ctx.RegisterModuleType("custom", customModuleFactory)
+		ctx.Register(config)
+
+		_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+		android.FailIfErrored(t, errs)
+		_, errs = ctx.PrepareBuildActions(config)
+		android.FailIfErrored(t, errs)
+
+		module := ctx.ModuleForTests("foo", "").Module().(*customModule)
+		blueprintCtx := ctx.Context.Context
+
+		actualBazelTarget := generateSoongModuleTarget(blueprintCtx, module)
+		if actualBazelTarget != testCase.expectedBazelTarget {
+			t.Errorf(
+				"Expected generated Bazel target to be '%s', got '%s'",
+				testCase.expectedBazelTarget,
+				actualBazelTarget,
+			)
+		}
+	}
+}
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..f22ee47 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,54 @@
 // 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 library path to the map, unless a path for this library already exists.
+func (libPaths LibraryPaths) addLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
+	if _, 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}
+	}
+}
+
+// Add a new library path to the map. Ensure that the build path to the library exists.
+func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
+	if hostPath != nil {
+		// Add a library only if the build path to it is known.
+		libPaths.addLibraryPath(ctx, lib, hostPath, installPath)
+	} else if !ctx.Config().AllowMissingDependencies() {
+		// Error on libraries with unknown build paths, unless missing dependencies are allowed.
+		android.ReportPathErrorf(ctx, "unknown build path to <uses-library> '%s'", lib)
+	} else {
+		// Not adding a library to the map will likely result in disabling dexpreopt.
+	}
+}
+
+// Add a new library path to the map, if the library exists (name is not nil).
+func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.PathContext, lib *string, hostPath, installPath android.Path) {
+	if lib != nil {
+		// Don't check the build paths, add in any case. Some libraries may be missing from the
+		// build, but their names still need to be added to <uses-library> tags in the manifest.
+		libPaths.addLibraryPath(ctx, *lib, hostPath, installPath)
+	}
+}
+
+// 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 +410,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..4dbda49 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -37,7 +37,6 @@
 	"fmt"
 	"path/filepath"
 	"runtime"
-	"sort"
 	"strings"
 
 	"android/soong/android"
@@ -82,15 +81,14 @@
 	}
 
 	if !dexpreoptDisabled(ctx, global, module) {
-		// Don't preopt individual boot jars, they will be preopted together.
-		if !global.BootJars.ContainsJar(module.Name) {
+		if clc := genClassLoaderContext(ctx, global, module); clc != nil {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
 			generateDM := shouldGenerateDM(module, global)
 
 			for archIdx, _ := range module.Archs {
-				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
+				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, *clc, profile, appImage, generateDM)
 			}
 		}
 	}
@@ -103,6 +101,11 @@
 		return true
 	}
 
+	// Don't preopt individual boot jars, they will be preopted together.
+	if global.BootJars.ContainsJar(module.Name) {
+		return true
+	}
+
 	// Don't preopt system server jars that are updatable.
 	if global.UpdatableSystemServerJars.ContainsJar(module.Name) {
 		return true
@@ -208,15 +211,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{}
@@ -224,13 +218,17 @@
 	return m[sdkVer]
 }
 
-func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) {
+func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) bool {
 	clc := m.getValue(sdkVer)
 	for _, lib := range libs {
-		p := pathForLibrary(module, lib)
-		clc.Host = append(clc.Host, p.Host)
-		clc.Target = append(clc.Target, p.Device)
+		if p := pathForLibrary(module, lib); p != nil {
+			clc.Host = append(clc.Host, p.Host)
+			clc.Target = append(clc.Target, p.Device)
+		} else {
+			return false
+		}
 	}
+	return true
 }
 
 func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
@@ -241,9 +239,79 @@
 	}
 }
 
+// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat
+// command for the dexpreopted module. There are three possible cases:
+//
+// 1. System server jars. They have a special class loader context that includes other system
+//    server jars.
+//
+// 2. Library jars or APKs which have precise list of their <uses-library> libs. Their class loader
+//    context includes build and on-device paths to these libs. In some cases it may happen that
+//    the path to a <uses-library> is unknown (e.g. the dexpreopted module may depend on stubs
+//    library, whose implementation library is missing from the build altogether). In such case
+//    dexpreopting with the <uses-library> is impossible, and dexpreopting without it is pointless,
+//    as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
+//    such cases the function returns nil, which disables dexpreopt.
+//
+// 2. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
+//    the unsafe &-classpath workaround that means empty class loader context and absence of runtime
+//    check that the class loader context provided by the PackageManager agrees with the stored
+//    class loader context recorded in the .odex file.
+//
+func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) *classLoaderContextMap {
+	classLoaderContexts := make(classLoaderContextMap)
+	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
+
+	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
+		// System server jars should be dexpreopted together: class loader context of each jar
+		// should include all preceding jars on the system server classpath.
+		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
+
+	} else if module.EnforceUsesLibraries {
+		// Unconditional class loader context.
+		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
+		if !classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...) {
+			return nil
+		}
+
+		// Conditional class loader context for API version < 28.
+		const httpLegacy = "org.apache.http.legacy"
+		if !contains(usesLibs, httpLegacy) {
+			if !classLoaderContexts.addLibs(28, module, httpLegacy) {
+				return nil
+			}
+		}
+
+		// Conditional class loader context for API version < 29.
+		usesLibs29 := []string{
+			"android.hidl.base-V1.0-java",
+			"android.hidl.manager-V1.0-java",
+		}
+		if !classLoaderContexts.addLibs(29, module, usesLibs29...) {
+			return nil
+		}
+
+		// Conditional class loader context for API version < 30.
+		const testBase = "android.test.base"
+		if !contains(usesLibs, testBase) {
+			if !classLoaderContexts.addLibs(30, module, testBase) {
+				return nil
+			}
+		}
+
+	} else {
+		// Pass special class loader context to skip the classpath and collision check.
+		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
+		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
+		// to the &.
+	}
+
+	return &classLoaderContexts
+}
+
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
-	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
-	appImage bool, generateDM bool) {
+	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap,
+	profile android.WritablePath, appImage bool, generateDM bool) {
 
 	arch := module.Archs[archIdx]
 
@@ -274,17 +342,12 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	classLoaderContexts := make(classLoaderContextMap)
 	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
 
 	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
-		// System server jars should be dexpreopted together: class loader context of each jar
-		// should include all preceding jars on the system server classpath.
-		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
-
 		// Copy the system server jar to a predefined location where dex2oat will find it.
 		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
 		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
@@ -298,29 +361,6 @@
 			Implicits(clc.Host).
 			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
 	} else if module.EnforceUsesLibraries {
-		// Unconditional class loader context.
-		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
-		classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...)
-
-		// Conditional class loader context for API version < 28.
-		const httpLegacy = "org.apache.http.legacy"
-		if !contains(usesLibs, httpLegacy) {
-			classLoaderContexts.addLibs(28, module, httpLegacy)
-		}
-
-		// Conditional class loader context for API version < 29.
-		usesLibs29 := []string{
-			"android.hidl.base-V1.0-java",
-			"android.hidl.manager-V1.0-java",
-		}
-		classLoaderContexts.addLibs(29, module, usesLibs29...)
-
-		// Conditional class loader context for API version < 30.
-		const testBase = "android.test.base"
-		if !contains(usesLibs, testBase) {
-			classLoaderContexts.addLibs(30, module, testBase)
-		}
-
 		// Generate command that saves target SDK version in a shell variable.
 		if module.ManifestPath != nil {
 			rule.Command().Text(`target_sdk_version="$(`).
@@ -342,7 +382,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 {
@@ -550,11 +590,11 @@
 }
 
 func pathForLibrary(module *ModuleConfig, lib string) *LibraryPath {
-	path, ok := module.LibraryPaths[lib]
-	if !ok {
-		panic(fmt.Errorf("unknown library path for %q", lib))
+	if path, ok := module.LibraryPaths[lib]; ok && path.Host != nil && path.Device != "error" {
+		return path
+	} else {
+		return nil
 	}
-	return path
 }
 
 func makefileMatch(pattern, s string) bool {
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 0f7b8df..5dd2a86 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -28,14 +28,17 @@
 
 func init() {
 	pctx.Import("android/soong/android")
+	RegisterPrebuiltEtcBuildComponents(android.InitRegistrationContext)
+}
 
-	android.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
-	android.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
-	android.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
-	android.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
-	android.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
-	android.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
-	android.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
+func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
+	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
+	ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
+	ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
+	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
+	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
+	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
 }
 
 type prebuiltEtcProperties struct {
@@ -70,6 +73,7 @@
 
 type PrebuiltEtcModule interface {
 	android.Module
+	BaseDir() string
 	SubDir() string
 	OutputFile() android.OutputPath
 }
@@ -167,6 +171,16 @@
 	return proptools.String(p.properties.Relative_install_path)
 }
 
+func (p *PrebuiltEtc) BaseDir() string {
+	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
+	// socInstallDirBase.
+	installBaseDir := p.installDirBase
+	if p.SocSpecific() && p.socInstallDirBase != "" {
+		installBaseDir = p.socInstallDirBase
+	}
+	return installBaseDir
+}
+
 func (p *PrebuiltEtc) Installable() bool {
 	return p.properties.Installable == nil || android.Bool(p.properties.Installable)
 }
@@ -191,13 +205,7 @@
 		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
 	}
 
-	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
-	// socInstallDirBase.
-	installBaseDir := p.installDirBase
-	if ctx.SocSpecific() && p.socInstallDirBase != "" {
-		installBaseDir = p.socInstallDirBase
-	}
-	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir())
+	p.installDirPath = android.PathForModuleInstall(ctx, p.BaseDir(), p.SubDir())
 
 	// This ensures that outputFilePath has the correct name for others to
 	// use, as the source file may have a different name.
diff --git a/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 778e1cd..0f5e30d 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.MaybeAddLibraryPath(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)
@@ -749,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 bc327cf..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)
 	})
 
diff --git a/java/app.go b/java/app.go
index e820048..34f96dd 100755
--- a/java/app.go
+++ b/java/app.go
@@ -586,7 +586,7 @@
 	return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 }
 
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.LibraryPaths) android.Path {
 	a.dexpreopter.installPath = a.installPath(ctx)
 	if a.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
@@ -597,7 +597,9 @@
 	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
 	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
 	a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
+	a.dexpreopter.libraryPaths.AddLibraryPaths(sdkLibs)
 	a.dexpreopter.manifestFile = a.mergedManifestFile
+	a.exportedSdkLibs = make(dexpreopt.LibraryPaths)
 
 	if ctx.ModuleName() != "framework-res" {
 		a.Module.compile(ctx, a.aaptSrcJar)
@@ -766,6 +768,15 @@
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
 
+	// The decision to enforce <uses-library> checks is made before adding implicit SDK libraries.
+	a.usesLibrary.freezeEnforceUsesLibraries()
+
+	// Add implicit SDK libraries to <uses-library> list.
+	for _, usesLib := range android.SortedStringKeys(a.aapt.sdkLibraries) {
+		a.usesLibrary.addLib(usesLib, inList(usesLib, optionalUsesLibs))
+	}
+
+	// Check that the <uses-library> list is coherent with the manifest.
 	if a.usesLibrary.enforceUsesLibraries() {
 		manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile)
 		apkDeps = append(apkDeps, manifestCheckFile)
@@ -778,7 +789,7 @@
 	a.linter.resources = a.aapt.resourceFiles
 	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
 
-	dexJarFile := a.dexBuildActions(ctx)
+	dexJarFile := a.dexBuildActions(ctx, a.aapt.sdkLibraries)
 
 	jniLibs, certificateDeps := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
 	jniJarFile := a.jniBuildActions(jniLibs, ctx)
@@ -963,6 +974,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)
 }
@@ -1031,8 +1044,9 @@
 
 	testProperties testProperties
 
-	testConfig android.Path
-	data       android.Paths
+	testConfig       android.Path
+	extraTestConfigs android.Paths
+	data             android.Paths
 }
 
 func (a *AndroidTest) InstallInTestcases() bool {
@@ -1060,6 +1074,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)
 }
 
@@ -1883,6 +1898,16 @@
 	usesLibraryProperties UsesLibraryProperties
 }
 
+func (u *usesLibrary) addLib(lib string, optional bool) {
+	if !android.InList(lib, u.usesLibraryProperties.Uses_libs) && !android.InList(lib, u.usesLibraryProperties.Optional_uses_libs) {
+		if optional {
+			u.usesLibraryProperties.Optional_uses_libs = append(u.usesLibraryProperties.Optional_uses_libs, lib)
+		} else {
+			u.usesLibraryProperties.Uses_libs = append(u.usesLibraryProperties.Uses_libs, lib)
+		}
+	}
+}
+
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
 	if !ctx.Config().UnbundledBuild() {
 		ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
@@ -1955,6 +1980,12 @@
 	return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, defaultEnforceUsesLibs)
 }
 
+// Freeze the value of `enforce_uses_libs` based on the current values of `uses_libs` and `optional_uses_libs`.
+func (u *usesLibrary) freezeEnforceUsesLibraries() {
+	enforce := u.enforceUsesLibraries()
+	u.usesLibraryProperties.Enforce_uses_libs = &enforce
+}
+
 // verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against the ones specified
 // in the uses_libs and optional_uses_libs properties.  It returns the path to a copy of the manifest.
 func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
diff --git a/java/app_test.go b/java/app_test.go
index a070318..b8d8616 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1038,6 +1038,35 @@
 	}
 }
 
+func checkSdkVersion(t *testing.T, config android.Config, expectedSdkVersion string) {
+	ctx := testContext()
+
+	run(t, ctx, config)
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+	link := foo.Output("package-res.apk")
+	linkFlags := strings.Split(link.Args["flags"], " ")
+	min := android.IndexList("--min-sdk-version", linkFlags)
+	target := android.IndexList("--target-sdk-version", linkFlags)
+
+	if min == -1 || target == -1 || min == len(linkFlags)-1 || target == len(linkFlags)-1 {
+		t.Fatalf("missing --min-sdk-version or --target-sdk-version in link flags: %q", linkFlags)
+	}
+
+	gotMinSdkVersion := linkFlags[min+1]
+	gotTargetSdkVersion := linkFlags[target+1]
+
+	if gotMinSdkVersion != expectedSdkVersion {
+		t.Errorf("incorrect --min-sdk-version, expected %q got %q",
+			expectedSdkVersion, gotMinSdkVersion)
+	}
+
+	if gotTargetSdkVersion != expectedSdkVersion {
+		t.Errorf("incorrect --target-sdk-version, expected %q got %q",
+			expectedSdkVersion, gotTargetSdkVersion)
+	}
+}
+
 func TestAppSdkVersion(t *testing.T) {
 	testCases := []struct {
 		name                  string
@@ -1107,38 +1136,85 @@
 				config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
 				config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
 				config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
+				checkSdkVersion(t, config, test.expectedMinSdkVersion)
 
-				ctx := testContext()
-
-				run(t, ctx, config)
-
-				foo := ctx.ModuleForTests("foo", "android_common")
-				link := foo.Output("package-res.apk")
-				linkFlags := strings.Split(link.Args["flags"], " ")
-				min := android.IndexList("--min-sdk-version", linkFlags)
-				target := android.IndexList("--target-sdk-version", linkFlags)
-
-				if min == -1 || target == -1 || min == len(linkFlags)-1 || target == len(linkFlags)-1 {
-					t.Fatalf("missing --min-sdk-version or --target-sdk-version in link flags: %q", linkFlags)
-				}
-
-				gotMinSdkVersion := linkFlags[min+1]
-				gotTargetSdkVersion := linkFlags[target+1]
-
-				if gotMinSdkVersion != test.expectedMinSdkVersion {
-					t.Errorf("incorrect --min-sdk-version, expected %q got %q",
-						test.expectedMinSdkVersion, gotMinSdkVersion)
-				}
-
-				if gotTargetSdkVersion != test.expectedMinSdkVersion {
-					t.Errorf("incorrect --target-sdk-version, expected %q got %q",
-						test.expectedMinSdkVersion, gotTargetSdkVersion)
-				}
 			})
 		}
 	}
 }
 
+func TestVendorAppSdkVersion(t *testing.T) {
+	testCases := []struct {
+		name                                  string
+		sdkVersion                            string
+		platformSdkInt                        int
+		platformSdkCodename                   string
+		platformSdkFinal                      bool
+		deviceCurrentApiLevelForVendorModules string
+		expectedMinSdkVersion                 string
+	}{
+		{
+			name:                                  "current final SDK",
+			sdkVersion:                            "current",
+			platformSdkInt:                        29,
+			platformSdkCodename:                   "REL",
+			platformSdkFinal:                      true,
+			deviceCurrentApiLevelForVendorModules: "29",
+			expectedMinSdkVersion:                 "29",
+		},
+		{
+			name:                                  "current final SDK",
+			sdkVersion:                            "current",
+			platformSdkInt:                        29,
+			platformSdkCodename:                   "REL",
+			platformSdkFinal:                      true,
+			deviceCurrentApiLevelForVendorModules: "28",
+			expectedMinSdkVersion:                 "28",
+		},
+		{
+			name:                                  "current final SDK",
+			sdkVersion:                            "current",
+			platformSdkInt:                        29,
+			platformSdkCodename:                   "Q",
+			platformSdkFinal:                      false,
+			deviceCurrentApiLevelForVendorModules: "current",
+			expectedMinSdkVersion:                 "Q",
+		},
+		{
+			name:                                  "current final SDK",
+			sdkVersion:                            "current",
+			platformSdkInt:                        29,
+			platformSdkCodename:                   "Q",
+			platformSdkFinal:                      false,
+			deviceCurrentApiLevelForVendorModules: "28",
+			expectedMinSdkVersion:                 "28",
+		},
+	}
+
+	for _, moduleType := range []string{"android_app", "android_library"} {
+		for _, sdkKind := range []string{"", "system_"} {
+			for _, test := range testCases {
+				t.Run(moduleType+" "+test.name, func(t *testing.T) {
+					bp := fmt.Sprintf(`%s {
+						name: "foo",
+						srcs: ["a.java"],
+						sdk_version: "%s%s",
+						vendor: true,
+					}`, moduleType, sdkKind, test.sdkVersion)
+
+					config := testAppConfig(nil, bp, nil)
+					config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
+					config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
+					config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
+					config.TestProductVariables.DeviceCurrentApiLevelForVendorModules = &test.deviceCurrentApiLevelForVendorModules
+					config.TestProductVariables.DeviceSystemSdkVersions = []string{"28", "29"}
+					checkSdkVersion(t, config, test.expectedMinSdkVersion)
+				})
+			}
+		}
+	}
+}
+
 func TestJNIABI(t *testing.T) {
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
@@ -2526,10 +2602,37 @@
 			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"],
+			static_libs: ["static-runtime-helper"],
+			uses_libs: ["foo"],
+			sdk_version: "current",
+			optional_uses_libs: [
+				"bar",
+				"baz",
+			],
+		}
+
+		android_app {
+			name: "app_with_stub_deps",
+			srcs: ["a.java"],
 			libs: ["qux", "quuz.stubs"],
+			static_libs: ["static-runtime-helper"],
 			uses_libs: ["foo"],
 			sdk_version: "current",
 			optional_uses_libs: [
@@ -2558,15 +2661,15 @@
 	run(t, ctx, config)
 
 	app := ctx.ModuleForTests("app", "android_common")
+	appWithStubDeps := ctx.ModuleForTests("app_with_stub_deps", "android_common")
 	prebuilt := ctx.ModuleForTests("prebuilt", "android_common")
 
 	// 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
@@ -2589,15 +2692,24 @@
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
 
-	// Test that only present libraries are preopted
+	// Test that all present libraries are preopted, including implicit SDK dependencies
 	cmd = app.Rule("dexpreopt").RuleParams.Command
-
-	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
+	w := `--target-classpath-for-sdk any` +
+		` /system/framework/foo.jar` +
+		`:/system/framework/quuz.jar` +
+		`:/system/framework/qux.jar` +
+		`:/system/framework/runtime-library.jar` +
+		`:/system/framework/bar.jar`
+	if !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
 
-	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
+	// TODO(skvadrik) fix dexpreopt for stub libraries for which the implementation is present
+	if appWithStubDeps.MaybeRule("dexpreopt").RuleParams.Command != "" {
+		t.Errorf("dexpreopt should be disabled for apps with dependencies on stub libraries")
+	}
 
+	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
 	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
diff --git a/java/config/config.go b/java/config/config.go
index 2f39c99..05da3b5 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -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")
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 b445456..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.
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/java.go b/java/java.go
index d5375a5..3606f77 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"}
@@ -674,31 +679,29 @@
 	return j.ApexModuleBase.AvailableFor(what)
 }
 
+func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext sdkContext, d dexer) {
+	sdkDep := decodeSdkDep(ctx, sdkContext)
+	if sdkDep.useModule {
+		ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
+		ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
+		ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
+		if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
+			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
+		}
+		if d.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
+			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
+		}
+	}
+	if sdkDep.systemModules != "" {
+		ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+	}
+}
+
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
 		j.linter.deps(ctx)
 
-		sdkDep := decodeSdkDep(ctx, sdkContext(j))
-		if sdkDep.useModule {
-			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
-			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
-			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
-			if j.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
-				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
-			}
-			if j.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
-				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
-			}
-		}
-		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")
-		}
+		sdkDeps(ctx, sdkContext(j), j.dexer)
 	}
 
 	syspropPublicStubs := syspropPublicStubs(ctx.Config())
@@ -973,12 +976,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 +995,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.MaybeAddLibraryPath(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 +1006,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 +1018,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 +1045,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 +1081,6 @@
 		}
 	})
 
-	j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
-
 	return deps
 }
 
@@ -1680,7 +1663,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 +1821,7 @@
 	return j.exportAidlIncludeDirs
 }
 
-func (j *Module) ExportedSdkLibs() []string {
-	// exportedSdkLibs is type []string
+func (j *Module) ExportedSdkLibs() dexpreopt.LibraryPaths {
 	return j.exportedSdkLibs
 }
 
@@ -1972,6 +1954,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 +1970,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.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath())
+
 	j.distFiles = j.GenerateTaggedDistFiles(ctx)
 }
 
@@ -2139,6 +2128,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 +2159,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 +2190,9 @@
 
 	testProperties testProperties
 
-	testConfig android.Path
-	data       android.Paths
+	testConfig       android.Path
+	extraTestConfigs android.Paths
+	data             android.Paths
 }
 
 type TestHost struct {
@@ -2214,6 +2213,7 @@
 	prebuiltTestProperties prebuiltTestProperties
 
 	testConfig android.Path
+	dexJarFile android.Path
 }
 
 func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -2232,6 +2232,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, ""))
 	})
@@ -2534,10 +2536,16 @@
 	// Functionality common to Module and Import.
 	embeddableInModuleAndImport
 
+	hiddenAPI
+	dexer
+
 	properties ImportProperties
 
+	// output file containing classes.dex and resources
+	dexJarFile android.Path
+
 	combinedClasspathFile android.Path
-	exportedSdkLibs       []string
+	exportedSdkLibs       dexpreopt.LibraryPaths
 	exportAidlIncludeDirs android.Paths
 }
 
@@ -2545,10 +2553,22 @@
 	return sdkSpecFrom(String(j.properties.Sdk_version))
 }
 
+func (j *Import) makeSdkVersion() string {
+	return j.sdkVersion().raw
+}
+
+func (j *Import) systemModules() string {
+	return "none"
+}
+
 func (j *Import) minSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
+func (j *Import) targetSdkVersion() sdkSpec {
+	return j.sdkVersion()
+}
+
 func (j *Import) MinSdkVersion() string {
 	return j.minSdkVersion().version.String()
 }
@@ -2575,6 +2595,10 @@
 
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+
+	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
+		sdkDeps(ctx, sdkContext(j), j.dexer)
+	}
 }
 
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2590,12 +2614,9 @@
 		TransformJetifier(ctx, outputFile, inputFile)
 	}
 	j.combinedClasspathFile = outputFile
+	j.exportedSdkLibs = make(dexpreopt.LibraryPaths)
 
-	// 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()...)
+	var flags javaBuilderFlags
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
@@ -2605,25 +2626,55 @@
 		case Dependency:
 			switch tag {
 			case libTag, staticLibTag:
+				flags.classpath = append(flags.classpath, dep.HeaderJars()...)
 				// sdk lib names from dependencies are re-exported
-				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+				j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs())
+			case bootClasspathTag:
+				flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...)
 			}
 		case SdkLibraryDependency:
 			switch tag {
 			case libTag:
+				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
 				// 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.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile)
+
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
+
+	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
+		sdkDep := decodeSdkDep(ctx, sdkContext(j))
+		if sdkDep.invalidVersion {
+			ctx.AddMissingDependencies(sdkDep.bootclasspath)
+			ctx.AddMissingDependencies(sdkDep.java9Classpath)
+		} else if sdkDep.useFiles {
+			// sdkDep.jar is actually equivalent to turbine header.jar.
+			flags.classpath = append(flags.classpath, sdkDep.jars...)
+		}
+
+		// Dex compilation
+		var dexOutputFile android.ModuleOutPath
+		dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
+		if ctx.Failed() {
+			return
+		}
+
+		j.dexJarFile = dexOutputFile
+	}
 }
 
 var _ Dependency = (*Import)(nil)
@@ -2654,7 +2705,7 @@
 }
 
 func (j *Import) DexJarBuildPath() android.Path {
-	return nil
+	return j.dexJarFile
 }
 
 func (j *Import) DexJarInstallPath() android.Path {
@@ -2665,7 +2716,7 @@
 	return j.exportAidlIncludeDirs
 }
 
-func (j *Import) ExportedSdkLibs() []string {
+func (j *Import) ExportedSdkLibs() dexpreopt.LibraryPaths {
 	return j.exportedSdkLibs
 }
 
@@ -2722,10 +2773,15 @@
 func ImportFactory() android.Module {
 	module := &Import{}
 
-	module.AddProperties(&module.properties)
+	module.AddProperties(
+		&module.properties,
+		&module.dexer.dexProperties,
+	)
 
 	module.initModuleAndImport(&module.ModuleBase)
 
+	module.dexProperties.Optimize.EnabledByDefault = false
+
 	android.InitPrebuiltModule(module, &module.properties.Jars)
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
diff --git a/java/java_test.go b/java/java_test.go
index 50c40c3..3f7bab1 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -20,7 +20,6 @@
 	"path/filepath"
 	"reflect"
 	"regexp"
-	"sort"
 	"strconv"
 	"strings"
 	"testing"
@@ -526,6 +525,8 @@
 		java_import {
 			name: "baz",
 			jars: ["b.jar"],
+			sdk_version: "current",
+			compile_dex: true,
 		}
 
 		dex_import {
@@ -556,8 +557,10 @@
 	fooModule := ctx.ModuleForTests("foo", "android_common")
 	javac := fooModule.Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
-	barJar := ctx.ModuleForTests("bar", "android_common").Rule("combineJar").Output
-	bazJar := ctx.ModuleForTests("baz", "android_common").Rule("combineJar").Output
+	barModule := ctx.ModuleForTests("bar", "android_common")
+	barJar := barModule.Rule("combineJar").Output
+	bazModule := ctx.ModuleForTests("baz", "android_common")
+	bazJar := bazModule.Rule("combineJar").Output
 	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
@@ -572,6 +575,11 @@
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String())
 	}
 
+	barDexJar := barModule.Module().(*Import).DexJarBuildPath()
+	if barDexJar != nil {
+		t.Errorf("bar dex jar build path expected to be nil, got %q", barDexJar)
+	}
+
 	if !strings.Contains(javac.Args["classpath"], sdklibStubsJar.String()) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], sdklibStubsJar.String())
 	}
@@ -580,6 +588,12 @@
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
 	}
 
+	bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().String()
+	expectedDexJar := buildDir + "/.intermediates/baz/android_common/dex/baz.jar"
+	if bazDexJar != expectedDexJar {
+		t.Errorf("baz dex jar build path expected %q, got %q", expectedDexJar, bazDexJar)
+	}
+
 	ctx.ModuleForTests("qux", "android_common").Rule("Cp")
 }
 
@@ -1496,8 +1510,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 b37f692..3a210cc 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -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/sdk.go b/java/sdk.go
index b44cd8e1..56fa12b 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -191,6 +191,26 @@
 	return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
 }
 
+func (s sdkSpec) forVendorPartition(ctx android.EarlyModuleContext) sdkSpec {
+	// If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
+	// use it instead of "current" for the vendor partition.
+	currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
+	if currentSdkVersion == "current" {
+		return s
+	}
+
+	if s.kind == sdkPublic || s.kind == sdkSystem {
+		if s.version.isCurrent() {
+			if i, err := strconv.Atoi(currentSdkVersion); err == nil {
+				version := sdkVersion(i)
+				return sdkSpec{s.kind, version, s.raw}
+			}
+			panic(fmt.Errorf("BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES must be either \"current\" or a number, but was %q", currentSdkVersion))
+		}
+	}
+	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() {
@@ -216,6 +236,10 @@
 	if !s.valid() {
 		return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
 	}
+
+	if ctx.DeviceSpecific() || ctx.SocSpecific() {
+		s = s.forVendorPartition(ctx)
+	}
 	if s.version.isNumbered() {
 		return s.version, nil
 	}
@@ -330,6 +354,10 @@
 		return sdkDep{}
 	}
 
+	if ctx.DeviceSpecific() || ctx.SocSpecific() {
+		sdkVersion = sdkVersion.forVendorPartition(ctx)
+	}
+
 	if !sdkVersion.validateSystemSdk(ctx) {
 		return sdkDep{}
 	}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 25f0134..1152ac3 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
@@ -1376,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 {
@@ -1410,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 {
@@ -1949,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 {
@@ -1972,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
@@ -1981,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 {
@@ -2056,6 +2063,17 @@
 	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) BaseDir() string {
+	return "etc"
+}
+
 // from android.PrebuiltEtcModule
 func (module *sdkLibraryXml) SubDir() string {
 	return "permissions"
@@ -2083,8 +2101,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/testing.go b/java/testing.go
index 1e725fa..1db6ef2 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -44,6 +44,9 @@
 		"prebuilts/sdk/17/public/android.jar":                      nil,
 		"prebuilts/sdk/17/public/framework.aidl":                   nil,
 		"prebuilts/sdk/17/system/android.jar":                      nil,
+		"prebuilts/sdk/28/public/android.jar":                      nil,
+		"prebuilts/sdk/28/public/framework.aidl":                   nil,
+		"prebuilts/sdk/28/system/android.jar":                      nil,
 		"prebuilts/sdk/29/public/android.jar":                      nil,
 		"prebuilts/sdk/29/public/framework.aidl":                   nil,
 		"prebuilts/sdk/29/system/android.jar":                      nil,
diff --git a/rust/Android.bp b/rust/Android.bp
index bbeb28d..8618207 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -19,7 +19,9 @@
         "prebuilt.go",
         "proc_macro.go",
         "project_json.go",
+        "protobuf.go",
         "rust.go",
+        "strip.go",
         "source_provider.go",
         "test.go",
         "testing.go",
@@ -33,6 +35,7 @@
         "coverage_test.go",
         "library_test.go",
         "project_json_test.go",
+        "protobuf_test.go",
         "rust_test.go",
         "source_provider_test.go",
         "test_test.go",
diff --git a/rust/androidmk.go b/rust/androidmk.go
index fda0a25..edae0e6 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -96,7 +96,6 @@
 
 	ret.Class = "EXECUTABLES"
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String())
 		if binary.coverageOutputZipFile.Valid() {
 			fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+binary.coverageOutputZipFile.String())
 		}
@@ -139,9 +138,6 @@
 	}
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		if !library.rlib() {
-			fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
-		}
 		if library.coverageOutputZipFile.Valid() {
 			fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+library.coverageOutputZipFile.String())
 		}
@@ -169,23 +165,32 @@
 		stem, suffix, _ := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 	})
 }
 
 func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ctx.SubAndroidMk(ret, bindgen.BaseSourceProvider)
-	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
-	})
+}
+
+func (proto *protobufDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.SubAndroidMk(ret, proto.BaseSourceProvider)
 }
 
 func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	var unstrippedOutputFile android.OptionalPath
 	// Soong installation is only supported for host modules. Have Make
 	// installation trigger Soong installation.
 	if ctx.Target().Os.Class == android.Host {
 		ret.OutputFile = android.OptionalPathForPath(compiler.path)
+	} else if compiler.strippedOutputFile.Valid() {
+		unstrippedOutputFile = ret.OutputFile
+		ret.OutputFile = compiler.strippedOutputFile
 	}
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		if compiler.strippedOutputFile.Valid() {
+			fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", unstrippedOutputFile)
+		}
 		path, file := filepath.Split(compiler.path.ToMakePath().String())
 		stem, suffix, _ := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
diff --git a/rust/binary.go b/rust/binary.go
index 1a82c92..1d02453 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -24,13 +24,11 @@
 }
 
 type BinaryCompilerProperties struct {
-	// passes -C prefer-dynamic to rustc, which tells it to dynamically link the stdlib
-	// (assuming it has no dylib dependencies already)
-	Prefer_dynamic *bool
 }
 
 type binaryDecorator struct {
 	*baseCompiler
+	stripper Stripper
 
 	Properties BinaryCompilerProperties
 }
@@ -60,10 +58,6 @@
 	return module, binary
 }
 
-func (binary *binaryDecorator) preferDynamic() bool {
-	return Bool(binary.Properties.Prefer_dynamic)
-}
-
 func (binary *binaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = binary.baseCompiler.compilerFlags(ctx, flags)
 
@@ -76,9 +70,6 @@
 			"-Wl,--no-undefined-version")
 	}
 
-	if binary.preferDynamic() {
-		flags.RustFlags = append(flags.RustFlags, "-C prefer-dynamic")
-	}
 	return flags
 }
 
@@ -96,7 +87,8 @@
 
 func (binary *binaryDecorator) compilerProps() []interface{} {
 	return append(binary.baseCompiler.compilerProps(),
-		&binary.Properties)
+		&binary.Properties,
+		&binary.stripper.StripProperties)
 }
 
 func (binary *binaryDecorator) nativeCoverage() bool {
@@ -105,15 +97,20 @@
 
 func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
-
 	srcPath, _ := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
-
 	outputFile := android.PathForModuleOut(ctx, fileName)
-	binary.unstrippedOutputFile = outputFile
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
 
 	outputs := TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+
+	if binary.stripper.NeedsStrip(ctx) {
+		strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
+		binary.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
+		binary.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
+	}
+
 	binary.coverageFile = outputs.coverageFile
 
 	var coverageFiles android.Paths
@@ -132,8 +129,9 @@
 	return binary.coverageOutputZipFile
 }
 
-func (binary *binaryDecorator) autoDep() autoDep {
-	if binary.preferDynamic() {
+func (binary *binaryDecorator) autoDep(ctx BaseModuleContext) autoDep {
+	// Binaries default to dylib dependencies for device, rlib for host.
+	if ctx.Device() {
 		return dylibAutoDep
 	} else {
 		return rlibAutoDep
diff --git a/rust/binary_test.go b/rust/binary_test.go
index b9c8698..cfef57a 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -17,44 +17,112 @@
 import (
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
-// Test that the prefer_dynamic property is handled correctly.
-func TestPreferDynamicBinary(t *testing.T) {
+// Test that rustlibs default linkage is correct for binaries.
+func TestBinaryLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz-buzz",
+			srcs: ["foo.rs"],
+			rustlibs: ["libfoo"],
+			host_supported: true,
+		}
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			host_supported: true,
+		}`)
+
+	fizzBuzzHost := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+	fizzBuzzDevice := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
+
+	if !android.InList("libfoo", fizzBuzzHost.Properties.AndroidMkRlibs) {
+		t.Errorf("rustlibs dependency libfoo should be an rlib dep for host modules")
+	}
+
+	if !android.InList("libfoo", fizzBuzzDevice.Properties.AndroidMkDylibs) {
+		t.Errorf("rustlibs dependency libfoo should be an dylib dep for device modules")
+	}
+}
+
+// Test that the path returned by HostToolPath is correct
+func TestHostToolPath(t *testing.T) {
 	ctx := testRust(t, `
 		rust_binary_host {
-			name: "fizz-buzz-dynamic",
+			name: "fizz-buzz",
 			srcs: ["foo.rs"],
-			prefer_dynamic: true,
-		}
+		}`)
 
+	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)
+	}
+}
+
+// Test that the flags being passed to rust_binary modules are as expected
+func TestBinaryFlags(t *testing.T) {
+	ctx := testRust(t, `
 		rust_binary_host {
 			name: "fizz-buzz",
 			srcs: ["foo.rs"],
 		}`)
 
 	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"]
+	flags := fizzBuzz.Args["rustcFlags"]
 	if strings.Contains(flags, "--test") {
 		t.Errorf("extra --test flag, rustcFlags: %#v", flags)
 	}
-	if !strings.Contains(flags, "prefer-dynamic") {
-		t.Errorf("missing prefer-dynamic flag, rustcFlags: %#v", flags)
+}
+
+func TestLinkObjects(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz-buzz",
+			srcs: ["foo.rs"],
+			shared_libs: ["libfoo"],
+		}
+		cc_library {
+			name: "libfoo",
+		}`)
+
+	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Output("fizz-buzz")
+	linkFlags := fizzBuzz.Args["linkFlags"]
+	if !strings.Contains(linkFlags, "/libfoo.so") {
+		t.Errorf("missing shared dependency 'libfoo.so' in linkFlags: %#v", linkFlags)
+	}
+}
+
+// Test that stripped versions are correctly generated and used.
+func TestStrippedBinary(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "foo",
+			srcs: ["foo.rs"],
+		}
+		rust_binary {
+			name: "bar",
+			srcs: ["foo.rs"],
+			strip: {
+				none: true
+			}
+		}
+	`)
+
+	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a")
+	foo.Output("stripped/foo")
+	// Check that the `cp` rules is using the stripped version as input.
+	cp := foo.Rule("android.Cp")
+	if !strings.HasSuffix(cp.Input.String(), "stripped/foo") {
+		t.Errorf("installed binary not based on stripped version: %v", cp.Input)
 	}
 
-	flags = fizzBuzz.Args["rustcFlags"]
-	if strings.Contains(flags, "--test") {
-		t.Errorf("extra --test flag, rustcFlags: %#v", flags)
-	}
-	if strings.Contains(flags, "prefer-dynamic") {
-		t.Errorf("unexpected prefer-dynamic flag, rustcFlags: %#v", flags)
+	fizzBar := ctx.ModuleForTests("bar", "android_arm64_armv8-a").MaybeOutput("stripped/bar")
+	if fizzBar.Rule != nil {
+		t.Errorf("stripped version of bar has been generated")
 	}
 }
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 9b09e61..0336308 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -21,7 +21,6 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	ccConfig "android/soong/cc/config"
 )
 
 var (
@@ -34,9 +33,9 @@
 	//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
 	_ = pctx.SourcePathVariable("bindgenCmd", "out/host/${config.HostPrebuiltTag}/bin/bindgen")
 	_ = pctx.SourcePathVariable("bindgenClang",
-		"${ccConfig.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/bin/clang")
+		"${cc_config.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/bin/clang")
 	_ = pctx.SourcePathVariable("bindgenLibClang",
-		"${ccConfig.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/lib64/libclang.so."+bindgenLibClangSoGit)
+		"${cc_config.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/lib64/libclang.so."+bindgenLibClangSoGit)
 
 	//TODO(ivanlozano) Switch this to RuleBuilder
 	bindgen = pctx.AndroidStaticRule("bindgen",
@@ -92,8 +91,8 @@
 	Properties BindgenProperties
 }
 
-func (b *bindgenDecorator) GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
-	ccToolchain := ccConfig.FindToolchain(ctx.Os(), ctx.Arch())
+func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
+	ccToolchain := ctx.RustModule().ccToolchain(ctx)
 
 	var cflags []string
 	var implicits android.Paths
@@ -102,14 +101,14 @@
 	implicits = append(implicits, deps.depSystemIncludePaths...)
 
 	// Default clang flags
-	cflags = append(cflags, "${ccConfig.CommonClangGlobalCflags}")
+	cflags = append(cflags, "${cc_config.CommonClangGlobalCflags}")
 	if ctx.Device() {
-		cflags = append(cflags, "${ccConfig.DeviceClangGlobalCflags}")
+		cflags = append(cflags, "${cc_config.DeviceClangGlobalCflags}")
 	}
 
 	// Toolchain clang flags
 	cflags = append(cflags, "-target "+ccToolchain.ClangTriple())
-	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainClangCflags(), "${config.", "${ccConfig."))
+	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainClangCflags(), "${config.", "${cc_config."))
 
 	// Dependency clang flags and include paths
 	cflags = append(cflags, deps.depClangFlags...)
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 c39a4a1..ddf1fac 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"`
@@ -123,8 +129,9 @@
 	location installLocation
 
 	coverageOutputZipFile android.OptionalPath
-	unstrippedOutputFile  android.Path
 	distFile              android.OptionalPath
+	// Stripped output file. If Valid(), this file will be installed instead of outputFile.
+	strippedOutputFile android.OptionalPath
 }
 
 func (compiler *baseCompiler) Disabled() bool {
@@ -139,6 +146,10 @@
 	panic("baseCompiler does not implement coverageOutputZipPath()")
 }
 
+func (compiler *baseCompiler) static() bool {
+	return false
+}
+
 var _ compiler = (*baseCompiler)(nil)
 
 func (compiler *baseCompiler) inData() bool {
@@ -159,11 +170,11 @@
 
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 
-	if Bool(compiler.Properties.No_lint) {
-		flags.RustFlags = append(flags.RustFlags, config.AllowAllLints)
-	} else {
-		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())
@@ -210,7 +221,15 @@
 				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
 			}
 
-			deps.Rustlibs = append(deps.Rustlibs, stdlib)
+			// For devices, we always link stdlibs in as dylibs except for ffi static libraries.
+			// (rustc does not support linking libstd as a dylib for ffi static libraries)
+			if ctx.Host() {
+				deps.Rustlibs = append(deps.Rustlibs, stdlib)
+			} else if ctx.RustModule().compiler.static() {
+				deps.Rlibs = append(deps.Rlibs, stdlib)
+			} else {
+				deps.Dylibs = append(deps.Dylibs, stdlib)
+			}
 		}
 	}
 	return deps
@@ -251,8 +270,12 @@
 	return false
 }
 
-func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) {
-	compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), file)
+func (compiler *baseCompiler) install(ctx ModuleContext) {
+	path := ctx.RustModule().outputFile
+	if compiler.strippedOutputFile.Valid() {
+		path = compiler.strippedOutputFile
+	}
+	compiler.path = ctx.InstallFile(compiler.installDir(ctx), path.Path().Base(), path.Path())
 }
 
 func (compiler *baseCompiler) getStem(ctx ModuleContext) string {
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index b853196..56a8ef8 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,101 @@
 		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}")
+			}
+
+		})
+	}
+}
+
+// Test that devices are linking the stdlib dynamically
+func TestStdDeviceLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz",
+			srcs: ["foo.rs"],
+		}
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}`)
+	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
+	fooRlib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib").Module().(*Module)
+	fooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+
+	if !android.InList("libstd", fizz.Properties.AndroidMkDylibs) {
+		t.Errorf("libstd is not linked dynamically for device binaries")
+	}
+	if !android.InList("libstd", fooRlib.Properties.AndroidMkDylibs) {
+		t.Errorf("libstd is not linked dynamically for rlibs")
+	}
+	if !android.InList("libstd", fooDylib.Properties.AndroidMkDylibs) {
+		t.Errorf("libstd is not linked dynamically for dylibs")
+	}
+}
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index ed9f90b..62d469e 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -10,6 +10,7 @@
 		"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..2f4d930 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{
@@ -52,10 +52,11 @@
 		"-Wl,--fatal-warnings",
 
 		"-Wl,--pack-dyn-relocs=android+relr",
+		"-Wl,--use-android-relr-tags",
 		"-Wl,--no-undefined",
 		"-Wl,--hash-style=gnu",
 
-		"-B${ccConfig.ClangBin}",
+		"-B${cc_config.ClangBin}",
 		"-fuse-ld=lld",
 	}
 )
@@ -81,8 +82,8 @@
 	pctx.StaticVariable("RustPath", "${RustBase}/${HostPrebuiltTag}/${RustVersion}")
 	pctx.StaticVariable("RustBin", "${RustPath}/bin")
 
-	pctx.ImportAs("ccConfig", "android/soong/cc/config")
-	pctx.StaticVariable("RustLinker", "${ccConfig.ClangBin}/clang++")
+	pctx.ImportAs("cc_config", "android/soong/cc/config")
+	pctx.StaticVariable("RustLinker", "${cc_config.ClangBin}/clang++")
 	pctx.StaticVariable("RustLinkerArgs", "")
 
 	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
diff --git a/rust/config/lints.go b/rust/config/lints.go
index e24ffac..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,36 +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.
 }
-var AllowAllLints = rustcAllowAll
+
+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/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index 4104400..a13a9a6 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -23,7 +23,7 @@
 var (
 	DarwinRustFlags     = []string{}
 	DarwinRustLinkFlags = []string{
-		"-B${ccConfig.MacToolPath}",
+		"-B${cc_config.MacToolPath}",
 	}
 	darwinX8664Rustflags = []string{}
 	darwinX8664Linkflags = []string{}
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index acc99e1..b55b2f2 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -23,7 +23,7 @@
 var (
 	LinuxRustFlags     = []string{}
 	LinuxRustLinkFlags = []string{
-		"-B${ccConfig.ClangBin}",
+		"-B${cc_config.ClangBin}",
 		"-fuse-ld=lld",
 	}
 	linuxX86Rustflags   = []string{}
diff --git a/rust/coverage.go b/rust/coverage.go
index 223ba4f..26375f5 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -53,7 +53,7 @@
 		flags.Coverage = true
 		coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
 		flags.RustFlags = append(flags.RustFlags,
-			"-Z profile", "-g", "-C opt-level=0", "-C link-dead-code", "-Z no-landing-pads")
+			"-Z profile", "-g", "-C opt-level=0", "-C link-dead-code")
 		flags.LinkFlags = append(flags.LinkFlags,
 			"--coverage", "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,getenv")
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 357c2e8..73673d0 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -56,7 +56,7 @@
 	fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
 	buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
 
-	rustcCoverageFlags := []string{"-Z profile", " -g ", "-C opt-level=0", "-C link-dead-code", "-Z no-landing-pads"}
+	rustcCoverageFlags := []string{"-Z profile", " -g ", "-C opt-level=0", "-C link-dead-code"}
 	for _, flag := range rustcCoverageFlags {
 		missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
 		containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
diff --git a/rust/library.go b/rust/library.go
index 6766d61..a442933 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -78,6 +78,7 @@
 type libraryDecorator struct {
 	*baseCompiler
 	*flagExporter
+	stripper Stripper
 
 	Properties        LibraryCompilerProperties
 	MutatedProperties LibraryMutatedProperties
@@ -176,13 +177,13 @@
 	library.MutatedProperties.VariantIsDylib = false
 }
 
-func (library *libraryDecorator) autoDep() autoDep {
+func (library *libraryDecorator) autoDep(ctx BaseModuleContext) autoDep {
 	if library.rlib() || library.static() {
 		return rlibAutoDep
 	} else if library.dylib() || library.shared() {
 		return dylibAutoDep
 	} else {
-		return rlibAutoDep
+		panic("autoDep called on library" + ctx.ModuleName() + "that has no enabled variants.")
 	}
 }
 
@@ -338,7 +339,8 @@
 func (library *libraryDecorator) compilerProps() []interface{} {
 	return append(library.baseCompiler.compilerProps(),
 		&library.Properties,
-		&library.MutatedProperties)
+		&library.MutatedProperties,
+		&library.stripper.StripProperties)
 }
 
 func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
@@ -371,7 +373,8 @@
 }
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
-	var outputFile android.WritablePath
+	var outputFile android.ModuleOutPath
+	var fileName string
 	var srcPath android.Path
 
 	if library.sourceProvider != nil {
@@ -381,6 +384,7 @@
 	}
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
 
 	if library.dylib() {
 		// We need prefer-dynamic for now to avoid linking in the static stdlib. See:
@@ -390,31 +394,37 @@
 	}
 
 	if library.rlib() {
-		fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix()
+		fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
 		outputs := TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
 		library.coverageFile = outputs.coverageFile
 	} else if library.dylib() {
-		fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix()
+		fileName = library.getStem(ctx) + ctx.toolchain().DylibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
 		outputs := TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
 		library.coverageFile = outputs.coverageFile
 	} else if library.static() {
-		fileName := library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
+		fileName = library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
 		outputs := TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
 		library.coverageFile = outputs.coverageFile
 	} else if library.shared() {
-		fileName := library.sharedLibFilename(ctx)
+		fileName = library.sharedLibFilename(ctx)
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
 		outputs := TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
 		library.coverageFile = outputs.coverageFile
 	}
 
+	if !library.rlib() && library.stripper.NeedsStrip(ctx) {
+		strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
+		library.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
+		library.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
+	}
+
 	var coverageFiles android.Paths
 	if library.coverageFile != nil {
 		coverageFiles = append(coverageFiles, library.coverageFile)
@@ -427,8 +437,8 @@
 	if library.rlib() || library.dylib() {
 		library.exportLinkDirs(deps.linkDirs...)
 		library.exportDepFlags(deps.depFlags...)
+		library.exportLinkObjects(deps.linkObjects...)
 	}
-	library.unstrippedOutputFile = outputFile
 
 	return outputFile
 }
diff --git a/rust/library_test.go b/rust/library_test.go
index 8a91cf1..f1bc050 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -144,6 +144,22 @@
 	}
 }
 
+func TestStaticLibraryLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_ffi_static {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}`)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+
+	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) {
+		t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v",
+			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
+	}
+}
+
 // Test that variants pull in the right type of rustlib autodep
 func TestAutoDeps(t *testing.T) {
 
@@ -190,3 +206,35 @@
 
 	}
 }
+
+// Test that stripped versions are correctly generated and used.
+func TestStrippedLibrary(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_dylib {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["foo.rs"],
+		}
+		rust_library_dylib {
+			name: "libbar",
+			crate_name: "bar",
+			srcs: ["foo.rs"],
+			strip: {
+				none: true
+			}
+		}
+	`)
+
+	foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib")
+	foo.Output("stripped/libfoo.dylib.so")
+	// Check that the `cp` rule is using the stripped version as input.
+	cp := foo.Rule("android.Cp")
+	if !strings.HasSuffix(cp.Input.String(), "stripped/libfoo.dylib.so") {
+		t.Errorf("installed binary not based on stripped version: %v", cp.Input)
+	}
+
+	fizzBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeOutput("stripped/libbar.dylib.so")
+	if fizzBar.Rule != nil {
+		t.Errorf("stripped version of bar has been generated")
+	}
+}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 3d081c1..f9c8934 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -99,9 +99,6 @@
 	if len(paths) > 0 {
 		ctx.PropertyErrorf("srcs", "prebuilt libraries can only have one entry in srcs (the prebuilt path)")
 	}
-
-	prebuilt.unstrippedOutputFile = srcPath
-
 	return srcPath
 }
 
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 3dd2521..0c6ec99 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -66,9 +66,6 @@
 	outputFile := android.PathForModuleOut(ctx, fileName)
 
 	srcPath, _ := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
-
-	procMacro.unstrippedOutputFile = outputFile
-
 	TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
 	return outputFile
 }
@@ -80,6 +77,6 @@
 	return stem + String(procMacro.baseCompiler.Properties.Suffix)
 }
 
-func (procMacro *procMacroDecorator) autoDep() autoDep {
+func (procMacro *procMacroDecorator) autoDep(ctx BaseModuleContext) autoDep {
 	return rlibAutoDep
 }
diff --git a/rust/protobuf.go b/rust/protobuf.go
new file mode 100644
index 0000000..897300f
--- /dev/null
+++ b/rust/protobuf.go
@@ -0,0 +1,109 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+)
+
+var (
+	defaultProtobufFlags = []string{""}
+)
+
+func init() {
+	android.RegisterModuleType("rust_protobuf", RustProtobufFactory)
+	android.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
+}
+
+var _ SourceProvider = (*protobufDecorator)(nil)
+
+type ProtobufProperties struct {
+	// Path to the proto file that will be used to generate the source
+	Proto *string `android:"path,arch_variant"`
+
+	// List of additional flags to pass to aprotoc
+	Proto_flags []string `android:"arch_variant"`
+}
+
+type protobufDecorator struct {
+	*BaseSourceProvider
+
+	Properties ProtobufProperties
+}
+
+func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
+	var protoFlags android.ProtoFlags
+	pluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
+
+	protoFlags.OutTypeFlag = "--rust_out"
+
+	protoFlags.Flags = append(protoFlags.Flags, " --plugin="+pluginPath.String())
+	protoFlags.Flags = append(protoFlags.Flags, defaultProtobufFlags...)
+	protoFlags.Flags = append(protoFlags.Flags, proto.Properties.Proto_flags...)
+
+	protoFlags.Deps = append(protoFlags.Deps, pluginPath)
+
+	protoFile := android.OptionalPathForModuleSrc(ctx, proto.Properties.Proto)
+	if !protoFile.Valid() {
+		ctx.PropertyErrorf("proto", "invalid path to proto file")
+	}
+
+	outDir := android.PathForModuleOut(ctx)
+	depFile := android.PathForModuleOut(ctx, proto.BaseSourceProvider.getStem(ctx)+".d")
+	outputs := android.WritablePaths{android.PathForModuleOut(ctx, proto.BaseSourceProvider.getStem(ctx)+".rs")}
+
+	rule := android.NewRuleBuilder()
+	android.ProtoRule(ctx, rule, protoFile.Path(), protoFlags, protoFlags.Deps, outDir, depFile, outputs)
+	rule.Build(pctx, ctx, "protoc_"+protoFile.Path().Rel(), "protoc "+protoFile.Path().Rel())
+
+	proto.BaseSourceProvider.OutputFile = outputs[0]
+	return outputs[0]
+}
+
+func (proto *protobufDecorator) SourceProviderProps() []interface{} {
+	return append(proto.BaseSourceProvider.SourceProviderProps(), &proto.Properties)
+}
+
+func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
+	deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps)
+	deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
+	return deps
+}
+
+// rust_protobuf generates protobuf rust code from the provided proto file. This uses the protoc-gen-rust plugin for
+// protoc. Additional flags to the protoc command can be passed via the proto_flags property. This module type will
+// create library variants that can be used as a crate dependency by adding it to the rlibs, dylibs, and rustlibs
+// properties of other modules.
+func RustProtobufFactory() android.Module {
+	module, _ := NewRustProtobuf(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+// A host-only variant of rust_protobuf. Refer to rust_protobuf for more details.
+func RustProtobufHostFactory() android.Module {
+	module, _ := NewRustProtobuf(android.HostSupported)
+	return module.Init()
+}
+
+func NewRustProtobuf(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
+	protobuf := &protobufDecorator{
+		BaseSourceProvider: NewSourceProvider(),
+		Properties:         ProtobufProperties{},
+	}
+
+	module := NewSourceProviderModule(hod, protobuf, false)
+
+	return module, protobuf
+}
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
new file mode 100644
index 0000000..a9dbf39
--- /dev/null
+++ b/rust/protobuf_test.go
@@ -0,0 +1,39 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestRustProtobuf(t *testing.T) {
+	ctx := testRust(t, `
+		rust_protobuf {
+			name: "librust_proto",
+			proto: "buf.proto",
+			crate_name: "rust_proto",
+			source_stem: "buf",
+		}
+	`)
+	// Check that there's a rule to generate the expected output
+	_ = ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Output("buf.rs")
+
+	// Check that libprotobuf is added as a dependency.
+	librust_proto := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Module().(*Module)
+	if !android.InList("libprotobuf", librust_proto.Properties.AndroidMkDylibs) {
+		t.Errorf("libprotobuf dependency missing for rust_protobuf (dependency missing from AndroidMkDylibs)")
+	}
+}
diff --git a/rust/rust.go b/rust/rust.go
index 54b2285..5c6cdb7 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -23,6 +23,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	cc_config "android/soong/cc/config"
 	"android/soong/rust/config"
 )
 
@@ -42,7 +43,7 @@
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
-	pctx.ImportAs("ccConfig", "android/soong/cc/config")
+	pctx.ImportAs("cc_config", "android/soong/cc/config")
 }
 
 type Flags struct {
@@ -244,13 +245,14 @@
 }
 
 type PathDeps struct {
-	DyLibs     RustLibraries
-	RLibs      RustLibraries
-	SharedLibs android.Paths
-	StaticLibs android.Paths
-	ProcMacros RustLibraries
-	linkDirs   []string
-	depFlags   []string
+	DyLibs      RustLibraries
+	RLibs       RustLibraries
+	SharedLibs  android.Paths
+	StaticLibs  android.Paths
+	ProcMacros  RustLibraries
+	linkDirs    []string
+	depFlags    []string
+	linkObjects []string
 	//ReexportedDeps android.Paths
 
 	// Used by bindgen modules which call clang
@@ -282,25 +284,30 @@
 	crateName() string
 
 	inData() bool
-	install(ctx ModuleContext, path android.Path)
+	install(ctx ModuleContext)
 	relativeInstallPath() string
 
 	nativeCoverage() bool
 
 	Disabled() bool
 	SetDisabled()
+
+	static() bool
 }
 
 type exportedFlagsProducer interface {
 	exportedLinkDirs() []string
 	exportedDepFlags() []string
+	exportedLinkObjects() []string
 	exportLinkDirs(...string)
 	exportDepFlags(...string)
+	exportLinkObjects(...string)
 }
 
 type flagExporter struct {
-	depFlags []string
-	linkDirs []string
+	depFlags    []string
+	linkDirs    []string
+	linkObjects []string
 }
 
 func (flagExporter *flagExporter) exportedLinkDirs() []string {
@@ -311,6 +318,10 @@
 	return flagExporter.depFlags
 }
 
+func (flagExporter *flagExporter) exportedLinkObjects() []string {
+	return flagExporter.linkObjects
+}
+
 func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
 	flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
 }
@@ -319,12 +330,17 @@
 	flagExporter.depFlags = android.FirstUniqueStrings(append(flagExporter.depFlags, flags...))
 }
 
+func (flagExporter *flagExporter) exportLinkObjects(flags ...string) {
+	flagExporter.linkObjects = android.FirstUniqueStrings(append(flagExporter.linkObjects, flags...))
+}
+
 var _ exportedFlagsProducer = (*flagExporter)(nil)
 
 func NewFlagExporter() *flagExporter {
 	return &flagExporter{
-		depFlags: []string{},
-		linkDirs: []string{},
+		depFlags:    []string{},
+		linkDirs:    []string{},
+		linkObjects: []string{},
 	}
 }
 
@@ -641,6 +657,10 @@
 	return mod.cachedToolchain
 }
 
+func (mod *Module) ccToolchain(ctx android.BaseModuleContext) cc_config.Toolchain {
+	return cc_config.FindToolchain(ctx.Os(), ctx.Arch())
+}
+
 func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 }
 
@@ -684,7 +704,7 @@
 
 		mod.outputFile = android.OptionalPathForPath(outputFile)
 		if mod.outputFile.Valid() && !mod.Properties.PreventInstall {
-			mod.compiler.install(ctx, mod.outputFile.Path())
+			mod.compiler.install(ctx)
 		}
 	}
 }
@@ -740,7 +760,7 @@
 )
 
 type autoDeppable interface {
-	autoDep() autoDep
+	autoDep(ctx BaseModuleContext) autoDep
 }
 
 func (mod *Module) begin(ctx BaseModuleContext) {
@@ -811,6 +831,7 @@
 			if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok && depTag != procMacroDepTag {
 				depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedLinkDirs()...)
 				depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
+				depPaths.linkObjects = append(depPaths.linkObjects, lib.exportedLinkObjects()...)
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -838,21 +859,18 @@
 					return
 				}
 			}
-			linkFile := ccDep.OutputFile()
-			linkPath := linkPathFromFilePath(linkFile.Path())
-			libName := libNameFromFilePath(linkFile.Path())
-			depFlag := "-l" + libName
+			linkObject := ccDep.OutputFile()
+			linkPath := linkPathFromFilePath(linkObject.Path())
 
-			if !linkFile.Valid() {
+			if !linkObject.Valid() {
 				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
 			}
 
 			exportDep := false
 			switch {
 			case cc.IsStaticDepTag(depTag):
-				depFlag = "-lstatic=" + libName
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
-				depPaths.depFlags = append(depPaths.depFlags, depFlag)
+				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, ccDep.IncludeDirs()...)
 				if mod, ok := ccDep.(*cc.Module); ok {
 					depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, mod.ExportedSystemIncludeDirs()...)
@@ -862,9 +880,8 @@
 				directStaticLibDeps = append(directStaticLibDeps, ccDep)
 				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
 			case cc.IsSharedDepTag(depTag):
-				depFlag = "-ldylib=" + libName
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
-				depPaths.depFlags = append(depPaths.depFlags, depFlag)
+				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, ccDep.IncludeDirs()...)
 				if mod, ok := ccDep.(*cc.Module); ok {
 					depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, mod.ExportedSystemIncludeDirs()...)
@@ -874,15 +891,15 @@
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
 				exportDep = true
 			case depTag == cc.CrtBeginDepTag:
-				depPaths.CrtBegin = linkFile
+				depPaths.CrtBegin = linkObject
 			case depTag == cc.CrtEndDepTag:
-				depPaths.CrtEnd = linkFile
+				depPaths.CrtEnd = linkObject
 			}
 
 			// Make sure these dependencies are propagated
 			if lib, ok := mod.compiler.(exportedFlagsProducer); ok && exportDep {
 				lib.exportLinkDirs(linkPath)
-				lib.exportDepFlags(depFlag)
+				lib.exportLinkObjects(linkObject.String())
 			}
 		}
 
@@ -956,14 +973,6 @@
 	return strings.Split(filepath.String(), filepath.Base())[0]
 }
 
-func libNameFromFilePath(filepath android.Path) string {
-	libName := strings.TrimSuffix(filepath.Base(), filepath.Ext())
-	if strings.HasPrefix(libName, "lib") {
-		libName = libName[3:]
-	}
-	return libName
-}
-
 func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	ctx := &depsContext{
 		BottomUpMutatorContext: actx,
@@ -988,8 +997,8 @@
 			{Mutator: "rust_libraries", Variation: "dylib"}}...),
 		dylibDepTag, deps.Dylibs...)
 
-	if deps.Rustlibs != nil {
-		autoDep := mod.compiler.(autoDeppable).autoDep()
+	if deps.Rustlibs != nil && !mod.compiler.Disabled() {
+		autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
 		actx.AddVariationDependencies(
 			append(commonDepVariations, []blueprint.Variation{
 				{Mutator: "rust_libraries", Variation: autoDep.variation}}...),
@@ -1046,9 +1055,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")
 	}
 }
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 04de48b..89ce359 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -62,6 +62,7 @@
 		"foo.c":      nil,
 		"src/bar.rs": nil,
 		"src/any.h":  nil,
+		"buf.proto":  nil,
 		"liby.so":    nil,
 		"libz.so":    nil,
 	}
@@ -132,25 +133,6 @@
 	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
 }
 
-// Test that we can extract the lib name from a lib path.
-func TestLibNameFromFilePath(t *testing.T) {
-	libBarPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so.so")
-	libLibPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/liblib.dylib.so")
-
-	libBarName := libNameFromFilePath(libBarPath)
-	libLibName := libNameFromFilePath(libLibPath)
-
-	expectedResult := "bar.so"
-	if libBarName != expectedResult {
-		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libBarName)
-	}
-
-	expectedResult = "lib.dylib"
-	if libLibName != expectedResult {
-		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libLibPath)
-	}
-}
-
 // Test that we can extract the link path from a lib path.
 func TestLinkPathFromFilePath(t *testing.T) {
 	barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 76679c2..755a369 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -38,7 +38,7 @@
 var _ SourceProvider = (*BaseSourceProvider)(nil)
 
 type SourceProvider interface {
-	GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path
+	GenerateSource(ctx ModuleContext, deps PathDeps) android.Path
 	Srcs() android.Paths
 	SourceProviderProps() []interface{}
 	SourceProviderDeps(ctx DepsContext, deps Deps) Deps
@@ -49,7 +49,7 @@
 	return android.Paths{sp.OutputFile}
 }
 
-func (sp *BaseSourceProvider) GenerateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
+func (sp *BaseSourceProvider) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
 	panic("BaseSourceProviderModule does not implement GenerateSource()")
 }
 
@@ -73,8 +73,8 @@
 	module.compiler = library
 
 	if !enableLints {
-		library.setNoLint()
-		module.setClippy(false)
+		library.disableLints()
+		module.disableClippy()
 	}
 
 	return module
diff --git a/rust/strip.go b/rust/strip.go
new file mode 100644
index 0000000..d1bbba6
--- /dev/null
+++ b/rust/strip.go
@@ -0,0 +1,30 @@
+// 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 rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+// Stripper encapsulates cc.Stripper.
+type Stripper struct {
+	cc.Stripper
+}
+
+func (s *Stripper) StripExecutableOrSharedLib(ctx ModuleContext, in android.Path, out android.ModuleOutPath) {
+	ccFlags := cc.StripFlags{Toolchain: ctx.RustModule().ccToolchain(ctx)}
+	s.Stripper.StripExecutableOrSharedLib(ctx, in, out, ccFlags)
+}
diff --git a/rust/test.go b/rust/test.go
index 05c361e..d93fc31 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -88,7 +88,7 @@
 	return append(test.binaryDecorator.compilerProps(), &test.Properties)
 }
 
-func (test *testDecorator) install(ctx ModuleContext, file android.Path) {
+func (test *testDecorator) install(ctx ModuleContext) {
 	test.testConfig = tradefed.AutoGenRustTestConfig(ctx,
 		test.Properties.Test_config,
 		test.Properties.Test_config_template,
@@ -103,7 +103,7 @@
 		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
 	}
 
-	test.binaryDecorator.install(ctx, file)
+	test.binaryDecorator.install(ctx)
 }
 
 func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -114,7 +114,7 @@
 	return flags
 }
 
-func (test *testDecorator) autoDep() autoDep {
+func (test *testDecorator) autoDep(ctx BaseModuleContext) autoDep {
 	return rlibAutoDep
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index 80e4148..0144c82 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -91,6 +91,12 @@
 			host_supported: true,
                         native_coverage: false,
 		}
+		rust_library {
+			name: "libprotobuf",
+			crate_name: "protobuf",
+			srcs: ["foo.rs"],
+			host_supported: true,
+		}
 
 ` + cc.GatherRequiredDepsForTest(android.NoOsType)
 	return bp
@@ -120,6 +126,8 @@
 	ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
 	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
+	ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
+	ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
 	ctx.RegisterModuleType("rust_prebuilt_library", PrebuiltLibraryFactory)
 	ctx.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
 	ctx.RegisterModuleType("rust_prebuilt_rlib", PrebuiltRlibFactory)
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..f3f4a4a 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"
 )
 
@@ -61,6 +64,12 @@
 
 	// install symlinks to the binary
 	Symlinks []string `android:"arch_variant"`
+
+	// Make this module available when building for ramdisk.
+	Ramdisk_available *bool
+
+	// Make this module available when building for recovery.
+	Recovery_available *bool
 }
 
 type TestProperties struct {
@@ -88,6 +97,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 +134,8 @@
 
 	data       android.Paths
 	testConfig android.Path
+
+	dataModules map[string]android.Path
 }
 
 func (s *ShBinary) HostToolPath() android.OptionalPath {
@@ -139,6 +164,29 @@
 	return s.properties.Symlinks
 }
 
+var _ android.ImageInterface = (*ShBinary)(nil)
+
+func (s *ShBinary) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (s *ShBinary) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return !s.ModuleBase.InstallInRecovery() && !s.ModuleBase.InstallInRamdisk()
+}
+
+func (s *ShBinary) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(s.properties.Ramdisk_available) || s.ModuleBase.InstallInRamdisk()
+}
+
+func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(s.properties.Recovery_available) || s.ModuleBase.InstallInRecovery()
+}
+
+func (s *ShBinary) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return nil
+}
+
+func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
+
 func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
 	s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
 	filename := proptools.String(s.properties.Filename)
@@ -192,6 +240,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 +315,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 +387,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/ui/build/rbe.go b/ui/build/rbe.go
index 99107e3..c4b829d 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -19,6 +19,7 @@
 	"math/rand"
 	"os"
 	"path/filepath"
+	"syscall"
 	"time"
 
 	"android/soong/ui/metrics"
@@ -50,14 +51,39 @@
 	return cmdPath
 }
 
-func getRBEVars(ctx Context, config Config) map[string]string {
+func sockAddr(dir string) (string, error) {
+	maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
 	rand.Seed(time.Now().UnixNano())
+	base := fmt.Sprintf("reproxy_%v.sock", rand.Intn(1000))
+
+	name := filepath.Join(dir, base)
+	if len(name) < maxNameLen {
+		return name, nil
+	}
+
+	name = filepath.Join("/tmp", base)
+	if len(name) < maxNameLen {
+		return name, nil
+	}
+
+	return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
+}
+
+func getRBEVars(ctx Context, config Config) map[string]string {
 	vars := map[string]string{
-		"RBE_server_address": fmt.Sprintf("unix://%v/reproxy_%v.sock", absPath(ctx, config.TempDir()), rand.Intn(1000)),
-		"RBE_log_path":       config.rbeLogPath(),
-		"RBE_re_proxy":       config.rbeReproxy(),
-		"RBE_exec_root":      config.rbeExecRoot(),
-		"RBE_output_dir":     config.rbeStatsOutputDir(),
+		"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() {
+		name, err := sockAddr(absPath(ctx, config.TempDir()))
+		if err != nil {
+			ctx.Fatalf("Error retrieving socket address: %v", err)
+			return nil
+		}
+		vars["RBE_server_address"] = fmt.Sprintf("unix://%v", name)
 	}
 	k, v := config.rbeAuth()
 	vars[k] = v
@@ -125,3 +151,13 @@
 		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/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 {