Merge "Use system modules for prebuilt SDKs >=30" into rvc-dev am: a91a16b484

Change-Id: Ide5005514be7f916212cde486f75230fec87c807
diff --git a/Android.bp b/Android.bp
index a81676f..342ca4c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -39,6 +39,7 @@
         "soong-android-soongconfig",
         "soong-env",
         "soong-shared",
+        "soong-ui-metrics_proto",
     ],
     srcs: [
         "android/androidmk.go",
@@ -54,6 +55,7 @@
         "android/hooks.go",
         "android/image.go",
         "android/makevars.go",
+        "android/metrics.go",
         "android/module.go",
         "android/mutator.go",
         "android/namespace.go",
@@ -142,8 +144,6 @@
         "cc/config/arm_device.go",
         "cc/config/arm64_device.go",
         "cc/config/arm64_fuchsia_device.go",
-        "cc/config/mips_device.go",
-        "cc/config/mips64_device.go",
         "cc/config/x86_device.go",
         "cc/config/x86_64_device.go",
         "cc/config/x86_64_fuchsia_device.go",
@@ -349,6 +349,7 @@
     deps: [
         "blueprint-proptools",
         "soong-android",
+        "soong-remoteexec",
     ],
     srcs: [
         "java/config/config.go",
@@ -390,6 +391,7 @@
     srcs: [
         "rust/androidmk.go",
         "rust/compiler.go",
+        "rust/coverage.go",
         "rust/binary.go",
         "rust/builder.go",
         "rust/library.go",
@@ -402,6 +404,7 @@
     testSrcs: [
         "rust/binary_test.go",
         "rust/compiler_test.go",
+        "rust/coverage_test.go",
         "rust/library_test.go",
         "rust/rust_test.go",
         "rust/test_test.go",
@@ -644,7 +647,8 @@
     arch: {
         arm: {
             src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a",
-            repack_objects_to_keep: ["unwind-arm.o", "libunwind.o", "pr-support.o"],
+            repack_objects_to_keep: [],
+            enabled: false,
         },
         arm64: {
             src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a",
diff --git a/OWNERS b/OWNERS
index 4ae045d..e1db459 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,3 +4,4 @@
 per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
 per-file tidy.go = srhines@google.com, chh@google.com
 per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
+per-file docs/map_files.md = danalbert@google.com, enh@google.com, jiyong@google.com
diff --git a/android/androidmk.go b/android/androidmk.go
index 6ba68af..d579e30 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -244,8 +244,8 @@
 		}
 	}
 
-	if amod.noticeFile.Valid() {
-		a.SetString("LOCAL_NOTICE_FILE", amod.noticeFile.String())
+	if len(amod.noticeFiles) > 0 {
+		a.SetString("LOCAL_NOTICE_FILE", strings.Join(amod.noticeFiles.Strings(), " "))
 	}
 
 	if host {
diff --git a/android/apex.go b/android/apex.go
index ede0965..75be150 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -33,6 +33,7 @@
 	ApexName string
 
 	MinSdkVersion int
+	Updatable     bool
 }
 
 // Extracted from ApexModule to make it easier to define custom subsets of the
@@ -116,6 +117,9 @@
 	// it returns 9 as string
 	ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error)
 
+	// Tests if the module comes from an updatable APEX.
+	Updatable() bool
+
 	// List of APEXes that this module tests. The module has access to
 	// the private part of the listed APEXes even when it is not included in the
 	// APEXes.
@@ -260,6 +264,10 @@
 	}
 }
 
+func (m *ApexModuleBase) Updatable() bool {
+	return m.ApexProperties.Info.Updatable
+}
+
 type byApexName []ApexInfo
 
 func (a byApexName) Len() int           { return len(a) }
@@ -413,21 +421,15 @@
 type DepNameToDepInfoMap map[string]ApexModuleDepInfo
 
 type ApexBundleDepsInfo struct {
-	minSdkVersion string
-	flatListPath  OutputPath
-	fullListPath  OutputPath
+	flatListPath OutputPath
+	fullListPath OutputPath
 }
 
 type ApexDepsInfoIntf interface {
-	MinSdkVersion() string
 	FlatListPath() Path
 	FullListPath() Path
 }
 
-func (d *ApexBundleDepsInfo) MinSdkVersion() string {
-	return d.minSdkVersion
-}
-
 func (d *ApexBundleDepsInfo) FlatListPath() Path {
 	return d.flatListPath
 }
@@ -442,8 +444,6 @@
 // 1. FullList with transitive deps and their parents in the dep graph
 // 2. FlatList with a flat list of transitive deps
 func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion string, depInfos DepNameToDepInfoMap) {
-	d.minSdkVersion = minSdkVersion
-
 	var fullContent strings.Builder
 	var flatContent strings.Builder
 
diff --git a/android/arch.go b/android/arch.go
index e440486..08c0256 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -33,8 +33,6 @@
 
 	Arm    = newArch("arm", "lib32")
 	Arm64  = newArch("arm64", "lib64")
-	Mips   = newArch("mips", "lib32")
-	Mips64 = newArch("mips64", "lib64")
 	X86    = newArch("x86", "lib32")
 	X86_64 = newArch("x86_64", "lib64")
 
@@ -46,8 +44,6 @@
 var archTypeMap = map[string]ArchType{
 	"arm":    Arm,
 	"arm64":  Arm64,
-	"mips":   Mips,
-	"mips64": Mips64,
 	"x86":    X86,
 	"x86_64": X86_64,
 }
@@ -64,12 +60,6 @@
         arm64: {
             // Host or device variants with arm64 architecture
         },
-        mips: {
-            // Host or device variants with mips architecture
-        },
-        mips64: {
-            // Host or device variants with mips64 architecture
-        },
         x86: {
             // Host or device variants with x86 architecture
         },
@@ -145,18 +135,6 @@
 		"exynos-m1",
 		"exynos-m2",
 	},
-	Mips: {
-		"mips32_fp",
-		"mips32r2_fp",
-		"mips32r2_fp_xburst",
-		"mips32r2dsp_fp",
-		"mips32r2dspr2_fp",
-		"mips32r6",
-	},
-	Mips64: {
-		"mips64r2",
-		"mips64r6",
-	},
 	X86: {
 		"amberlake",
 		"atom",
@@ -193,15 +171,6 @@
 	Arm: {
 		"neon",
 	},
-	Mips: {
-		"dspr2",
-		"rev6",
-		"msa",
-	},
-	Mips64: {
-		"rev6",
-		"msa",
-	},
 	X86: {
 		"ssse3",
 		"sse4",
@@ -239,19 +208,6 @@
 			"neon",
 		},
 	},
-	Mips: {
-		"mips32r2dspr2_fp": {
-			"dspr2",
-		},
-		"mips32r6": {
-			"rev6",
-		},
-	},
-	Mips64: {
-		"mips64r6": {
-			"rev6",
-		},
-	},
 	X86: {
 		"amberlake": {
 			"ssse3",
@@ -616,7 +572,7 @@
 		LinuxBionic: []ArchType{X86_64},
 		Darwin:      []ArchType{X86_64},
 		Windows:     []ArchType{X86, X86_64},
-		Android:     []ArchType{Arm, Arm64, Mips, Mips64, X86, X86_64},
+		Android:     []ArchType{Arm, Arm64, X86, X86_64},
 		Fuchsia:     []ArchType{Arm64, X86_64},
 	}
 )
@@ -1656,15 +1612,6 @@
 		{"arm64", "armv8-2a", "cortex-a75", []string{"arm64-v8a"}},
 		{"arm64", "armv8-2a", "cortex-a76", []string{"arm64-v8a"}},
 		{"arm64", "armv8-2a", "kryo385", []string{"arm64-v8a"}},
-		{"mips", "mips32-fp", "", []string{"mips"}},
-		{"mips", "mips32r2-fp", "", []string{"mips"}},
-		{"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
-		//{"mips", "mips32r6", "", []string{"mips"}},
-		{"mips", "mips32r2dsp-fp", "", []string{"mips"}},
-		{"mips", "mips32r2dspr2-fp", "", []string{"mips"}},
-		// mips64r2 is mismatching 64r2 and 64r6 libraries during linking to libgcc
-		//{"mips64", "mips64r2", "", []string{"mips64"}},
-		{"mips64", "mips64r6", "", []string{"mips64"}},
 		{"x86", "", "", []string{"x86"}},
 		{"x86", "atom", "", []string{"x86"}},
 		{"x86", "haswell", "", []string{"x86"}},
diff --git a/android/config.go b/android/config.go
index bf52c45..ee31c10 100644
--- a/android/config.go
+++ b/android/config.go
@@ -847,16 +847,7 @@
 }
 
 func (c *config) LibartImgDeviceBaseAddress() string {
-	archType := Common
-	if len(c.Targets[Android]) > 0 {
-		archType = c.Targets[Android][0].Arch.ArchType
-	}
-	switch archType {
-	default:
-		return "0x70000000"
-	case Mips, Mips64:
-		return "0x5C000000"
-	}
+	return "0x70000000"
 }
 
 func (c *config) ArtUseReadBarrier() bool {
@@ -915,13 +906,18 @@
 	return apexJarPair[0], apexJarPair[1]
 }
 
-func (c *config) BootJars() []string {
-	jars := c.productVariables.BootJars
-	for _, p := range c.productVariables.UpdatableBootJars {
+func GetJarsFromApexJarPairs(apexJarPairs []string) []string {
+	modules := make([]string, len(apexJarPairs))
+	for i, p := range apexJarPairs {
 		_, jar := SplitApexJarPair(p)
-		jars = append(jars, jar)
+		modules[i] = jar
 	}
-	return jars
+	return modules
+}
+
+func (c *config) BootJars() []string {
+	return append(GetJarsFromApexJarPairs(c.productVariables.BootJars),
+		GetJarsFromApexJarPairs(c.productVariables.UpdatableBootJars)...)
 }
 
 func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
diff --git a/android/metrics.go b/android/metrics.go
new file mode 100644
index 0000000..b7aee54
--- /dev/null
+++ b/android/metrics.go
@@ -0,0 +1,87 @@
+// 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 (
+	"io/ioutil"
+	"runtime"
+
+	"github.com/golang/protobuf/proto"
+
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+)
+
+var soongMetricsOnceKey = NewOnceKey("soong metrics")
+
+type SoongMetrics struct {
+	Modules  int
+	Variants int
+}
+
+func ReadSoongMetrics(config Config) SoongMetrics {
+	return config.Get(soongMetricsOnceKey).(SoongMetrics)
+}
+
+func init() {
+	RegisterSingletonType("soong_metrics", soongMetricsSingletonFactory)
+}
+
+func soongMetricsSingletonFactory() Singleton { return soongMetricsSingleton{} }
+
+type soongMetricsSingleton struct{}
+
+func (soongMetricsSingleton) GenerateBuildActions(ctx SingletonContext) {
+	metrics := SoongMetrics{}
+	ctx.VisitAllModules(func(m Module) {
+		if ctx.PrimaryModule(m) == m {
+			metrics.Modules++
+		}
+		metrics.Variants++
+	})
+	ctx.Config().Once(soongMetricsOnceKey, func() interface{} {
+		return metrics
+	})
+}
+
+func collectMetrics(config Config) *soong_metrics_proto.SoongBuildMetrics {
+	metrics := &soong_metrics_proto.SoongBuildMetrics{}
+
+	soongMetrics := ReadSoongMetrics(config)
+	metrics.Modules = proto.Uint32(uint32(soongMetrics.Modules))
+	metrics.Variants = proto.Uint32(uint32(soongMetrics.Variants))
+
+	memStats := runtime.MemStats{}
+	runtime.ReadMemStats(&memStats)
+	metrics.MaxHeapSize = proto.Uint64(memStats.HeapSys)
+	metrics.TotalAllocCount = proto.Uint64(memStats.Mallocs)
+	metrics.TotalAllocSize = proto.Uint64(memStats.TotalAlloc)
+
+	return metrics
+}
+
+func WriteMetrics(config Config, metricsFile string) error {
+	metrics := collectMetrics(config)
+
+	buf, err := proto.Marshal(metrics)
+	if err != nil {
+		return err
+	}
+	err = ioutil.WriteFile(absolutePath(metricsFile), buf, 0666)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/android/module.go b/android/module.go
index baf348a..7e64f0a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -19,6 +19,7 @@
 	"os"
 	"path"
 	"path/filepath"
+	"regexp"
 	"strings"
 	"text/scanner"
 
@@ -135,6 +136,13 @@
 	// GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1]
 	GetTagPath() []blueprint.DependencyTag
 
+	// GetPathString is supposed to be called in visit function passed in WalkDeps()
+	// and returns a multi-line string showing the modules and dependency tags
+	// among them along the top-down dependency path from a start module to current child module.
+	// skipFirst when set to true, the output doesn't include the start module,
+	// which is already printed when this function is used along with ModuleErrorf().
+	GetPathString(skipFirst bool) string
+
 	AddMissingDependencies(missingDeps []string)
 
 	Target() Target
@@ -182,6 +190,7 @@
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
+	InstallForceOS() *OsType
 
 	RequiredModuleNames() []string
 	HostRequiredModuleNames() []string
@@ -224,12 +233,13 @@
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
+	InstallForceOS() *OsType
 	SkipInstall()
 	IsSkipInstall() bool
 	ExportedToMake() bool
 	InitRc() Paths
 	VintfFragments() Paths
-	NoticeFile() OptionalPath
+	NoticeFiles() Paths
 
 	AddProperties(props ...interface{})
 	GetProperties() []interface{}
@@ -250,6 +260,8 @@
 	RequiredModuleNames() []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
+
+	filesToInstall() InstallPaths
 }
 
 // Qualified id for a module
@@ -703,9 +715,9 @@
 	primaryVisibilityProperty visibilityProperty
 
 	noAddressSanitizer bool
-	installFiles       Paths
+	installFiles       InstallPaths
 	checkbuildFiles    Paths
-	noticeFile         OptionalPath
+	noticeFiles        Paths
 
 	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
 	// Only set on the final variant of each module
@@ -950,22 +962,20 @@
 	return m.commonProperties.NamespaceExportedToMake
 }
 
-func (m *ModuleBase) computeInstallDeps(
-	ctx blueprint.ModuleContext) Paths {
+func (m *ModuleBase) computeInstallDeps(ctx blueprint.ModuleContext) InstallPaths {
 
-	result := Paths{}
+	var result InstallPaths
 	// TODO(ccross): we need to use WalkDeps and have some way to know which dependencies require installation
-	ctx.VisitDepsDepthFirstIf(isFileInstaller,
-		func(m blueprint.Module) {
-			fileInstaller := m.(fileInstaller)
-			files := fileInstaller.filesToInstall()
-			result = append(result, files...)
-		})
+	ctx.VisitDepsDepthFirst(func(m blueprint.Module) {
+		if a, ok := m.(Module); ok {
+			result = append(result, a.filesToInstall()...)
+		}
+	})
 
 	return result
 }
 
-func (m *ModuleBase) filesToInstall() Paths {
+func (m *ModuleBase) filesToInstall() InstallPaths {
 	return m.installFiles
 }
 
@@ -1001,12 +1011,16 @@
 	return false
 }
 
+func (m *ModuleBase) InstallForceOS() *OsType {
+	return nil
+}
+
 func (m *ModuleBase) Owner() string {
 	return String(m.commonProperties.Owner)
 }
 
-func (m *ModuleBase) NoticeFile() OptionalPath {
-	return m.noticeFile
+func (m *ModuleBase) NoticeFiles() Paths {
+	return m.noticeFiles
 }
 
 func (m *ModuleBase) setImageVariation(variant string) {
@@ -1059,8 +1073,8 @@
 }
 
 func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
-	allInstalledFiles := Paths{}
-	allCheckbuildFiles := Paths{}
+	var allInstalledFiles InstallPaths
+	var allCheckbuildFiles Paths
 	ctx.VisitAllModuleVariants(func(module Module) {
 		a := module.base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
@@ -1079,7 +1093,7 @@
 		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
 			Output:    name,
-			Implicits: allInstalledFiles,
+			Implicits: allInstalledFiles.Paths(),
 			Default:   !ctx.Config().EmbeddedInMake(),
 		})
 		deps = append(deps, name)
@@ -1265,12 +1279,25 @@
 			}
 		})
 
-		notice := proptools.StringDefault(m.commonProperties.Notice, "NOTICE")
+		m.noticeFiles = make([]Path, 0)
+		optPath := OptionalPath{}
+		notice := proptools.StringDefault(m.commonProperties.Notice, "")
 		if module := SrcIsModule(notice); module != "" {
-			m.noticeFile = ctx.ExpandOptionalSource(&notice, "notice")
-		} else {
+			optPath = ctx.ExpandOptionalSource(&notice, "notice")
+		} else if notice != "" {
 			noticePath := filepath.Join(ctx.ModuleDir(), notice)
-			m.noticeFile = ExistentPathForSource(ctx, noticePath)
+			optPath = ExistentPathForSource(ctx, noticePath)
+		}
+		if optPath.Valid() {
+			m.noticeFiles = append(m.noticeFiles, optPath.Path())
+		} else {
+			for _, notice = range []string{"LICENSE", "LICENCE", "NOTICE"} {
+				noticePath := filepath.Join(ctx.ModuleDir(), notice)
+				optPath = ExistentPathForSource(ctx, noticePath)
+				if optPath.Valid() {
+					m.noticeFiles = append(m.noticeFiles, optPath.Path())
+				}
+			}
 		}
 
 		m.module.GenerateAndroidBuildActions(ctx)
@@ -1415,8 +1442,8 @@
 type moduleContext struct {
 	bp blueprint.ModuleContext
 	baseModuleContext
-	installDeps     Paths
-	installFiles    Paths
+	installDeps     InstallPaths
+	installFiles    InstallPaths
 	checkbuildFiles Paths
 	module          Module
 
@@ -1712,6 +1739,41 @@
 	return b.tagPath
 }
 
+// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
+// a dependency tag.
+var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`)
+
+// PrettyPrintTag returns string representation of the tag, but prefers
+// custom String() method if available.
+func PrettyPrintTag(tag blueprint.DependencyTag) string {
+	// Use tag's custom String() method if available.
+	if stringer, ok := tag.(fmt.Stringer); ok {
+		return stringer.String()
+	}
+
+	// Otherwise, get a default string representation of the tag's struct.
+	tagString := fmt.Sprintf("%#v", tag)
+
+	// Remove the boilerplate from BaseDependencyTag as it adds no value.
+	tagString = tagCleaner.ReplaceAllString(tagString, "")
+	return tagString
+}
+
+func (b *baseModuleContext) GetPathString(skipFirst bool) string {
+	sb := strings.Builder{}
+	tagPath := b.GetTagPath()
+	walkPath := b.GetWalkPath()
+	if !skipFirst {
+		sb.WriteString(walkPath[0].String())
+	}
+	for i, m := range walkPath[1:] {
+		sb.WriteString("\n")
+		sb.WriteString(fmt.Sprintf("           via tag %s\n", PrettyPrintTag(tagPath[i])))
+		sb.WriteString(fmt.Sprintf("    -> %s", m.String()))
+	}
+	return sb.String()
+}
+
 func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
 	m.bp.VisitAllModuleVariants(func(module blueprint.Module) {
 		visit(module.(Module))
@@ -1836,6 +1898,10 @@
 	return m.module.InstallBypassMake()
 }
 
+func (m *moduleContext) InstallForceOS() *OsType {
+	return m.module.InstallForceOS()
+}
+
 func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
@@ -1879,7 +1945,7 @@
 
 	if !m.skipInstall(fullInstallPath) {
 
-		deps = append(deps, m.installDeps...)
+		deps = append(deps, m.installDeps.Paths()...)
 
 		var implicitDeps, orderOnlyDeps Paths
 
@@ -1960,20 +2026,6 @@
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 }
 
-type fileInstaller interface {
-	filesToInstall() Paths
-}
-
-func isFileInstaller(m blueprint.Module) bool {
-	_, ok := m.(fileInstaller)
-	return ok
-}
-
-func isAndroidModule(m blueprint.Module) bool {
-	_, ok := m.(Module)
-	return ok
-}
-
 func findStringInSlice(str string, slice []string) int {
 	for i, s := range slice {
 		if s == str {
diff --git a/android/notices.go b/android/notices.go
index bf273b5..07cf3e4 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -22,7 +22,7 @@
 
 func init() {
 	pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
-	pctx.SourcePathVariable("generate_notice", "build/make/tools/generate-notice-files.py")
+	pctx.SourcePathVariable("generate_notice", "build/soong/scripts/generate-notice-files.py")
 
 	pctx.HostBinToolVariable("minigzip", "minigzip")
 }
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 5a43ea9..0de356e 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -232,30 +232,10 @@
 	}, argNames...)
 }
 
-// RBEExperimentalFlag indicates which flag should be set for the AndroidRemoteStaticRule
-// to use RBE.
-type RBEExperimentalFlag int
-
-const (
-	// RBE_NOT_EXPERIMENTAL indicates the rule should use RBE in every build that has
-	// UseRBE set.
-	RBE_NOT_EXPERIMENTAL RBEExperimentalFlag = iota
-	// RBE_JAVAC indicates the rule should use RBE only if the RBE_JAVAC variable is
-	// set in an RBE enabled build.
-	RBE_JAVAC
-	// RBE_R8 indicates the rule should use RBE only if the RBE_R8 variable is set in
-	// an RBE enabled build.
-	RBE_R8
-	// RBE_D8 indicates the rule should use RBE only if the RBE_D8 variable is set in
-	// an RBE enabled build.
-	RBE_D8
-)
-
 // RemoteRuleSupports configures rules with whether they have Goma and/or RBE support.
 type RemoteRuleSupports struct {
-	Goma    bool
-	RBE     bool
-	RBEFlag RBEExperimentalFlag
+	Goma bool
+	RBE  bool
 }
 
 // AndroidRemoteStaticRule wraps blueprint.StaticRule but uses goma or RBE's parallelism if goma or RBE are enabled
@@ -277,18 +257,6 @@
 			params.Pool = localPool
 		}
 
-		if ctx.Config().UseRBE() && supports.RBE {
-			if supports.RBEFlag == RBE_JAVAC && !ctx.Config().UseRBEJAVAC() {
-				params.Pool = localPool
-			}
-			if supports.RBEFlag == RBE_R8 && !ctx.Config().UseRBER8() {
-				params.Pool = localPool
-			}
-			if supports.RBEFlag == RBE_D8 && !ctx.Config().UseRBED8() {
-				params.Pool = localPool
-			}
-		}
-
 		return params, nil
 	}, argNames...)
 }
diff --git a/android/paths.go b/android/paths.go
index 0edda38..8bb9a96 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -53,6 +53,7 @@
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
+	InstallForceOS() *OsType
 }
 
 var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -469,6 +470,14 @@
 // FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each.  It
 // modifies the Paths slice contents in place, and returns a subslice of the original slice.
 func FirstUniquePaths(list Paths) Paths {
+	// 128 was chosen based on BenchmarkFirstUniquePaths results.
+	if len(list) > 128 {
+		return firstUniquePathsMap(list)
+	}
+	return firstUniquePathsList(list)
+}
+
+func firstUniquePathsList(list Paths) Paths {
 	k := 0
 outer:
 	for i := 0; i < len(list); i++ {
@@ -483,6 +492,20 @@
 	return list[:k]
 }
 
+func firstUniquePathsMap(list Paths) Paths {
+	k := 0
+	seen := make(map[Path]bool, len(list))
+	for i := 0; i < len(list); i++ {
+		if seen[list[i]] {
+			continue
+		}
+		seen[list[i]] = true
+		list[k] = list[i]
+		k++
+	}
+	return list[:k]
+}
+
 // LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each.  It
 // modifies the Paths slice contents in place, and returns a subslice of the original slice.
 func LastUniquePaths(list Paths) Paths {
@@ -1205,22 +1228,40 @@
 // PathForModuleInstall returns a Path representing the install path for the
 // module appended with paths...
 func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
+	os := ctx.Os()
+	if forceOS := ctx.InstallForceOS(); forceOS != nil {
+		os = *forceOS
+	}
+	partition := modulePartition(ctx, os)
+
+	ret := pathForInstall(ctx, os, partition, ctx.Debug(), pathComponents...)
+
+	if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
+		ret = ret.ToMakePath()
+	}
+
+	return ret
+}
+
+func pathForInstall(ctx PathContext, os OsType, partition string, debug bool,
+	pathComponents ...string) InstallPath {
+
 	var outPaths []string
-	if ctx.Device() {
-		partition := modulePartition(ctx)
+
+	if os.Class == Device {
 		outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
 	} else {
-		switch ctx.Os() {
+		switch os {
 		case Linux:
-			outPaths = []string{"host", "linux-x86"}
+			outPaths = []string{"host", "linux-x86", partition}
 		case LinuxBionic:
 			// TODO: should this be a separate top level, or shared with linux-x86?
-			outPaths = []string{"host", "linux_bionic-x86"}
+			outPaths = []string{"host", "linux_bionic-x86", partition}
 		default:
-			outPaths = []string{"host", ctx.Os().String() + "-x86"}
+			outPaths = []string{"host", os.String() + "-x86", partition}
 		}
 	}
-	if ctx.Debug() {
+	if debug {
 		outPaths = append([]string{"debug"}, outPaths...)
 	}
 	outPaths = append(outPaths, pathComponents...)
@@ -1231,9 +1272,6 @@
 	}
 
 	ret := InstallPath{basePath{path, ctx.Config(), ""}, ""}
-	if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
-		ret = ret.ToMakePath()
-	}
 
 	return ret
 }
@@ -1261,47 +1299,76 @@
 	return "/" + rel
 }
 
-func modulePartition(ctx ModuleInstallPathContext) string {
+func modulePartition(ctx ModuleInstallPathContext, os OsType) string {
 	var partition string
-	if ctx.InstallInData() {
-		partition = "data"
-	} else if ctx.InstallInTestcases() {
+	if ctx.InstallInTestcases() {
+		// "testcases" install directory can be used for host or device modules.
 		partition = "testcases"
-	} else if ctx.InstallInRamdisk() {
-		if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
-			partition = "recovery/root/first_stage_ramdisk"
+	} else if os.Class == Device {
+		if ctx.InstallInData() {
+			partition = "data"
+		} else if ctx.InstallInRamdisk() {
+			if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
+				partition = "recovery/root/first_stage_ramdisk"
+			} else {
+				partition = "ramdisk"
+			}
+			if !ctx.InstallInRoot() {
+				partition += "/system"
+			}
+		} else if ctx.InstallInRecovery() {
+			if ctx.InstallInRoot() {
+				partition = "recovery/root"
+			} else {
+				// the layout of recovery partion is the same as that of system partition
+				partition = "recovery/root/system"
+			}
+		} else if ctx.SocSpecific() {
+			partition = ctx.DeviceConfig().VendorPath()
+		} else if ctx.DeviceSpecific() {
+			partition = ctx.DeviceConfig().OdmPath()
+		} else if ctx.ProductSpecific() {
+			partition = ctx.DeviceConfig().ProductPath()
+		} else if ctx.SystemExtSpecific() {
+			partition = ctx.DeviceConfig().SystemExtPath()
+		} else if ctx.InstallInRoot() {
+			partition = "root"
 		} else {
-			partition = "ramdisk"
+			partition = "system"
 		}
-		if !ctx.InstallInRoot() {
-			partition += "/system"
+		if ctx.InstallInSanitizerDir() {
+			partition = "data/asan/" + partition
 		}
-	} else if ctx.InstallInRecovery() {
-		if ctx.InstallInRoot() {
-			partition = "recovery/root"
-		} else {
-			// the layout of recovery partion is the same as that of system partition
-			partition = "recovery/root/system"
-		}
-	} else if ctx.SocSpecific() {
-		partition = ctx.DeviceConfig().VendorPath()
-	} else if ctx.DeviceSpecific() {
-		partition = ctx.DeviceConfig().OdmPath()
-	} else if ctx.ProductSpecific() {
-		partition = ctx.DeviceConfig().ProductPath()
-	} else if ctx.SystemExtSpecific() {
-		partition = ctx.DeviceConfig().SystemExtPath()
-	} else if ctx.InstallInRoot() {
-		partition = "root"
-	} else {
-		partition = "system"
-	}
-	if ctx.InstallInSanitizerDir() {
-		partition = "data/asan/" + partition
 	}
 	return partition
 }
 
+type InstallPaths []InstallPath
+
+// Paths returns the InstallPaths as a Paths
+func (p InstallPaths) Paths() Paths {
+	if p == nil {
+		return nil
+	}
+	ret := make(Paths, len(p))
+	for i, path := range p {
+		ret[i] = path
+	}
+	return ret
+}
+
+// Strings returns the string forms of the install paths.
+func (p InstallPaths) Strings() []string {
+	if p == nil {
+		return nil
+	}
+	ret := make([]string, len(p))
+	for i, path := range p {
+		ret[i] = path.String()
+	}
+	return ret
+}
+
 // validateSafePath validates a path that we trust (may contain ninja variables).
 // Ensures that each path component does not attempt to leave its component.
 func validateSafePath(pathComponents ...string) (string, error) {
diff --git a/android/paths_test.go b/android/paths_test.go
index 7a32026..9b45d3f 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -18,6 +18,7 @@
 	"errors"
 	"fmt"
 	"reflect"
+	"strconv"
 	"strings"
 	"testing"
 
@@ -205,6 +206,7 @@
 	inRamdisk      bool
 	inRecovery     bool
 	inRoot         bool
+	forceOS        *OsType
 }
 
 func (m moduleInstallPathContextImpl) Config() Config {
@@ -241,6 +243,10 @@
 	return false
 }
 
+func (m moduleInstallPathContextImpl) InstallForceOS() *OsType {
+	return m.forceOS
+}
+
 func pathTestConfig(buildDir string) Config {
 	return TestConfig(buildDir, nil, "", nil)
 }
@@ -598,6 +604,40 @@
 			},
 			in:  []string{"nativetest", "my_test"},
 			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+		}, {
+			name: "device testcases",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inTestcases: true,
+			},
+			in:  []string{"my_test", "my_test_bin"},
+			out: "target/product/test_device/testcases/my_test/my_test_bin",
+		}, {
+			name: "host testcases",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					os:     hostTarget.Os,
+					target: hostTarget,
+				},
+				inTestcases: true,
+			},
+			in:  []string{"my_test", "my_test_bin"},
+			out: "host/linux-x86/testcases/my_test/my_test_bin",
+		}, {
+			name: "forced host testcases",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inTestcases: true,
+				forceOS:     &Linux,
+			},
+			in:  []string{"my_test", "my_test_bin"},
+			out: "host/linux-x86/testcases/my_test/my_test_bin",
 		},
 	}
 
@@ -1216,3 +1256,51 @@
 	// out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex
 	// boot.art oat/arm/boot.vdex
 }
+
+func BenchmarkFirstUniquePaths(b *testing.B) {
+	implementations := []struct {
+		name string
+		f    func(Paths) Paths
+	}{
+		{
+			name: "list",
+			f:    firstUniquePathsList,
+		},
+		{
+			name: "map",
+			f:    firstUniquePathsMap,
+		},
+	}
+	const maxSize = 1024
+	uniquePaths := make(Paths, maxSize)
+	for i := range uniquePaths {
+		uniquePaths[i] = PathForTesting(strconv.Itoa(i))
+	}
+	samePath := make(Paths, maxSize)
+	for i := range samePath {
+		samePath[i] = uniquePaths[0]
+	}
+
+	f := func(b *testing.B, imp func(Paths) Paths, paths Paths) {
+		for i := 0; i < b.N; i++ {
+			b.ReportAllocs()
+			paths = append(Paths(nil), paths...)
+			imp(paths)
+		}
+	}
+
+	for n := 1; n <= maxSize; n <<= 1 {
+		b.Run(strconv.Itoa(n), func(b *testing.B) {
+			for _, implementation := range implementations {
+				b.Run(implementation.name, func(b *testing.B) {
+					b.Run("same", func(b *testing.B) {
+						f(b, implementation.f, samePath[:n])
+					})
+					b.Run("unique", func(b *testing.B) {
+						f(b, implementation.f, uniquePaths[:n])
+					})
+				})
+			}
+		})
+	}
+}
diff --git a/android/util.go b/android/util.go
index ade851e..8dbf214 100644
--- a/android/util.go
+++ b/android/util.go
@@ -141,6 +141,16 @@
 	return false
 }
 
+// Returns true if any string in the given list has the given suffix.
+func SuffixInList(list []string, suffix string) bool {
+	for _, s := range list {
+		if strings.HasSuffix(s, suffix) {
+			return true
+		}
+	}
+	return false
+}
+
 // IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element.
 func IndexListPred(pred func(s string) bool, list []string) int {
 	for i, l := range list {
@@ -193,6 +203,14 @@
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
 // each.  It modifies the slice contents in place, and returns a subslice of the original slice.
 func FirstUniqueStrings(list []string) []string {
+	// 128 was chosen based on BenchmarkFirstUniqueStrings results.
+	if len(list) > 128 {
+		return firstUniqueStringsMap(list)
+	}
+	return firstUniqueStringsList(list)
+}
+
+func firstUniqueStringsList(list []string) []string {
 	k := 0
 outer:
 	for i := 0; i < len(list); i++ {
@@ -207,6 +225,20 @@
 	return list[:k]
 }
 
+func firstUniqueStringsMap(list []string) []string {
+	k := 0
+	seen := make(map[string]bool, len(list))
+	for i := 0; i < len(list); i++ {
+		if seen[list[i]] {
+			continue
+		}
+		seen[list[i]] = true
+		list[k] = list[i]
+		k++
+	}
+	return list[:k]
+}
+
 // LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of
 // each.  It modifies the slice contents in place, and returns a subslice of the original slice.
 func LastUniqueStrings(list []string) []string {
diff --git a/android/util_test.go b/android/util_test.go
index 1f9ca36..25b52ca 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"strconv"
 	"testing"
 )
 
@@ -59,15 +60,25 @@
 }
 
 func TestFirstUniqueStrings(t *testing.T) {
-	for _, testCase := range firstUniqueStringsTestCases {
-		out := FirstUniqueStrings(testCase.in)
-		if !reflect.DeepEqual(out, testCase.out) {
+	f := func(t *testing.T, imp func([]string) []string, in, want []string) {
+		t.Helper()
+		out := imp(in)
+		if !reflect.DeepEqual(out, want) {
 			t.Errorf("incorrect output:")
-			t.Errorf("     input: %#v", testCase.in)
-			t.Errorf("  expected: %#v", testCase.out)
+			t.Errorf("     input: %#v", in)
+			t.Errorf("  expected: %#v", want)
 			t.Errorf("       got: %#v", out)
 		}
 	}
+
+	for _, testCase := range firstUniqueStringsTestCases {
+		t.Run("list", func(t *testing.T) {
+			f(t, firstUniqueStringsList, testCase.in, testCase.out)
+		})
+		t.Run("map", func(t *testing.T) {
+			f(t, firstUniqueStringsMap, testCase.in, testCase.out)
+		})
+	}
 }
 
 var lastUniqueStringsTestCases = []struct {
@@ -568,3 +579,51 @@
 		})
 	}
 }
+
+func BenchmarkFirstUniqueStrings(b *testing.B) {
+	implementations := []struct {
+		name string
+		f    func([]string) []string
+	}{
+		{
+			name: "list",
+			f:    firstUniqueStringsList,
+		},
+		{
+			name: "map",
+			f:    firstUniqueStringsMap,
+		},
+	}
+	const maxSize = 1024
+	uniqueStrings := make([]string, maxSize)
+	for i := range uniqueStrings {
+		uniqueStrings[i] = strconv.Itoa(i)
+	}
+	sameString := make([]string, maxSize)
+	for i := range sameString {
+		sameString[i] = uniqueStrings[0]
+	}
+
+	f := func(b *testing.B, imp func([]string) []string, s []string) {
+		for i := 0; i < b.N; i++ {
+			b.ReportAllocs()
+			s = append([]string(nil), s...)
+			imp(s)
+		}
+	}
+
+	for n := 1; n <= maxSize; n <<= 1 {
+		b.Run(strconv.Itoa(n), func(b *testing.B) {
+			for _, implementation := range implementations {
+				b.Run(implementation.name, func(b *testing.B) {
+					b.Run("same", func(b *testing.B) {
+						f(b, implementation.f, sameString[:n])
+					})
+					b.Run("unique", func(b *testing.B) {
+						f(b, implementation.f, uniqueStrings[:n])
+					})
+				})
+			}
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 118e107..3c08405 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -49,6 +49,14 @@
 			Exclude_static_libs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 
+		Malloc_zero_contents struct {
+			Cflags []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+
+		Malloc_pattern_fill_contents struct {
+			Cflags []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+
 		Safestack struct {
 			Cflags []string `android:"arch_variant"`
 		} `android:"arch_variant"`
@@ -95,6 +103,9 @@
 			Sanitize struct {
 				Address *bool
 			}
+			Optimize struct {
+				Enabled *bool
+			}
 		}
 
 		Pdk struct {
@@ -207,6 +218,8 @@
 	Unbundled_build                  *bool `json:",omitempty"`
 	Unbundled_build_sdks_from_source *bool `json:",omitempty"`
 	Malloc_not_svelte                *bool `json:",omitempty"`
+	Malloc_zero_contents             *bool `json:",omitempty"`
+	Malloc_pattern_fill_contents     *bool `json:",omitempty"`
 	Safestack                        *bool `json:",omitempty"`
 	HostStaticBinaries               *bool `json:",omitempty"`
 	Binder32bit                      *bool `json:",omitempty"`
@@ -373,8 +386,10 @@
 		AAPTCharacteristics: stringPtr("nosdcard"),
 		AAPTPrebuiltDPI:     []string{"xhdpi", "xxhdpi"},
 
-		Malloc_not_svelte: boolPtr(true),
-		Safestack:         boolPtr(false),
+		Malloc_not_svelte:            boolPtr(true),
+		Malloc_zero_contents:         boolPtr(false),
+		Malloc_pattern_fill_contents: boolPtr(false),
+		Safestack:                    boolPtr(false),
 	}
 
 	if runtime.GOOS == "linux" {
diff --git a/android/visibility.go b/android/visibility.go
index 2cb0023..68da1c4 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -140,7 +140,7 @@
 }
 
 func (r packageRule) String() string {
-	return fmt.Sprintf("//%s:__pkg__", r.pkg)
+	return fmt.Sprintf("//%s", r.pkg) // :__pkg__ is the default, so skip it.
 }
 
 // A subpackagesRule is a visibility rule that matches modules in a specific package (i.e.
@@ -496,6 +496,24 @@
 
 	rule := effectiveVisibilityRules(ctx.Config(), qualified)
 
+	// Modules are implicitly visible to other modules in the same package,
+	// without checking the visibility rules. Here we need to add that visibility
+	// explicitly.
+	if rule != nil && !rule.matches(qualified) {
+		if len(rule) == 1 {
+			if _, ok := rule[0].(privateRule); ok {
+				// If the rule is //visibility:private we can't append another
+				// visibility to it. Semantically we need to convert it to a package
+				// visibility rule for the location where the result is used, but since
+				// modules are implicitly visible within the package we get the same
+				// result without any rule at all, so just make it an empty list to be
+				// appended below.
+				rule = compositeRule{}
+			}
+		}
+		rule = append(rule, packageRule{dir})
+	}
+
 	return rule.Strings()
 }
 
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 8860984..5a62324 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -111,6 +111,7 @@
 
 			"LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING": "dex_preopt.profile",
 			"LOCAL_TEST_CONFIG":                      "test_config",
+			"LOCAL_RRO_THEME":                        "theme",
 		})
 	addStandardProperties(bpparser.ListType,
 		map[string]string{
@@ -383,11 +384,15 @@
 	if err != nil {
 		return err
 	}
-	if val.(*bpparser.Bool).Value {
+	boolValue, ok := val.(*bpparser.Bool)
+	if !ok {
+		return fmt.Errorf("value should evaluate to boolean literal")
+	}
+	if boolValue.Value {
 		thirtyTwo := &bpparser.String{
 			Value: "32",
 		}
-		setVariable(ctx.file, false, ctx.prefix, "compile_multilib", thirtyTwo, true)
+		return setVariable(ctx.file, false, ctx.prefix, "compile_multilib", thirtyTwo, true)
 	}
 	return nil
 }
@@ -825,8 +830,6 @@
 var propertyPrefixes = []struct{ mk, bp string }{
 	{"arm", "arch.arm"},
 	{"arm64", "arch.arm64"},
-	{"mips", "arch.mips"},
-	{"mips64", "arch.mips64"},
 	{"x86", "arch.x86"},
 	{"x86_64", "arch.x86_64"},
 	{"32", "multilib.lib32"},
@@ -923,6 +926,7 @@
 	"BUILD_HOST_JAVA_LIBRARY":        "java_library_host",
 	"BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
 	"BUILD_PACKAGE":                  "android_app",
+	"BUILD_RRO_PACKAGE":              "runtime_resource_overlay",
 
 	"BUILD_CTS_EXECUTABLE":          "cc_binary",               // will be further massaged by bpfix depending on the output path
 	"BUILD_CTS_SUPPORT_PACKAGE":     "cts_support_package",     // will be rewritten to android_test by bpfix
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 7e1a72c..d9bde94 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1341,6 +1341,48 @@
 }
 `,
 	},
+	{
+		desc: "undefined_boolean_var",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= a.cpp
+LOCAL_MODULE:= test
+LOCAL_32_BIT_ONLY := $(FLAG)
+include $(BUILD_EXECUTABLE)
+`,
+		expected: `
+cc_binary {
+    name: "test",
+    srcs: ["a.cpp"],
+    // ANDROIDMK TRANSLATION ERROR: value should evaluate to boolean literal
+    // LOCAL_32_BIT_ONLY := $(FLAG)
+
+}
+`,
+	},
+	{
+		desc: "runtime_resource_overlay",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := foo
+LOCAL_PRODUCT_MODULE := true
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SDK_VERSION := current
+LOCAL_RRO_THEME := FooTheme
+
+include $(BUILD_RRO_PACKAGE)
+`,
+		expected: `
+runtime_resource_overlay {
+	name: "foo",
+	product_specific: true,
+	resource_dirs: ["res"],
+	sdk_version: "current",
+	theme: "FooTheme",
+
+}
+`,
+	},
 }
 
 func TestEndToEnd(t *testing.T) {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 2303efe..09d3607 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -120,9 +120,12 @@
 			if len(fi.symlinks) > 0 {
 				fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
 			}
+			if len(fi.dataPaths) > 0 {
+				fmt.Println(w, "LOCAL_TEST_DATA :=", strings.Join(cc.AndroidMkDataPaths(fi.dataPaths), " "))
+			}
 
-			if fi.module != nil && fi.module.NoticeFile().Valid() {
-				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", fi.module.NoticeFile().Path().String())
+			if fi.module != nil && len(fi.module.NoticeFiles()) > 0 {
+				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
 			}
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
@@ -166,7 +169,7 @@
 			fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
 		}
 		if fi.class == javaSharedLib {
-			javaModule := fi.module.(javaLibrary)
+			javaModule := fi.module.(java.Dependency)
 			// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.jar.jar
@@ -308,7 +311,7 @@
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 
 				if apexType == imageApex {
-					fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
+					fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String())
 				}
 
 				if a.installedFilesFile != nil {
diff --git a/apex/apex.go b/apex/apex.go
index 57fc650..0efbdce 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -18,7 +18,6 @@
 	"fmt"
 	"path"
 	"path/filepath"
-	"regexp"
 	"sort"
 	"strings"
 	"sync"
@@ -53,6 +52,7 @@
 
 var (
 	sharedLibTag   = dependencyTag{name: "sharedLib", payload: true}
+	jniLibTag      = dependencyTag{name: "jniLib", payload: true}
 	executableTag  = dependencyTag{name: "executable", payload: true}
 	javaLibTag     = dependencyTag{name: "javaLib", payload: true}
 	prebuiltTag    = dependencyTag{name: "prebuilt", payload: true}
@@ -61,6 +61,7 @@
 	certificateTag = dependencyTag{name: "certificate"}
 	usesTag        = dependencyTag{name: "uses"}
 	androidAppTag  = dependencyTag{name: "androidApp", payload: true}
+	rroTag         = dependencyTag{name: "rro", payload: true}
 	apexAvailWl    = makeApexAvailableWhitelist()
 
 	inverseApexAvailWl = invertApexWhiteList(apexAvailWl)
@@ -243,19 +244,6 @@
 	//
 	// Module separator
 	//
-	m["com.android.extservices"] = []string{
-		"flatbuffer_headers",
-		"liblua",
-		"libtextclassifier",
-		"libtextclassifier_hash_static",
-		"libtflite_static",
-		"libutf",
-		"libz_current",
-		"tensorflow_headers",
-	}
-	//
-	// Module separator
-	//
 	m["com.android.neuralnetworks"] = []string{
 		"android.hardware.neuralnetworks@1.0",
 		"android.hardware.neuralnetworks@1.1",
@@ -726,6 +714,7 @@
 	android.RegisterModuleType("apex_defaults", defaultsFactory)
 	android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
 	android.RegisterModuleType("override_apex", overrideApexFactory)
+	android.RegisterModuleType("apex_set", apexSetFactory)
 
 	android.PreDepsMutators(RegisterPreDepsMutators)
 	android.PostDepsMutators(RegisterPostDepsMutators)
@@ -762,6 +751,7 @@
 		apexBundles = []android.ApexInfo{{
 			ApexName:      mctx.ModuleName(),
 			MinSdkVersion: a.minSdkVersion(mctx),
+			Updatable:     proptools.Bool(a.properties.Updatable),
 		}}
 		directDep = true
 	} else if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -962,10 +952,13 @@
 	})
 }
 
-type apexNativeDependencies struct {
+type ApexNativeDependencies struct {
 	// List of native libraries
 	Native_shared_libs []string
 
+	// List of JNI libraries
+	Jni_libs []string
+
 	// List of native executables
 	Binaries []string
 
@@ -975,19 +968,19 @@
 
 type apexMultilibProperties struct {
 	// Native dependencies whose compile_multilib is "first"
-	First apexNativeDependencies
+	First ApexNativeDependencies
 
 	// Native dependencies whose compile_multilib is "both"
-	Both apexNativeDependencies
+	Both ApexNativeDependencies
 
 	// Native dependencies whose compile_multilib is "prefer32"
-	Prefer32 apexNativeDependencies
+	Prefer32 ApexNativeDependencies
 
 	// Native dependencies whose compile_multilib is "32"
-	Lib32 apexNativeDependencies
+	Lib32 ApexNativeDependencies
 
 	// Native dependencies whose compile_multilib is "64"
-	Lib64 apexNativeDependencies
+	Lib64 ApexNativeDependencies
 }
 
 type apexBundleProperties struct {
@@ -1009,11 +1002,7 @@
 	// Default: /system/sepolicy/apex/<module_name>_file_contexts.
 	File_contexts *string `android:"path"`
 
-	// List of native shared libs that are embedded inside this APEX bundle
-	Native_shared_libs []string
-
-	// List of executables that are embedded inside this APEX bundle
-	Binaries []string
+	ApexNativeDependencies
 
 	// List of java libraries that are embedded inside this APEX bundle
 	Java_libs []string
@@ -1021,9 +1010,6 @@
 	// List of prebuilt files that are embedded inside this APEX bundle
 	Prebuilts []string
 
-	// List of tests that are embedded inside this APEX bundle
-	Tests []string
-
 	// Name of the apex_key module that provides the private key to sign APEX
 	Key *string
 
@@ -1090,7 +1076,7 @@
 	// Default is false.
 	Updatable *bool
 
-	// The minimum SDK version that this apex must be compatible with.
+	// The minimum SDK version that this apex must be compatibile with.
 	Min_sdk_version *string
 }
 
@@ -1122,6 +1108,9 @@
 	// List of APKs to package inside APEX
 	Apps []string
 
+	// List of runtime resource overlays (RROs) inside APEX
+	Rros []string
+
 	// Names of modules to be overridden. Listed modules can only be other binaries
 	// (in Make or Soong).
 	// This does not completely prevent installation of the overridden binaries, but if both
@@ -1217,6 +1206,7 @@
 	module     android.Module
 	// list of symlinks that will be created in installDir that point to this apexFile
 	symlinks      []string
+	dataPaths     android.Paths
 	transitiveDep bool
 	moduleDir     string
 
@@ -1227,6 +1217,8 @@
 	jacocoReportClassesFile android.Path     // only for javalibs and apps
 	certificate             java.Certificate // only for apps
 	overriddenPackageName   string           // only for apps
+
+	isJniLib bool
 }
 
 func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
@@ -1250,16 +1242,20 @@
 	return af.builtFile != nil && af.builtFile.String() != ""
 }
 
+func (af *apexFile) apexRelativePath(path string) string {
+	return filepath.Join(af.installDir, path)
+}
+
 // Path() returns path of this apex file relative to the APEX root
 func (af *apexFile) Path() string {
-	return filepath.Join(af.installDir, af.builtFile.Base())
+	return af.apexRelativePath(af.builtFile.Base())
 }
 
 // SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
 func (af *apexFile) SymlinkPaths() []string {
 	var ret []string
 	for _, symlink := range af.symlinks {
-		ret = append(ret, filepath.Join(af.installDir, symlink))
+		ret = append(ret, af.apexRelativePath(symlink))
 	}
 	return ret
 }
@@ -1339,7 +1335,7 @@
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
-	native_shared_libs []string, binaries []string, tests []string,
+	nativeModules ApexNativeDependencies,
 	target android.Target, imageVariation string) {
 	// Use *FarVariation* to be able to depend on modules having
 	// conflicting variations with this module. This is required since
@@ -1349,16 +1345,22 @@
 		{Mutator: "image", Variation: imageVariation},
 		{Mutator: "link", Variation: "shared"},
 		{Mutator: "version", Variation: ""}, // "" is the non-stub variant
-	}...), sharedLibTag, native_shared_libs...)
+	}...), sharedLibTag, nativeModules.Native_shared_libs...)
+
+	ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
+		{Mutator: "image", Variation: imageVariation},
+		{Mutator: "link", Variation: "shared"},
+		{Mutator: "version", Variation: ""}, // "" is the non-stub variant
+	}...), jniLibTag, nativeModules.Jni_libs...)
 
 	ctx.AddFarVariationDependencies(append(target.Variations(),
 		blueprint.Variation{Mutator: "image", Variation: imageVariation}),
-		executableTag, binaries...)
+		executableTag, nativeModules.Binaries...)
 
 	ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
 		{Mutator: "image", Variation: imageVariation},
 		{Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
-	}...), testTag, tests...)
+	}...), testTag, nativeModules.Tests...)
 }
 
 func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -1391,41 +1393,39 @@
 		}
 	}
 	for i, target := range targets {
-		// When multilib.* is omitted for native_shared_libs, it implies
-		// multilib.both.
-		ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
-			{Mutator: "image", Variation: a.getImageVariation(config)},
-			{Mutator: "link", Variation: "shared"},
-		}...), sharedLibTag, a.properties.Native_shared_libs...)
-
-		// When multilib.* is omitted for tests, it implies
-		// multilib.both.
-		ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
-			{Mutator: "image", Variation: a.getImageVariation(config)},
-			{Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
-		}...), testTag, a.properties.Tests...)
+		// When multilib.* is omitted for native_shared_libs/jni_libs/tests, it implies
+		// multilib.both
+		addDependenciesForNativeModules(ctx,
+			ApexNativeDependencies{
+				Native_shared_libs: a.properties.Native_shared_libs,
+				Tests:              a.properties.Tests,
+				Jni_libs:           a.properties.Jni_libs,
+				Binaries:           nil,
+			},
+			target, a.getImageVariation(config))
 
 		// Add native modules targetting both ABIs
 		addDependenciesForNativeModules(ctx,
-			a.properties.Multilib.Both.Native_shared_libs,
-			a.properties.Multilib.Both.Binaries,
-			a.properties.Multilib.Both.Tests,
+			a.properties.Multilib.Both,
 			target,
 			a.getImageVariation(config))
 
 		isPrimaryAbi := i == 0
 		if isPrimaryAbi {
 			// When multilib.* is omitted for binaries, it implies
-			// multilib.first.
-			ctx.AddFarVariationDependencies(append(target.Variations(),
-				blueprint.Variation{Mutator: "image", Variation: a.getImageVariation(config)}),
-				executableTag, a.properties.Binaries...)
+			// multilib.first
+			addDependenciesForNativeModules(ctx,
+				ApexNativeDependencies{
+					Native_shared_libs: nil,
+					Tests:              nil,
+					Jni_libs:           nil,
+					Binaries:           a.properties.Binaries,
+				},
+				target, a.getImageVariation(config))
 
 			// Add native modules targetting the first ABI
 			addDependenciesForNativeModules(ctx,
-				a.properties.Multilib.First.Native_shared_libs,
-				a.properties.Multilib.First.Binaries,
-				a.properties.Multilib.First.Tests,
+				a.properties.Multilib.First,
 				target,
 				a.getImageVariation(config))
 		}
@@ -1434,32 +1434,24 @@
 		case "lib32":
 			// Add native modules targetting 32-bit ABI
 			addDependenciesForNativeModules(ctx,
-				a.properties.Multilib.Lib32.Native_shared_libs,
-				a.properties.Multilib.Lib32.Binaries,
-				a.properties.Multilib.Lib32.Tests,
+				a.properties.Multilib.Lib32,
 				target,
 				a.getImageVariation(config))
 
 			addDependenciesForNativeModules(ctx,
-				a.properties.Multilib.Prefer32.Native_shared_libs,
-				a.properties.Multilib.Prefer32.Binaries,
-				a.properties.Multilib.Prefer32.Tests,
+				a.properties.Multilib.Prefer32,
 				target,
 				a.getImageVariation(config))
 		case "lib64":
 			// Add native modules targetting 64-bit ABI
 			addDependenciesForNativeModules(ctx,
-				a.properties.Multilib.Lib64.Native_shared_libs,
-				a.properties.Multilib.Lib64.Binaries,
-				a.properties.Multilib.Lib64.Tests,
+				a.properties.Multilib.Lib64,
 				target,
 				a.getImageVariation(config))
 
 			if !has32BitTarget {
 				addDependenciesForNativeModules(ctx,
-					a.properties.Multilib.Prefer32.Native_shared_libs,
-					a.properties.Multilib.Prefer32.Binaries,
-					a.properties.Multilib.Prefer32.Tests,
+					a.properties.Multilib.Prefer32,
 					target,
 					a.getImageVariation(config))
 			}
@@ -1468,8 +1460,8 @@
 				for _, sanitizer := range ctx.Config().SanitizeDevice() {
 					if sanitizer == "hwaddress" {
 						addDependenciesForNativeModules(ctx,
-							[]string{"libclang_rt.hwasan-aarch64-android"},
-							nil, nil, target, a.getImageVariation(config))
+							ApexNativeDependencies{[]string{"libclang_rt.hwasan-aarch64-android"}, nil, nil, nil},
+							target, a.getImageVariation(config))
 						break
 					}
 				}
@@ -1528,6 +1520,8 @@
 func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
 		androidAppTag, a.overridableProperties.Apps...)
+	ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
+		rroTag, a.overridableProperties.Rros...)
 }
 
 func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
@@ -1605,6 +1599,8 @@
 	return android.InList(sanitizerName, globalSanitizerNames)
 }
 
+var _ cc.Coverage = (*apexBundle)(nil)
+
 func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
 	return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
 }
@@ -1621,6 +1617,8 @@
 	a.properties.IsCoverageVariant = coverage
 }
 
+func (a *apexBundle) EnableCoverageIfNeeded() {}
+
 // TODO(jiyong) move apexFileFor* close to the apexFile type definition
 func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
 	// Decide the APEX-local directory by the multilib of the library
@@ -1632,10 +1630,10 @@
 	case "lib64":
 		dirInApex = "lib64"
 	}
-	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
 	if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
 	}
+	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
 	if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
 		// Special case for Bionic libs and other libs installed with them. This is
 		// to prevent those libs from being included in the search path
@@ -1655,13 +1653,15 @@
 }
 
 func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
-	dirInApex := filepath.Join("bin", cc.RelativeInstallPath())
+	dirInApex := "bin"
 	if cc.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
 	}
+	dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
 	fileToCopy := cc.OutputFile().Path()
 	af := newApexFile(ctx, fileToCopy, cc.Name(), dirInApex, nativeExecutable, cc)
 	af.symlinks = cc.Symlinks()
+	af.dataPaths = cc.DataPaths()
 	return af
 }
 
@@ -1692,16 +1692,10 @@
 	return af
 }
 
-// TODO(b/146586360): replace javaLibrary(in apex/apex.go) with java.Dependency
-type javaLibrary interface {
-	android.Module
-	java.Dependency
-}
-
-func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaLibrary) apexFile {
+func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib java.Dependency, module android.Module) apexFile {
 	dirInApex := "javalib"
 	fileToCopy := lib.DexJar()
-	af := newApexFile(ctx, fileToCopy, lib.Name(), dirInApex, javaSharedLib, lib)
+	af := newApexFile(ctx, fileToCopy, module.Name(), dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = lib.JacocoReportClassesFile()
 	return af
 }
@@ -1744,6 +1738,21 @@
 	return af
 }
 
+func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
+	rroDir := "overlay"
+	dirInApex := filepath.Join(rroDir, rro.Theme())
+	fileToCopy := rro.OutputFile()
+	af := newApexFile(ctx, fileToCopy, rro.Name(), dirInApex, app, rro)
+	af.certificate = rro.Certificate()
+
+	if a, ok := rro.(interface {
+		OverriddenManifestPackageName() string
+	}); ok {
+		af.overriddenPackageName = a.OverriddenManifestPackageName()
+	}
+	return af
+}
+
 // Context "decorator", overriding the InstallBypassMake method to always reply `true`.
 type flattenedApexContext struct {
 	android.ModuleContext
@@ -1793,24 +1802,6 @@
 	return intVer
 }
 
-// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
-// a dependency tag.
-var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`)
-
-func PrettyPrintTag(tag blueprint.DependencyTag) string {
-	// Use tag's custom String() method if available.
-	if stringer, ok := tag.(fmt.Stringer); ok {
-		return stringer.String()
-	}
-
-	// Otherwise, get a default string representation of the tag's struct.
-	tagString := fmt.Sprintf("%#v", tag)
-
-	// Remove the boilerplate from BaseDependencyTag as it adds no value.
-	tagString = tagCleaner.ReplaceAllString(tagString, "")
-	return tagString
-}
-
 // Ensures that the dependencies are marked as available for this APEX
 func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
 	// Let's be practical. Availability for test, host, and the VNDK apex isn't important
@@ -1845,14 +1836,7 @@
 		if to.AvailableFor(apexName) || whitelistedApexAvailable(apexName, toName) {
 			return true
 		}
-		message := ""
-		tagPath := ctx.GetTagPath()
-		// Skip the first module as that will be added at the start of the error message by ctx.ModuleErrorf().
-		walkPath := ctx.GetWalkPath()[1:]
-		for i, m := range walkPath {
-			message = fmt.Sprintf("%s\n           via tag %s\n    -> %s", message, PrettyPrintTag(tagPath[i]), m.String())
-		}
-		ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, message)
+		ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, ctx.GetPathString(true))
 		// Visit this module's dependencies to check and report any issues with their availability.
 		return true
 	})
@@ -1946,16 +1930,23 @@
 		depName := ctx.OtherModuleName(child)
 		if _, isDirectDep := parent.(*apexBundle); isDirectDep {
 			switch depTag {
-			case sharedLibTag:
+			case sharedLibTag, jniLibTag:
+				isJniLib := depTag == jniLibTag
 				if c, ok := child.(*cc.Module); ok {
 					// bootstrap bionic libs are treated as provided by system
 					if c.HasStubsVariants() && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
 						provideNativeLibs = append(provideNativeLibs, c.OutputFile().Path().Base())
 					}
-					filesInfo = append(filesInfo, apexFileForNativeLibrary(ctx, c, handleSpecialLibs))
+					fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
+					fi.isJniLib = isJniLib
+					filesInfo = append(filesInfo, fi)
 					return true // track transitive dependencies
 				} else {
-					ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName)
+					propertyName := "native_shared_libs"
+					if isJniLib {
+						propertyName = "jni_libs"
+					}
+					ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
 				}
 			case executableTag:
 				if cc, ok := child.(*cc.Module); ok {
@@ -1972,7 +1963,7 @@
 				}
 			case javaLibTag:
 				if javaLib, ok := child.(*java.Library); ok {
-					af := apexFileForJavaLibrary(ctx, javaLib)
+					af := apexFileForJavaLibrary(ctx, javaLib, javaLib)
 					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 					} else {
@@ -1980,7 +1971,7 @@
 						return true // track transitive dependencies
 					}
 				} else if sdkLib, ok := child.(*java.SdkLibrary); ok {
-					af := apexFileForJavaLibrary(ctx, sdkLib)
+					af := apexFileForJavaLibrary(ctx, sdkLib, sdkLib)
 					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 						return false
@@ -2001,6 +1992,12 @@
 				} else {
 					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
 				}
+			case rroTag:
+				if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
+					filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+				} else {
+					ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
+				}
 			case prebuiltTag:
 				if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
 					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
@@ -2018,13 +2015,13 @@
 						// We do not add this variation to `filesInfo`, as it has no output;
 						// however, we do add the other variations of this module as indirect
 						// dependencies (see below).
-						return true
 					} else {
 						// Single-output test module (where `test_per_src: false`).
 						af := apexFileForExecutable(ctx, ccTest)
 						af.class = nativeTest
 						filesInfo = append(filesInfo, af)
 					}
+					return true // track transitive dependencies
 				} else {
 					ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
 				}
@@ -2070,8 +2067,8 @@
 							//
 							// Always include if we are a host-apex however since those won't have any
 							// system libraries.
-							if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.requiredDeps) {
-								a.requiredDeps = append(a.requiredDeps, cc.Name())
+							if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.BaseModuleName(), a.requiredDeps) {
+								a.requiredDeps = append(a.requiredDeps, cc.BaseModuleName())
 							}
 							requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base())
 							// Don't track further
@@ -2103,7 +2100,7 @@
 						filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
 					}
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
-					ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", PrettyPrintTag(depTag), depName)
+					ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
 				}
 			}
 		}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a99d2ea..5461deb 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -88,6 +88,24 @@
 	}
 }
 
+// withNativeBridgeTargets sets configuration with targets including:
+// - X86_64 (primary)
+// - X86 (secondary)
+// - Arm64 on X86_64 (native bridge)
+// - Arm on X86 (native bridge)
+func withNativeBridgeEnabled(fs map[string][]byte, config android.Config) {
+	config.Targets[android.Android] = []android.Target{
+		{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}},
+			NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+		{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}},
+			NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+		{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}},
+			NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64"},
+		{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
+			NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm"},
+	}
+}
+
 func withManifestPackageNameOverrides(specs []string) testCustomizer {
 	return func(fs map[string][]byte, config android.Config) {
 		config.TestProductVariables.ManifestPackageNameOverrides = specs
@@ -157,11 +175,14 @@
 		"testkey2.pem":                               nil,
 		"myapex-arm64.apex":                          nil,
 		"myapex-arm.apex":                            nil,
+		"myapex.apks":                                nil,
 		"frameworks/base/api/current.txt":            nil,
 		"framework/aidl/a.aidl":                      nil,
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
 		"dummy.txt":                                  nil,
+		"baz":                                        nil,
+		"bar/baz":                                    nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -198,6 +219,7 @@
 	ctx.RegisterModuleType("apex_defaults", defaultsFactory)
 	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
 	ctx.RegisterModuleType("override_apex", overrideApexFactory)
+	ctx.RegisterModuleType("apex_set", apexSetFactory)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
@@ -235,6 +257,14 @@
 	os.RemoveAll(buildDir)
 }
 
+// ensure that 'result' equals 'expected'
+func ensureEquals(t *testing.T, result string, expected string) {
+	t.Helper()
+	if result != expected {
+		t.Errorf("%q != %q", expected, result)
+	}
+}
+
 // ensure that 'result' contains 'expected'
 func ensureContains(t *testing.T, result string, expected string) {
 	t.Helper()
@@ -502,6 +532,7 @@
 			native_shared_libs: ["mylib"],
 			java_libs: ["myjar"],
 			apps: ["AppFoo"],
+			rros: ["rro"],
 		}
 
 		prebuilt_etc {
@@ -542,12 +573,19 @@
 			system_modules: "none",
 			apex_available: [ "myapex" ],
 		}
+
+		runtime_resource_overlay {
+			name: "rro",
+			theme: "blue",
+		}
+
 	`)
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"etc/myetc",
 		"javalib/myjar.jar",
 		"lib64/mylib.so",
 		"app/AppFoo/AppFoo.apk",
+		"overlay/blue/rro.apk",
 	})
 }
 
@@ -1566,6 +1604,64 @@
 	ensureListContains(t, dirs, "bin/foo/bar")
 }
 
+func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			multilib: {
+				both: {
+					native_shared_libs: ["mylib"],
+					binaries: ["mybin"],
+				},
+			},
+			compile_multilib: "both",
+			native_bridge_supported: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			relative_install_path: "foo/bar",
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			native_bridge_supported: true,
+		}
+
+		cc_binary {
+			name: "mybin",
+			relative_install_path: "foo/bar",
+			system_shared_libs: [],
+			static_executable: true,
+			stl: "none",
+			apex_available: [ "myapex" ],
+			native_bridge_supported: true,
+			compile_multilib: "both", // default is "first" for binary
+			multilib: {
+				lib64: {
+					suffix: "64",
+				},
+			},
+		}
+	`, withNativeBridgeEnabled)
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"bin/foo/bar/mybin",
+		"bin/foo/bar/mybin64",
+		"bin/arm/foo/bar/mybin",
+		"bin/arm64/foo/bar/mybin64",
+		"lib/foo/bar/mylib.so",
+		"lib/arm/foo/bar/mylib.so",
+		"lib64/foo/bar/mylib.so",
+		"lib64/arm64/foo/bar/mylib.so",
+	})
+}
+
 func TestUseVendor(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -2439,15 +2535,7 @@
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
-		`+vndkLibrariesTxtFiles("current"),
-		withTargets(map[android.OsType][]android.Target{
-			android.Android: []android.Target{
-				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-				{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm64", NativeBridgeRelativePath: "x86_64"},
-				{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm", NativeBridgeRelativePath: "x86"},
-			},
-		}))
+		`+vndkLibrariesTxtFiles("current"), withNativeBridgeEnabled)
 
 	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
 		"lib/libvndk.so",
@@ -2542,7 +2630,8 @@
 		withBinder32bit,
 		withTargets(map[android.OsType][]android.Target{
 			android.Android: []android.Target{
-				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
+					NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
 			},
 		}),
 	)
@@ -3182,14 +3271,31 @@
 			private_key: "testkey.pem",
 		}
 
+		filegroup {
+			name: "fg",
+			srcs: [
+				"baz",
+				"bar/baz"
+			],
+		}
+
 		cc_test {
 			name: "mytest",
 			gtest: false,
 			srcs: ["mytest.cpp"],
 			relative_install_path: "test",
+			shared_libs: ["mylib"],
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			data: [":fg"],
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
 		}
 
 		cc_test {
@@ -3211,8 +3317,13 @@
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
-	// Ensure that test dep is copied into apex.
+	// Ensure that test dep (and their transitive dependencies) are copied into apex.
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest")
+	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+	//Ensure that test data are copied into apex.
+	ensureContains(t, copyCmds, "image.apex/bin/test/baz")
+	ensureContains(t, copyCmds, "image.apex/bin/test/bar/baz")
 
 	// Ensure that test deps built with `test_per_src` are copied into apex.
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest1")
@@ -3532,6 +3643,7 @@
 			dex_preopt: {
 				enabled: false,
 			},
+			apex_available: ["myapex"],
 		}
 
 		android_app_import {
@@ -3543,6 +3655,7 @@
 				enabled: false,
 			},
 			filename: "AwesomePrebuiltAppFooPriv.apk",
+			apex_available: ["myapex"],
 		}
 	`)
 
@@ -3584,6 +3697,7 @@
 			filename: "AppFooPrebuilt.apk",
 			presigned: true,
 			prefer: true,
+			apex_available: ["myapex"],
 		}
 	`, withFiles(map[string][]byte{
 		"AppFooPrebuilt.apk": nil,
@@ -4279,6 +4393,47 @@
 	ensureRealfileExists(t, files, "lib64/myotherlib.so") // this is a real file
 }
 
+func TestApexWithJniLibs(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			jni_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["mylib2"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "mylib2",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+	`)
+
+	rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	// Notice mylib2.so (transitive dep) is not added as a jni_lib
+	ensureEquals(t, rule.Args["opt"], "-a jniLibs mylib.so")
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"lib64/mylib.so",
+		"lib64/mylib2.so",
+	})
+}
+
 func TestApexMutatorsDontRunIfDisabled(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -4300,6 +4455,29 @@
 	}
 }
 
+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 {
@@ -4340,6 +4518,13 @@
 				"system/sepolicy/apex/some-updatable-apex-file_contexts",
 			],
 		}
+
+		filegroup {
+			name: "some-non-updatable-apex-file_contexts",
+			srcs: [
+				"system/sepolicy/apex/some-non-updatable-apex-file_contexts",
+			],
+		}
 	`
 	bp += cc.GatherRequiredDepsForTest(android.Android)
 	bp += java.GatherRequiredDepsForTest()
@@ -4352,6 +4537,7 @@
 		"apex_manifest.json":                 nil,
 		"AndroidManifest.xml":                nil,
 		"system/sepolicy/apex/some-updatable-apex-file_contexts":       nil,
+		"system/sepolicy/apex/some-non-updatable-apex-file_contexts":   nil,
 		"system/sepolicy/apex/com.android.art.something-file_contexts": nil,
 		"framework/aidl/a.aidl": nil,
 	}
@@ -4423,6 +4609,14 @@
 		}
 
 		java_library {
+			name: "some-non-updatable-apex-lib",
+			srcs: ["a.java"],
+			apex_available: [
+				"some-non-updatable-apex",
+			],
+		}
+
+		java_library {
 			name: "some-platform-lib",
 			srcs: ["a.java"],
 			sdk_version: "current",
@@ -4443,16 +4637,30 @@
 			name: "some-updatable-apex",
 			key: "some-updatable-apex.key",
 			java_libs: ["some-updatable-apex-lib"],
+			updatable: true,
+			min_sdk_version: "current",
+		}
+
+		apex {
+			name: "some-non-updatable-apex",
+			key: "some-non-updatable-apex.key",
+			java_libs: ["some-non-updatable-apex-lib"],
 		}
 
 		apex_key {
 			name: "some-updatable-apex.key",
 		}
 
+		apex_key {
+			name: "some-non-updatable-apex.key",
+		}
+
 		apex {
 			name: "com.android.art.something",
 			key: "com.android.art.something.key",
 			java_libs: ["some-art-lib"],
+			updatable: true,
+			min_sdk_version: "current",
 		}
 
 		apex_key {
@@ -4465,55 +4673,68 @@
 
 	// updatable jar from ART apex in the ART boot image => ok
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"some-art-lib"}
+		config.ArtApexJars = []string{"com.android.art.something:some-art-lib"}
 	}
 	testNoUpdatableJarsInBootImage(t, "", bp, transform)
 
 	// updatable jar from ART apex in the framework boot image => error
 	error = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"some-art-lib"}
+		config.BootJars = []string{"com.android.art.something:some-art-lib"}
 	}
 	testNoUpdatableJarsInBootImage(t, error, bp, transform)
 
 	// updatable jar from some other apex in the ART boot image => error
 	error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"some-updatable-apex-lib"}
+		config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+
+	// non-updatable jar from some other apex in the ART boot image => error
+	error = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
 	}
 	testNoUpdatableJarsInBootImage(t, error, bp, transform)
 
 	// updatable jar from some other apex in the framework boot image => error
 	error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"some-updatable-apex-lib"}
+		config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
 	}
 	testNoUpdatableJarsInBootImage(t, error, bp, transform)
 
+	// non-updatable jar from some other apex in the framework boot image => ok
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+
 	// nonexistent jar in the ART boot image => error
 	error = "failed to find a dex jar path for module 'nonexistent'"
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"nonexistent"}
+		config.ArtApexJars = []string{"platform:nonexistent"}
 	}
 	testNoUpdatableJarsInBootImage(t, error, bp, transform)
 
 	// nonexistent jar in the framework boot image => error
 	error = "failed to find a dex jar path for module 'nonexistent'"
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"nonexistent"}
+		config.BootJars = []string{"platform:nonexistent"}
 	}
 	testNoUpdatableJarsInBootImage(t, error, bp, transform)
 
 	// platform jar in the ART boot image => error
-	error = "module 'some-platform-lib' is part of the platform and not allowed in the ART boot image"
+	error = "module 'some-platform-lib' is not allowed in the ART boot image"
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"some-platform-lib"}
+		config.ArtApexJars = []string{"platform:some-platform-lib"}
 	}
 	testNoUpdatableJarsInBootImage(t, error, bp, transform)
 
 	// platform jar in the framework boot image => ok
 	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"some-platform-lib"}
+		config.BootJars = []string{"platform:some-platform-lib"}
 	}
 	testNoUpdatableJarsInBootImage(t, "", bp, transform)
 }
@@ -4570,6 +4791,48 @@
 	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
 }
 
+// TODO(jungjw): Move this to proptools
+func intPtr(i int) *int {
+	return &i
+}
+
+func TestApexSet(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex_set {
+			name: "myapex",
+			set: "myapex.apks",
+			filename: "foo_v2.apex",
+			overrides: ["foo"],
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		config.TestProductVariables.Platform_sdk_version = intPtr(30)
+		config.TestProductVariables.DeviceArch = proptools.StringPtr("arm")
+		config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm64")
+	})
+
+	m := ctx.ModuleForTests("myapex", "android_common")
+
+	// Check extract_apks tool parameters.
+	extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+	actual := extractedApex.Args["abis"]
+	expected := "ARMEABI_V7A,ARM64_V8A"
+	if actual != expected {
+		t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
+	}
+	actual = extractedApex.Args["sdk-version"]
+	expected = "30"
+	if actual != expected {
+		t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
+	}
+
+	a := m.Module().(*ApexSet)
+	expectedOverrides := []string{"foo"}
+	actualOverrides := android.AndroidMkEntriesForTest(t, config, "", a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+	if !reflect.DeepEqual(actualOverrides, expectedOverrides) {
+		t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES - expected %q vs actual %q", expectedOverrides, actualOverrides)
+	}
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/builder.go b/apex/builder.go
index f04771c..bb65f1d 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -61,6 +61,7 @@
 	pctx.HostBinToolVariable("zipalign", "zipalign")
 	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
 	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
+	pctx.HostBinToolVariable("extract_apks", "extract_apks")
 }
 
 var (
@@ -185,6 +186,17 @@
 		optCommands = append(optCommands, "-v name "+*a.properties.Apex_name)
 	}
 
+	// collect jniLibs. Notice that a.filesInfo is already sorted
+	var jniLibs []string
+	for _, fi := range a.filesInfo {
+		if fi.isJniLib {
+			jniLibs = append(jniLibs, fi.builtFile.Base())
+		}
+	}
+	if len(jniLibs) > 0 {
+		optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
+	}
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
 		Input:  manifestSrc,
@@ -225,10 +237,8 @@
 			return false
 		}
 
-		notice := to.NoticeFile()
-		if notice.Valid() {
-			noticeFiles = append(noticeFiles, notice.Path())
-		}
+		notices := to.NoticeFiles()
+		noticeFiles = append(noticeFiles, notices...)
 
 		return true
 	})
@@ -341,6 +351,19 @@
 			symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
 			copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
 		}
+		for _, d := range fi.dataPaths {
+			// TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible
+			relPath := d.Rel()
+			dataPath := d.String()
+			if !strings.HasSuffix(dataPath, relPath) {
+				panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath))
+			}
+
+			dataDest := android.PathForModuleOut(ctx, "image"+suffix, fi.apexRelativePath(relPath)).String()
+
+			copyCommands = append(copyCommands, "cp -f "+d.String()+" "+dataDest)
+			implicitInputs = append(implicitInputs, d)
+		}
 	}
 
 	// TODO(jiyong): use RuleBuilder
@@ -397,6 +420,9 @@
 			pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
 			if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
 				executablePaths = append(executablePaths, pathInApex)
+				for _, d := range f.dataPaths {
+					readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.Rel()))
+				}
 				for _, s := range f.symlinks {
 					executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
 				}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index d089c28..03266c5 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -16,13 +16,29 @@
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
+	"android/soong/java"
+	"github.com/google/blueprint"
 
 	"github.com/google/blueprint/proptools"
 )
 
+var (
+	extractMatchingApex = pctx.StaticRule(
+		"extractMatchingApex",
+		blueprint.RuleParams{
+			Command: `rm -rf "$out" && ` +
+				`${extract_apks} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
+				`-sdk-version=${sdk-version} -abis=${abis} -screen-densities=all -extract-single ` +
+				`${in}`,
+			CommandDeps: []string{"${extract_apks}"},
+		},
+		"abis", "allow-prereleased", "sdk-version")
+)
+
 type Prebuilt struct {
 	android.ModuleBase
 	prebuilt android.Prebuilt
@@ -208,3 +224,117 @@
 		},
 	}}
 }
+
+type ApexSet struct {
+	android.ModuleBase
+	prebuilt android.Prebuilt
+
+	properties ApexSetProperties
+
+	installDir      android.InstallPath
+	installFilename string
+	outputApex      android.WritablePath
+
+	// list of commands to create symlinks for backward compatibility.
+	// these commands will be attached as LOCAL_POST_INSTALL_CMD
+	compatSymlinks []string
+}
+
+type ApexSetProperties struct {
+	// the .apks file path that contains prebuilt apex files to be extracted.
+	Set *string
+
+	// whether the extracted apex file installable.
+	Installable *bool
+
+	// optional name for the installed apex. If unspecified, name of the
+	// module is used as the file name
+	Filename *string
+
+	// names of modules to be overridden. Listed modules can only be other binaries
+	// (in Make or Soong).
+	// This does not completely prevent installation of the overridden binaries, but if both
+	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+	// from PRODUCT_PACKAGES.
+	Overrides []string
+
+	// apexes in this set use prerelease SDK version
+	Prerelease *bool
+}
+
+func (a *ApexSet) installable() bool {
+	return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
+}
+
+func (a *ApexSet) InstallFilename() string {
+	return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
+}
+
+func (a *ApexSet) Prebuilt() *android.Prebuilt {
+	return &a.prebuilt
+}
+
+func (a *ApexSet) Name() string {
+	return a.prebuilt.Name(a.ModuleBase.Name())
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func apexSetFactory() android.Module {
+	module := &ApexSet{}
+	module.AddProperties(&module.properties)
+	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	a.installFilename = a.InstallFilename()
+	if !strings.HasSuffix(a.installFilename, imageApexSuffix) {
+		ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
+	}
+
+	apexSet := a.prebuilt.SingleSourcePath(ctx)
+	a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
+	ctx.Build(pctx,
+		android.BuildParams{
+			Rule:        extractMatchingApex,
+			Description: "Extract an apex from an apex set",
+			Inputs:      android.Paths{apexSet},
+			Output:      a.outputApex,
+			Args: map[string]string{
+				"abis":              strings.Join(java.SupportedAbis(ctx), ","),
+				"allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
+				"sdk-version":       ctx.Config().PlatformSdkVersion(),
+			},
+		})
+	a.installDir = android.PathForModuleInstall(ctx, "apex")
+	if a.installable() {
+		ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+	}
+
+	// in case that apex_set replaces source apex (using prefer: prop)
+	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+	// or that apex_set overrides other apexes (using overrides: prop)
+	for _, overridden := range a.properties.Overrides {
+		a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
+	}
+}
+
+func (a *ApexSet) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(a.outputApex),
+		Include:    "$(BUILD_PREBUILT)",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_STEM", a.installFilename)
+				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable())
+				entries.AddStrings("LOCAL_OVERRIDES_MODULES", a.properties.Overrides...)
+				if len(a.compatSymlinks) > 0 {
+					entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(a.compatSymlinks, " && "))
+				}
+			},
+		},
+	}}
+}
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index 523ac26..05cdfcd 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -117,44 +117,7 @@
 	})
 
 	t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
-		ctx, _ := testApex(t, bp+`
-			cc_library {
-				name: "libprofile-extras",
-				vendor_available: true,
-				recovery_available: true,
-				native_coverage: false,
-				system_shared_libs: [],
-				stl: "none",
-				notice: "custom_notice",
-			}
-			cc_library {
-				name: "libprofile-clang-extras",
-				vendor_available: true,
-				recovery_available: true,
-				native_coverage: false,
-				system_shared_libs: [],
-				stl: "none",
-				notice: "custom_notice",
-			}
-			cc_library {
-				name: "libprofile-extras_ndk",
-				vendor_available: true,
-				native_coverage: false,
-				system_shared_libs: [],
-				stl: "none",
-				notice: "custom_notice",
-				sdk_version: "current",
-			}
-			cc_library {
-				name: "libprofile-clang-extras_ndk",
-				vendor_available: true,
-				native_coverage: false,
-				system_shared_libs: [],
-				stl: "none",
-				notice: "custom_notice",
-				sdk_version: "current",
-			}
-		`, func(fs map[string][]byte, config android.Config) {
+		ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
 			config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
 		})
 
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 59d1502..4cdfb31 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -60,6 +60,10 @@
 func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	cflags := []string{
 		"-nostdlibinc",
+
+		// Make paths in deps files relative
+		"-no-canonical-prefixes",
+
 		"-O2",
 		"-isystem bionic/libc/include",
 		"-isystem bionic/libc/kernel/uapi",
diff --git a/build_kzip.bash b/build_kzip.bash
index 008030f..0018ea9 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -19,16 +19,20 @@
 # The extraction might fail for some source files, so run with -k and then check that
 # sufficiently many files were generated.
 declare -r out="${OUT_DIR:-out}"
+
 # Build extraction files for C++ and Java. Build `merge_zips` which we use later.
 build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java
-#Build extraction file for Go files in build/soong directory.
+
+# Build extraction file for Go the files in build/{blueprint,soong} directories.
 declare -r abspath_out=$(realpath "${out}")
 declare -r go_extractor=$(realpath prebuilts/build-tools/linux-x86/bin/go_extractor)
 declare -r go_root=$(realpath prebuilts/go/linux-x86)
+declare -r vnames_path=$(realpath build/soong/vnames.go.json)
+declare -r source_root=$PWD
 for dir in blueprint soong; do
   (cd "build/$dir";
-   "$go_extractor" --goroot="$go_root" --rules=vnames.go.json --canonicalize_package_corpus \
-    --output "${abspath_out}/soong/build_${dir}.go.kzip" ./...
+   KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" --rules="${vnames_path}" \
+   --canonicalize_package_corpus --output "${abspath_out}/soong/build_${dir}.go.kzip" ./...
   )
 done
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 5438b14..3ce1a89 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -92,6 +92,9 @@
 				if len(c.Properties.AndroidMkWholeStaticLibs) > 0 {
 					entries.AddStrings("LOCAL_WHOLE_STATIC_LIBRARIES", c.Properties.AndroidMkWholeStaticLibs...)
 				}
+				if len(c.Properties.AndroidMkHeaderLibs) > 0 {
+					entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
+				}
 				entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
 				if c.UseVndk() {
 					entries.SetBool("LOCAL_USE_VNDK", true)
@@ -146,7 +149,7 @@
 	return []android.AndroidMkEntries{entries}
 }
 
-func androidMkWriteTestData(data android.Paths, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func AndroidMkDataPaths(data android.Paths) []string {
 	var testFiles []string
 	for _, d := range data {
 		rel := d.Rel()
@@ -157,6 +160,11 @@
 		path = strings.TrimSuffix(path, rel)
 		testFiles = append(testFiles, path+":"+rel)
 	}
+	return testFiles
+}
+
+func androidMkWriteTestData(data android.Paths, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	testFiles := AndroidMkDataPaths(data)
 	if len(testFiles) > 0 {
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 			entries.AddStrings("LOCAL_TEST_DATA", testFiles...)
diff --git a/cc/builder.go b/cc/builder.go
index d8e90b6..41cc0c7 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -38,13 +38,6 @@
 )
 
 var (
-	abiCheckAllowFlags = []string{
-		"-allow-unreferenced-changes",
-		"-allow-unreferenced-elf-symbol-changes",
-	}
-)
-
-var (
 	pctx = android.NewPackageContext("android/soong/cc")
 
 	cc = pctx.AndroidRemoteStaticRule("cc", android.RemoteRuleSupports{Goma: true, RBE: true},
@@ -65,7 +58,7 @@
 
 	ld, ldRE = remoteexec.StaticRules(pctx, "ld",
 		blueprint.RuleParams{
-			Command: "$ldCmd ${crtBegin} @${out}.rsp " +
+			Command: "$reTemplate$ldCmd ${crtBegin} @${out}.rsp " +
 				"${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
 			CommandDeps:    []string{"$ldCmd"},
 			Rspfile:        "${out}.rsp",
@@ -73,28 +66,29 @@
 			// clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
 			Restat: true,
 		},
-		&remoteexec.REParams{Labels: map[string]string{"type": "link", "tool": "clang"},
+		&remoteexec.REParams{
+			Labels:          map[string]string{"type": "link", "tool": "clang"},
 			ExecStrategy:    "${config.RECXXLinksExecStrategy}",
 			Inputs:          []string{"${out}.rsp"},
 			RSPFile:         "${out}.rsp",
-			OutputFiles:     []string{"${out}"},
+			OutputFiles:     []string{"${out}", "$implicitOutputs"},
 			ToolchainInputs: []string{"$ldCmd"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
-		}, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags"}, nil)
+		}, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags"}, []string{"implicitOutputs"})
 
 	partialLd, partialLdRE = remoteexec.StaticRules(pctx, "partialLd",
 		blueprint.RuleParams{
 			// Without -no-pie, clang 7.0 adds -pie to link Android files,
 			// but -r and -pie cannot be used together.
-			Command:     "$ldCmd -fuse-ld=lld -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
+			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"},
-			OutputFiles:     []string{"${out}"},
+			OutputFiles:     []string{"${out}", "$implicitOutputs"},
 			ToolchainInputs: []string{"$ldCmd"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
-		}, []string{"ldCmd", "ldFlags"}, []string{"inCommaList"})
+		}, []string{"ldCmd", "ldFlags"}, []string{"inCommaList", "implicitOutputs"})
 
 	ar = pctx.AndroidStaticRule("ar",
 		blueprint.RuleParams{
@@ -199,30 +193,43 @@
 	_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
 
 	// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
-	sAbiDump = pctx.AndroidStaticRule("sAbiDump",
+	sAbiDump, sAbiDumpRE = remoteexec.StaticRules(pctx, "sAbiDump",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && $sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
+			Command:     "rm -f $out && $reTemplate$sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
 			CommandDeps: []string{"$sAbiDumper"},
-		},
-		"cFlags", "exportDirs")
+		}, &remoteexec.REParams{
+			Labels:       map[string]string{"type": "abi-dump", "tool": "header-abi-dumper"},
+			ExecStrategy: "${config.REAbiDumperExecStrategy}",
+			Platform: map[string]string{
+				remoteexec.PoolKey:      "${config.RECXXPool}",
+				"InputRootAbsolutePath": android.AbsSrcDirForExistingUseCases(),
+			},
+		}, []string{"cFlags", "exportDirs"}, nil)
 
 	_ = pctx.SourcePathVariable("sAbiLinker", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-linker")
+	_ = pctx.SourcePathVariable("sAbiLinkerLibs", "prebuilts/clang-tools/${config.HostPrebuiltTag}/lib64")
 
-	sAbiLink = pctx.AndroidStaticRule("sAbiLink",
+	sAbiLink, sAbiLinkRE = remoteexec.StaticRules(pctx, "sAbiLink",
 		blueprint.RuleParams{
-			Command:        "$sAbiLinker -o ${out} $symbolFilter -arch $arch  $exportedHeaderFlags @${out}.rsp ",
+			Command:        "$reTemplate$sAbiLinker -o ${out} $symbolFilter -arch $arch  $exportedHeaderFlags @${out}.rsp ",
 			CommandDeps:    []string{"$sAbiLinker"},
 			Rspfile:        "${out}.rsp",
 			RspfileContent: "${in}",
-		},
-		"symbolFilter", "arch", "exportedHeaderFlags")
+		}, &remoteexec.REParams{
+			Labels:          map[string]string{"type": "tool", "name": "abi-linker"},
+			ExecStrategy:    "${config.REAbiLinkerExecStrategy}",
+			Inputs:          []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicits"},
+			RSPFile:         "${out}.rsp",
+			OutputFiles:     []string{"$out"},
+			ToolchainInputs: []string{"$sAbiLinker"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXPool}"},
+		}, []string{"symbolFilter", "arch", "exportedHeaderFlags"}, []string{"implicits"})
 
 	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
 
 	sAbiDiff = pctx.RuleFunc("sAbiDiff",
 		func(ctx android.PackageRuleContext) blueprint.RuleParams {
-			// TODO(b/78139997): Add -check-all-apis back
-			commandStr := "($sAbiDiffer ${allowFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
+			commandStr := "($sAbiDiffer ${extraFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
 			commandStr += "|| (echo 'error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py ${createReferenceDumpFlags} -l ${libName}'"
 			commandStr += " && (mkdir -p $$DIST_DIR/abidiffs && cp ${out} $$DIST_DIR/abidiffs/)"
 			commandStr += " && exit 1)"
@@ -231,7 +238,7 @@
 				CommandDeps: []string{"$sAbiDiffer"},
 			}
 		},
-		"allowFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
+		"extraFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
 
 	unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
 		blueprint.RuleParams{
@@ -256,7 +263,11 @@
 	kytheExtract = pctx.StaticRule("kythe",
 		blueprint.RuleParams{
 			Command: `rm -f $out && ` +
-				`KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out KYTHE_VNAMES=$kytheVnames KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
+				`KYTHE_CORPUS=${kytheCorpus} ` +
+				`KYTHE_OUTPUT_FILE=$out ` +
+				`KYTHE_VNAMES=$kytheVnames ` +
+				`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
+				`KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative ` +
 				`$cxxExtractor $cFlags $in `,
 			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
 		},
@@ -567,8 +578,12 @@
 			sAbiDumpFile := android.ObjPathWithExt(ctx, subdir, srcFile, "sdump")
 			sAbiDumpFiles = append(sAbiDumpFiles, sAbiDumpFile)
 
+			dumpRule := sAbiDump
+			if ctx.Config().IsEnvTrue("RBE_ABI_DUMPER") {
+				dumpRule = sAbiDumpRE
+			}
 			ctx.Build(pctx, android.BuildParams{
-				Rule:        sAbiDump,
+				Rule:        dumpRule,
 				Description: "header-abi-dumper " + srcFile.Rel(),
 				Output:      sAbiDumpFile,
 				Input:       srcFile,
@@ -672,8 +687,17 @@
 	}
 
 	rule := ld
+	args := map[string]string{
+		"ldCmd":         ldCmd,
+		"crtBegin":      crtBegin.String(),
+		"libFlags":      strings.Join(libFlagsList, " "),
+		"extraLibFlags": flags.extraLibFlags,
+		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
+		"crtEnd":        crtEnd.String(),
+	}
 	if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = ldRE
+		args["implicitOutputs"] = strings.Join(implicitOutputs.Strings(), ",")
 	}
 
 	ctx.Build(pctx, android.BuildParams{
@@ -683,14 +707,7 @@
 		ImplicitOutputs: implicitOutputs,
 		Inputs:          objFiles,
 		Implicits:       deps,
-		Args: map[string]string{
-			"ldCmd":         ldCmd,
-			"crtBegin":      crtBegin.String(),
-			"libFlags":      strings.Join(libFlagsList, " "),
-			"extraLibFlags": flags.extraLibFlags,
-			"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
-			"crtEnd":        crtEnd.String(),
-		},
+		Args:            args,
 	})
 }
 
@@ -715,17 +732,30 @@
 	for _, tag := range excludedSymbolTags {
 		symbolFilterStr += " --exclude-symbol-tag " + tag
 	}
+	rule := sAbiLink
+	args := map[string]string{
+		"symbolFilter":        symbolFilterStr,
+		"arch":                ctx.Arch().ArchType.Name,
+		"exportedHeaderFlags": exportedHeaderFlags,
+	}
+	if ctx.Config().IsEnvTrue("RBE_ABI_LINKER") {
+		rule = sAbiLinkRE
+		rbeImplicits := implicits.Strings()
+		for _, p := range strings.Split(exportedHeaderFlags, " ") {
+			if len(p) > 2 {
+				// Exclude the -I prefix.
+				rbeImplicits = append(rbeImplicits, p[2:])
+			}
+		}
+		args["implicits"] = strings.Join(rbeImplicits, ",")
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        sAbiLink,
+		Rule:        rule,
 		Description: "header-abi-linker " + outputFile.Base(),
 		Output:      outputFile,
 		Inputs:      sAbiDumps,
 		Implicits:   implicits,
-		Args: map[string]string{
-			"symbolFilter":        symbolFilterStr,
-			"arch":                ctx.Arch().ArchType.Name,
-			"exportedHeaderFlags": exportedHeaderFlags,
-		},
+		Args:        args,
 	})
 	return android.OptionalPathForPath(outputFile)
 }
@@ -742,27 +772,36 @@
 }
 
 func SourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
-	baseName, exportedHeaderFlags string, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
+	baseName, exportedHeaderFlags string, checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 	createReferenceDumpFlags := ""
 
-	localAbiCheckAllowFlags := append([]string(nil), abiCheckAllowFlags...)
-	if exportedHeaderFlags == "" {
-		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-advice-only")
+	var extraFlags []string
+	if checkAllApis {
+		extraFlags = append(extraFlags, "-check-all-apis")
+	} else {
+		extraFlags = append(extraFlags,
+			"-allow-unreferenced-changes",
+			"-allow-unreferenced-elf-symbol-changes")
 	}
+
+	if exportedHeaderFlags == "" {
+		extraFlags = append(extraFlags, "-advice-only")
+	}
+
 	if isLlndk || isNdk {
 		createReferenceDumpFlags = "--llndk"
 		if isLlndk {
 			// TODO(b/130324828): "-consider-opaque-types-different" should apply to
 			// both LLNDK and NDK shared libs. However, a known issue in header-abi-diff
 			// breaks libaaudio. Remove the if-guard after the issue is fixed.
-			localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-consider-opaque-types-different")
+			extraFlags = append(extraFlags, "-consider-opaque-types-different")
 		}
 	}
 	if isVndkExt {
-		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-allow-extensions")
+		extraFlags = append(extraFlags, "-allow-extensions")
 	}
 
 	ctx.Build(pctx, android.BuildParams{
@@ -775,7 +814,7 @@
 			"referenceDump":            referenceDump.String(),
 			"libName":                  libName,
 			"arch":                     ctx.Arch().ArchType.Name,
-			"allowFlags":               strings.Join(localAbiCheckAllowFlags, " "),
+			"extraFlags":               strings.Join(extraFlags, " "),
 			"createReferenceDumpFlags": createReferenceDumpFlags,
 		},
 	})
diff --git a/cc/cc.go b/cc/cc.go
index 01bf5f1..fbd5bdf 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -223,6 +223,7 @@
 	AndroidMkStaticLibs       []string `blueprint:"mutated"`
 	AndroidMkRuntimeLibs      []string `blueprint:"mutated"`
 	AndroidMkWholeStaticLibs  []string `blueprint:"mutated"`
+	AndroidMkHeaderLibs       []string `blueprint:"mutated"`
 	HideFromMake              bool     `blueprint:"mutated"`
 	PreventInstall            bool     `blueprint:"mutated"`
 	ApexesProvidingSharedLibs []string `blueprint:"mutated"`
@@ -436,7 +437,6 @@
 	ndkLateStubDepTag     = DependencyTag{Name: "ndk late stub", Library: true}
 	vndkExtDepTag         = DependencyTag{Name: "vndk extends", Library: true}
 	runtimeDepTag         = DependencyTag{Name: "runtime lib"}
-	coverageDepTag        = DependencyTag{Name: "coverage"}
 	testPerSrcDepTag      = DependencyTag{Name: "test_per_src"}
 )
 
@@ -744,6 +744,15 @@
 	return c.outputFile
 }
 
+func (c *Module) CoverageFiles() android.Paths {
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			return library.objs().coverageFiles
+		}
+	}
+	panic(fmt.Errorf("CoverageFiles called on non-library module: %q", c.BaseModuleName()))
+}
+
 var _ LinkableInterface = (*Module)(nil)
 
 func (c *Module) UnstrippedOutputFile() android.Path {
@@ -1202,6 +1211,11 @@
 		return false
 	}
 
+	// Coverage builds have extra symbols.
+	if ctx.mod.isCoverageVariant() {
+		return false
+	}
+
 	if ctx.ctx.Fuchsia() {
 		return false
 	}
@@ -1381,6 +1395,15 @@
 	return ok && test.isAllTestsVariation()
 }
 
+func (c *Module) DataPaths() android.Paths {
+	if p, ok := c.installer.(interface {
+		dataPaths() android.Paths
+	}); ok {
+		return p.dataPaths()
+	}
+	return nil
+}
+
 func (c *Module) getNameSuffixWithVndkVersion(ctx android.ModuleContext) string {
 	// Returns the name suffix for product and vendor variants. If the VNDK version is not
 	// "current", it will append the VNDK version to the name suffix.
@@ -2487,13 +2510,16 @@
 			// When combining coverage files for shared libraries and executables, coverage files
 			// in static libraries act as if they were whole static libraries. The same goes for
 			// source based Abi dump files.
-			// This should only be done for cc.Modules
 			if c, ok := ccDep.(*Module); ok {
 				staticLib := c.linker.(libraryInterface)
 				depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
 					staticLib.objs().coverageFiles...)
 				depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
 					staticLib.objs().sAbiDumpFiles...)
+			} else if c, ok := ccDep.(LinkableInterface); ok {
+				// Handle non-CC modules here
+				depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
+					c.CoverageFiles()...)
 			}
 		}
 
@@ -2605,6 +2631,9 @@
 		case wholeStaticDepTag:
 			c.Properties.AndroidMkWholeStaticLibs = append(
 				c.Properties.AndroidMkWholeStaticLibs, makeLibName(depName))
+		case headerDepTag:
+			c.Properties.AndroidMkHeaderLibs = append(
+				c.Properties.AndroidMkHeaderLibs, makeLibName(depName))
 		}
 	})
 
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 19aedd9..9383463 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -37,10 +37,8 @@
 	}
 
 	arm64Ldflags = []string{
-		"-Wl,-m,aarch64_elf64_le_vec",
 		"-Wl,--hash-style=gnu",
 		"-Wl,-z,separate-code",
-		"-fuse-ld=gold",
 		"-Wl,--icf=safe",
 	}
 
@@ -94,7 +92,6 @@
 
 	pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " "))
 	pctx.StaticVariable("Arm64Lldflags", strings.Join(arm64Lldflags, " "))
-	pctx.StaticVariable("Arm64IncludeFlags", bionicHeaders("arm64"))
 
 	pctx.StaticVariable("Arm64ClangCflags", strings.Join(ClangFilterUnknownCflags(arm64Cflags), " "))
 	pctx.StaticVariable("Arm64ClangLdflags", strings.Join(ClangFilterUnknownCflags(arm64Ldflags), " "))
@@ -166,7 +163,7 @@
 }
 
 func (t *toolchainArm64) IncludeFlags() string {
-	return "${config.Arm64IncludeFlags}"
+	return ""
 }
 
 func (t *toolchainArm64) ClangTriple() string {
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index d37e486..f01c638 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -175,7 +175,6 @@
 
 	pctx.StaticVariable("ArmLdflags", strings.Join(armLdflags, " "))
 	pctx.StaticVariable("ArmLldflags", strings.Join(armLldflags, " "))
-	pctx.StaticVariable("ArmIncludeFlags", bionicHeaders("arm"))
 
 	// Clang cflags
 	pctx.StaticVariable("ArmToolchainClangCflags", strings.Join(ClangFilterUnknownCflags(armToolchainCflags), " "))
@@ -269,7 +268,7 @@
 }
 
 func (t *toolchainArm) IncludeFlags() string {
-	return "${config.ArmIncludeFlags}"
+	return ""
 }
 
 func (t *toolchainArm) ClangTriple() string {
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 2e0b241..24dc6b9 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -51,7 +51,7 @@
 	// http://b/153759688
 	"-fuse-init-array",
 
-	// arm + arm64 + mips + mips64
+	// arm + arm64
 	"-fgcse-after-reload",
 	"-frerun-cse-after-loop",
 	"-frename-registers",
@@ -70,11 +70,6 @@
 	"-fno-tree-copy-prop",
 	"-fno-tree-loop-optimize",
 
-	// mips + mips64
-	"-msynci",
-	"-mno-synci",
-	"-mno-fused-madd",
-
 	// x86 + x86_64
 	"-finline-limit=300",
 	"-fno-inline-functions-called-once",
@@ -87,10 +82,8 @@
 
 // Ldflags that should be filtered out when linking with clang lld
 var ClangUnknownLldflags = sorted([]string{
-	"-fuse-ld=gold",
 	"-Wl,--fix-cortex-a8",
 	"-Wl,--no-fix-cortex-a8",
-	"-Wl,-m,aarch64_elf64_le_vec",
 })
 
 var ClangLibToolingUnknownCflags = sorted([]string{})
diff --git a/cc/config/global.go b/cc/config/global.go
index d5ecf7f..1dd8a2d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -162,7 +162,7 @@
 
 		// http://b/131390872
 		// Automatically initialize any uninitialized stack variables.
-		// Prefer zero-init if both options are set.
+		// Prefer zero-init if multiple options are set.
 		if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
 		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
@@ -170,8 +170,8 @@
 		} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
 		} else {
-			// Default to pattern initialization.
-			flags = append(flags, "-ftrivial-auto-var-init=pattern")
+			// Default to zero initialization.
+			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
 		}
 
 		return strings.Join(flags, " ")
@@ -257,22 +257,15 @@
 		return ""
 	})
 
-	pctx.VariableFunc("RECXXLinksPool", envOverrideFunc("RBE_CXX_LINKS_POOL", remoteexec.DefaultPool))
-	pctx.VariableFunc("RECXXLinksExecStrategy", envOverrideFunc("RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("RECXXPool", remoteexec.EnvOverrideFunc("RBE_CXX_POOL", remoteexec.DefaultPool))
+	pctx.VariableFunc("RECXXLinksPool", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_POOL", remoteexec.DefaultPool))
+	pctx.VariableFunc("RECXXLinksExecStrategy", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REAbiDumperExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_DUMPER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REAbiLinkerExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 }
 
 var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
 
-func bionicHeaders(kernelArch string) string {
-	return strings.Join([]string{
-		"-isystem bionic/libc/include",
-		"-isystem bionic/libc/kernel/uapi",
-		"-isystem bionic/libc/kernel/uapi/asm-" + kernelArch,
-		"-isystem bionic/libc/kernel/android/scsi",
-		"-isystem bionic/libc/kernel/android/uapi",
-	}, " ")
-}
-
 func envOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
 	return func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv(envVar); override != "" {
diff --git a/cc/config/mips64_device.go b/cc/config/mips64_device.go
deleted file mode 100644
index c2af951..0000000
--- a/cc/config/mips64_device.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2015 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
-	"strings"
-
-	"android/soong/android"
-)
-
-var (
-	mips64Cflags = []string{
-		"-Umips",
-
-		// Help catch common 32/64-bit errors.
-		"-Werror=implicit-function-declaration",
-	}
-
-	mips64ClangCflags = append(mips64Cflags, []string{
-		"-fintegrated-as",
-	}...)
-
-	mips64Cppflags = []string{}
-
-	mips64Ldflags = []string{
-		"-Wl,--allow-shlib-undefined",
-	}
-
-	mips64ArchVariantCflags = map[string][]string{
-		"mips64r2": []string{
-			"-mips64r2",
-			"-msynci",
-		},
-		"mips64r6": []string{
-			"-mips64r6",
-			"-msynci",
-		},
-	}
-)
-
-const (
-	mips64GccVersion = "4.9"
-)
-
-func init() {
-	pctx.StaticVariable("mips64GccVersion", mips64GccVersion)
-
-	pctx.SourcePathVariable("Mips64GccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/mips/mips64el-linux-android-${mips64GccVersion}")
-
-	pctx.StaticVariable("Mips64IncludeFlags", bionicHeaders("mips"))
-
-	// Clang cflags
-	pctx.StaticVariable("Mips64ClangCflags", strings.Join(ClangFilterUnknownCflags(mips64ClangCflags), " "))
-	pctx.StaticVariable("Mips64ClangLdflags", strings.Join(ClangFilterUnknownCflags(mips64Ldflags), " "))
-	pctx.StaticVariable("Mips64ClangCppflags", strings.Join(ClangFilterUnknownCflags(mips64Cppflags), " "))
-
-	// Extended cflags
-
-	// Architecture variant cflags
-	for variant, cflags := range mips64ArchVariantCflags {
-		pctx.StaticVariable("Mips64"+variant+"VariantClangCflags",
-			strings.Join(ClangFilterUnknownCflags(cflags), " "))
-	}
-}
-
-type toolchainMips64 struct {
-	toolchain64Bit
-	clangCflags          string
-	toolchainClangCflags string
-}
-
-func (t *toolchainMips64) Name() string {
-	return "mips64"
-}
-
-func (t *toolchainMips64) GccRoot() string {
-	return "${config.Mips64GccRoot}"
-}
-
-func (t *toolchainMips64) GccTriple() string {
-	return "mips64el-linux-android"
-}
-
-func (t *toolchainMips64) GccVersion() string {
-	return mips64GccVersion
-}
-
-func (t *toolchainMips64) IncludeFlags() string {
-	return "${config.Mips64IncludeFlags}"
-}
-
-func (t *toolchainMips64) ClangTriple() string {
-	return t.GccTriple()
-}
-
-func (t *toolchainMips64) ToolchainClangCflags() string {
-	return t.toolchainClangCflags
-}
-
-func (t *toolchainMips64) ClangAsflags() string {
-	return "-fno-integrated-as"
-}
-
-func (t *toolchainMips64) ClangCflags() string {
-	return t.clangCflags
-}
-
-func (t *toolchainMips64) ClangCppflags() string {
-	return "${config.Mips64ClangCppflags}"
-}
-
-func (t *toolchainMips64) ClangLdflags() string {
-	return "${config.Mips64ClangLdflags}"
-}
-
-func (t *toolchainMips64) ClangLldflags() string {
-	// TODO: define and use Mips64ClangLldflags
-	return "${config.Mips64ClangLdflags}"
-}
-
-func (toolchainMips64) LibclangRuntimeLibraryArch() string {
-	return "mips64"
-}
-
-func mips64ToolchainFactory(arch android.Arch) Toolchain {
-	return &toolchainMips64{
-		clangCflags:          "${config.Mips64ClangCflags}",
-		toolchainClangCflags: "${config.Mips64" + arch.ArchVariant + "VariantClangCflags}",
-	}
-}
-
-func init() {
-	registerToolchainFactory(android.Android, android.Mips64, mips64ToolchainFactory)
-}
diff --git a/cc/config/mips_device.go b/cc/config/mips_device.go
deleted file mode 100644
index ddbc41b..0000000
--- a/cc/config/mips_device.go
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2015 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
-	"strings"
-
-	"android/soong/android"
-)
-
-var (
-	mipsCflags = []string{
-		"-fomit-frame-pointer",
-		"-Umips",
-	}
-
-	mipsClangCflags = append(mipsCflags, []string{
-		"-fPIC",
-		"-fintegrated-as",
-	}...)
-
-	mipsCppflags = []string{}
-
-	mipsLdflags = []string{
-		"-Wl,--allow-shlib-undefined",
-	}
-
-	mipsToolchainLdflags = []string{
-		"-Wl,-melf32ltsmip",
-	}
-
-	mipsArchVariantCflags = map[string][]string{
-		"mips32-fp": []string{
-			"-mips32",
-			"-mfp32",
-			"-modd-spreg",
-			"-mno-synci",
-		},
-		"mips32r2-fp": []string{
-			"-mips32r2",
-			"-mfp32",
-			"-modd-spreg",
-			"-msynci",
-		},
-		"mips32r2-fp-xburst": []string{
-			"-mips32r2",
-			"-mfp32",
-			"-modd-spreg",
-			"-mno-fused-madd",
-			"-mno-synci",
-		},
-		"mips32r2dsp-fp": []string{
-			"-mips32r2",
-			"-mfp32",
-			"-modd-spreg",
-			"-mdsp",
-			"-msynci",
-		},
-		"mips32r2dspr2-fp": []string{
-			"-mips32r2",
-			"-mfp32",
-			"-modd-spreg",
-			"-mdspr2",
-			"-msynci",
-		},
-		"mips32r6": []string{
-			"-mips32r6",
-			"-mfp64",
-			"-mno-odd-spreg",
-			"-msynci",
-		},
-	}
-)
-
-const (
-	mipsGccVersion = "4.9"
-)
-
-func init() {
-	pctx.StaticVariable("mipsGccVersion", mipsGccVersion)
-
-	pctx.SourcePathVariable("MipsGccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/mips/mips64el-linux-android-${mipsGccVersion}")
-
-	pctx.StaticVariable("MipsToolchainLdflags", strings.Join(mipsToolchainLdflags, " "))
-	pctx.StaticVariable("MipsIncludeFlags", bionicHeaders("mips"))
-
-	// Clang cflags
-	pctx.StaticVariable("MipsClangCflags", strings.Join(ClangFilterUnknownCflags(mipsClangCflags), " "))
-	pctx.StaticVariable("MipsClangLdflags", strings.Join(ClangFilterUnknownCflags(mipsLdflags), " "))
-	pctx.StaticVariable("MipsClangCppflags", strings.Join(ClangFilterUnknownCflags(mipsCppflags), " "))
-
-	// Extended cflags
-
-	// Architecture variant cflags
-	for variant, cflags := range mipsArchVariantCflags {
-		pctx.StaticVariable("Mips"+variant+"VariantClangCflags",
-			strings.Join(ClangFilterUnknownCflags(cflags), " "))
-	}
-}
-
-type toolchainMips struct {
-	toolchain32Bit
-	clangCflags          string
-	toolchainClangCflags string
-}
-
-func (t *toolchainMips) Name() string {
-	return "mips"
-}
-
-func (t *toolchainMips) GccRoot() string {
-	return "${config.MipsGccRoot}"
-}
-
-func (t *toolchainMips) GccTriple() string {
-	return "mips64el-linux-android"
-}
-
-func (t *toolchainMips) GccVersion() string {
-	return mipsGccVersion
-}
-
-func (t *toolchainMips) IncludeFlags() string {
-	return "${config.MipsIncludeFlags}"
-}
-
-func (t *toolchainMips) ClangTriple() string {
-	return "mipsel-linux-android"
-}
-
-func (t *toolchainMips) ToolchainClangLdflags() string {
-	return "${config.MipsToolchainLdflags}"
-}
-
-func (t *toolchainMips) ToolchainClangCflags() string {
-	return t.toolchainClangCflags
-}
-
-func (t *toolchainMips) ClangAsflags() string {
-	return "-fPIC -fno-integrated-as"
-}
-
-func (t *toolchainMips) ClangCflags() string {
-	return t.clangCflags
-}
-
-func (t *toolchainMips) ClangCppflags() string {
-	return "${config.MipsClangCppflags}"
-}
-
-func (t *toolchainMips) ClangLdflags() string {
-	return "${config.MipsClangLdflags}"
-}
-
-func (t *toolchainMips) ClangLldflags() string {
-	// TODO: define and use MipsClangLldflags
-	return "${config.MipsClangLdflags}"
-}
-
-func (toolchainMips) LibclangRuntimeLibraryArch() string {
-	return "mips"
-}
-
-func mipsToolchainFactory(arch android.Arch) Toolchain {
-	return &toolchainMips{
-		clangCflags:          "${config.MipsClangCflags}",
-		toolchainClangCflags: "${config.Mips" + arch.ArchVariant + "VariantClangCflags}",
-	}
-}
-
-func init() {
-	registerToolchainFactory(android.Android, android.Mips, mipsToolchainFactory)
-}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index dd52a0e..4ac9e58 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -30,10 +30,12 @@
 		}
 		return strings.Join([]string{
 			"-*",
+			"bugprone*",
 			"clang-diagnostic-unused-command-line-argument",
 			"google*",
 			"misc-macro-parentheses",
 			"performance*",
+			"-bugprone-narrowing-conversions",
 			"-google-readability*",
 			"-google-runtime-references",
 		}, ",")
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index bcfae5d..1e25a3b 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -103,7 +103,6 @@
 
 	pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " "))
 	pctx.StaticVariable("X86_64Lldflags", strings.Join(x86_64Lldflags, " "))
-	pctx.StaticVariable("X86_64IncludeFlags", bionicHeaders("x86"))
 
 	// Clang cflags
 	pctx.StaticVariable("X86_64ClangCflags", strings.Join(ClangFilterUnknownCflags(x86_64Cflags), " "))
@@ -145,7 +144,7 @@
 }
 
 func (t *toolchainX86_64) IncludeFlags() string {
-	return "${config.X86_64IncludeFlags}"
+	return ""
 }
 
 func (t *toolchainX86_64) ClangTriple() string {
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 64392dc..fe83098 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -114,7 +114,6 @@
 
 	pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " "))
 	pctx.StaticVariable("X86Lldflags", strings.Join(x86Lldflags, " "))
-	pctx.StaticVariable("X86IncludeFlags", bionicHeaders("x86"))
 
 	// Clang cflags
 	pctx.StaticVariable("X86ClangCflags", strings.Join(ClangFilterUnknownCflags(x86ClangCflags), " "))
@@ -156,7 +155,7 @@
 }
 
 func (t *toolchainX86) IncludeFlags() string {
-	return "${config.X86IncludeFlags}"
+	return ""
 }
 
 func (t *toolchainX86) ClangTriple() string {
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index fb1cdeb..fa625e3 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -70,8 +70,6 @@
 	pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
 	pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLldflags, " "))
 
-	pctx.StaticVariable("LinuxBionicIncludeFlags", bionicHeaders("x86"))
-
 	// Use the device gcc toolchain for now
 	pctx.StaticVariable("LinuxBionicGccRoot", "${X86_64GccRoot}")
 }
@@ -97,7 +95,7 @@
 }
 
 func (t *toolchainLinuxBionic) IncludeFlags() string {
-	return "${config.LinuxBionicIncludeFlags}"
+	return ""
 }
 
 func (t *toolchainLinuxBionic) ClangTriple() string {
diff --git a/cc/coverage.go b/cc/coverage.go
index bde07fd..f885fcb 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -65,10 +65,10 @@
 	if cov.Properties.NeedCoverageVariant {
 		ctx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, coverageDepTag, getGcovProfileLibraryName(ctx))
+		}, CoverageDepTag, getGcovProfileLibraryName(ctx))
 		ctx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, coverageDepTag, getClangProfileLibraryName(ctx))
+		}, CoverageDepTag, getClangProfileLibraryName(ctx))
 	}
 	return deps
 }
@@ -92,7 +92,7 @@
 			// flags that the module may use.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
 		} else if clangCoverage {
-			flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-fprofile-instr-generate", "-fcoverage-mapping")
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-fprofile-instr-generate", "-fcoverage-mapping", "-Wno-pass-failed")
 		}
 	}
 
@@ -134,14 +134,14 @@
 		if gcovCoverage {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
 
-			coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), coverageDepTag).(*Module)
+			coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module)
 			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
 
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
 		} else if clangCoverage {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fprofile-instr-generate")
 
-			coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), coverageDepTag).(*Module)
+			coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
 			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
 		}
 	}
@@ -150,25 +150,30 @@
 }
 
 func (cov *coverage) begin(ctx BaseModuleContext) {
+	if ctx.Host() {
+		// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
+		// Just turn off for now.
+	} else {
+		cov.Properties = SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), ctx.useSdk(), ctx.sdkVersion())
+	}
+}
+
+func SetCoverageProperties(ctx android.BaseModuleContext, properties CoverageProperties, moduleTypeHasCoverage bool,
+	useSdk bool, sdkVersion string) CoverageProperties {
 	// Coverage is disabled globally
 	if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
-		return
+		return properties
 	}
 
 	var needCoverageVariant bool
 	var needCoverageBuild bool
 
-	if ctx.Host() {
-		// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
-		// Just turn off for now.
-	} else if !ctx.nativeCoverage() {
-		// Native coverage is not supported for this module type.
-	} else {
+	if moduleTypeHasCoverage {
 		// Check if Native_coverage is set to false.  This property defaults to true.
-		needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true)
-		if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" {
+		needCoverageVariant = BoolDefault(properties.Native_coverage, true)
+		if useSdk && sdkVersion != "current" {
 			// Native coverage is not supported for SDK versions < 23
-			if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
+			if fromApi, err := strconv.Atoi(sdkVersion); err == nil && fromApi < 23 {
 				needCoverageVariant = false
 			}
 		}
@@ -179,8 +184,10 @@
 		}
 	}
 
-	cov.Properties.NeedCoverageBuild = needCoverageBuild
-	cov.Properties.NeedCoverageVariant = needCoverageVariant
+	properties.NeedCoverageBuild = needCoverageBuild
+	properties.NeedCoverageVariant = needCoverageVariant
+
+	return properties
 }
 
 // Coverage is an interface for non-CC modules to implement to be mutated for coverage
@@ -190,6 +197,7 @@
 	PreventInstall()
 	HideFromMake()
 	MarkAsCoverageVariant(bool)
+	EnableCoverageIfNeeded()
 }
 
 func coverageMutator(mctx android.BottomUpMutatorContext) {
@@ -212,14 +220,17 @@
 			m[1].(*Module).coverage.Properties.IsCoverageVariant = true
 		}
 	} else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) {
-		// APEX modules fall here
+		// APEX and Rust modules fall here
 
 		// Note: variant "" is also created because an APEX can be depended on by another
 		// module which are split into "" and "cov" variants. e.g. when cc_test refers
 		// to an APEX via 'data' property.
 		m := mctx.CreateVariations("", "cov")
-		m[0].(Coverage).MarkAsCoverageVariant(true)
+		m[0].(Coverage).MarkAsCoverageVariant(false)
 		m[0].(Coverage).PreventInstall()
 		m[0].(Coverage).HideFromMake()
+
+		m[1].(Coverage).MarkAsCoverageVariant(true)
+		m[1].(Coverage).EnableCoverageIfNeeded()
 	}
 }
diff --git a/cc/fuzz.go b/cc/fuzz.go
index ee24300..58c1888 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -35,6 +35,9 @@
 	Componentid *int64 `json:"componentid,omitempty"`
 	// Hotlists in Google's bug tracking system that bugs should be marked with.
 	Hotlists []string `json:"hotlists,omitempty"`
+	// Specify whether this fuzz target was submitted by a researcher. Defaults
+	// to false.
+	Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
 }
 
 func (f *FuzzConfig) String() string {
@@ -126,7 +129,7 @@
 func collectAllSharedDependencies(ctx android.SingletonContext, module android.Module) android.Paths {
 	var fringe []android.Module
 
-	seen := make(map[android.Module]bool)
+	seen := make(map[string]bool)
 
 	// Enumerate the first level of dependencies, as we discard all non-library
 	// modules in the BFS loop below.
@@ -140,15 +143,15 @@
 
 	for i := 0; i < len(fringe); i++ {
 		module := fringe[i]
-		if seen[module] {
+		if seen[module.Name()] {
 			continue
 		}
-		seen[module] = true
+		seen[module.Name()] = true
 
 		ccModule := module.(*Module)
 		sharedLibraries = append(sharedLibraries, ccModule.UnstrippedOutputFile())
 		ctx.VisitDirectDeps(module, func(dep android.Module) {
-			if isValidSharedDependency(dep) && !seen[dep] {
+			if isValidSharedDependency(dep) && !seen[dep.Name()] {
 				fringe = append(fringe, dep)
 			}
 		})
@@ -198,6 +201,11 @@
 	return installLocation
 }
 
+// Get the device-only shared library symbols install directory.
+func sharedLibrarySymbolsInstallLocation(libraryPath android.Path, archString string) string {
+	return filepath.Join("$(PRODUCT_OUT)/symbols/data/fuzz/", archString, "/lib/", libraryPath.Base())
+}
+
 func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
 	fuzz.binaryDecorator.baseInstaller.dir = filepath.Join(
 		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
@@ -250,13 +258,13 @@
 	}
 
 	// Grab the list of required shared libraries.
-	seen := make(map[android.Module]bool)
+	seen := make(map[string]bool)
 	var sharedLibraries android.Paths
 	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if seen[child] {
+		if seen[child.Name()] {
 			return false
 		}
-		seen[child] = true
+		seen[child.Name()] = true
 
 		if isValidSharedDependency(child) {
 			sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
@@ -269,6 +277,12 @@
 		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
 			sharedLibraryInstallLocation(
 				lib, ctx.Host(), ctx.Arch().ArchType.String()))
+
+		// Also add the dependency on the shared library symbols dir.
+		if !ctx.Host() {
+			fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+				sharedLibrarySymbolsInstallLocation(lib, ctx.Arch().ArchType.String()))
+		}
 	}
 }
 
@@ -355,10 +369,10 @@
 			return
 		}
 
-		// Discard vendor-NDK-linked + ramdisk + recovery modules, they're duplicates of
+		// Discard ramdisk + recovery modules, they're duplicates of
 		// fuzz targets we're going to package anyway.
 		if !ccModule.Enabled() || ccModule.Properties.PreventInstall ||
-			ccModule.UseVndk() || ccModule.InRamdisk() || ccModule.InRecovery() {
+			ccModule.InRamdisk() || ccModule.InRecovery() {
 			return
 		}
 
@@ -367,8 +381,6 @@
 			return
 		}
 
-		s.fuzzTargets[module.Name()] = true
-
 		hostOrTargetString := "target"
 		if ccModule.Host() {
 			hostOrTargetString = "host"
@@ -421,12 +433,24 @@
 				continue
 			}
 			sharedLibraryInstalled[installDestination] = true
+
 			// Escape all the variables, as the install destination here will be called
 			// via. $(eval) in Make.
 			installDestination = strings.ReplaceAll(
 				installDestination, "$", "$$")
 			s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
 				library.String()+":"+installDestination)
+
+			// Ensure that on device, the library is also reinstalled to the /symbols/
+			// dir. Symbolized DSO's are always installed to the device when fuzzing, but
+			// we want symbolization tools (like `stack`) to be able to find the symbols
+			// in $ANDROID_PRODUCT_OUT/symbols automagically.
+			if !ccModule.Host() {
+				symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, archString)
+				symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
+				s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
+					library.String()+":"+symbolsInstallDestination)
+			}
 		}
 
 		// The executable.
@@ -458,6 +482,17 @@
 		builder.Build(pctx, ctx, "create-"+fuzzZip.String(),
 			"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
 
+		// Don't add modules to 'make haiku' that are set to not be exported to the
+		// fuzzing infrastructure.
+		if config := fuzzModule.Properties.Fuzz_config; config != nil {
+			if ccModule.Host() && !BoolDefault(config.Fuzz_on_haiku_host, true) {
+				return
+			} else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
+				return
+			}
+		}
+
+		s.fuzzTargets[module.Name()] = true
 		archDirs[archOs] = append(archDirs[archOs], fileToZip{fuzzZip, ""})
 	})
 
diff --git a/cc/gen_stub_libs.py b/cc/gen_stub_libs.py
index 0de703c..7deb804 100755
--- a/cc/gen_stub_libs.py
+++ b/cc/gen_stub_libs.py
@@ -26,8 +26,6 @@
 ALL_ARCHITECTURES = (
     'arm',
     'arm64',
-    'mips',
-    'mips64',
     'x86',
     'x86_64',
 )
diff --git a/cc/library.go b/cc/library.go
index 8d4992c..ecea2ea 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -102,6 +102,10 @@
 
 		// Symbol tags that should be ignored from the symbol file
 		Exclude_symbol_tags []string
+
+		// Run checks on all APIs (in addition to the ones referred by
+		// one of exported ELF symbols.)
+		Check_all_apis *bool
 	}
 
 	// Order symbols in .bss section by their sizes.  Only useful for shared libraries.
@@ -1072,7 +1076,9 @@
 		refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
 		if refAbiDumpFile != nil {
 			library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
-				refAbiDumpFile, fileName, exportedHeaderFlags, ctx.isLlndk(ctx.Config()), ctx.isNdk(), ctx.isVndkExt())
+				refAbiDumpFile, fileName, exportedHeaderFlags,
+				Bool(library.Properties.Header_abi_checker.Check_all_apis),
+				ctx.isLlndk(ctx.Config()), ctx.isNdk(), ctx.isVndkExt())
 		}
 	}
 }
diff --git a/cc/linkable.go b/cc/linkable.go
index 4a70d48..de36f90 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -12,6 +12,7 @@
 	CcLibraryInterface() bool
 
 	OutputFile() android.OptionalPath
+	CoverageFiles() android.Paths
 
 	IncludeDirs() android.Paths
 	SetDepsInLinkOrder([]android.Path)
@@ -83,4 +84,5 @@
 
 	CrtBeginDepTag = DependencyTag{Name: "crtbegin"}
 	CrtEndDepTag   = DependencyTag{Name: "crtend"}
+	CoverageDepTag = DependencyTag{Name: "coverage"}
 )
diff --git a/cc/linker.go b/cc/linker.go
index d56c733..57a0c01 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -449,11 +449,10 @@
 		}
 	}
 
-	if ctx.useSdk() && (ctx.Arch().ArchType != android.Mips && ctx.Arch().ArchType != android.Mips64) {
+	if ctx.useSdk() {
 		// The bionic linker now has support gnu style hashes (which are much faster!), but shipping
 		// to older devices requires the old style hash. Fortunately, we can build with both and
 		// it'll work anywhere.
-		// This is not currently supported on MIPS architectures.
 		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--hash-style=both")
 	}
 
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 5744bb2..60f931d 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"path/filepath"
-	"strings"
 
 	"github.com/google/blueprint"
 
@@ -131,14 +130,6 @@
 
 	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
 
-	// When generating NDK prebuilts, skip installing MIPS headers,
-	// but keep them when doing regular platform build.
-	// Ndk_abis property is only set to true with build/soong/scripts/build-ndk-prebuilts.sh
-	// TODO: Revert this once MIPS is supported in NDK again.
-	if ctx.Config().NdkAbis() && strings.Contains(ctx.ModuleName(), "mips") {
-		return
-	}
-
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
 	for _, header := range srcFiles {
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 119ca40..1597b88 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -135,8 +135,6 @@
 	firstArchVersions := map[android.ArchType]int{
 		android.Arm:    minVersion,
 		android.Arm64:  21,
-		android.Mips:   minVersion,
-		android.Mips64: 21,
 		android.X86:    minVersion,
 		android.X86_64: 21,
 	}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 2ef3195..ac990f3 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -43,6 +43,11 @@
 	// Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined
 	// symbols, etc), default true.
 	Check_elf_files *bool
+
+	// Optionally provide an import library if this is a Windows PE DLL prebuilt.
+	// This is needed only if this library is linked by other modules in build time.
+	// Only makes sense for the Windows target.
+	Windows_import_lib *string `android:"path,arch_variant"`
 }
 
 type prebuiltLinker struct {
@@ -109,9 +114,16 @@
 
 		in := android.PathForModuleSrc(ctx, srcs[0])
 
+		if p.static() {
+			return in
+		}
+
 		if p.shared() {
 			p.unstrippedOutputFile = in
 			libName := p.libraryDecorator.getLibName(ctx) + flags.Toolchain.ShlibSuffix()
+			outputFile := android.PathForModuleOut(ctx, libName)
+			var implicits android.Paths
+
 			if p.needsStrip(ctx) {
 				stripped := android.PathForModuleOut(ctx, "stripped", libName)
 				p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
@@ -122,10 +134,41 @@
 			// depending on a table of contents file instead of the library itself.
 			tocFile := android.PathForModuleOut(ctx, libName+".toc")
 			p.tocFile = android.OptionalPathForPath(tocFile)
-			TransformSharedObjectToToc(ctx, in, tocFile, builderFlags)
-		}
+			TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
 
-		return in
+			if ctx.Windows() && p.properties.Windows_import_lib != nil {
+				// Consumers of this library actually links to the import library in build
+				// time and dynamically links to the DLL in run time. i.e.
+				// a.exe <-- static link --> foo.lib <-- dynamic link --> foo.dll
+				importLibSrc := android.PathForModuleSrc(ctx, String(p.properties.Windows_import_lib))
+				importLibName := p.libraryDecorator.getLibName(ctx) + ".lib"
+				importLibOutputFile := android.PathForModuleOut(ctx, importLibName)
+				implicits = append(implicits, importLibOutputFile)
+
+				ctx.Build(pctx, android.BuildParams{
+					Rule:        android.Cp,
+					Description: "prebuilt import library",
+					Input:       importLibSrc,
+					Output:      importLibOutputFile,
+					Args: map[string]string{
+						"cpFlags": "-L",
+					},
+				})
+			}
+
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        android.Cp,
+				Description: "prebuilt shared library",
+				Implicits:   implicits,
+				Input:       in,
+				Output:      outputFile,
+				Args: map[string]string{
+					"cpFlags": "-L",
+				},
+			})
+
+			return outputFile
+		}
 	}
 
 	return nil
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 242d835..adb44bd 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -22,6 +22,25 @@
 	"github.com/google/blueprint"
 )
 
+func testPrebuilt(t *testing.T, bp string, fs map[string][]byte) *android.TestContext {
+	config := TestConfig(buildDir, android.Android, nil, bp, fs)
+	ctx := CreateTestContext()
+
+	// Enable androidmk support.
+	// * Register the singleton
+	// * Configure that we are inside make
+	// * Add CommonOS to ensure that androidmk processing works.
+	android.RegisterAndroidMkBuildComponents(ctx)
+	android.SetInMakeForTests(config)
+
+	ctx.Register(config)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+	return ctx
+}
+
 func TestPrebuilt(t *testing.T) {
 	bp := `
 		cc_library {
@@ -84,7 +103,15 @@
 		}
 	`
 
-	ctx := testPrebuilt(t, bp)
+	ctx := testPrebuilt(t, bp, map[string][]byte{
+		"liba.so": nil,
+		"libb.a":  nil,
+		"libd.so": nil,
+		"libe.a":  nil,
+		"libf.a":  nil,
+		"libf.so": nil,
+		"crtx.o":  nil,
+	})
 
 	// Verify that all the modules exist and that their dependencies were connected correctly
 	liba := ctx.ModuleForTests("liba", "android_arm64_armv8-a_shared").Module()
@@ -143,35 +170,6 @@
 	}
 }
 
-func testPrebuilt(t *testing.T, bp string) *android.TestContext {
-
-	fs := map[string][]byte{
-		"liba.so": nil,
-		"libb.a":  nil,
-		"libd.so": nil,
-		"libe.a":  nil,
-		"libf.a":  nil,
-		"libf.so": nil,
-		"crtx.o":  nil,
-	}
-	config := TestConfig(buildDir, android.Android, nil, bp, fs)
-	ctx := CreateTestContext()
-
-	// Enable androidmk support.
-	// * Register the singleton
-	// * Configure that we are inside make
-	// * Add CommonOS to ensure that androidmk processing works.
-	android.RegisterAndroidMkBuildComponents(ctx)
-	android.SetInMakeForTests(config)
-
-	ctx.Register(config)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-	return ctx
-}
-
 func TestPrebuiltLibraryShared(t *testing.T) {
 	ctx := testPrebuilt(t, `
 	cc_prebuilt_library_shared {
@@ -181,10 +179,12 @@
         none: true,
     },
 	}
-	`)
+	`, map[string][]byte{
+		"libf.so": nil,
+	})
 
 	shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module)
-	assertString(t, shared.OutputFile().String(), "libf.so")
+	assertString(t, shared.OutputFile().Path().Base(), "libtest.so")
 }
 
 func TestPrebuiltLibraryStatic(t *testing.T) {
@@ -193,10 +193,12 @@
 		name: "libtest",
 		srcs: ["libf.a"],
 	}
-	`)
+	`, map[string][]byte{
+		"libf.a": nil,
+	})
 
 	static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module)
-	assertString(t, static.OutputFile().String(), "libf.a")
+	assertString(t, static.OutputFile().Path().Base(), "libf.a")
 }
 
 func TestPrebuiltLibrary(t *testing.T) {
@@ -213,11 +215,59 @@
         none: true,
     },
 	}
-	`)
+	`, map[string][]byte{
+		"libf.a":  nil,
+		"libf.so": nil,
+	})
 
 	shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module)
-	assertString(t, shared.OutputFile().String(), "libf.so")
+	assertString(t, shared.OutputFile().Path().Base(), "libtest.so")
 
 	static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module)
-	assertString(t, static.OutputFile().String(), "libf.a")
+	assertString(t, static.OutputFile().Path().Base(), "libf.a")
+}
+
+func TestPrebuiltLibraryStem(t *testing.T) {
+	ctx := testPrebuilt(t, `
+	cc_prebuilt_library {
+		name: "libfoo",
+		stem: "libbar",
+		static: {
+			srcs: ["libfoo.a"],
+		},
+		shared: {
+			srcs: ["libfoo.so"],
+		},
+		strip: {
+			none: true,
+		},
+	}
+	`, map[string][]byte{
+		"libfoo.a":  nil,
+		"libfoo.so": nil,
+	})
+
+	static := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*Module)
+	assertString(t, static.OutputFile().Path().Base(), "libfoo.a")
+
+	shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module)
+	assertString(t, shared.OutputFile().Path().Base(), "libbar.so")
+}
+
+func TestPrebuiltLibrarySharedStem(t *testing.T) {
+	ctx := testPrebuilt(t, `
+	cc_prebuilt_library_shared {
+		name: "libfoo",
+		stem: "libbar",
+		srcs: ["libfoo.so"],
+		strip: {
+			none: true,
+		},
+	}
+	`, map[string][]byte{
+		"libfoo.so": nil,
+	})
+
+	shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module)
+	assertString(t, shared.OutputFile().Path().Base(), "libbar.so")
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 371f270..3412c9b 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -307,8 +307,8 @@
 		}
 	}
 
-	// CFI needs gold linker, and mips toolchain does not have one.
-	if !ctx.Config().EnableCFI() || ctx.Arch().ArchType == android.Mips || ctx.Arch().ArchType == android.Mips64 {
+	// Is CFI actually enabled?
+	if !ctx.Config().EnableCFI() {
 		s.Cfi = nil
 		s.Diag.Cfi = nil
 	}
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 26e7c8d..4012def 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -81,6 +81,17 @@
 	return outPath
 }
 
+func combineNotices(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cat,
+		Inputs:      paths,
+		Output:      outPath,
+		Description: "combine notices for " + out,
+	})
+	return outPath
+}
+
 func writeStringToFile(ctx android.SingletonContext, content, out string) android.OutputPath {
 	outPath := android.PathForOutput(ctx, out)
 	ctx.Build(pctx, android.BuildParams{
diff --git a/cc/stl.go b/cc/stl.go
index 34ff30c..4e74c7f 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -239,11 +239,6 @@
 			flags.Local.CppFlags = append(flags.Local.CppFlags, "-nostdinc++")
 			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 			if ctx.Windows() {
-				if stl.Properties.SelectedStl == "libc++_static" {
-					// These are transitively needed by libc++_static.
-					flags.extraLibFlags = append(flags.extraLibFlags,
-						"-lmsvcrt", "-lucrt")
-				}
 				// Use SjLj exceptions for 32-bit.  libgcc_eh implements SjLj
 				// exception model for 32-bit.
 				if ctx.Arch().ArchType == android.X86 {
diff --git a/cc/test.go b/cc/test.go
index 95abfbf..2439c94 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -160,6 +160,10 @@
 	return test.baseCompiler.Properties.Srcs
 }
 
+func (test *testBinary) dataPaths() android.Paths {
+	return test.data
+}
+
 func (test *testBinary) isAllTestsVariation() bool {
 	stem := test.binaryDecorator.Properties.Stem
 	return stem != nil && *stem == ""
diff --git a/cc/testing.go b/cc/testing.go
index 6119920..be020c5 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -192,6 +192,45 @@
 			symbol_file: "",
 			sdk_version: "current",
 		}
+
+		// Coverage libraries
+		cc_library {
+			name: "libprofile-extras",
+			vendor_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			system_shared_libs: [],
+			stl: "none",
+			notice: "custom_notice",
+		}
+		cc_library {
+			name: "libprofile-clang-extras",
+			vendor_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			system_shared_libs: [],
+			stl: "none",
+			notice: "custom_notice",
+		}
+		cc_library {
+			name: "libprofile-extras_ndk",
+			vendor_available: true,
+			native_coverage: false,
+			system_shared_libs: [],
+			stl: "none",
+			notice: "custom_notice",
+			sdk_version: "current",
+		}
+		cc_library {
+			name: "libprofile-clang-extras_ndk",
+			vendor_available: true,
+			native_coverage: false,
+			system_shared_libs: [],
+			stl: "none",
+			notice: "custom_notice",
+			sdk_version: "current",
+		}
+
 		cc_library {
 			name: "libdl",
 			no_libcrt: true,
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index c79a653..5801fc7 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -658,14 +658,14 @@
 			headers = append(headers, l.snapshotHeaders()...)
 		}
 
-		if m.NoticeFile().Valid() {
+		if len(m.NoticeFiles()) > 0 {
 			noticeName := ctx.ModuleName(m) + ".txt"
 			noticeOut := filepath.Join(noticeDir, noticeName)
 			// skip already copied notice file
 			if !installedNotices[noticeOut] {
 				installedNotices[noticeOut] = true
-				snapshotOutputs = append(snapshotOutputs, copyFile(
-					ctx, m.NoticeFile().Path(), noticeOut))
+				snapshotOutputs = append(snapshotOutputs, combineNotices(
+					ctx, m.NoticeFiles(), noticeOut))
 			}
 		}
 	})
diff --git a/cc/vndk.go b/cc/vndk.go
index 8eab8ba..dbe1f3b 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -660,13 +660,13 @@
 		moduleNames[stem] = ctx.ModuleName(m)
 		modulePaths[stem] = ctx.ModuleDir(m)
 
-		if m.NoticeFile().Valid() {
+		if len(m.NoticeFiles()) > 0 {
 			noticeName := stem + ".txt"
 			// skip already copied notice file
 			if _, ok := noticeBuilt[noticeName]; !ok {
 				noticeBuilt[noticeName] = true
-				snapshotOutputs = append(snapshotOutputs, copyFile(
-					ctx, m.NoticeFile().Path(), filepath.Join(noticeDir, noticeName)))
+				snapshotOutputs = append(snapshotOutputs, combineNotices(
+					ctx, m.NoticeFiles(), filepath.Join(noticeDir, noticeName)))
 			}
 		}
 
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index 4a146da..a638db2 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -21,6 +21,7 @@
 	"fmt"
 	"io"
 	"log"
+	"math"
 	"os"
 	"regexp"
 	"strings"
@@ -32,9 +33,10 @@
 )
 
 type TargetConfig struct {
-	sdkVersion       int32
-	screenDpi        map[android_bundle_proto.ScreenDensity_DensityAlias]bool
-	abis             map[android_bundle_proto.Abi_AbiAlias]bool
+	sdkVersion int32
+	screenDpi  map[android_bundle_proto.ScreenDensity_DensityAlias]bool
+	// Map holding <ABI alias>:<its sequence number in the flag> info.
+	abis             map[android_bundle_proto.Abi_AbiAlias]int
 	allowPrereleased bool
 	stem             string
 }
@@ -88,6 +90,7 @@
 }
 
 // Matchers for selection criteria
+
 type abiTargetingMatcher struct {
 	*android_bundle_proto.AbiTargeting
 }
@@ -99,12 +102,28 @@
 	if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
 		return true
 	}
+	// Find the one that appears first in the abis flags.
+	abiIdx := math.MaxInt32
 	for _, v := range m.GetValue() {
-		if _, ok := config.abis[v.Alias]; ok {
-			return true
+		if i, ok := config.abis[v.Alias]; ok {
+			if i < abiIdx {
+				abiIdx = i
+			}
 		}
 	}
-	return false
+	if abiIdx == math.MaxInt32 {
+		return false
+	}
+	// See if any alternatives appear before the above one.
+	for _, a := range m.GetAlternatives() {
+		if i, ok := config.abis[a.Alias]; ok {
+			if i < abiIdx {
+				// There is a better alternative. Skip this one.
+				return false
+			}
+		}
+	}
+	return true
 }
 
 type apkDescriptionMatcher struct {
@@ -161,16 +180,55 @@
 			userCountriesTargetingMatcher{m.UserCountriesTargeting}.matches(config))
 }
 
+// A higher number means a higher priority.
+// This order must be kept identical to bundletool's.
+var multiAbiPriorities = map[android_bundle_proto.Abi_AbiAlias]int{
+	android_bundle_proto.Abi_ARMEABI:     1,
+	android_bundle_proto.Abi_ARMEABI_V7A: 2,
+	android_bundle_proto.Abi_ARM64_V8A:   3,
+	android_bundle_proto.Abi_X86:         4,
+	android_bundle_proto.Abi_X86_64:      5,
+	android_bundle_proto.Abi_MIPS:        6,
+	android_bundle_proto.Abi_MIPS64:      7,
+}
+
 type multiAbiTargetingMatcher struct {
 	*android_bundle_proto.MultiAbiTargeting
 }
 
-func (t multiAbiTargetingMatcher) matches(_ TargetConfig) bool {
+func (t multiAbiTargetingMatcher) matches(config TargetConfig) bool {
 	if t.MultiAbiTargeting == nil {
 		return true
 	}
-	log.Fatal("multiABI based selection is not implemented")
-	return false
+	if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
+		return true
+	}
+	// Find the one with the highest priority.
+	highestPriority := 0
+	for _, v := range t.GetValue() {
+		for _, a := range v.GetAbi() {
+			if _, ok := config.abis[a.Alias]; ok {
+				if highestPriority < multiAbiPriorities[a.Alias] {
+					highestPriority = multiAbiPriorities[a.Alias]
+				}
+			}
+		}
+	}
+	if highestPriority == 0 {
+		return false
+	}
+	// See if there are any matching alternatives with a higher priority.
+	for _, v := range t.GetAlternatives() {
+		for _, a := range v.GetAbi() {
+			if _, ok := config.abis[a.Alias]; ok {
+				if highestPriority < multiAbiPriorities[a.Alias] {
+					// There's a better one. Skip this one.
+					return false
+				}
+			}
+		}
+	}
+	return true
 }
 
 type screenDensityTargetingMatcher struct {
@@ -349,13 +407,28 @@
 	return nil
 }
 
+func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error {
+	if len(selected.entries) != 1 {
+		return fmt.Errorf("Too many matching entries for extract-single:\n%v", selected.entries)
+	}
+	apk, ok := apkSet.entries[selected.entries[0]]
+	if !ok {
+		return fmt.Errorf("Couldn't find apk path %s", selected.entries[0])
+	}
+	inputReader, _ := apk.Open()
+	_, err := io.Copy(outFile, inputReader)
+	return err
+}
+
 // Arguments parsing
 var (
-	outputZip    = flag.String("o", "", "output zip containing extracted entries")
+	outputFile   = flag.String("o", "", "output file containing extracted entries")
 	targetConfig = TargetConfig{
 		screenDpi: map[android_bundle_proto.ScreenDensity_DensityAlias]bool{},
-		abis:      map[android_bundle_proto.Abi_AbiAlias]bool{},
+		abis:      map[android_bundle_proto.Abi_AbiAlias]int{},
 	}
+	extractSingle = flag.Bool("extract-single", false,
+		"extract a single target and output it uncompressed. only available for standalone apks and apexes.")
 )
 
 // Parse abi values
@@ -368,19 +441,12 @@
 }
 
 func (a abiFlagValue) Set(abiList string) error {
-	if abiList == "none" {
-		return nil
-	}
-	if abiList == "all" {
-		targetConfig.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE] = true
-		return nil
-	}
-	for _, abi := range strings.Split(abiList, ",") {
+	for i, abi := range strings.Split(abiList, ",") {
 		v, ok := android_bundle_proto.Abi_AbiAlias_value[abi]
 		if !ok {
 			return fmt.Errorf("bad ABI value: %q", abi)
 		}
-		targetConfig.abis[android_bundle_proto.Abi_AbiAlias(v)] = true
+		targetConfig.abis[android_bundle_proto.Abi_AbiAlias(v)] = i
 	}
 	return nil
 }
@@ -414,20 +480,21 @@
 
 func processArgs() {
 	flag.Usage = func() {
-		fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-zip> -sdk-version value -abis value -screen-densities value  <APK set>`)
+		fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+
+			`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] <APK set>`)
 		flag.PrintDefaults()
 		os.Exit(2)
 	}
 	version := flag.Uint("sdk-version", 0, "SDK version")
 	flag.Var(abiFlagValue{&targetConfig}, "abis",
-		"'all' or comma-separated ABIs list of ARMEABI ARMEABI_V7A ARM64_V8A X86 X86_64 MIPS MIPS64")
+		"comma-separated ABIs list of ARMEABI ARMEABI_V7A ARM64_V8A X86 X86_64 MIPS MIPS64")
 	flag.Var(screenDensityFlagValue{&targetConfig}, "screen-densities",
 		"'all' or comma-separated list of screen density names (NODPI LDPI MDPI TVDPI HDPI XHDPI XXHDPI XXXHDPI)")
 	flag.BoolVar(&targetConfig.allowPrereleased, "allow-prereleased", false,
 		"allow prereleased")
-	flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name")
+	flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
 	flag.Parse()
-	if (*outputZip == "") || len(flag.Args()) != 1 || *version == 0 || targetConfig.stem == "" {
+	if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 || (targetConfig.stem == "" && !*extractSingle) {
 		flag.Usage()
 	}
 	targetConfig.sdkVersion = int32(*version)
@@ -450,18 +517,24 @@
 		log.Fatalf("there are no entries for the target configuration: %#v", targetConfig)
 	}
 
-	outFile, err := os.Create(*outputZip)
+	outFile, err := os.Create(*outputFile)
 	if err != nil {
 		log.Fatal(err)
 	}
 	defer outFile.Close()
-	writer := zip.NewWriter(outFile)
-	defer func() {
-		if err := writer.Close(); err != nil {
-			log.Fatal(err)
-		}
-	}()
-	if err = apkSet.writeApks(sel, targetConfig, writer); err != nil {
+
+	if *extractSingle {
+		err = apkSet.extractAndCopySingle(sel, outFile)
+	} else {
+		writer := zip.NewWriter(outFile)
+		defer func() {
+			if err := writer.Close(); err != nil {
+				log.Fatal(err)
+			}
+		}()
+		err = apkSet.writeApks(sel, targetConfig, writer)
+	}
+	if err != nil {
 		log.Fatal(err)
 	}
 }
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
index 1d7726b..bc4d377 100644
--- a/cmd/extract_apks/main_test.go
+++ b/cmd/extract_apks/main_test.go
@@ -35,20 +35,20 @@
 	configs   []TestConfigDesc
 }
 
-var (
-	testCases = []TestDesc{
+func TestSelectApks_ApkSet(t *testing.T) {
+	testCases := []TestDesc{
 		{
 			protoText: `
 variant {
   targeting {
-    sdk_version_targeting { 
+    sdk_version_targeting {
       value { min { value: 29 } } } }
   apk_set {
-    module_metadata { 
+    module_metadata {
       name: "base" targeting {} delivery_type: INSTALL_TIME }
     apk_description {
       targeting {
-        screen_density_targeting { 
+        screen_density_targeting {
           value { density_alias: LDPI } }
         sdk_version_targeting {
           value { min { value: 21 } } } }
@@ -71,7 +71,10 @@
     apk_description {
       targeting {
         abi_targeting {
-	      value { alias: ARMEABI_V7A } }
+          value { alias: ARMEABI_V7A }
+          alternatives { alias: ARM64_V8A }
+          alternatives { alias: X86 }
+          alternatives { alias: X86_64 } }
         sdk_version_targeting {
           value { min { value: 21 } } } }
       path: "splits/base-armeabi_v7a.apk"
@@ -79,7 +82,10 @@
     apk_description {
       targeting {
         abi_targeting {
-          value { alias: ARM64_V8A } }
+          value { alias: ARM64_V8A }
+          alternatives { alias: ARMEABI_V7A }
+          alternatives { alias: X86 }
+          alternatives { alias: X86_64 } }
         sdk_version_targeting {
           value { min { value: 21 } } } }
       path: "splits/base-arm64_v8a.apk"
@@ -87,7 +93,10 @@
     apk_description {
       targeting {
         abi_targeting {
-          value { alias: X86 } }
+          value { alias: X86 }
+          alternatives { alias: ARMEABI_V7A }
+          alternatives { alias: ARM64_V8A }
+          alternatives { alias: X86_64 } }
         sdk_version_targeting {
           value { min { value: 21 } } } }
       path: "splits/base-x86.apk"
@@ -95,7 +104,10 @@
     apk_description {
       targeting {
         abi_targeting {
-          value { alias: X86_64 } }
+          value { alias: X86_64 }
+          alternatives { alias: ARMEABI_V7A }
+          alternatives { alias: ARM64_V8A }
+          alternatives { alias: X86 } }
         sdk_version_targeting {
           value { min { value: 21 } } } }
       path: "splits/base-x86_64.apk"
@@ -113,9 +125,9 @@
 						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
 							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
 						},
-						abis: map[bp.Abi_AbiAlias]bool{
-							bp.Abi_ARMEABI_V7A: true,
-							bp.Abi_ARM64_V8A:   true,
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARMEABI_V7A: 0,
+							bp.Abi_ARM64_V8A:   1,
 						},
 					},
 					expected: SelectionResult{
@@ -125,7 +137,6 @@
 							"splits/base-mdpi.apk",
 							"splits/base-master.apk",
 							"splits/base-armeabi_v7a.apk",
-							"splits/base-arm64_v8a.apk",
 						},
 					},
 				},
@@ -136,7 +147,7 @@
 						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
 							bp.ScreenDensity_LDPI: true,
 						},
-						abis: map[bp.Abi_AbiAlias]bool{},
+						abis: map[bp.Abi_AbiAlias]int{},
 					},
 					expected: SelectionResult{
 						"base",
@@ -153,23 +164,44 @@
 						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
 							bp.ScreenDensity_LDPI: true,
 						},
-						abis: map[bp.Abi_AbiAlias]bool{},
+						abis: map[bp.Abi_AbiAlias]int{},
 					},
 					expected: SelectionResult{
 						"",
 						nil,
 					},
 				},
+				{
+					name: "four",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_MDPI: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARM64_V8A:   0,
+							bp.Abi_ARMEABI_V7A: 1,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"splits/base-mdpi.apk",
+							"splits/base-master.apk",
+							"splits/base-arm64_v8a.apk",
+						},
+					},
+				},
 			},
 		},
 		{
 			protoText: `
 variant {
   targeting {
-    sdk_version_targeting { 
+    sdk_version_targeting {
       value { min { value: 10000 } } } }
   apk_set {
-    module_metadata { 
+    module_metadata {
       name: "base" targeting {} delivery_type: INSTALL_TIME }
     apk_description {
       targeting {
@@ -183,7 +215,7 @@
 					targetConfig: TargetConfig{
 						sdkVersion:       30,
 						screenDpi:        map[bp.ScreenDensity_DensityAlias]bool{},
-						abis:             map[bp.Abi_AbiAlias]bool{},
+						abis:             map[bp.Abi_AbiAlias]int{},
 						allowPrereleased: true,
 					},
 					expected: SelectionResult{
@@ -194,9 +226,160 @@
 			},
 		},
 	}
-)
+	for _, testCase := range testCases {
+		var toc bp.BuildApksResult
+		if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
+			t.Fatal(err)
+		}
+		for _, config := range testCase.configs {
+			actual := selectApks(&toc, config.targetConfig)
+			if !reflect.DeepEqual(config.expected, actual) {
+				t.Errorf("%s: expected %v, got %v", config.name, config.expected, actual)
+			}
+		}
+	}
+}
 
-func TestSelectApks(t *testing.T) {
+func TestSelectApks_ApexSet(t *testing.T) {
+	testCases := []TestDesc{
+		{
+			protoText: `
+variant {
+  targeting {
+    sdk_version_targeting {
+      value { min { value: 29 } } } }
+  apk_set {
+    module_metadata {
+      name: "base" targeting {} delivery_type: INSTALL_TIME }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: X86 } }
+          alternatives { abi { alias: X86_64 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-armeabi_v7a.apex"
+      apex_apk_metadata { } }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: X86 } }
+          alternatives { abi { alias: X86_64 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-arm64_v8a.apex"
+      apex_apk_metadata { } }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: X86 } }
+          alternatives { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: X86_64 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-x86.apex"
+      apex_apk_metadata { } }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: X86_64 } }
+          alternatives { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: X86 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-x86_64.apex"
+      apex_apk_metadata { } } }
+}
+bundletool {
+  version: "0.10.3" }
+
+`,
+			configs: []TestConfigDesc{
+				{
+					name: "order matches priorities",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARM64_V8A:   0,
+							bp.Abi_ARMEABI_V7A: 1,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-arm64_v8a.apex",
+						},
+					},
+				},
+				{
+					name: "order doesn't match priorities",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARMEABI_V7A: 0,
+							bp.Abi_ARM64_V8A:   1,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-arm64_v8a.apex",
+						},
+					},
+				},
+				{
+					name: "single choice",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARMEABI_V7A: 0,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-armeabi_v7a.apex",
+						},
+					},
+				},
+				{
+					name: "cross platform",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARM64_V8A: 0,
+							bp.Abi_MIPS64:    1,
+							bp.Abi_X86:       2,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-x86.apex",
+						},
+					},
+				},
+			},
+		},
+	}
 	for _, testCase := range testCases {
 		var toc bp.BuildApksResult
 		if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
diff --git a/cmd/path_interposer/main.go b/cmd/path_interposer/main.go
index cd28b96..a4fe3e4 100644
--- a/cmd/path_interposer/main.go
+++ b/cmd/path_interposer/main.go
@@ -53,14 +53,7 @@
 		os.Exit(1)
 	}
 
-	disableError := false
-	if e, ok := os.LookupEnv("TEMPORARY_DISABLE_PATH_RESTRICTIONS"); ok {
-		disableError = e == "1" || e == "y" || e == "yes" || e == "on" || e == "true"
-	}
-
 	exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{
-		disableError: disableError,
-
 		sendLog:       paths.SendLog,
 		config:        paths.GetConfig,
 		lookupParents: lookupParents,
@@ -79,8 +72,6 @@
 socket at <interposer>_log.`)
 
 type mainOpts struct {
-	disableError bool
-
 	sendLog       func(logSocket string, entry *paths.LogEntry, done chan interface{})
 	config        func(name string) paths.PathConfig
 	lookupParents func() []paths.LogProcess
@@ -131,7 +122,7 @@
 			}, waitForLog)
 			defer func() { <-waitForLog }()
 		}
-		if config.Error && !opts.disableError {
+		if config.Error {
 			return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.", base)
 		}
 	}
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 2536a53..6b79823 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -17,9 +17,11 @@
     deps: [
         "blueprint",
         "blueprint-bootstrap",
+        "golang-protobuf-proto",
         "soong",
         "soong-android",
         "soong-env",
+        "soong-ui-metrics_proto",
     ],
     srcs: [
         "main.go",
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 30381e0..bd1a9ed 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -131,4 +131,15 @@
 			os.Exit(1)
 		}
 	}
+
+	// TODO(ccross): make this a command line argument.  Requires plumbing through blueprint
+	//  to affect the command line of the primary builder.
+	if docFile == "" {
+		metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb")
+		err = android.WriteMetrics(configuration, metricsFile)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
+			os.Exit(1)
+		}
+	}
 }
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index db61fba..1d94f02 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -173,6 +173,7 @@
 	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
 	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"build_error")))
 	stat.AddOutput(status.NewCriticalPath(log))
+	stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
 
 	buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
 	buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
diff --git a/cuj/cuj.go b/cuj/cuj.go
index c7ff8ff..3333012 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -33,8 +33,9 @@
 )
 
 type Test struct {
-	name string
-	args []string
+	name   string
+	args   []string
+	before func() error
 
 	results TestResults
 }
@@ -119,6 +120,15 @@
 	t.results.metrics = met
 }
 
+// Touch the Intent.java file to cause a rebuild of the frameworks to monitor the
+// incremental build speed as mentioned b/152046247. Intent.java file was chosen
+// as it is a key component of the framework and is often modified.
+func touchIntentFile() error {
+	const intentFileName = "frameworks/base/core/java/android/content/Intent.java"
+	currentTime := time.Now().Local()
+	return os.Chtimes(intentFileName, currentTime, currentTime)
+}
+
 func main() {
 	outDir := os.Getenv("OUT_DIR")
 	if outDir == "" {
@@ -170,6 +180,36 @@
 			name: "framework_rebuild_twice",
 			args: []string{"framework"},
 		},
+		{
+			// Scenario major_inc_build (b/152046247): tracking build speed of major incremental build.
+			name: "major_inc_build_droid",
+			args: []string{"droid"},
+		},
+		{
+			name:   "major_inc_build_framework_minus_apex_after_droid_build",
+			args:   []string{"framework-minus-apex"},
+			before: touchIntentFile,
+		},
+		{
+			name:   "major_inc_build_framework_after_droid_build",
+			args:   []string{"framework"},
+			before: touchIntentFile,
+		},
+		{
+			name:   "major_inc_build_sync_after_droid_build",
+			args:   []string{"sync"},
+			before: touchIntentFile,
+		},
+		{
+			name:   "major_inc_build_droid_rebuild",
+			args:   []string{"droid"},
+			before: touchIntentFile,
+		},
+		{
+			name:   "major_inc_build_update_api_after_droid_rebuild",
+			args:   []string{"update-api"},
+			before: touchIntentFile,
+		},
 	}
 
 	cujMetrics := metrics.NewCriticalUserJourneysMetrics()
@@ -178,6 +218,12 @@
 	for i, t := range tests {
 		logsSubDir := fmt.Sprintf("%02d_%s", i, t.name)
 		logsDir := filepath.Join(cujDir, "logs", logsSubDir)
+		if t.before != nil {
+			if err := t.before(); err != nil {
+				fmt.Printf("error running before function on test %q: %v\n", t.name, err)
+				break
+			}
+		}
 		t.Run(logsDir)
 		if t.results.err != nil {
 			fmt.Printf("error running test %q: %s\n", t.name, t.results.err)
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index f984966..f1dde9c 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -82,7 +82,7 @@
 
 	if !dexpreoptDisabled(ctx, global, module) {
 		// Don't preopt individual boot jars, they will be preopted together.
-		if !contains(global.BootJars, module.Name) {
+		if !contains(android.GetJarsFromApexJarPairs(global.BootJars), module.Name) {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
@@ -113,7 +113,7 @@
 	// Also preopt system server jars since selinux prevents system server from loading anything from
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
 	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
-	if global.OnlyPreoptBootImageAndSystemServer && !contains(global.BootJars, module.Name) &&
+	if global.OnlyPreoptBootImageAndSystemServer && !contains(android.GetJarsFromApexJarPairs(global.BootJars), module.Name) &&
 		!contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
@@ -566,15 +566,6 @@
 	return filepath.Join("/apex", apex, "javalib", jar+".jar")
 }
 
-func GetJarsFromApexJarPairs(apexJarPairs []string) []string {
-	modules := make([]string, len(apexJarPairs))
-	for i, p := range apexJarPairs {
-		_, jar := android.SplitApexJarPair(p)
-		modules[i] = jar
-	}
-	return modules
-}
-
 var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars")
 
 // TODO: eliminate the superficial global config parameter by moving global config definition
@@ -582,7 +573,7 @@
 func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
 	return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
 		return android.RemoveListFromList(global.SystemServerJars,
-			GetJarsFromApexJarPairs(global.UpdatableSystemServerJars))
+			android.GetJarsFromApexJarPairs(global.UpdatableSystemServerJars))
 	}).([]string)
 }
 
diff --git a/doc.go b/doc.go
index 543c460..299fd2b 100644
--- a/doc.go
+++ b/doc.go
@@ -46,8 +46,8 @@
 //
 // Target architecture
 // The target architecture is the preferred architecture supported by the selected
-// device.  It is most commonly 32-bit arm, but may also be 64-bit arm, 32-bit or
-// 64-bit x86, or mips.
+// device.  It is most commonly 32-bit arm, but may also be 64-bit arm, 32-bit
+// x86, or 64-bit x86.
 //
 // Secondary architecture
 // The secondary architecture specifies the architecture to compile a second copy
diff --git a/docs/map_files.md b/docs/map_files.md
new file mode 100644
index 0000000..9fc0d14
--- /dev/null
+++ b/docs/map_files.md
@@ -0,0 +1,174 @@
+# Native API Map Files
+
+Native APIs such as those exposed by the NDK, LL-NDK, or APEX are described by
+map.txt files. These files are [linker version scripts] with comments that are
+semantically meaningful to [gen_stub_libs.py]. For an example of a map file, see
+[libc.map.txt].
+
+[gen_stub_libs.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/gen_stub_libs.py
+[libc.map.txt]: https://cs.android.com/android/platform/superproject/+/master:bionic/libc/libc.map.txt
+[linker version scripts]: https://www.gnu.org/software/gnulib/manual/html_node/LD-Version-Scripts.html
+
+## Basic format
+
+A linker version script defines at least one alphanumeric "version" definition,
+each of which contain a list of symbols. For example:
+
+```txt
+MY_API_R { # introduced=R
+  global:
+    api_foo;
+    api_bar;
+  local:
+    *;
+};
+
+MY_API_S { # introduced=S
+  global:
+    api_baz;
+} MY_API_R;
+```
+
+Comments on the same line as either a version definition or a symbol name have
+meaning. If you need to add any comments that should not be interpreted by the
+stub generator, keep them on their own line. For a list of supported comments,
+see the "Tags" section.
+
+Here, `api_foo` and `api_bar` are exposed in the generated stubs with the
+`MY_API_R` version and `api_baz` is exposed with the `MY_API_S` version. No
+other symbols are defined as public by this API. `MY_API_S` inherits all symbols
+defined by `MY_API_R`.
+
+When generating NDK API stubs from this version script, the stub library for R
+will define `api_foo` and `api_bar`. The stub library for S will define all
+three APIs.
+
+Note that, with few exceptions (see "Special version names" below), the name of
+the version has no inherent meaning.
+
+These map files can (and should) also be used as version scripts for building
+the implementation library rather than just defining the stub interface by using
+the `version_script` property of `cc_library`. This has the effect of limiting
+symbol visibility of the library to expose only the interface named by the map
+file. Without this, APIs that you have not explicitly exposed will still be
+available to users via `dlsym`. Note: All comments are ignored in this case. Any
+symbol named in any `global:` group will be visible.
+
+## Special version names
+
+Version names that end with `_PRIVATE` or `_PLATFORM` will not be exposed in any
+stubs, but will be exposed in the implementation library. Using either of these
+naming schemes is equivalent to marking the version with the `platform-only`
+tag. See the docs for `platform-only` for more information.
+
+## Tags
+
+Comments on the same line as a version definition or a symbol name are
+interpreted by the stub generator. Multiple space-delimited tags may be used on
+the same line. The supported tags are:
+
+### apex
+
+Indicates that the version or symbol is to be exposed in the APEX stubs rather
+than the NDK. May be used in combination with `llndk` if the symbol is exposed
+to both APEX and the LL-NDK.
+
+### future
+
+Indicates that the version or symbol is first introduced in the "future" API
+level. This is an abitrarily high API level used to define APIs that have not
+yet been added to a specific release.
+
+### introduced
+
+Indicates the version in which an API was first introduced. For example,
+`introduced=21` specifies that the API was first added (or first made public) in
+API level 21. This tag can be applied to either a version definition or an
+individual symbol. If applied to a version, all symbols contained in the version
+will have the tag applied. An `introduced` tag on a symbol overrides the value
+set for the version, if both are defined.
+
+Note: The map file alone does not contain all the information needed to
+determine which API level an API was added in. The `first_version` property of
+`ndk_library` will dictate which API levels stubs are generated for. If the
+module sets `first_version: "21"`, no symbols were introduced before API 21.
+
+Codenames can (and typically should) be used when defining new APIs. This allows
+the actual number of the API level to remain vague during development of that
+release. For example, `introduced=S` can be used to define APIs added in S. Any
+code name known to the build system can be used. For a list of versions known to
+the build system, see `out/soong/api_levels.json` (if not present, run `m
+out/soong/api_levels.json` to generate it).
+
+Architecture-specific variants of this tag exist:
+
+* `introduced-arm=VERSION`
+* `introduced-arm64=VERSION`
+* `introduced-x86=VERSION`
+* `introduced-x86_64=VERSION`
+
+The architecture-specific tag will take precedence over the architecture-generic
+tag when generating stubs for that architecture if both are present. If the
+symbol is defined with only architecture-specific tags, it will not be present
+for architectures that are not named.
+
+Note: The architecture-specific tags should, in general, not be used. These are
+primarily needed for APIs that were wrongly inconsistently exposed by libc/libm
+in old versions of Android before the stubs were well maintained. Think hard
+before using an architecture-specific tag for a new API.
+
+### llndk
+
+Indicates that the version or symbol is to be exposed in the LL-NDK stubs rather
+than the NDK. May be used in combination with `apex` if the symbol is exposed to
+both APEX and the LL-NDK.
+
+### platform-only
+
+Indicates that the version or symbol is public in the implementation library but
+should not be exposed in the stub library. Developers can still access them via
+`dlsym`, but they will not be exposed in the stubs so it should at least be
+clear to the developer that they are up to no good.
+
+The typical use for this tag is for exposing an API to the platform that is not
+for use by the NDK, LL-NDK, or APEX. It is preferable to keep such APIs in an
+entirely separate library to protect them from access via `dlsym`, but this is
+not always possible.
+
+### var
+
+Used to define a public global variable. By default all symbols are exposed as
+functions. In the uncommon situation of exposing a global variable, the `var`
+tag may be used.
+
+### versioned=VERSION
+
+Behaves similarly to `introduced` but defines the first version that the stub
+library should apply symbol versioning. For example:
+
+```txt
+R { # introduced=R
+  global:
+    foo;
+    bar; # versioned=S
+  local:
+    *;
+};
+```
+
+The stub library for R will contain symbols for both `foo` and `bar`, but only
+`foo` will include a versioned symbol `foo@R`. The stub library for S will
+contain both symbols, as well as the versioned symbols `foo@R` and `bar@R`.
+
+This tag is not commonly needed and is only used to hide symbol versioning
+mistakes that shipped as part of the platform.
+
+Note: Like `introduced`, the map file does not tell the whole story. The
+`ndk_library` Soong module may define a `unversioned_until` property that sets
+the default for the entire map file.
+
+### weak
+
+Indicates that the symbol should be [weak] in the stub library.
+
+[weak]: https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html
diff --git a/java/androidmk.go b/java/androidmk.go
index 41a3dc7..ba1af0d 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -626,6 +626,11 @@
 }
 
 func (a *AndroidAppImport) AndroidMkEntries() []android.AndroidMkEntries {
+	if !a.IsForPlatform() {
+		// The non-platform variant is placed inside APEX. No reason to
+		// make it available to Make.
+		return nil
+	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "APPS",
 		OutputFile: android.OptionalPathForPath(a.outputFile),
diff --git a/java/app.go b/java/app.go
index 9a922f3..7faa666 100755
--- a/java/app.go
+++ b/java/app.go
@@ -96,16 +96,16 @@
 	return Bool(as.properties.Privileged)
 }
 
-var targetCpuAbi = map[string]string{
+var TargetCpuAbi = map[string]string{
 	"arm":    "ARMEABI_V7A",
 	"arm64":  "ARM64_V8A",
 	"x86":    "X86",
 	"x86_64": "X86_64",
 }
 
-func supportedAbis(ctx android.ModuleContext) []string {
+func SupportedAbis(ctx android.ModuleContext) []string {
 	abiName := func(archVar string, deviceArch string) string {
-		if abi, found := targetCpuAbi[deviceArch]; found {
+		if abi, found := TargetCpuAbi[deviceArch]; found {
 			return abi
 		}
 		ctx.ModuleErrorf("Invalid %s: %s", archVar, deviceArch)
@@ -138,7 +138,7 @@
 			Output:      as.packedOutput,
 			Inputs:      android.Paths{as.prebuilt.SingleSourcePath(ctx)},
 			Args: map[string]string{
-				"abis":              strings.Join(supportedAbis(ctx), ","),
+				"abis":              strings.Join(SupportedAbis(ctx), ","),
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
 				"screen-densities":  screenDensities,
 				"sdk-version":       ctx.Config().PlatformSdkVersion(),
@@ -414,19 +414,47 @@
 }
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
-	if Bool(a.appProperties.Updatable) {
+	if Bool(a.appProperties.Updatable) || a.ApexModuleBase.Updatable() {
 		if !a.sdkVersion().stable() {
 			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.sdkVersion())
 		}
 		if String(a.deviceProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
+		if minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx); err == nil {
+			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
+		} else {
+			ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
+		}
 	}
 
 	a.checkPlatformAPI(ctx)
 	a.checkSdkVersions(ctx)
 }
 
+// If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it.
+// This check is enforced for "updatable" APKs (including APK-in-APEX).
+// b/155209650: until min_sdk_version is properly supported, use sdk_version instead.
+// because, sdk_version is overridden by min_sdk_version (if set as smaller)
+// and linkType is checked with dependencies so we can be sure that the whole dependency tree
+// will meet the requirements.
+func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion sdkVersion) {
+	// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if !IsJniDepTag(ctx.OtherModuleDependencyTag(m)) {
+			return
+		}
+		dep, _ := m.(*cc.Module)
+		jniSdkVersion, err := android.ApiStrToNum(ctx, dep.SdkVersion())
+		if err != nil || int(minSdkVersion) < jniSdkVersion {
+			ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
+				dep.SdkVersion(), minSdkVersion, ctx.ModuleName())
+			return
+		}
+
+	})
+}
+
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
@@ -603,16 +631,20 @@
 			return false
 		}
 
-		path := child.(android.Module).NoticeFile()
-		if path.Valid() {
-			noticePathSet[path.Path()] = true
+		paths := child.(android.Module).NoticeFiles()
+		if len(paths) > 0 {
+			for _, path := range paths {
+				noticePathSet[path] = true
+			}
 		}
 		return true
 	})
 
 	// If the app has one, add it too.
-	if a.NoticeFile().Valid() {
-		noticePathSet[a.NoticeFile().Path()] = true
+	if len(a.NoticeFiles()) > 0 {
+		for _, path := range a.NoticeFiles() {
+			noticePathSet[path] = true
+		}
 	}
 
 	if len(noticePathSet) == 0 {
@@ -923,6 +955,8 @@
 	a.appProperties.IsCoverageVariant = coverage
 }
 
+func (a *AndroidApp) EnableCoverageIfNeeded() {}
+
 var _ cc.Coverage = (*AndroidApp)(nil)
 
 // android_app compiles sources and Android resources into an Android application package `.apk` file.
@@ -958,6 +992,7 @@
 }
 
 type appTestProperties struct {
+	// The name of the android_app module that the tests will run against.
 	Instrumentation_for *string
 
 	// if specified, the instrumentation target package name in the manifest is overwritten by it.
@@ -1217,6 +1252,7 @@
 type AndroidAppImport struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.ApexModuleBase
 	prebuilt android.Prebuilt
 
 	properties   AndroidAppImportProperties
@@ -1454,7 +1490,9 @@
 
 	// TODO: Optionally compress the output apk.
 
-	a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
+	if a.IsForPlatform() {
+		a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
+	}
 
 	// TODO: androidmk converter jni libs
 }
@@ -1505,6 +1543,13 @@
 	return Bool(a.properties.Privileged)
 }
 
+func (a *AndroidAppImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	// android_app_import might have extra dependencies via uses_libs property.
+	// Don't track the dependency as we don't automatically add those libraries
+	// to the classpath. It should be explicitly added to java_libs property of APEX
+	return false
+}
+
 func (a *AndroidAppImport) sdkVersion() sdkSpec {
 	return sdkSpecFrom("")
 }
@@ -1559,6 +1604,7 @@
 		module.processVariants(ctx)
 	})
 
+	android.InitApexModule(module)
 	InitJavaModule(module, android.DeviceSupported)
 	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
 
@@ -1598,6 +1644,7 @@
 
 	module.dexpreopter.isTest = true
 
+	android.InitApexModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
@@ -1651,6 +1698,15 @@
 	Overrides []string
 }
 
+// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
+// a RuntimeResourceOverlay module.
+type RuntimeResourceOverlayModule interface {
+	android.Module
+	OutputFile() android.Path
+	Certificate() Certificate
+	Theme() string
+}
+
 func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
 	sdkDep := decodeSdkDep(ctx, sdkContext(r))
 	if sdkDep.hasFrameworkLibs() {
@@ -1717,6 +1773,18 @@
 	return r.sdkVersion()
 }
 
+func (r *RuntimeResourceOverlay) Certificate() Certificate {
+	return r.certificate
+}
+
+func (r *RuntimeResourceOverlay) OutputFile() android.Path {
+	return r.outputFile
+}
+
+func (r *RuntimeResourceOverlay) Theme() string {
+	return String(r.properties.Theme)
+}
+
 // runtime_resource_overlay generates a resource-only apk file that can overlay application and
 // system resources at run time.
 func RuntimeResourceOverlayFactory() android.Module {
diff --git a/java/app_test.go b/java/app_test.go
index 80f4567..b4e4fee 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -473,6 +473,127 @@
 	}
 }
 
+func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
+	testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "current",
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "current",
+		}
+	`)
+}
+
+func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "29",
+		}
+
+		ndk_prebuilt_object {
+			name: "ndk_crtbegin_so.29",
+			sdk_version: "29",
+		}
+
+		ndk_prebuilt_object {
+			name: "ndk_crtend_so.29",
+			sdk_version: "29",
+		}
+	`
+	fs := map[string][]byte{
+		"prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o": nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":   nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtbegin_so.o":   nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o":     nil,
+	}
+
+	ctx, _ := testJavaWithConfig(t, testConfig(nil, bp, fs))
+
+	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
+	var crtbeginFound, crtendFound bool
+	for _, input := range inputs {
+		switch input.String() {
+		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o":
+			crtbeginFound = true
+		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":
+			crtendFound = true
+		}
+	}
+	if !crtbeginFound || !crtendFound {
+		t.Error("should link with ndk_crtbegin_so.29 and ndk_crtend_so.29")
+	}
+}
+
+func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",  // this APK should support 29
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			sdk_version: "current",
+		}
+	`
+	testJavaError(t, `"libjni" .*: sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp)
+}
+
+func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",  // this APK should support 29
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			sdk_version: "27",
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "current",
+		}
+	`
+	testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp)
+}
+
 func TestResourceDirs(t *testing.T) {
 	testCases := []struct {
 		name      string
diff --git a/java/builder.go b/java/builder.go
index 714d76a..b834029 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/remoteexec"
 )
 
 var (
@@ -38,12 +39,13 @@
 	// this, all java rules write into separate directories and then are combined into a .jar file
 	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
 	// .srcjar files are unzipped into a temporary directory when compiled with javac.
-	javac = pctx.AndroidRemoteStaticRule("javac", android.RemoteRuleSupports{Goma: true, RBE: true, RBEFlag: android.RBE_JAVAC},
+	// TODO(b/143658984): goma can't handle the --system argument to javac.
+	javac, javacRE = remoteexec.StaticRules(pctx, "javac",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
 				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
-				`${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ` +
+				`${config.SoongJavacWrapper} $reTemplate${config.JavacCmd} ` +
 				`${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
 				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
@@ -58,9 +60,12 @@
 			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
 			Rspfile:          "$out.rsp",
 			RspfileContent:   "$in",
-		},
-		"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
-		"outDir", "annoDir", "javaVersion")
+		}, &remoteexec.REParams{
+			Labels:       map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
+			ExecStrategy: "${config.REJavacExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
+			"outDir", "annoDir", "javaVersion"}, nil)
 
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
@@ -199,6 +204,7 @@
 func init() {
 	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/java/config")
+	pctx.Import("android/soong/remoteexec")
 }
 
 type javaBuilderFlags struct {
@@ -409,8 +415,12 @@
 		outDir = filepath.Join(shardDir, outDir)
 		annoDir = filepath.Join(shardDir, annoDir)
 	}
+	rule := javac
+	if ctx.Config().IsEnvTrue("RBE_JAVAC") {
+		rule = javacRE
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        javac,
+		Rule:        rule,
 		Description: desc,
 		Output:      outputFile,
 		Inputs:      srcFiles,
diff --git a/java/config/config.go b/java/config/config.go
index 9ac5a50..b25d14a 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -22,6 +22,7 @@
 	_ "github.com/google/blueprint/bootstrap"
 
 	"android/soong/android"
+	"android/soong/remoteexec"
 )
 
 var (
@@ -139,30 +140,16 @@
 	pctx.HostJavaToolVariable("MetalavaJar", "metalava.jar")
 	pctx.HostJavaToolVariable("DokkaJar", "dokka.jar")
 	pctx.HostJavaToolVariable("JetifierJar", "jetifier.jar")
+	pctx.HostJavaToolVariable("R8Jar", "r8-compat-proguard.jar")
+	pctx.HostJavaToolVariable("D8Jar", "d8.jar")
 
 	pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
 	pctx.HostBinToolVariable("DexpreoptGen", "dexpreopt_gen")
 
-	pctx.VariableFunc("JavacWrapper", func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv("JAVAC_WRAPPER"); override != "" {
-			return override + " "
-		}
-		return ""
-	})
-
-	pctx.VariableFunc("R8Wrapper", func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv("R8_WRAPPER"); override != "" {
-			return override + " "
-		}
-		return ""
-	})
-
-	pctx.VariableFunc("D8Wrapper", func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv("D8_WRAPPER"); override != "" {
-			return override + " "
-		}
-		return ""
-	})
+	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.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
 
diff --git a/java/dex.go b/java/dex.go
index 6afdb6d..27ec6ee 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -20,12 +20,13 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
+	"android/soong/remoteexec"
 )
 
-var d8 = pctx.AndroidRemoteStaticRule("d8", android.RemoteRuleSupports{RBE: true, RBEFlag: android.RBE_D8},
+var d8, d8RE = remoteexec.StaticRules(pctx, "d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`${config.D8Wrapper}${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+			`$reTemplate${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
 			`${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{
@@ -33,14 +34,19 @@
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
-	},
-	"outDir", "d8Flags", "zipFlags")
+	}, &remoteexec.REParams{
+		Labels:          map[string]string{"type": "compile", "compiler": "d8"},
+		Inputs:          []string{"${config.D8Jar}"},
+		ExecStrategy:    "${config.RED8ExecStrategy}",
+		ToolchainInputs: []string{"${config.JavaCmd}"},
+		Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+	}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
 
-var r8 = pctx.AndroidRemoteStaticRule("r8", android.RemoteRuleSupports{RBE: true, RBEFlag: android.RBE_R8},
+var r8, r8RE = remoteexec.StaticRules(pctx, "r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && ` +
-			`${config.R8Wrapper}${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
+			`$reTemplate${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
 			`--force-proguard-compatibility ` +
 			`--no-data-resources ` +
 			`-printmapping $outDict ` +
@@ -53,8 +59,13 @@
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
-	},
-	"outDir", "outDict", "r8Flags", "zipFlags")
+	}, &remoteexec.REParams{
+		Labels:          map[string]string{"type": "compile", "compiler": "r8"},
+		Inputs:          []string{"$implicits", "${config.R8Jar}"},
+		ExecStrategy:    "${config.RER8ExecStrategy}",
+		ToolchainInputs: []string{"${config.JavaCmd}"},
+		Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+	}, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
 
 func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
 	flags := j.deviceProperties.Dxflags
@@ -185,24 +196,34 @@
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		j.proguardDictionary = proguardDictionary
 		r8Flags, r8Deps := j.r8Flags(ctx, flags)
+		rule := r8
+		args := map[string]string{
+			"r8Flags":  strings.Join(r8Flags, " "),
+			"zipFlags": zipFlags,
+			"outDict":  j.proguardDictionary.String(),
+			"outDir":   outDir.String(),
+		}
+		if ctx.Config().IsEnvTrue("RBE_R8") {
+			rule = r8RE
+			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
+		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:           r8,
+			Rule:           rule,
 			Description:    "r8",
 			Output:         javalibJar,
 			ImplicitOutput: proguardDictionary,
 			Input:          classesJar,
 			Implicits:      r8Deps,
-			Args: map[string]string{
-				"r8Flags":  strings.Join(r8Flags, " "),
-				"zipFlags": zipFlags,
-				"outDict":  j.proguardDictionary.String(),
-				"outDir":   outDir.String(),
-			},
+			Args:           args,
 		})
 	} else {
 		d8Flags, d8Deps := j.d8Flags(ctx, flags)
+		rule := d8
+		if ctx.Config().IsEnvTrue("RBE_D8") {
+			rule = d8RE
+		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        d8,
+			Rule:        rule,
 			Description: "d8",
 			Output:      javalibJar,
 			Input:       classesJar,
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 28a2c8a..4725b07 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -131,7 +131,8 @@
 	global := dexpreopt.GetGlobalConfig(ctx)
 	bootImage := defaultBootImageConfig(ctx)
 	dexFiles := bootImage.dexPathsDeps.Paths()
-	dexLocations := bootImage.dexLocationsDeps
+	// The dex locations for all Android variants are identical.
+	dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
 	if global.UseArtImage {
 		bootImage = artBootImageConfig(ctx)
 	}
@@ -159,6 +160,8 @@
 		images = append(images, variant.images)
 		imagesDeps = append(imagesDeps, variant.imagesDeps)
 	}
+	// The image locations for all Android variants are identical.
+	imageLocations := bootImage.getAnyAndroidVariant().imageLocations()
 
 	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
 
@@ -202,7 +205,7 @@
 		Archs:                   archs,
 		DexPreoptImages:         images,
 		DexPreoptImagesDeps:     imagesDeps,
-		DexPreoptImageLocations: bootImage.imageLocations,
+		DexPreoptImageLocations: imageLocations,
 
 		PreoptBootClassPathDexFiles:     dexFiles,
 		PreoptBootClassPathDexLocations: dexLocations,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 1b26667..dffdc24 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -29,29 +29,10 @@
 	RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
 }
 
-// The image "location" is a symbolic path that with multiarchitecture
-// support doesn't really exist on the device. Typically it is
-// /system/framework/boot.art and should be the same for all supported
-// architectures on the device. The concrete architecture specific
-// content actually ends up in a "filename" that contains an
-// architecture specific directory name such as arm, arm64, mips,
-// mips64, x86, x86_64.
-//
-// Here are some example values for an x86_64 / x86 configuration:
-//
-// bootImages["x86_64"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86_64/boot.art"
-// dexpreopt.PathToLocation(bootImages["x86_64"], "x86_64") = "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art"
-//
-// bootImages["x86"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86/boot.art"
-// dexpreopt.PathToLocation(bootImages["x86"])= "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art"
-//
-// The location is passed as an argument to the ART tools like dex2oat instead of the real path. The ART tools
-// will then reconstruct the real path, so the rules must have a dependency on the real path.
-
 // Target-independent description of pre-compiled boot image.
 type bootImageConfig struct {
-	// Whether this image is an extension.
-	extension bool
+	// If this image is an extension, the image that it extends.
+	extends *bootImageConfig
 
 	// Image name (used in directory names and ninja rule names).
 	name string
@@ -71,17 +52,10 @@
 	// The names of jars that constitute this image.
 	modules []string
 
-	// The "locations" of jars.
-	dexLocations     []string // for this image
-	dexLocationsDeps []string // for the dependency images and in this image
-
 	// File paths to jars.
 	dexPaths     android.WritablePaths // for this image
 	dexPathsDeps android.WritablePaths // for the dependency images and in this image
 
-	// The "locations" of the dependency images and in this image.
-	imageLocations []string
-
 	// File path to a zip archive with all image files (or nil, if not needed).
 	zip android.WritablePath
 
@@ -99,6 +73,10 @@
 	// Target for which the image is generated.
 	target android.Target
 
+	// The "locations" of jars.
+	dexLocations     []string // for this image
+	dexLocationsDeps []string // for the dependency images and in this image
+
 	// Paths to image files.
 	images     android.OutputPath  // first image file
 	imagesDeps android.OutputPaths // all files
@@ -121,13 +99,23 @@
 	return nil
 }
 
+// Return any (the first) variant which is for the device (as opposed to for the host)
+func (image bootImageConfig) getAnyAndroidVariant() *bootImageVariant {
+	for _, variant := range image.variants {
+		if variant.target.Os == android.Android {
+			return variant
+		}
+	}
+	return nil
+}
+
 func (image bootImageConfig) moduleName(idx int) string {
 	// Dexpreopt on the boot class path produces multiple files. The first dex file
 	// is converted into 'name'.art (to match the legacy assumption that 'name'.art
 	// exists), and the rest are converted to 'name'-<jar>.art.
-	m := image.modules[idx]
+	_, m := android.SplitApexJarPair(image.modules[idx])
 	name := image.stem
-	if idx != 0 || image.extension {
+	if idx != 0 || image.extends != nil {
 		name += "-" + stemOf(m)
 	}
 	return name
@@ -152,6 +140,24 @@
 	return ret
 }
 
+// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
+// exist on the device. Typically it is /apex/com.android.art/javalib/boot.art and should be the
+// same for all supported architectures on the device. The concrete architecture specific files
+// actually end up in architecture-specific sub-directory such as arm, arm64, x86, or x86_64.
+//
+// For example a physical file
+// "/apex/com.android.art/javalib/x86/boot.art" has "image location"
+// "/apex/com.android.art/javalib/boot.art" (which is not an actual file).
+//
+// The location is passed as an argument to the ART tools like dex2oat instead of the real path.
+// ART tools will then reconstruct the architecture-specific real path.
+func (image *bootImageVariant) imageLocations() (imageLocations []string) {
+	if image.extends != nil {
+		imageLocations = image.extends.getVariant(image.target).imageLocations()
+	}
+	return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType))
+}
+
 func concat(lists ...[]string) []string {
 	var size int
 	for _, l := range lists {
@@ -181,11 +187,6 @@
 		return true
 	}
 
-	if len(ctx.Config().Targets[android.Android]) == 0 {
-		// Host-only build
-		return true
-	}
-
 	return false
 }
 
@@ -204,7 +205,10 @@
 	// Include dexpreopt files for the primary boot image.
 	files := map[android.ArchType]android.OutputPaths{}
 	for _, variant := range artBootImageConfig(ctx).variants {
-		files[variant.target.Arch.ArchType] = variant.imagesDeps
+		// We also generate boot images for host (for testing), but we don't need those in the apex.
+		if variant.target.Os == android.Android {
+			files[variant.target.Arch.ArchType] = variant.imagesDeps
+		}
 	}
 	return files
 }
@@ -257,34 +261,35 @@
 	}
 
 	name := ctx.ModuleName(module)
-	index := android.IndexList(name, image.modules)
+	index := android.IndexList(name, android.GetJarsFromApexJarPairs(image.modules))
 	if index == -1 {
 		return -1, nil
 	}
 
 	// Check that this module satisfies constraints for a particular boot image.
 	apex, isApexModule := module.(android.ApexModule)
+	fromUpdatableApex := isApexModule && apex.Updatable()
 	if image.name == artBootImageName {
 		if isApexModule && strings.HasPrefix(apex.ApexName(), "com.android.art.") {
-			// ok, found the jar in the ART apex
-		} else if isApexModule && !apex.IsForPlatform() {
-			// this jar is part of an updatable apex other than ART, fail immediately
-			ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the ART boot image", name, apex.ApexName())
+			// ok: found the jar in the ART apex
 		} else if isApexModule && apex.IsForPlatform() && Bool(module.(*Library).deviceProperties.Hostdex) {
-			// this is a special "hostdex" variant, skip it and resume search
+			// exception (skip and continue): special "hostdex" platform variant
 			return -1, nil
 		} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-			// this is Jacoco platform variant for a coverage build, skip it and resume search
+			// exception (skip and continue): Jacoco platform variant for a coverage build
 			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())
 		} else {
-			// this (installable) jar is part of the platform, fail immediately
-			ctx.Errorf("module '%s' is part of the platform and not allowed in the ART boot image", name)
+			// 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)
 		}
 	} else if image.name == frameworkBootImageName {
-		if !isApexModule || apex.IsForPlatform() {
-			// ok, this jar is part of the platform
+		if !fromUpdatableApex {
+			// ok: this jar is part of the platform or a non-updatable apex
 		} else {
-			// this jar is part of an updatable apex, fail immediately
+			// 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())
 		}
 	} else {
@@ -309,13 +314,13 @@
 	// Ensure all modules were converted to paths
 	for i := range bootDexJars {
 		if bootDexJars[i] == nil {
+			_, m := android.SplitApexJarPair(image.modules[i])
 			if ctx.Config().AllowMissingDependencies() {
-				missingDeps = append(missingDeps, image.modules[i])
+				missingDeps = append(missingDeps, m)
 				bootDexJars[i] = android.PathForOutput(ctx, "missing")
 			} else {
 				ctx.Errorf("failed to find a dex jar path for module '%s'"+
-					", note that some jars may be filtered out by module constraints",
-					image.modules[i])
+					", note that some jars may be filtered out by module constraints", m)
 			}
 		}
 	}
@@ -362,9 +367,10 @@
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	arch := image.target.Arch.ArchType
-	symbolsDir := image.symbolsDir.Join(ctx, image.installSubdir, arch.String())
+	os := image.target.Os.String() // We need to distinguish host-x86 and device-x86.
+	symbolsDir := image.symbolsDir.Join(ctx, os, image.installSubdir, arch.String())
 	symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
-	outputDir := image.dir.Join(ctx, image.installSubdir, arch.String())
+	outputDir := image.dir.Join(ctx, os, image.installSubdir, arch.String())
 	outputPath := outputDir.Join(ctx, image.stem+".oat")
 	oatLocation := dexpreopt.PathToLocation(outputPath, arch)
 	imagePath := outputPath.ReplaceExtension(ctx, "art")
@@ -410,7 +416,7 @@
 		cmd.FlagWithInput("--dirty-image-objects=", global.DirtyImageObjects.Path())
 	}
 
-	if image.extension {
+	if image.extends != nil {
 		artImage := image.primaryImages
 		cmd.
 			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
@@ -432,13 +438,18 @@
 		FlagWithArg("--oat-location=", oatLocation).
 		FlagWithArg("--image=", imagePath.String()).
 		FlagWithArg("--instruction-set=", arch.String()).
-		FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
-		FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
 		FlagWithArg("--android-root=", global.EmptyDirectory).
 		FlagWithArg("--no-inline-from=", "core-oj.jar").
 		Flag("--force-determinism").
 		Flag("--abort-on-hard-verifier-error")
 
+	// Use the default variant/features for host builds.
+	// The map below contains only device CPU info (which might be x86 on some devices).
+	if image.target.Os == android.Android {
+		cmd.FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch])
+		cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
+	}
+
 	if global.BootFlags != "" {
 		cmd.Flag(global.BootFlags)
 	}
@@ -450,7 +461,6 @@
 	cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
 
 	installDir := filepath.Join("/", image.installSubdir, arch.String())
-	vdexInstallDir := filepath.Join("/", image.installSubdir)
 
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
@@ -469,11 +479,10 @@
 		cmd.ImplicitOutput(vdex)
 		zipFiles = append(zipFiles, vdex)
 
-		// The vdex files are identical between architectures, install them to a shared location.  The Make rules will
-		// only use the install rules for one architecture, and will create symlinks into the architecture-specific
-		// directories.
+		// Note that the vdex files are identical between architectures.
+		// Make rules will create symlinks to share them between architectures.
 		vdexInstalls = append(vdexInstalls,
-			android.RuleBuilderInstall{vdex, filepath.Join(vdexInstallDir, vdex.Base())})
+			android.RuleBuilderInstall{vdex, filepath.Join(installDir, vdex.Base())})
 	}
 
 	for _, unstrippedOat := range image.moduleFiles(ctx, symbolsDir, ".oat") {
@@ -484,7 +493,7 @@
 			android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
 	}
 
-	rule.Build(pctx, ctx, image.name+"JarsDexpreopt_"+arch.String(), "dexpreopt "+image.name+" jars "+arch.String())
+	rule.Build(pctx, ctx, image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
 
 	// save output and installed files for makevars
 	image.installs = rule.Installs()
@@ -534,7 +543,7 @@
 			Tool(globalSoong.Profman).
 			FlagWithInput("--create-profile-from=", bootImageProfile).
 			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
-			FlagForEachArg("--dex-location=", image.dexLocationsDeps).
+			FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
 			FlagWithOutput("--reference-profile-file=", profile)
 
 		rule.Install(profile, "/system/etc/boot-image.prof")
@@ -585,7 +594,7 @@
 			Flag("--generate-boot-profile").
 			FlagWithInput("--create-profile-from=", bootFrameworkProfile).
 			FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
-			FlagForEachArg("--dex-location=", image.dexLocationsDeps).
+			FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
 			FlagWithOutput("--reference-profile-file=", profile)
 
 		rule.Install(profile, "/system/etc/boot-image.bprof")
@@ -605,7 +614,7 @@
 
 	return ctx.Config().Once(updatableBcpPackagesRuleKey, func() interface{} {
 		global := dexpreopt.GetGlobalConfig(ctx)
-		updatableModules := dexpreopt.GetJarsFromApexJarPairs(global.UpdatableBootJars)
+		updatableModules := android.GetJarsFromApexJarPairs(global.UpdatableBootJars)
 
 		// Collect `permitted_packages` for updatable boot jars.
 		var updatablePackages []string
@@ -657,27 +666,32 @@
 	var allPhonies android.Paths
 	for _, image := range image.variants {
 		arch := image.target.Arch.ArchType
+		suffix := arch.String()
+		// Host and target might both use x86 arch. We need to ensure the names are unique.
+		if image.target.Os.Class == android.Host {
+			suffix = "host-" + suffix
+		}
 		// Create a rule to call oatdump.
-		output := android.PathForOutput(ctx, "boot."+arch.String()+".oatdump.txt")
+		output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt")
 		rule := android.NewRuleBuilder()
 		rule.Command().
 			// TODO: for now, use the debug version for better error reporting
 			BuiltTool(ctx, "oatdumpd").
 			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
 			FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
-			FlagWithArg("--image=", strings.Join(image.imageLocations, ":")).Implicits(image.imagesDeps.Paths()).
+			FlagWithArg("--image=", strings.Join(image.imageLocations(), ":")).Implicits(image.imagesDeps.Paths()).
 			FlagWithOutput("--output=", output).
 			FlagWithArg("--instruction-set=", arch.String())
-		rule.Build(pctx, ctx, "dump-oat-boot-"+arch.String(), "dump oat boot "+arch.String())
+		rule.Build(pctx, ctx, "dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
 
 		// Create a phony rule that depends on the output file and prints the path.
-		phony := android.PathForPhony(ctx, "dump-oat-boot-"+arch.String())
+		phony := android.PathForPhony(ctx, "dump-oat-boot-"+suffix)
 		rule = android.NewRuleBuilder()
 		rule.Command().
 			Implicit(output).
 			ImplicitOutput(phony).
 			Text("echo").FlagWithArg("Output in ", output.String())
-		rule.Build(pctx, ctx, "phony-dump-oat-boot-"+arch.String(), "dump oat boot "+arch.String())
+		rule.Build(pctx, ctx, "phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
 
 		allPhonies = append(allPhonies, phony)
 	}
@@ -715,21 +729,25 @@
 	if image != nil {
 		ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPathsDeps.Strings(), " "))
-		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.dexLocationsDeps, " "))
+		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.getAnyAndroidVariant().dexLocationsDeps, " "))
 
 		var imageNames []string
 		for _, current := range append(d.otherImages, image) {
 			imageNames = append(imageNames, current.name)
-			for _, current := range current.variants {
-				sfx := current.name + "_" + current.target.Arch.ArchType.String()
-				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, current.vdexInstalls.String())
-				ctx.Strict("DEXPREOPT_IMAGE_"+sfx, current.images.String())
-				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(current.imagesDeps.Strings(), " "))
-				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, current.installs.String())
-				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, current.unstrippedInstalls.String())
+			for _, variant := range current.variants {
+				suffix := ""
+				if variant.target.Os.Class == android.Host {
+					suffix = "_host"
+				}
+				sfx := variant.name + suffix + "_" + variant.target.Arch.ArchType.String()
+				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, variant.vdexInstalls.String())
+				ctx.Strict("DEXPREOPT_IMAGE_"+sfx, variant.images.String())
+				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " "))
+				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
+				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
 			}
-
-			ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(current.imageLocations, ":"))
+			imageLocations := current.getAnyAndroidVariant().imageLocations()
+			ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(imageLocations, ":"))
 			ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
 		}
 		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index e7b3c3b..0946bf0 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -48,7 +48,7 @@
 
 	pathCtx := android.PathContextForTesting(config)
 	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-	dexpreoptConfig.BootJars = []string{"foo", "bar", "baz"}
+	dexpreoptConfig.BootJars = []string{"platform:foo", "platform:bar", "platform:baz"}
 	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
 
 	ctx := testContext()
@@ -62,7 +62,7 @@
 	bootArt := dexpreoptBootJars.Output("boot-foo.art")
 
 	expectedInputs := []string{
-		"dex_artjars/apex/com.android.art/javalib/arm64/boot.art",
+		"dex_artjars/android/apex/com.android.art/javalib/arm64/boot.art",
 		"dex_bootjars_input/foo.jar",
 		"dex_bootjars_input/bar.jar",
 		"dex_bootjars_input/baz.jar",
@@ -81,23 +81,23 @@
 	}
 
 	expectedOutputs := []string{
-		"dex_bootjars/system/framework/arm64/boot.invocation",
+		"dex_bootjars/android/system/framework/arm64/boot.invocation",
 
-		"dex_bootjars/system/framework/arm64/boot-foo.art",
-		"dex_bootjars/system/framework/arm64/boot-bar.art",
-		"dex_bootjars/system/framework/arm64/boot-baz.art",
+		"dex_bootjars/android/system/framework/arm64/boot-foo.art",
+		"dex_bootjars/android/system/framework/arm64/boot-bar.art",
+		"dex_bootjars/android/system/framework/arm64/boot-baz.art",
 
-		"dex_bootjars/system/framework/arm64/boot-foo.oat",
-		"dex_bootjars/system/framework/arm64/boot-bar.oat",
-		"dex_bootjars/system/framework/arm64/boot-baz.oat",
+		"dex_bootjars/android/system/framework/arm64/boot-foo.oat",
+		"dex_bootjars/android/system/framework/arm64/boot-bar.oat",
+		"dex_bootjars/android/system/framework/arm64/boot-baz.oat",
 
-		"dex_bootjars/system/framework/arm64/boot-foo.vdex",
-		"dex_bootjars/system/framework/arm64/boot-bar.vdex",
-		"dex_bootjars/system/framework/arm64/boot-baz.vdex",
+		"dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
+		"dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
+		"dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
 
-		"dex_bootjars_unstripped/system/framework/arm64/boot-foo.oat",
-		"dex_bootjars_unstripped/system/framework/arm64/boot-bar.oat",
-		"dex_bootjars_unstripped/system/framework/arm64/boot-baz.oat",
+		"dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
+		"dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
+		"dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
 	}
 
 	for i := range expectedOutputs {
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index f8356d1..41a2ca7 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -61,6 +61,10 @@
 			targets = append(targets, target)
 		}
 	}
+	// We may also need the images on host in order to run host-based tests.
+	for _, target := range ctx.Config().Targets[android.BuildOs] {
+		targets = append(targets, target)
+	}
 
 	return targets
 }
@@ -75,6 +79,29 @@
 	return moduleName
 }
 
+func getDexLocation(ctx android.PathContext, target android.Target, module string) string {
+	apex, jar := android.SplitApexJarPair(module)
+
+	name := stemOf(jar) + ".jar"
+
+	var subdir string
+	if apex == "platform" {
+		// Special apex name "platform" denotes jars do not come from an apex, but are part
+		// of the platform. Such jars are installed on the /system partition on device.
+		subdir = "system/framework"
+	} else if apex == "system_ext" {
+		subdir = "system_ext/framework"
+	} else {
+		subdir = filepath.Join("apex", apex, "javalib")
+	}
+
+	if target.Os.Class == android.Host {
+		return filepath.Join(ctx.Config().Getenv("OUT_DIR"), "host", ctx.Config().PrebuiltOS(), subdir, name)
+	} else {
+		return filepath.Join("/", subdir, name)
+	}
+}
+
 var (
 	bootImageConfigKey     = android.NewOnceKey("bootImageConfig")
 	artBootImageName       = "art"
@@ -92,44 +119,30 @@
 		artModules := global.ArtApexJars
 		// With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco.
 		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-			artModules = append(artModules, "jacocoagent")
+			artModules = append(artModules, "com.android.art:jacocoagent")
 		}
-		frameworkModules := android.RemoveListFromList(global.BootJars,
-			concat(artModules, dexpreopt.GetJarsFromApexJarPairs(global.UpdatableBootJars)))
+		frameworkModules := android.RemoveListFromList(global.BootJars, artModules)
 
 		artSubdir := "apex/com.android.art/javalib"
 		frameworkSubdir := "system/framework"
 
-		var artLocations, frameworkLocations []string
-		for _, m := range artModules {
-			artLocations = append(artLocations, filepath.Join("/"+artSubdir, stemOf(m)+".jar"))
-		}
-		for _, m := range frameworkModules {
-			frameworkLocations = append(frameworkLocations, filepath.Join("/"+frameworkSubdir, stemOf(m)+".jar"))
-		}
-
 		// ART config for the primary boot image in the ART apex.
 		// It includes the Core Libraries.
 		artCfg := bootImageConfig{
-			extension:        false,
-			name:             artBootImageName,
-			stem:             "boot",
-			installSubdir:    artSubdir,
-			modules:          artModules,
-			dexLocations:     artLocations,
-			dexLocationsDeps: artLocations,
+			name:          artBootImageName,
+			stem:          "boot",
+			installSubdir: artSubdir,
+			modules:       artModules,
 		}
 
 		// Framework config for the boot image extension.
 		// It includes framework libraries and depends on the ART config.
 		frameworkCfg := bootImageConfig{
-			extension:        true,
-			name:             frameworkBootImageName,
-			stem:             "boot",
-			installSubdir:    frameworkSubdir,
-			modules:          frameworkModules,
-			dexLocations:     frameworkLocations,
-			dexLocationsDeps: append(artLocations, frameworkLocations...),
+			extends:       &artCfg,
+			name:          frameworkBootImageName,
+			stem:          "boot",
+			installSubdir: frameworkSubdir,
+			modules:       frameworkModules,
 		}
 
 		configs := map[string]*bootImageConfig{
@@ -145,28 +158,31 @@
 			// expands to <stem>.art for primary image and <stem>-<1st module>.art for extension
 			imageName := c.firstModuleNameOrStem() + ".art"
 
-			c.imageLocations = []string{c.dir.Join(ctx, c.installSubdir, imageName).String()}
-
 			// The path to bootclasspath dex files needs to be known at module
 			// GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled.
 			// Set up known paths for them, the singleton rules will copy them there.
 			// TODO(b/143682396): use module dependencies instead
 			inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
 			for _, m := range c.modules {
-				c.dexPaths = append(c.dexPaths, inputDir.Join(ctx, stemOf(m)+".jar"))
+				_, jar := android.SplitApexJarPair(m)
+				c.dexPaths = append(c.dexPaths, inputDir.Join(ctx, stemOf(jar)+".jar"))
 			}
 			c.dexPathsDeps = c.dexPaths
 
 			// Create target-specific variants.
 			for _, target := range targets {
 				arch := target.Arch.ArchType
-				imageDir := c.dir.Join(ctx, c.installSubdir, arch.String())
+				imageDir := c.dir.Join(ctx, target.Os.String(), c.installSubdir, arch.String())
 				variant := &bootImageVariant{
 					bootImageConfig: c,
 					target:          target,
 					images:          imageDir.Join(ctx, imageName),
 					imagesDeps:      c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"),
 				}
+				for _, m := range c.modules {
+					variant.dexLocations = append(variant.dexLocations, getDexLocation(ctx, target, m))
+				}
+				variant.dexLocationsDeps = variant.dexLocations
 				c.variants = append(c.variants, variant)
 			}
 
@@ -177,8 +193,8 @@
 		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
 		for i := range targets {
 			frameworkCfg.variants[i].primaryImages = artCfg.variants[i].images
+			frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
 		}
-		frameworkCfg.imageLocations = append(artCfg.imageLocations, frameworkCfg.imageLocations...)
 
 		return configs
 	}).(map[string]*bootImageConfig)
@@ -202,7 +218,7 @@
 			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(p)
 		}
 
-		bootclasspath := append(copyOf(image.dexLocationsDeps), updatableBootclasspath...)
+		bootclasspath := append(copyOf(image.getAnyAndroidVariant().dexLocationsDeps), updatableBootclasspath...)
 		return bootclasspath
 	})
 }
@@ -217,7 +233,7 @@
 
 func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
 	ctx.Strict("PRODUCT_BOOTCLASSPATH", strings.Join(defaultBootclasspath(ctx), ":"))
-	ctx.Strict("PRODUCT_DEX2OAT_BOOTCLASSPATH", strings.Join(defaultBootImageConfig(ctx).dexLocationsDeps, ":"))
+	ctx.Strict("PRODUCT_DEX2OAT_BOOTCLASSPATH", strings.Join(defaultBootImageConfig(ctx).getAnyAndroidVariant().dexLocationsDeps, ":"))
 	ctx.Strict("PRODUCT_SYSTEM_SERVER_CLASSPATH", strings.Join(systemServerClasspath(ctx), ":"))
 
 	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules, ":"))
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 3ca68cf..cb17c28 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -24,6 +24,7 @@
 
 	"android/soong/android"
 	"android/soong/java/config"
+	"android/soong/remoteexec"
 )
 
 func init() {
@@ -1399,7 +1400,32 @@
 	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
 	// Metalava uses lots of memory, restrict the number of metalava jobs that can run in parallel.
 	rule.HighMem()
-	cmd := rule.Command().BuiltTool(ctx, "metalava").
+	cmd := rule.Command()
+	if ctx.Config().IsEnvTrue("RBE_METALAVA") {
+		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
+		execStrategy := remoteexec.LocalExecStrategy
+		if v := ctx.Config().Getenv("RBE_METALAVA_EXEC_STRATEGY"); v != "" {
+			execStrategy = v
+		}
+		pool := "metalava"
+		if v := ctx.Config().Getenv("RBE_METALAVA_POOL"); v != "" {
+			pool = v
+		}
+		inputs := []string{android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "metalava.jar").String()}
+		inputs = append(inputs, sourcepaths.Strings()...)
+		if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
+			inputs = append(inputs, strings.Split(v, ",")...)
+		}
+		cmd.Text((&remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "lang": "java", "compiler": "metalava"},
+			ExecStrategy:    execStrategy,
+			Inputs:          inputs,
+			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
+			Platform:        map[string]string{remoteexec.PoolKey: pool},
+		}).NoVarTemplate(ctx.Config()))
+	}
+
+	cmd.BuiltTool(ctx, "metalava").
 		Flag(config.JavacVmFlags).
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithArg("-source ", javaVersion.String()).
@@ -1529,9 +1555,9 @@
 			msg += fmt.Sprintf(``+
 				`2. You can update the baseline by executing the following\n`+
 				`   command:\n`+
-				`       cp \\ \n`+
-				`       "'"$PWD"$'/%s" \\ \n`+
-				`       "'"$PWD"$'/%s" \n`+
+				`       cp \\\n`+
+				`       "'"$PWD"$'/%s" \\\n`+
+				`       "'"$PWD"$'/%s"\n`+
 				`   To submit the revised baseline.txt to the main Android\n`+
 				`   repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
 		} else {
diff --git a/java/java.go b/java/java.go
index a5d69e3..efd1869 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2368,6 +2368,12 @@
 
 	// set the name of the output
 	Stem *string
+
+	Aidl struct {
+		// directories that should be added as include directories for any aidl sources of modules
+		// that depend on this module, as well as to aidl for this module.
+		Export_include_dirs []string
+	}
 }
 
 type Import struct {
@@ -2381,6 +2387,7 @@
 
 	combinedClasspathFile android.Path
 	exportedSdkLibs       []string
+	exportAidlIncludeDirs android.Paths
 }
 
 func (j *Import) sdkVersion() sdkSpec {
@@ -2458,6 +2465,8 @@
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			jarName, outputFile)
 	}
+
+	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 }
 
 var _ Dependency = (*Import)(nil)
@@ -2492,7 +2501,7 @@
 }
 
 func (j *Import) AidlIncludeDirs() android.Paths {
-	return nil
+	return j.exportAidlIncludeDirs
 }
 
 func (j *Import) ExportedSdkLibs() []string {
diff --git a/java/java_test.go b/java/java_test.go
index 01ddccf..f61f4bb 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1523,3 +1523,27 @@
 		t.Errorf("bootclasspath of %q must start with --system and end with %q, but was %#v.", moduleName, expectedSuffix, bootClasspath)
 	}
 }
+
+func TestAidlExportIncludeDirsFromImports(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["aidl/foo/IFoo.aidl"],
+			libs: ["bar"],
+		}
+
+		java_import {
+			name: "bar",
+			jars: ["a.jar"],
+			aidl: {
+				export_include_dirs: ["aidl/bar"],
+			},
+		}
+	`)
+
+	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	expectedAidlFlag := "-Iaidl/bar"
+	if !strings.Contains(aidlCommand, expectedAidlFlag) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+	}
+}
diff --git a/java/sdk.go b/java/sdk.go
index f96ecde..2a08f32 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -321,6 +321,28 @@
 	}
 }
 
+func (s sdkSpec) validateSystemSdk(ctx android.EarlyModuleContext) bool {
+	// Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
+	// Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
+	// sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
+	if s.kind != sdkSystem || !s.version.isNumbered() {
+		return true
+	}
+	allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
+	if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+		systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
+		if len(systemSdkVersions) > 0 {
+			allowedVersions = systemSdkVersions
+		}
+	}
+	if len(allowedVersions) > 0 && !android.InList(s.version.String(), allowedVersions) {
+		ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
+			s.raw, allowedVersions)
+		return false
+	}
+	return true
+}
+
 func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
 	sdkVersion := sdkContext.sdkVersion()
 	if !sdkVersion.valid() {
@@ -331,6 +353,9 @@
 	if ctx.Config().IsPdkBuild() {
 		sdkVersion = sdkVersion.forPdkBuild(ctx)
 	}
+	if !sdkVersion.validateSystemSdk(ctx) {
+		return sdkDep{}
+	}
 
 	if sdkVersion.usePrebuilt(ctx) {
 		dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), sdkVersion.kind.String())
@@ -384,21 +409,6 @@
 		}
 	}
 
-	// Ensures that the specificed system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor apks)
-	// or PRODUCT_SYSTEMSDK_VERSIONS (for other apks or when BOARD_SYSTEMSDK_VERSIONS is not set)
-	if sdkVersion.kind == sdkSystem && sdkVersion.version.isNumbered() {
-		allowed_versions := ctx.DeviceConfig().PlatformSystemSdkVersions()
-		if ctx.DeviceSpecific() || ctx.SocSpecific() {
-			if len(ctx.DeviceConfig().SystemSdkVersions()) > 0 {
-				allowed_versions = ctx.DeviceConfig().SystemSdkVersions()
-			}
-		}
-		if len(allowed_versions) > 0 && !android.InList(sdkVersion.version.String(), allowed_versions) {
-			ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
-				sdkVersion.raw, allowed_versions)
-		}
-	}
-
 	switch sdkVersion.kind {
 	case sdkPrivate:
 		return sdkDep{
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 24a88d3..7e81c1e 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1672,6 +1672,9 @@
 
 	// The Java stubs source files.
 	Stub_srcs []string
+
+	// The naming scheme.
+	Naming_scheme *string
 }
 
 type scopeProperties struct {
@@ -1701,9 +1704,14 @@
 	}
 
 	s.Libs = sdk.properties.Libs
+	s.Naming_scheme = sdk.commonProperties.Naming_scheme
 }
 
 func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+	if s.Naming_scheme != nil {
+		propertySet.AddProperty("naming_scheme", proptools.String(s.Naming_scheme))
+	}
+
 	for _, apiScope := range allApiScopes {
 		if properties, ok := s.Scopes[apiScope]; ok {
 			scopeSet := propertySet.AddPropertySet(apiScope.propertyName)
diff --git a/java/testing.go b/java/testing.go
index d6a2446..1967148 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -50,6 +50,8 @@
 		"api/test-current.txt":   nil,
 		"api/test-removed.txt":   nil,
 		"framework/aidl/a.aidl":  nil,
+		"aidl/foo/IFoo.aidl":     nil,
+		"aidl/bar/IBar.aidl":     nil,
 		"assets_a/a":             nil,
 		"assets_b/b":             nil,
 
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index d43dc6c..99e29dc 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -82,17 +82,31 @@
 
 func init() {
 	pctx.VariableFunc("Wrapper", func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv("RBE_WRAPPER"); override != "" {
-			return override
-		}
-		return DefaultWrapperPath
+		return wrapper(ctx.Config())
 	})
 }
 
-// Generate the remote execution wrapper template to be added as a prefix to the rule's command.
-func (r *REParams) Template() string {
-	template := "${remoteexec.Wrapper}"
+func wrapper(cfg android.Config) string {
+	if override := cfg.Getenv("RBE_WRAPPER"); override != "" {
+		return override
+	}
+	return DefaultWrapperPath
+}
 
+// Template generates the remote execution wrapper template to be added as a prefix to the rule's
+// command.
+func (r *REParams) Template() string {
+	return "${remoteexec.Wrapper}" + r.wrapperArgs()
+}
+
+// NoVarTemplate generate the remote execution wrapper template without variables, to be used in
+// RuleBuilder.
+func (r *REParams) NoVarTemplate(cfg android.Config) string {
+	return wrapper(cfg) + r.wrapperArgs()
+}
+
+func (r *REParams) wrapperArgs() string {
+	args := ""
 	var kvs []string
 	labels := r.Labels
 	if len(labels) == 0 {
@@ -102,7 +116,7 @@
 		kvs = append(kvs, k+"="+v)
 	}
 	sort.Strings(kvs)
-	template += " --labels=" + strings.Join(kvs, ",")
+	args += " --labels=" + strings.Join(kvs, ",")
 
 	var platform []string
 	for k, v := range r.Platform {
@@ -116,40 +130,52 @@
 	}
 	if platform != nil {
 		sort.Strings(platform)
-		template += " --platform=\"" + strings.Join(platform, ",") + "\""
+		args += " --platform=\"" + strings.Join(platform, ",") + "\""
 	}
 
 	strategy := r.ExecStrategy
 	if strategy == "" {
 		strategy = defaultExecStrategy
 	}
-	template += " --exec_strategy=" + strategy
+	args += " --exec_strategy=" + strategy
 
 	if len(r.Inputs) > 0 {
-		template += " --inputs=" + strings.Join(r.Inputs, ",")
+		args += " --inputs=" + strings.Join(r.Inputs, ",")
 	}
 
 	if r.RSPFile != "" {
-		template += " --input_list_paths=" + r.RSPFile
+		args += " --input_list_paths=" + r.RSPFile
 	}
 
 	if len(r.OutputFiles) > 0 {
-		template += " --output_files=" + strings.Join(r.OutputFiles, ",")
+		args += " --output_files=" + strings.Join(r.OutputFiles, ",")
 	}
 
 	if len(r.ToolchainInputs) > 0 {
-		template += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
+		args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
 	}
 
-	return template + " -- "
+	return args + " -- "
 }
 
 // StaticRules returns a pair of rules based on the given RuleParams, where the first rule is a
 // locally executable rule and the second rule is a remotely executable rule.
 func StaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams *REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
 	ruleParamsRE := ruleParams
-	ruleParamsRE.Command = reParams.Template() + ruleParamsRE.Command
+	ruleParams.Command = strings.ReplaceAll(ruleParams.Command, "$reTemplate", "")
+	ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, "$reTemplate", reParams.Template())
 
 	return ctx.AndroidStaticRule(name, ruleParams, commonArgs...),
 		ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
 }
+
+// EnvOverrideFunc retrieves a variable func that evaluates to the value of the given environment
+// variable if set, otherwise the given default.
+func EnvOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
+	return func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv(envVar); override != "" {
+			return override
+		}
+		return defaultVal
+	}
+}
diff --git a/remoteexec/remoteexec_test.go b/remoteexec/remoteexec_test.go
index 30e891c..56985d3 100644
--- a/remoteexec/remoteexec_test.go
+++ b/remoteexec/remoteexec_test.go
@@ -17,6 +17,8 @@
 import (
 	"fmt"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestTemplate(t *testing.T) {
@@ -64,6 +66,22 @@
 	}
 }
 
+func TestNoVarTemplate(t *testing.T) {
+	params := &REParams{
+		Labels:      map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"},
+		Inputs:      []string{"$in"},
+		OutputFiles: []string{"$out"},
+		Platform: map[string]string{
+			ContainerImageKey: DefaultImage,
+			PoolKey:           "default",
+		},
+	}
+	want := fmt.Sprintf("prebuilts/remoteexecution-client/live/rewrapper --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage)
+	if got := params.NoVarTemplate(android.NullConfig("")); got != want {
+		t.Errorf("NoVarTemplate() returned\n%s\nwant\n%s", got, want)
+	}
+}
+
 func TestTemplateDeterminism(t *testing.T) {
 	r := &REParams{
 		Labels:      map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"},
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 0fba739..0e2bea3 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -46,6 +46,12 @@
 }
 
 func (mod *Module) AndroidMk() android.AndroidMkData {
+	if mod.Properties.HideFromMake {
+		return android.AndroidMkData{
+			Disabled: true,
+		}
+	}
+
 	ret := android.AndroidMkData{
 		OutputFile: mod.outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
@@ -84,6 +90,9 @@
 	ret.DistFile = binary.distFile
 	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())
+		}
 	})
 }
 
@@ -124,6 +133,10 @@
 		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())
+		}
+
 	})
 }
 
diff --git a/rust/binary.go b/rust/binary.go
index fda056e..c25ae09 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -35,9 +35,10 @@
 type binaryDecorator struct {
 	*baseCompiler
 
-	Properties           BinaryCompilerProperties
-	distFile             android.OptionalPath
-	unstrippedOutputFile android.Path
+	Properties            BinaryCompilerProperties
+	distFile              android.OptionalPath
+	coverageOutputZipFile android.OptionalPath
+	unstrippedOutputFile  android.Path
 }
 
 var _ compiler = (*binaryDecorator)(nil)
@@ -104,6 +105,10 @@
 		&binary.Properties)
 }
 
+func (binary *binaryDecorator) nativeCoverage() bool {
+	return true
+}
+
 func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
 
@@ -114,7 +119,21 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 
-	TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	outputs := TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	binary.coverageFile = outputs.coverageFile
+
+	var coverageFiles android.Paths
+	if outputs.coverageFile != nil {
+		coverageFiles = append(coverageFiles, binary.coverageFile)
+	}
+	if len(deps.coverageFiles) > 0 {
+		coverageFiles = append(coverageFiles, deps.coverageFiles...)
+	}
+	binary.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, binary.getStem(ctx))
 
 	return outputFile
 }
+
+func (binary *binaryDecorator) coverageOutputZipPath() android.OptionalPath {
+	return binary.coverageOutputZipFile
+}
diff --git a/rust/builder.go b/rust/builder.go
index 27eeec2..fbe0e53 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -18,6 +18,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
 )
@@ -36,44 +37,57 @@
 			Depfile: "$out.d",
 		},
 		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd")
+
+	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",
+			CommandDeps:    []string{"${SoongZipCmd}"},
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$in",
+		})
 )
 
-func init() {
+type buildOutput struct {
+	outputFile   android.Path
+	coverageFile android.Path
+}
 
+func init() {
+	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
 }
 
 func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) {
+	outputFile android.WritablePath, includeDirs []string) buildOutput {
 	flags.RustFlags = append(flags.RustFlags, "-C lto")
 
-	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs)
 }
 
 func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) {
-	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs)
+	outputFile android.WritablePath, includeDirs []string) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs)
 }
 
 func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) {
-	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs)
+	outputFile android.WritablePath, includeDirs []string) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs)
 }
 
 func TransformSrctoStatic(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) {
+	outputFile android.WritablePath, includeDirs []string) buildOutput {
 	flags.RustFlags = append(flags.RustFlags, "-C lto")
-	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs)
 }
 
 func TransformSrctoShared(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) {
+	outputFile android.WritablePath, includeDirs []string) buildOutput {
 	flags.RustFlags = append(flags.RustFlags, "-C lto")
-	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs)
 }
 
 func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps,
-	flags Flags, outputFile android.WritablePath, includeDirs []string) {
-	transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs)
+	flags Flags, outputFile android.WritablePath, includeDirs []string) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs)
 }
 
 func rustLibsToPaths(libs RustLibraries) android.Paths {
@@ -85,11 +99,15 @@
 }
 
 func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, crate_type string, includeDirs []string) {
+	outputFile android.WritablePath, crate_type string, includeDirs []string) buildOutput {
 
 	var inputs android.Paths
 	var implicits android.Paths
+	var output buildOutput
 	var libFlags, rustcFlags, linkFlags []string
+	var implicitOutputs android.WritablePaths
+
+	output.outputFile = outputFile
 	crate_name := ctx.(ModuleContext).CrateName()
 	targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
 
@@ -108,8 +126,8 @@
 	}
 	// TODO once we have static libraries in the host prebuilt .bp, this
 	// should be unconditionally added.
-	if !ctx.Host() {
-		// If we're on a device build, do not use an implicit sysroot
+	if !(ctx.Host() && ctx.TargetPrimary()) {
+		// If we're not targeting the host primary arch, do not use an implicit sysroot
 		rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
 	}
 	// Collect linker flags
@@ -141,12 +159,26 @@
 		implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
 	}
 
+	if flags.Coverage {
+		var gcnoFile android.WritablePath
+
+		if outputFile.Ext() != "" {
+			gcnoFile = android.PathForModuleOut(ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcno"))
+		} else {
+			gcnoFile = android.PathForModuleOut(ctx, outputFile.Base()+".gcno")
+		}
+
+		implicitOutputs = append(implicitOutputs, gcnoFile)
+		output.coverageFile = gcnoFile
+	}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        rustc,
-		Description: "rustc " + main.Rel(),
-		Output:      outputFile,
-		Inputs:      inputs,
-		Implicits:   implicits,
+		Rule:            rustc,
+		Description:     "rustc " + main.Rel(),
+		Output:          outputFile,
+		ImplicitOutputs: implicitOutputs,
+		Inputs:          inputs,
+		Implicits:       implicits,
 		Args: map[string]string{
 			"rustcFlags": strings.Join(rustcFlags, " "),
 			"linkFlags":  strings.Join(linkFlags, " "),
@@ -156,4 +188,23 @@
 		},
 	})
 
+	return output
+}
+
+func TransformCoverageFilesToZip(ctx android.ModuleContext,
+	covFiles android.Paths, baseName string) android.OptionalPath {
+	if len(covFiles) > 0 {
+
+		outputFile := android.PathForModuleOut(ctx, baseName+".zip")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        zip,
+			Description: "zip " + outputFile.Base(),
+			Inputs:      covFiles,
+			Output:      outputFile,
+		})
+
+		return android.OptionalPathForPath(outputFile)
+	}
+	return android.OptionalPath{}
 }
diff --git a/rust/compiler.go b/rust/compiler.go
index 4593165..5f098bc 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -110,6 +110,7 @@
 	linkDirs      []string
 	edition       string
 	src           android.Path //rustc takes a single src file
+	coverageFile  android.Path //rustc generates a single gcno file
 
 	// Install related
 	dir      string
@@ -120,6 +121,10 @@
 	location installLocation
 }
 
+func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
+	panic("baseCompiler does not implement coverageOutputZipPath()")
+}
+
 var _ compiler = (*baseCompiler)(nil)
 
 func (compiler *baseCompiler) inData() bool {
@@ -183,8 +188,8 @@
 
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
-			// If we're building for host, use the compiler's stdlibs
-			if ctx.Host() {
+			// If we're building for the primary host target, use the compiler's stdlibs
+			if ctx.Host() && ctx.TargetPrimary() {
 				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
 			}
 
@@ -192,9 +197,12 @@
 			// static linking is the default, if one of our static
 			// dependencies uses a dynamic library, we need to dynamically
 			// link the stdlib as well.
-			if (len(deps.Dylibs) > 0) || (!ctx.Host()) {
+			if (len(deps.Dylibs) > 0) || ctx.Device() {
 				// Dynamically linked stdlib
 				deps.Dylibs = append(deps.Dylibs, stdlib)
+			} else if ctx.Host() && !ctx.TargetPrimary() {
+				// Otherwise use the static in-tree stdlib for host secondary arch
+				deps.Rlibs = append(deps.Rlibs, stdlib+".static")
 			}
 		}
 	}
@@ -222,13 +230,20 @@
 	if ctx.toolchain().Is64Bit() && compiler.dir64 != "" {
 		dir = compiler.dir64
 	}
-	if !ctx.Host() || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
+	}
+	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 	return android.PathForModuleInstall(ctx, dir, compiler.subDir,
 		compiler.relativeInstallPath(), compiler.relative)
 }
 
+func (compiler *baseCompiler) nativeCoverage() bool {
+	return false
+}
+
 func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) {
 	compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), file)
 }
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index bbf9f8d..bcde757 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -74,3 +74,33 @@
 		  host_supported: true,
 		}`)
 }
+
+func TestInstallDir(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_dylib {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		rust_binary {
+			name: "fizzbuzz",
+			srcs: ["foo.rs"],
+		}`)
+
+	install_path_lib64 := ctx.ModuleForTests("libfoo",
+		"android_arm64_armv8-a_dylib").Module().(*Module).compiler.(*libraryDecorator).path.String()
+	install_path_lib32 := ctx.ModuleForTests("libfoo",
+		"android_arm_armv7-a-neon_dylib").Module().(*Module).compiler.(*libraryDecorator).path.String()
+	install_path_bin := ctx.ModuleForTests("fizzbuzz",
+		"android_arm64_armv8-a").Module().(*Module).compiler.(*binaryDecorator).path.String()
+
+	if !strings.HasSuffix(install_path_lib64, "system/lib64/libfoo.dylib.so") {
+		t.Fatalf("unexpected install path for 64-bit library: %#v", install_path_lib64)
+	}
+	if !strings.HasSuffix(install_path_lib32, "system/lib/libfoo.dylib.so") {
+		t.Fatalf("unexpected install path for 32-bit library: %#v", install_path_lib32)
+	}
+	if !strings.HasSuffix(install_path_bin, "system/bin/fizzbuzz") {
+		t.Fatalf("unexpected install path for binary: %#v", install_path_bin)
+	}
+}
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 60796d8..180fd8b 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -27,7 +27,6 @@
 		"-Wl,--icf=safe",
 		"-Wl,-z,max-page-size=4096",
 
-		"-Wl,--execute-only",
 		"-Wl,-z,separate-code",
 	}
 
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
index aedb42b..ac2580b 100644
--- a/rust/config/arm_device.go
+++ b/rust/config/arm_device.go
@@ -50,7 +50,7 @@
 }
 
 type toolchainArm struct {
-	toolchain64Bit
+	toolchain32Bit
 	toolchainRustFlags string
 }
 
diff --git a/rust/config/global.go b/rust/config/global.go
index 690d83e..63624c0 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.40.0"
+	RustDefaultVersion = "1.43.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/coverage.go b/rust/coverage.go
new file mode 100644
index 0000000..9be57dc
--- /dev/null
+++ b/rust/coverage.go
@@ -0,0 +1,72 @@
+// 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 (
+	"github.com/google/blueprint"
+
+	"android/soong/cc"
+)
+
+var CovLibraryName = "libprofile-extras"
+
+type coverage struct {
+	Properties cc.CoverageProperties
+
+	// Whether binaries containing this module need --coverage added to their ldflags
+	linkCoverage bool
+}
+
+func (cov *coverage) props() []interface{} {
+	return []interface{}{&cov.Properties}
+}
+
+func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
+	if cov.Properties.NeedCoverageVariant {
+		ctx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, cc.CoverageDepTag, CovLibraryName)
+	}
+
+	return deps
+}
+
+func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+
+	if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
+		return flags, deps
+	}
+
+	if cov.Properties.CoverageEnabled {
+		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")
+		flags.LinkFlags = append(flags.LinkFlags,
+			"--coverage", "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,getenv")
+		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+	}
+
+	return flags, deps
+}
+
+func (cov *coverage) begin(ctx BaseModuleContext) {
+	if ctx.Host() {
+		// Host coverage not yet supported.
+	} else {
+		// Update useSdk and sdkVersion args if Rust modules become SDK aware.
+		cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), false, "")
+	}
+}
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
new file mode 100644
index 0000000..27acad3
--- /dev/null
+++ b/rust/coverage_test.go
@@ -0,0 +1,181 @@
+// 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 (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+// Test that coverage flags are being correctly generated.
+func TestCoverageFlags(t *testing.T) {
+	ctx := testRustCov(t, `
+		rust_library {
+			name: "libfoo_cov",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		rust_binary {
+			name: "fizz_cov",
+			srcs: ["foo.rs"],
+		}
+        rust_binary {
+			name: "buzzNoCov",
+			srcs: ["foo.rs"],
+			native_coverage: false,
+		}
+		rust_library {
+			name: "libbar_nocov",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			native_coverage: false,
+		}`)
+
+	// Make sure native_coverage: false isn't creating a coverage variant.
+	if android.InList("android_arm64_armv8-a_dylib_cov", ctx.ModuleVariantsForTests("libbar_nocov")) {
+		t.Fatalf("coverage variant created for module 'libbar_nocov' with native coverage disabled")
+	}
+
+	// Just test the dylib variants unless the library coverage logic changes to distinguish between the types.
+	libfooCov := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustc")
+	libbarNoCov := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
+	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"}
+	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"
+
+		if !strings.Contains(fizzCov.Args["rustcFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["rustcFlags"])
+		}
+		if !strings.Contains(libfooCov.Args["rustcFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["rustcFlags"])
+		}
+		if strings.Contains(buzzNoCov.Args["rustcFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["rustcFlags"])
+		}
+		if strings.Contains(libbarNoCov.Args["rustcFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["rustcFlags"])
+		}
+	}
+
+	linkCoverageFlags := []string{"--coverage", " -g "}
+	for _, flag := range linkCoverageFlags {
+		missingErrorStr := "missing rust linker flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
+		containsErrorStr := "contains rust linker flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
+
+		if !strings.Contains(fizzCov.Args["linkFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["linkFlags"])
+		}
+		if !strings.Contains(libfooCov.Args["linkFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["linkFlags"])
+		}
+		if strings.Contains(buzzNoCov.Args["linkFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["linkFlags"])
+		}
+		if strings.Contains(libbarNoCov.Args["linkFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["linkFlags"])
+		}
+	}
+
+}
+
+// Test coverage files are included correctly
+func TestCoverageZip(t *testing.T) {
+	ctx := testRustCov(t, `
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			rlibs: ["librlib"],
+			crate_name: "foo",
+		}
+		rust_library_rlib {
+			name: "librlib",
+			srcs: ["foo.rs"],
+			crate_name: "rlib",
+		}
+		rust_binary {
+			name: "fizz",
+			rlibs: ["librlib"],
+			static_libs: ["libfoo"],
+			srcs: ["foo.rs"],
+		}
+		cc_binary {
+			name: "buzz",
+			static_libs: ["libfoo"],
+			srcs: ["foo.c"],
+		}
+		cc_library {
+			name: "libbar",
+			static_libs: ["libfoo"],
+			compile_multilib: "64",
+			srcs: ["foo.c"],
+		}`)
+
+	fizzZipInputs := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings()
+	libfooZipInputs := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib_cov").Rule("zip").Inputs.Strings()
+	buzzZipInputs := ctx.ModuleForTests("buzz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings()
+	libbarZipInputs := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared_cov").Rule("zip").Inputs.Strings()
+
+	// Make sure the expected number of input files are included.
+	if len(fizzZipInputs) != 3 {
+		t.Fatalf("expected only 3 coverage inputs for rust 'fizz' binary, got %#v: %#v", len(fizzZipInputs), fizzZipInputs)
+	}
+	if len(libfooZipInputs) != 2 {
+		t.Fatalf("expected only 2 coverage inputs for rust 'libfoo' library, got %#v: %#v", len(libfooZipInputs), libfooZipInputs)
+	}
+	if len(buzzZipInputs) != 2 {
+		t.Fatalf("expected only 2 coverage inputs for cc 'buzz' binary, got %#v: %#v", len(buzzZipInputs), buzzZipInputs)
+	}
+	if len(libbarZipInputs) != 2 {
+		t.Fatalf("expected only 2 coverage inputs for cc 'libbar' library, got %#v: %#v", len(libbarZipInputs), libbarZipInputs)
+	}
+
+	// Make sure the expected inputs are provided to the zip rule.
+	if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
+		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") ||
+		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_cov/fizz.gcno") {
+		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
+	}
+	if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
+		!android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.dylib.gcno") {
+		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
+	}
+	if !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_cov/obj/foo.gcno") ||
+		!android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") {
+		t.Fatalf("missing expected coverage files for cc 'buzz' binary: %#v", buzzZipInputs)
+	}
+	if !android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/obj/foo.gcno") ||
+		!android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") {
+		t.Fatalf("missing expected coverage files for cc 'libbar' library: %#v", libbarZipInputs)
+	}
+}
+
+func TestCoverageDeps(t *testing.T) {
+	ctx := testRustCov(t, `
+		rust_binary {
+			name: "fizz",
+			srcs: ["foo.rs"],
+		}`)
+
+	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
+	if !strings.Contains(fizz.Args["linkFlags"], "libprofile-extras.a") {
+		t.Fatalf("missing expected coverage 'libprofile-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
+	}
+}
diff --git a/rust/library.go b/rust/library.go
index 0cf2dd0..8aa033c 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -75,11 +75,12 @@
 type libraryDecorator struct {
 	*baseCompiler
 
-	Properties           LibraryCompilerProperties
-	MutatedProperties    LibraryMutatedProperties
-	distFile             android.OptionalPath
-	unstrippedOutputFile android.Path
-	includeDirs          android.Paths
+	Properties            LibraryCompilerProperties
+	MutatedProperties     LibraryMutatedProperties
+	distFile              android.OptionalPath
+	coverageOutputZipFile android.OptionalPath
+	unstrippedOutputFile  android.Path
+	includeDirs           android.Paths
 }
 
 type libraryInterface interface {
@@ -107,6 +108,10 @@
 	BuildOnlyShared()
 }
 
+func (library *libraryDecorator) nativeCoverage() bool {
+	return true
+}
+
 func (library *libraryDecorator) exportedDirs() []string {
 	return library.linkDirs
 }
@@ -281,7 +286,7 @@
 }
 
 func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
-	module := newModule(hod, android.MultilibFirst)
+	module := newModule(hod, android.MultilibBoth)
 
 	library := &libraryDecorator{
 		MutatedProperties: LibraryMutatedProperties{
@@ -318,6 +323,8 @@
 
 	if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
 		deps = library.baseCompiler.bionicDeps(ctx, deps)
+		deps.CrtBegin = "crtbegin_so"
+		deps.CrtEnd = "crtend_so"
 	}
 
 	return deps
@@ -349,24 +356,37 @@
 		fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		outputs := TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		library.coverageFile = outputs.coverageFile
 	} else if library.dylib() {
 		fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		outputs := TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		library.coverageFile = outputs.coverageFile
 	} else if library.static() {
 		fileName := library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		outputs := TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		library.coverageFile = outputs.coverageFile
 	} else if library.shared() {
 		fileName := library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		outputs := TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		library.coverageFile = outputs.coverageFile
 	}
 
+	var coverageFiles android.Paths
+	if library.coverageFile != nil {
+		coverageFiles = append(coverageFiles, library.coverageFile)
+	}
+	if len(deps.coverageFiles) > 0 {
+		coverageFiles = append(coverageFiles, deps.coverageFiles...)
+	}
+	library.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, library.getStem(ctx))
+
 	if library.rlib() || library.dylib() {
 		library.reexportDirs(deps.linkDirs...)
 		library.reexportDepFlags(deps.depFlags...)
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 45bef9e..1d97650 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -69,3 +69,7 @@
 	deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
 	return deps
 }
+
+func (prebuilt *prebuiltLibraryDecorator) nativeCoverage() bool {
+	return false
+}
diff --git a/rust/rust.go b/rust/rust.go
index 5cc8845..8cf2e6d 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -40,6 +40,7 @@
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
 		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
+		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
 }
@@ -51,6 +52,7 @@
 	LinkFlags       []string      // Flags that apply to linker
 	RustFlagsDeps   android.Paths // Files depended on by compiler flags
 	Toolchain       config.Toolchain
+	Coverage        bool
 }
 
 type BaseProperties struct {
@@ -60,6 +62,8 @@
 	AndroidMkSharedLibs    []string
 	AndroidMkStaticLibs    []string
 	SubName                string `blueprint:"mutated"`
+	PreventInstall         bool
+	HideFromMake           bool
 }
 
 type Module struct {
@@ -72,6 +76,7 @@
 	multilib android.Multilib
 
 	compiler         compiler
+	coverage         *coverage
 	cachedToolchain  config.Toolchain
 	subAndroidMkOnce map[subAndroidMkProvider]bool
 	outputFile       android.OptionalPath
@@ -224,6 +229,8 @@
 	depFlags   []string
 	//ReexportedDeps android.Paths
 
+	coverageFiles android.Paths
+
 	CrtBegin android.OptionalPath
 	CrtEnd   android.OptionalPath
 }
@@ -245,6 +252,34 @@
 	inData() bool
 	install(ctx ModuleContext, path android.Path)
 	relativeInstallPath() string
+
+	nativeCoverage() bool
+}
+
+func (mod *Module) isCoverageVariant() bool {
+	return mod.coverage.Properties.IsCoverageVariant
+}
+
+var _ cc.Coverage = (*Module)(nil)
+
+func (mod *Module) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+	return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant
+}
+
+func (mod *Module) PreventInstall() {
+	mod.Properties.PreventInstall = true
+}
+
+func (mod *Module) HideFromMake() {
+	mod.Properties.HideFromMake = true
+}
+
+func (mod *Module) MarkAsCoverageVariant(coverage bool) {
+	mod.coverage.Properties.IsCoverageVariant = coverage
+}
+
+func (mod *Module) EnableCoverageIfNeeded() {
+	mod.coverage.Properties.CoverageEnabled = mod.coverage.Properties.NeedCoverageBuild
 }
 
 func defaultsFactory() android.Module {
@@ -268,6 +303,7 @@
 		&ProcMacroCompilerProperties{},
 		&PrebuiltProperties{},
 		&TestProperties{},
+		&cc.CoverageProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -395,6 +431,18 @@
 	return false
 }
 
+func (mod *Module) CoverageFiles() android.Paths {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(*libraryDecorator); ok {
+			if library.coverageFile != nil {
+				return android.Paths{library.coverageFile}
+			}
+			return android.Paths{}
+		}
+	}
+	panic(fmt.Errorf("CoverageFiles called on non-library module: %q", mod.BaseModuleName()))
+}
+
 var _ cc.LinkableInterface = (*Module)(nil)
 
 func (mod *Module) Init() android.Module {
@@ -403,6 +451,10 @@
 	if mod.compiler != nil {
 		mod.AddProperties(mod.compiler.compilerProps()...)
 	}
+	if mod.coverage != nil {
+		mod.AddProperties(mod.coverage.props()...)
+	}
+
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
 
 	android.InitDefaultableModule(mod)
@@ -432,6 +484,7 @@
 }
 func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	module := newBaseModule(hod, multilib)
+	module.coverage = &coverage{}
 	return module
 }
 
@@ -454,6 +507,7 @@
 	toolchain() config.Toolchain
 	baseModuleName() string
 	CrateName() string
+	nativeCoverage() bool
 }
 
 type depsContext struct {
@@ -466,6 +520,14 @@
 	moduleContextImpl
 }
 
+func (ctx *moduleContextImpl) nativeCoverage() bool {
+	return ctx.mod.nativeCoverage()
+}
+
+func (mod *Module) nativeCoverage() bool {
+	return mod.compiler != nil && mod.compiler.nativeCoverage()
+}
+
 type moduleContextImpl struct {
 	mod *Module
 	ctx BaseModuleContext
@@ -508,9 +570,17 @@
 
 	if mod.compiler != nil {
 		flags = mod.compiler.compilerFlags(ctx, flags)
+	}
+	if mod.coverage != nil {
+		flags, deps = mod.coverage.flags(ctx, flags, deps)
+	}
+
+	if mod.compiler != nil {
 		outputFile := mod.compiler.compile(ctx, flags, deps)
 		mod.outputFile = android.OptionalPathForPath(outputFile)
-		mod.compiler.install(ctx, mod.outputFile.Path())
+		if !mod.Properties.PreventInstall {
+			mod.compiler.install(ctx, mod.outputFile.Path())
+		}
 	}
 }
 
@@ -521,6 +591,10 @@
 		deps = mod.compiler.compilerDeps(ctx, deps)
 	}
 
+	if mod.coverage != nil {
+		deps = mod.coverage.deps(ctx, deps)
+	}
+
 	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
 	deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
 	deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
@@ -553,6 +627,12 @@
 	testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
 )
 
+func (mod *Module) begin(ctx BaseModuleContext) {
+	if mod.coverage != nil {
+		mod.coverage.begin(ctx)
+	}
+}
+
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
@@ -588,6 +668,7 @@
 					ctx.ModuleErrorf("mod %q not an rlib library", depName)
 					return
 				}
+				depPaths.coverageFiles = append(depPaths.coverageFiles, rustDep.CoverageFiles()...)
 				directRlibDeps = append(directRlibDeps, rustDep)
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName)
 			case procMacroDepTag:
@@ -642,6 +723,7 @@
 				depFlag = "-lstatic=" + libName
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 				depPaths.depFlags = append(depPaths.depFlags, depFlag)
+				depPaths.coverageFiles = append(depPaths.coverageFiles, ccDep.CoverageFiles()...)
 				directStaticLibDeps = append(directStaticLibDeps, ccDep)
 				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
 			case cc.SharedDepTag:
@@ -772,6 +854,29 @@
 	actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
 }
 
+func BeginMutator(ctx android.BottomUpMutatorContext) {
+	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
+		mod.beginMutator(ctx)
+	}
+}
+
+type baseModuleContext struct {
+	android.BaseModuleContext
+	moduleContextImpl
+}
+
+func (mod *Module) beginMutator(actx android.BottomUpMutatorContext) {
+	ctx := &baseModuleContext{
+		BaseModuleContext: actx,
+		moduleContextImpl: moduleContextImpl{
+			mod: mod,
+		},
+	}
+	ctx.ctx = ctx
+
+	mod.begin(ctx)
+}
+
 func (mod *Module) Name() string {
 	name := mod.ModuleBase.Name()
 	if p, ok := mod.compiler.(interface {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 020581d..d658ee2 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -21,6 +21,8 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/cc"
 )
@@ -57,6 +59,7 @@
 
 	fs := map[string][]byte{
 		"foo.rs":     nil,
+		"foo.c":      nil,
 		"src/bar.rs": nil,
 		"liby.so":    nil,
 		"libz.so":    nil,
@@ -68,6 +71,14 @@
 }
 
 func testRust(t *testing.T, bp string) *android.TestContext {
+	return testRustContext(t, bp, false)
+}
+
+func testRustCov(t *testing.T, bp string) *android.TestContext {
+	return testRustContext(t, bp, true)
+}
+
+func testRustContext(t *testing.T, bp string, coverage bool) *android.TestContext {
 	// TODO (b/140435149)
 	if runtime.GOOS != "linux" {
 		t.Skip("Only the Linux toolchain is supported for Rust")
@@ -76,6 +87,11 @@
 	t.Helper()
 	config := testConfig(bp)
 
+	if coverage {
+		config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
+		config.TestProductVariables.CoveragePaths = []string{"*"}
+	}
+
 	t.Helper()
 	ctx := CreateTestContext()
 	ctx.Register(config)
@@ -215,25 +231,6 @@
 			srcs: ["foo.rs"],
 			crate_name: "bar",
 		}
-		// Make a dummy libstd to let resolution go through
-		rust_library_dylib {
-			name: "libstd",
-			crate_name: "std",
-			srcs: ["foo.rs"],
-			no_stdlibs: true,
-		}
-		rust_library_dylib {
-			name: "libterm",
-			crate_name: "term",
-			srcs: ["foo.rs"],
-			no_stdlibs: true,
-		}
-		rust_library_dylib {
-			name: "libtest",
-			crate_name: "test",
-			srcs: ["foo.rs"],
-			no_stdlibs: true,
-		}
 		rust_proc_macro {
 			name: "libpm",
 			rlibs: ["libbar"],
@@ -259,7 +256,7 @@
 		rust_binary {
 			name: "fizz-buzz",
 			srcs: ["foo.rs"],
-                        no_stdlibs: true,
+			no_stdlibs: true,
 		}`)
 	module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
 
@@ -267,3 +264,16 @@
 		t.Errorf("no_stdlibs did not suppress dependency on libstd")
 	}
 }
+
+// Test that libraries provide both 32-bit and 64-bit variants.
+func TestMultilib(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_rlib {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}`)
+
+	_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib")
+	_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib")
+}
diff --git a/rust/test.go b/rust/test.go
index 04f844c..94568c1 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -50,6 +50,10 @@
 	testConfig android.Path
 }
 
+func (test *testDecorator) nativeCoverage() bool {
+	return true
+}
+
 func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
 	module := newModule(hod, android.MultilibFirst)
 
diff --git a/rust/testing.go b/rust/testing.go
index f9adec8..09008a8 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -22,51 +22,11 @@
 func GatherRequiredDepsForTest() string {
 	bp := `
 		rust_prebuilt_dylib {
-				name: "libarena_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
-				name: "libfmt_macros_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
-				name: "libgraphviz_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
-				name: "libserialize_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
 				name: "libstd_x86_64-unknown-linux-gnu",
 				srcs: [""],
 				host_supported: true,
 		}
 		rust_prebuilt_dylib {
-				name: "libsyntax_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
-				name: "libsyntax_ext_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
-				name: "libsyntax_pos_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
-				name: "libterm_x86_64-unknown-linux-gnu",
-				srcs: [""],
-				host_supported: true,
-		}
-		rust_prebuilt_dylib {
 				name: "libtest_x86_64-unknown-linux-gnu",
 				srcs: [""],
 				host_supported: true,
@@ -81,6 +41,36 @@
 			nocrt: true,
 			system_shared_libs: [],
 		}
+		rust_library_dylib {
+			name: "libstd",
+			crate_name: "std",
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			host_supported: true,
+		}
+		rust_library_rlib {
+			name: "libstd.static",
+			crate_name: "std",
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			host_supported: true,
+		}
+		rust_library_dylib {
+			name: "libtest",
+			crate_name: "test",
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			host_supported: true,
+
+		}
+		rust_library_rlib {
+			name: "libtest.static",
+			crate_name: "test",
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			host_supported: true,
+		}
+
 ` + cc.GatherRequiredDepsForTest(android.NoOsType)
 	return bp
 }
@@ -108,6 +98,7 @@
 		// rust mutators
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
 		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
+		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 
 	return ctx
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 076b3f5..dd0966f 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1 +1,3 @@
 per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
+per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
+per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index 7e3a82c..c60eaa1 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -1,5 +1,15 @@
 #!/bin/bash -e
 
+# This is a wrapper around "m" that builds the given modules in multi-arch mode
+# for all architectures supported by Mainline modules. The make (kati) stage is
+# skipped, so the build targets in the arguments can only be Soong modules or
+# intermediate output files - make targets and normal installed paths are not
+# supported.
+#
+# This script is typically used with "sdk" or "module_export" modules, which
+# Soong will install in $OUT_DIR/soong/mainline-sdks (cf
+# PathForMainlineSdksInstall in android/paths.go).
+
 export OUT_DIR=${OUT_DIR:-out}
 
 if [ -e ${OUT_DIR}/soong/.soong.in_make ]; then
@@ -8,11 +18,16 @@
   # expected to be supplied by the .mk files, and that might cause errors in
   # "m --skip-make" below. We therefore default to a different out dir
   # location in that case.
-  AML_OUT_DIR=out-aml
+  AML_OUT_DIR=out/aml
   echo "Avoiding in-make OUT_DIR '${OUT_DIR}' - building in '${AML_OUT_DIR}' instead"
   OUT_DIR=${AML_OUT_DIR}
 fi
 
+if [ ! -e "build/envsetup.sh" ]; then
+  echo "$0 must be run from the top of the tree"
+  exit 1
+fi
+
 source build/envsetup.sh
 
 my_get_build_var() {
@@ -22,13 +37,13 @@
   OUT_DIR=${OUT_DIR}/get_build_var get_build_var "$@"
 }
 
-PLATFORM_SDK_VERSION=$(my_get_build_var PLATFORM_SDK_VERSION)
-PLATFORM_VERSION=$(my_get_build_var PLATFORM_VERSION)
-PLATFORM_VERSION_ALL_CODENAMES=$(my_get_build_var PLATFORM_VERSION_ALL_CODENAMES)
+readonly PLATFORM_SDK_VERSION="$(my_get_build_var PLATFORM_SDK_VERSION)"
+readonly PLATFORM_VERSION="$(my_get_build_var PLATFORM_VERSION)"
+PLATFORM_VERSION_ALL_CODENAMES="$(my_get_build_var PLATFORM_VERSION_ALL_CODENAMES)"
 
 # PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to
 # turn this into ["O","P"].
-PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}
+PLATFORM_VERSION_ALL_CODENAMES="${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}"
 PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
 
 # Logic from build/make/core/goma.mk
@@ -46,10 +61,16 @@
   USE_GOMA=false
 fi
 
-SOONG_OUT=${OUT_DIR}/soong
+readonly SOONG_OUT=${OUT_DIR}/soong
 mkdir -p ${SOONG_OUT}
-SOONG_VARS=${SOONG_OUT}/soong.variables
+readonly SOONG_VARS=${SOONG_OUT}/soong.variables
 
+# Aml_abis: true
+#   -  This flag configures Soong to compile for all architectures required for
+#      Mainline modules.
+# CrossHost: linux_bionic
+# CrossHostArch: x86_64
+#   -  Enable Bionic on host as ART needs prebuilts for it.
 cat > ${SOONG_VARS}.new << EOF
 {
     "Platform_sdk_version": ${PLATFORM_SDK_VERSION},
@@ -58,6 +79,9 @@
 
     "DeviceName": "generic_arm64",
     "HostArch": "x86_64",
+    "HostSecondaryArch": "x86",
+    "CrossHost": "linux_bionic",
+    "CrossHostArch": "x86_64",
     "Aml_abis": true,
 
     "UseGoma": ${USE_GOMA}
@@ -72,4 +96,9 @@
   mv ${SOONG_VARS}.new ${SOONG_VARS}
 fi
 
+# We use force building LLVM components flag (even though we actually don't
+# compile them) because we don't have bionic host prebuilts
+# for them.
+export FORCE_BUILD_LLVM_COMPONENTS=true
+
 m --skip-make "$@"
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
new file mode 100755
index 0000000..f836ea9
--- /dev/null
+++ b/scripts/build-mainline-modules.sh
@@ -0,0 +1,68 @@
+#!/bin/bash -e
+
+# Non exhaustive list of modules where we want prebuilts. More can be added as
+# needed.
+MAINLINE_MODULES=(
+  com.android.art.debug
+  com.android.art.release
+  com.android.art.testing
+  com.android.conscrypt
+  com.android.runtime
+  com.android.tzdata
+  com.android.i18n
+)
+
+# List of SDKs and module exports we know of.
+MODULES_SDK_AND_EXPORTS=(
+  art-module-sdk
+  art-module-test-exports
+  conscrypt-module-sdk
+  conscrypt-module-test-exports
+  conscrypt-module-host-exports
+  runtime-module-sdk
+)
+
+# We want to create apex modules for all supported architectures.
+PRODUCTS=(
+  aosp_arm
+  aosp_arm64
+  aosp_x86
+  aosp_x86_64
+)
+
+if [ ! -e "build/make/core/Makefile" ]; then
+  echo "$0 must be run from the top of the tree"
+  exit 1
+fi
+
+echo_and_run() {
+  echo "$*"
+  "$@"
+}
+
+OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
+DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
+
+for product in "${PRODUCTS[@]}"; do
+  echo_and_run build/soong/soong_ui.bash --make-mode $@ \
+    TARGET_PRODUCT=${product} \
+    ${MAINLINE_MODULES[@]}
+
+  PRODUCT_OUT=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var PRODUCT_OUT)
+  TARGET_ARCH=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var TARGET_ARCH)
+  rm -rf ${DIST_DIR}/${TARGET_ARCH}/
+  mkdir -p ${DIST_DIR}/${TARGET_ARCH}/
+  for module in "${MAINLINE_MODULES[@]}"; do
+    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/
+  done
+done
+
+
+# Create multi-archs SDKs in a different out directory. The multi-arch script
+# uses Soong in --skip-make mode which cannot use the same directory as normal
+# mode with make.
+export OUT_DIR=${OUT_DIR}/aml
+echo_and_run build/soong/scripts/build-aml-prebuilts.sh ${MODULES_SDK_AND_EXPORTS[@]}
+
+rm -rf ${DIST_DIR}/mainline-sdks
+echo_and_run cp -R ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}
diff --git a/scripts/build_broken_logs.go b/scripts/build_broken_logs.go
index 8021e55..82ba749 100644
--- a/scripts/build_broken_logs.go
+++ b/scripts/build_broken_logs.go
@@ -54,11 +54,13 @@
 	DefaultDeprecated
 )
 
-var buildBrokenSettings = []struct {
+type Setting struct {
 	name     string
 	behavior BuildBrokenBehavior
 	warnings []string
-}{
+}
+
+var buildBrokenSettings = []Setting{
 	{
 		name:     "BUILD_BROKEN_DUP_RULES",
 		behavior: DefaultFalse,
@@ -68,6 +70,19 @@
 		name:     "BUILD_BROKEN_USES_NETWORK",
 		behavior: DefaultDeprecated,
 	},
+	{
+		name:     "BUILD_BROKEN_USES_BUILD_COPY_HEADERS",
+		behavior: DefaultTrue,
+		warnings: []string{
+			"COPY_HEADERS has been deprecated",
+			"COPY_HEADERS is deprecated",
+		},
+	},
+}
+
+type Branch struct {
+	Settings []Setting
+	Logs     []ProductLog
 }
 
 type ProductBranch struct {
@@ -82,35 +97,48 @@
 }
 
 type Log struct {
-	BuildBroken []*bool
-	HasBroken   []bool
+	WarningModuleTypes []string
+	ErrorModuleTypes   []string
+
+	BuildBroken map[string]*bool
+	HasBroken   map[string]int
 }
 
 func Merge(l, l2 Log) Log {
-	if len(l.BuildBroken) == 0 {
-		l.BuildBroken = make([]*bool, len(buildBrokenSettings))
+	if l.BuildBroken == nil {
+		l.BuildBroken = map[string]*bool{}
 	}
-	if len(l.HasBroken) == 0 {
-		l.HasBroken = make([]bool, len(buildBrokenSettings))
+	if l.HasBroken == nil {
+		l.HasBroken = map[string]int{}
 	}
 
-	if len(l.BuildBroken) != len(l2.BuildBroken) || len(l.HasBroken) != len(l2.HasBroken) {
-		panic("mis-matched logs")
-	}
-
-	for i, v := range l.BuildBroken {
+	for n, v := range l.BuildBroken {
 		if v == nil {
-			l.BuildBroken[i] = l2.BuildBroken[i]
+			l.BuildBroken[n] = l2.BuildBroken[n]
 		}
 	}
-	for i := range l.HasBroken {
-		l.HasBroken[i] = l.HasBroken[i] || l2.HasBroken[i]
+	for n, v := range l2.BuildBroken {
+		if _, ok := l.BuildBroken[n]; !ok {
+			l.BuildBroken[n] = v
+		}
+	}
+
+	for n := range l.HasBroken {
+		if l.HasBroken[n] < l2.HasBroken[n] {
+			l.HasBroken[n] = l2.HasBroken[n]
+		}
+	}
+	for n := range l2.HasBroken {
+		if _, ok := l.HasBroken[n]; !ok {
+			l.HasBroken[n] = l2.HasBroken[n]
+		}
 	}
 
 	return l
 }
 
-func PrintResults(products []ProductLog) {
+func PrintResults(branch Branch) {
+	products := branch.Logs
 	devices := map[string]Log{}
 	deviceNames := []string{}
 
@@ -124,39 +152,48 @@
 
 	sort.Strings(deviceNames)
 
-	for i, setting := range buildBrokenSettings {
+	for _, setting := range branch.Settings {
 		printed := false
+		n := setting.name
 
 		for _, device := range deviceNames {
 			log := devices[device]
 
 			if setting.behavior == DefaultTrue {
-				if log.BuildBroken[i] == nil || *log.BuildBroken[i] == false {
-					if log.HasBroken[i] {
+				if log.BuildBroken[n] == nil || *log.BuildBroken[n] == false {
+					if log.HasBroken[n] > 0 {
 						printed = true
-						fmt.Printf("  %s needs to set %s := true\n", device, setting.name)
+						plural := ""
+						if log.HasBroken[n] > 1 {
+							plural = "s"
+						}
+						fmt.Printf("  %s needs to set %s := true  (%d instance%s)\n", device, setting.name, log.HasBroken[n], plural)
 					}
-				} else if !log.HasBroken[i] {
+				} else if log.HasBroken[n] == 0 {
 					printed = true
 					fmt.Printf("  %s sets %s := true, but does not need it\n", device, setting.name)
 				}
 			} else if setting.behavior == DefaultFalse {
-				if log.BuildBroken[i] == nil {
+				if log.BuildBroken[n] == nil {
 					// Nothing to be done
-				} else if *log.BuildBroken[i] == false {
+				} else if *log.BuildBroken[n] == false {
 					printed = true
 					fmt.Printf("  %s sets %s := false, which is the default and can be removed\n", device, setting.name)
-				} else if !log.HasBroken[i] {
+				} else if log.HasBroken[n] == 0 {
 					printed = true
 					fmt.Printf("  %s sets %s := true, but does not need it\n", device, setting.name)
 				}
 			} else if setting.behavior == DefaultDeprecated {
-				if log.BuildBroken[i] != nil {
+				if log.BuildBroken[n] != nil {
 					printed = true
-					if log.HasBroken[i] {
-						fmt.Printf("  %s sets %s := %v, which is deprecated, but has failures\n", device, setting.name, *log.BuildBroken[i])
+					if log.HasBroken[n] > 0 {
+						plural := ""
+						if log.HasBroken[n] > 1 {
+							plural = "s"
+						}
+						fmt.Printf("  %s sets %s := %v, which is deprecated, but has %d failure%s\n", device, setting.name, *log.BuildBroken[n], log.HasBroken[n], plural)
 					} else {
-						fmt.Printf("  %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[i])
+						fmt.Printf("  %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[n])
 					}
 				}
 			}
@@ -168,17 +205,45 @@
 	}
 }
 
-func ParseBranch(name string) []ProductLog {
+func ParseBranch(name string) Branch {
 	products, err := filepath.Glob(filepath.Join(name, "*"))
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	ret := []ProductLog{}
+	ret := Branch{Logs: []ProductLog{}}
 	for _, product := range products {
 		product = filepath.Base(product)
 
-		ret = append(ret, ParseProduct(ProductBranch{Branch: name, Name: product}))
+		ret.Logs = append(ret.Logs, ParseProduct(ProductBranch{Branch: name, Name: product}))
+	}
+
+	ret.Settings = append(ret.Settings, buildBrokenSettings...)
+	if len(ret.Logs) > 0 {
+		for _, mtype := range ret.Logs[0].WarningModuleTypes {
+			if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
+				continue
+			}
+			ret.Settings = append(ret.Settings, Setting{
+				name:     "BUILD_BROKEN_USES_" + mtype,
+				behavior: DefaultTrue,
+				warnings: []string{mtype + " has been deprecated"},
+			})
+		}
+		for _, mtype := range ret.Logs[0].ErrorModuleTypes {
+			if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
+				continue
+			}
+			ret.Settings = append(ret.Settings, Setting{
+				name:     "BUILD_BROKEN_USES_" + mtype,
+				behavior: DefaultFalse,
+				warnings: []string{mtype + " has been deprecated"},
+			})
+		}
+	}
+
+	for _, productLog := range ret.Logs {
+		ScanProduct(ret.Settings, productLog)
 	}
 	return ret
 }
@@ -192,15 +257,15 @@
 	ret := ProductLog{
 		ProductBranch: p,
 		Log: Log{
-			BuildBroken: make([]*bool, len(buildBrokenSettings)),
-			HasBroken:   make([]bool, len(buildBrokenSettings)),
+			BuildBroken: map[string]*bool{},
+			HasBroken:   map[string]int{},
 		},
 	}
 
 	lines := strings.Split(string(soongLog), "\n")
 	for _, line := range lines {
 		fields := strings.Split(line, " ")
-		if len(fields) != 5 {
+		if len(fields) < 5 {
 			continue
 		}
 
@@ -208,30 +273,35 @@
 			ret.Device = fields[4]
 		}
 
+		if fields[3] == "DEFAULT_WARNING_BUILD_MODULE_TYPES" {
+			ret.WarningModuleTypes = fields[4:]
+		}
+		if fields[3] == "DEFAULT_ERROR_BUILD_MODULE_TYPES" {
+			ret.ErrorModuleTypes = fields[4:]
+		}
+
 		if strings.HasPrefix(fields[3], "BUILD_BROKEN_") {
-			for i, setting := range buildBrokenSettings {
-				if setting.name == fields[3] {
-					ret.BuildBroken[i] = ParseBoolPtr(fields[4])
-				}
-			}
+			ret.BuildBroken[fields[3]] = ParseBoolPtr(fields[4])
 		}
 	}
 
-	stdLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "std_full.log"))
+	return ret
+}
+
+func ScanProduct(settings []Setting, l ProductLog) {
+	stdLog, err := ioutil.ReadFile(filepath.Join(l.Branch, l.Name, "std_full.log"))
 	if err != nil {
 		log.Fatal(err)
 	}
 	stdStr := string(stdLog)
 
-	for i, setting := range buildBrokenSettings {
+	for _, setting := range settings {
 		for _, warning := range setting.warnings {
 			if strings.Contains(stdStr, warning) {
-				ret.HasBroken[i] = true
+				l.HasBroken[setting.name] += strings.Count(stdStr, warning)
 			}
 		}
 	}
-
-	return ret
 }
 
 func ParseBoolPtr(str string) *bool {
diff --git a/scripts/generate-notice-files.py b/scripts/generate-notice-files.py
new file mode 100755
index 0000000..49011b2
--- /dev/null
+++ b/scripts/generate-notice-files.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012 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.
+"""
+Usage: generate-notice-files --text-output [plain text output file] \
+               --html-output [html output file] \
+               --xml-output [xml output file] \
+               -t [file title] -s [directory of notices]
+
+Generate the Android notice files, including both text and html files.
+
+-h to display this usage message and exit.
+"""
+from collections import defaultdict
+import argparse
+import hashlib
+import itertools
+import os
+import os.path
+import re
+import sys
+
+MD5_BLOCKSIZE = 1024 * 1024
+HTML_ESCAPE_TABLE = {
+    "&": "&amp;",
+    '"': "&quot;",
+    "'": "&apos;",
+    ">": "&gt;",
+    "<": "&lt;",
+    }
+
+def hexify(s):
+    return ("%02x"*len(s)) % tuple(map(ord, s))
+
+def md5sum(filename):
+    """Calculate an MD5 of the file given by FILENAME,
+    and return hex digest as a string.
+    Output should be compatible with md5sum command"""
+
+    f = open(filename, "rb")
+    sum = hashlib.md5()
+    while 1:
+        block = f.read(MD5_BLOCKSIZE)
+        if not block:
+            break
+        sum.update(block)
+    f.close()
+    return hexify(sum.digest())
+
+
+def html_escape(text):
+    """Produce entities within text."""
+    return "".join(HTML_ESCAPE_TABLE.get(c,c) for c in text)
+
+HTML_OUTPUT_CSS="""
+<style type="text/css">
+body { padding: 0; font-family: sans-serif; }
+.same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
+.label { font-weight: bold; }
+.file-list { margin-left: 1em; color: blue; }
+</style>
+"""
+
+def combine_notice_files_html(file_hash, input_dir, output_filename):
+    """Combine notice files in FILE_HASH and output a HTML version to OUTPUT_FILENAME."""
+
+    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
+
+    # Set up a filename to row id table (anchors inside tables don't work in
+    # most browsers, but href's to table row ids do)
+    id_table = {}
+    id_count = 0
+    for value in file_hash:
+        for filename in value:
+             id_table[filename] = id_count
+        id_count += 1
+
+    # Open the output file, and output the header pieces
+    output_file = open(output_filename, "wb")
+
+    print >> output_file, "<html><head>"
+    print >> output_file, HTML_OUTPUT_CSS
+    print >> output_file, '</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">'
+
+    # Output our table of contents
+    print >> output_file, '<div class="toc">'
+    print >> output_file, "<ul>"
+
+    # Flatten the list of lists into a single list of filenames
+    sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
+
+    # Print out a nice table of contents
+    for filename in sorted_filenames:
+        stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
+        print >> output_file, '<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename)
+
+    print >> output_file, "</ul>"
+    print >> output_file, "</div><!-- table of contents -->"
+    # Output the individual notice file lists
+    print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">'
+    for value in file_hash:
+        print >> output_file, '<tr id="id%d"><td class="same-license">' % id_table.get(value[0])
+        print >> output_file, '<div class="label">Notices for file(s):</div>'
+        print >> output_file, '<div class="file-list">'
+        for filename in value:
+            print >> output_file, "%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))
+        print >> output_file, "</div><!-- file-list -->"
+        print >> output_file
+        print >> output_file, '<pre class="license-text">'
+        print >> output_file, html_escape(open(value[0]).read())
+        print >> output_file, "</pre><!-- license-text -->"
+        print >> output_file, "</td></tr><!-- same-license -->"
+        print >> output_file
+        print >> output_file
+        print >> output_file
+
+    # Finish off the file output
+    print >> output_file, "</table>"
+    print >> output_file, "</body></html>"
+    output_file.close()
+
+def combine_notice_files_text(file_hash, input_dir, output_filename, file_title):
+    """Combine notice files in FILE_HASH and output a text version to OUTPUT_FILENAME."""
+
+    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
+    output_file = open(output_filename, "wb")
+    print >> output_file, file_title
+    for value in file_hash:
+      print >> output_file, "============================================================"
+      print >> output_file, "Notices for file(s):"
+      for filename in value:
+        print >> output_file, SRC_DIR_STRIP_RE.sub(r"\1", filename)
+      print >> output_file, "------------------------------------------------------------"
+      print >> output_file, open(value[0]).read()
+    output_file.close()
+
+def combine_notice_files_xml(files_with_same_hash, input_dir, output_filename):
+    """Combine notice files in FILE_HASH and output a XML version to OUTPUT_FILENAME."""
+
+    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
+
+    # Set up a filename to row id table (anchors inside tables don't work in
+    # most browsers, but href's to table row ids do)
+    id_table = {}
+    for file_key in files_with_same_hash.keys():
+        for filename in files_with_same_hash[file_key]:
+             id_table[filename] = file_key
+
+    # Open the output file, and output the header pieces
+    output_file = open(output_filename, "wb")
+
+    print >> output_file, '<?xml version="1.0" encoding="utf-8"?>'
+    print >> output_file, "<licenses>"
+
+    # Flatten the list of lists into a single list of filenames
+    sorted_filenames = sorted(id_table.keys())
+
+    # Print out a nice table of contents
+    for filename in sorted_filenames:
+        stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
+        print >> output_file, '<file-name contentId="%s">%s</file-name>' % (id_table.get(filename), stripped_filename)
+
+    print >> output_file
+    print >> output_file
+
+    processed_file_keys = []
+    # Output the individual notice file lists
+    for filename in sorted_filenames:
+        file_key = id_table.get(filename)
+        if file_key in processed_file_keys:
+            continue
+        processed_file_keys.append(file_key)
+
+        print >> output_file, '<file-content contentId="%s"><![CDATA[%s]]></file-content>' % (file_key, html_escape(open(filename).read()))
+        print >> output_file
+
+    # Finish off the file output
+    print >> output_file, "</licenses>"
+    output_file.close()
+
+def get_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '--text-output', required=True,
+        help='The text output file path.')
+    parser.add_argument(
+        '--html-output',
+        help='The html output file path.')
+    parser.add_argument(
+        '--xml-output',
+        help='The xml output file path.')
+    parser.add_argument(
+        '-t', '--title', required=True,
+        help='The file title.')
+    parser.add_argument(
+        '-s', '--source-dir', required=True,
+        help='The directory containing notices.')
+    parser.add_argument(
+        '-i', '--included-subdirs', action='append',
+        help='The sub directories which should be included.')
+    parser.add_argument(
+        '-e', '--excluded-subdirs', action='append',
+        help='The sub directories which should be excluded.')
+    return parser.parse_args()
+
+def main(argv):
+    args = get_args()
+
+    txt_output_file = args.text_output
+    html_output_file = args.html_output
+    xml_output_file = args.xml_output
+    file_title = args.title
+    included_subdirs = []
+    excluded_subdirs = []
+    if args.included_subdirs is not None:
+        included_subdirs = args.included_subdirs
+    if args.excluded_subdirs is not None:
+        excluded_subdirs = args.excluded_subdirs
+
+    # Find all the notice files and md5 them
+    input_dir = os.path.normpath(args.source_dir)
+    files_with_same_hash = defaultdict(list)
+    for root, dir, files in os.walk(input_dir):
+        for file in files:
+            matched = True
+            if len(included_subdirs) > 0:
+                matched = False
+                for subdir in included_subdirs:
+                    if (root == (input_dir + '/' + subdir) or
+                        root.startswith(input_dir + '/' + subdir + '/')):
+                        matched = True
+                        break
+            elif len(excluded_subdirs) > 0:
+                for subdir in excluded_subdirs:
+                    if (root == (input_dir + '/' + subdir) or
+                        root.startswith(input_dir + '/' + subdir + '/')):
+                        matched = False
+                        break
+            if matched and file.endswith(".txt"):
+                filename = os.path.join(root, file)
+                file_md5sum = md5sum(filename)
+                files_with_same_hash[file_md5sum].append(filename)
+
+    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(files_with_same_hash.keys())]
+
+    combine_notice_files_text(filesets, input_dir, txt_output_file, file_title)
+
+    if html_output_file is not None:
+        combine_notice_files_html(filesets, input_dir, html_output_file)
+
+    if xml_output_file is not None:
+        combine_notice_files_xml(files_with_same_hash, input_dir, xml_output_file)
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/scripts/jsonmodify.py b/scripts/jsonmodify.py
index 4b2c3c2..ba1109e 100755
--- a/scripts/jsonmodify.py
+++ b/scripts/jsonmodify.py
@@ -112,9 +112,10 @@
 
   if args.out:
     with open(args.out, "w") as f:
-      json.dump(obj, f, indent=2)
+      json.dump(obj, f, indent=2, separators=(',', ': '))
+      f.write('\n')
   else:
-    print(json.dumps(obj, indent=2))
+    print(json.dumps(obj, indent=2, separators=(',', ': ')))
 
 
 if __name__ == '__main__':
diff --git a/scripts/mergenotice.py b/scripts/mergenotice.py
index 407ae8c..fe99073 100755
--- a/scripts/mergenotice.py
+++ b/scripts/mergenotice.py
@@ -16,7 +16,7 @@
 #
 """
 Merges input notice files to the output file while ignoring duplicated files
-This script shouldn't be confused with build/make/tools/generate-notice-files.py
+This script shouldn't be confused with build/soong/scripts/generate-notice-files.py
 which is responsible for creating the final notice file for all artifacts
 installed. This script has rather limited scope; it is meant to create a merged
 notice file for a set of modules that are packaged together, e.g. in an APEX.
diff --git a/scripts/reverse-deps.sh b/scripts/reverse-deps.sh
new file mode 100755
index 0000000..02b7dcb
--- /dev/null
+++ b/scripts/reverse-deps.sh
@@ -0,0 +1,246 @@
+#!/bin/bash
+
+set -eu
+
+# 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.
+
+# Tool to evaluate the transitive closure of the ninja dependency graph of the
+# files and targets depending on a given target.
+#
+# i.e. the list of things that could change after changing a target.
+
+readonly me=$(basename "${0}")
+
+readonly usage="usage: ${me} {options} target [target...]
+
+Evaluate the reverse transitive closure of ninja targets depending on one or
+more targets.
+
+Options:
+
+  -(no)quiet        Suppresses progress output to stderr and interactive
+    alias -(no)q    prompts. By default, when stderr is a tty, progress gets
+                    reported to stderr; when both stderr and stdin are tty,
+                    the script asks user whether to delete intermediate files.
+                    When suppressed or not prompted, script always deletes the
+                    temporary / intermediate files.
+  -sep=<delim>      Use 'delim' as output field separator between notice
+                    checksum and notice filename in notice output.
+                    e.g. sep='\t'
+                    (Default space)
+  -csv              Shorthand for -sep=','
+
+At minimum, before running this script, you must first run:
+$ source build/envsetup.sh
+$ lunch
+$ m nothing
+to setup the build environment, choose a target platform, and build the ninja
+dependency graph.
+"
+
+function die() { echo -e "${*}" >&2; exit 2; }
+
+# Reads one input target per line from stdin; outputs (isnotice target) tuples.
+#
+# output target is a ninja target that the input target depends on
+# isnotice in {0,1} with 1 for output targets believed to be license or notice
+#
+# only argument is the dependency depth indicator
+function getDeps() {
+    (tr '\n' '\0' | xargs -0 "${ninja_bin}" -f "${ninja_file}" -t query) \
+    | awk -v depth="${1}" '
+      BEGIN {
+        inoutput = 0
+      }
+      $0 ~ /^\S\S*:$/ {
+        inoutput = 0
+      }
+      inoutput != 0 {
+        print gensub(/^\s*/, "", "g")" "depth
+      }
+      $1 == "outputs:" {
+        inoutput = 1
+      }
+    '
+}
+
+
+if [ -z "${ANDROID_BUILD_TOP}" ]; then
+    die "${me}: Run 'lunch' to configure the build environment"
+fi
+
+if [ -z "${TARGET_PRODUCT}" ]; then
+    die "${me}: Run 'lunch' to configure the build environment"
+fi
+
+ninja_file="${ANDROID_BUILD_TOP}/out/combined-${TARGET_PRODUCT}.ninja"
+if [ ! -f "${ninja_file}" ]; then
+    die "${me}: Run 'm nothing' to build the dependency graph"
+fi
+
+ninja_bin="${ANDROID_BUILD_TOP}/prebuilts/build-tools/linux-x86/bin/ninja"
+if [ ! -x "${ninja_bin}" ]; then
+    die "${me}: Cannot find ninja executable expected at ${ninja_bin}"
+fi
+
+
+# parse the command-line
+
+declare -a targets # one or more targets to evaluate
+
+quiet=false      # whether to suppress progress
+
+sep=" "          # output separator between depth and target
+
+use_stdin=false  # whether to read targets from stdin i.e. target -
+
+while [ $# -gt 0 ]; do
+    case "${1:-}" in
+      -)
+        use_stdin=true
+      ;;
+      -*)
+        flag=$(expr "${1}" : '^-*\(.*\)$')
+        case "${flag:-}" in
+          q) ;&
+          quiet)
+            quiet=true;;
+          noq) ;&
+          noquiet)
+            quiet=false;;
+          csv)
+            sep=",";;
+          sep)
+            sep="${2?"${usage}"}"; shift;;
+          sep=*)
+            sep=$(expr "${flag}" : '^sep=\(.*\)$';;
+          *)
+            die "Unknown flag ${1}"
+          ;;
+        esac
+      ;;
+      *)
+        targets+=("${1:-}")
+      ;;
+    esac
+    shift
+done
+
+if [ ! -v targets[0] ] && ! ${use_stdin}; then
+    die "${usage}\n\nNo target specified."
+fi
+
+# showProgress when stderr is a tty
+if [ -t 2 ] && ! ${quiet}; then
+    showProgress=true
+else
+    showProgress=false
+fi
+
+# interactive when both stderr and stdin are tty
+if ${showProgress} && [ -t 0 ]; then
+    interactive=true
+else
+    interactive=false
+fi
+
+
+readonly tmpFiles=$(mktemp -d "${TMPDIR}.tdeps.XXXXXXXXX")
+if [ -z "${tmpFiles}" ]; then
+    die "${me}: unable to create temporary directory"
+fi
+
+# The deps files contain unique (isnotice target) tuples where
+# isnotice in {0,1} with 1 when ninja target `target` is a license or notice.
+readonly oldDeps="${tmpFiles}/old"
+readonly newDeps="${tmpFiles}/new"
+readonly allDeps="${tmpFiles}/all"
+
+if ${use_stdin}; then # start deps by reading 1 target per line from stdin
+  awk '
+    NF > 0 {
+      print gensub(/\s*$/, "", "g", gensub(/^\s*/, "", "g"))" "0
+    }
+  ' >"${newDeps}"
+else # start with no deps by clearing file
+  : >"${newDeps}"
+fi
+
+# extend deps by appending targets from command-line
+for idx in "${!targets[*]}"; do
+    echo "${targets[${idx}]} 0" >>"${newDeps}"
+done
+
+# remove duplicates and start with new, old and all the same
+sort -u <"${newDeps}" >"${allDeps}"
+cp "${allDeps}" "${newDeps}"
+cp "${allDeps}" "${oldDeps}"
+
+# report depth of dependenciens when showProgress
+depth=0
+
+while [ $(wc -l < "${newDeps}") -gt 0 ]; do
+    if ${showProgress}; then
+        echo "depth ${depth} has "$(wc -l < "${newDeps}")" targets" >&2
+    fi
+    depth=$(expr ${depth} + 1)
+    ( # recalculate dependencies by combining unique inputs of new deps w. old
+        cut -d\  -f1 "${newDeps}" | getDeps "${depth}"
+        cat "${oldDeps}"
+    ) | sort -n | awk '
+      BEGIN {
+        prev = ""
+      }
+      {
+        depth = $NF
+        $NF = ""
+        gsub(/\s*$/, "")
+        if ($0 != prev) {
+          print gensub(/\s*$/, "", "g")" "depth
+        }
+        prev = $0
+      }
+    ' >"${allDeps}"
+    # recalculate new dependencies as net additions to old dependencies
+    set +e
+    diff "${oldDeps}" "${allDeps}" --old-line-format='' \
+      --new-line-format='%L' --unchanged-line-format='' > "${newDeps}"
+    set -e
+    # recalculate old dependencies for next iteration
+    cp "${allDeps}" "${oldDeps}"
+done
+
+# found all deps -- clean up last iteration of old and new
+rm -f "${oldDeps}"
+rm -f "${newDeps}"
+
+if ${showProgress}; then
+    echo $(wc -l < "${allDeps}")" targets" >&2
+fi
+
+awk -v sep="${sep}" '{
+  depth = $NF
+  $NF = ""
+  gsub(/\s*$/, "")
+  print depth sep $0
+}' "${allDeps}" | sort -n
+
+if ${interactive}; then
+    echo -n "$(date '+%F %-k:%M:%S') Delete ${tmpFiles} ? [n] " >&2
+    read answer
+    case "${answer}" in [yY]*) rm -fr "${tmpFiles}";; esac
+else
+    rm -fr "${tmpFiles}"
+fi
diff --git a/scripts/transitive-deps.sh b/scripts/transitive-deps.sh
new file mode 100755
index 0000000..c6f8b76
--- /dev/null
+++ b/scripts/transitive-deps.sh
@@ -0,0 +1,491 @@
+#!/bin/bash
+
+set -eu
+
+# 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.
+
+# Tool to evaluate the transitive closure of the ninja dependency graph of the
+# files and targets a given target depends on.
+#
+# i.e. the list of things that, if changed, could cause a change to a target.
+
+readonly me=$(basename "${0}")
+
+readonly usage="usage: ${me} {options} target [target...]
+
+Evaluate the transitive closure of files and ninja targets that one or more
+targets depend on.
+
+Dependency Options:
+
+  -(no)order_deps   Whether to include order-only dependencies. (Default false)
+  -(no)implicit     Whether to include implicit dependencies. (Default true)
+  -(no)explicit     Whether to include regular / explicit deps. (Default true)
+
+  -nofollow         Unanchored regular expression. Matching paths and targets
+                    always get reported. Their dependencies do not get reported
+                    unless first encountered in a 'container' file type.
+                    Multiple allowed and combined using '|'.
+                    e.g. -nofollow='*.so' not -nofollow='.so$'
+                    -nofollow='*.so|*.dex' or -nofollow='*.so' -nofollow='.dex'
+                    (Defaults to no matches)
+  -container        Unanchored regular expression. Matching file extensions get
+                    treated as 'container' files for -nofollow option.
+                    Multiple allowed and combines using '|'
+                    (Default 'apex|apk|zip|jar|tar|tgz')
+
+Output Options:
+
+  -(no)quiet        Suppresses progress output to stderr and interactive
+    alias -(no)q    prompts. By default, when stderr is a tty, progress gets
+                    reported to stderr; when both stderr and stdin are tty,
+                    the script asks user whether to delete intermediate files.
+                    When suppressed or not prompted, script always deletes the
+                    temporary / intermediate files.
+  -sep=<delim>      Use 'delim' as output field separator between notice
+                    checksum and notice filename in notice output.
+                    e.g. sep='\t'
+                    (Default space)
+  -csv              Shorthand for -sep=','
+  -directories=<f>  Output directory names of dependencies to 'f'.
+    alias -d        User '/dev/stdout' to send directories to stdout. Defaults
+                    to no directory output.
+  -notices=<file>   Output license and notice file paths to 'file'.
+    alias -n        Use '/dev/stdout' to send notices to stdout. Defaults to no
+                    license/notice output.
+  -projects=<file>  Output git project names to 'file'. Use '/dev/stdout' to
+    alias -p        send projects to stdout. Defaults to no project output.
+  -targets=<fils>   Output target dependencies to 'file'. Use '/dev/stdout' to
+    alias -t        send targets to stdout.
+                    When no directory, notice, project or target output options
+                    given, defaults to stdout. Otherwise, defaults to no target
+                    output.
+
+At minimum, before running this script, you must first run:
+$ source build/envsetup.sh
+$ lunch
+$ m nothing
+to setup the build environment, choose a target platform, and build the ninja
+dependency graph.
+"
+
+function die() { echo -e "${*}" >&2; exit 2; }
+
+# Reads one input target per line from stdin; outputs (isnotice target) tuples.
+#
+# output target is a ninja target that the input target depends on
+# isnotice in {0,1} with 1 for output targets believed to be license or notice
+function getDeps() {
+    (tr '\n' '\0' | xargs -0 -r "${ninja_bin}" -f "${ninja_file}" -t query) \
+    | awk -v include_order="${include_order_deps}" \
+        -v include_implicit="${include_implicit_deps}" \
+        -v include_explicit="${include_deps}" \
+        -v containers="${container_types}" \
+    '
+      BEGIN {
+        ininput = 0
+        isnotice = 0
+        currFileName = ""
+        currExt = ""
+      }
+      $1 == "outputs:" {
+        ininput = 0
+      }
+      ininput == 0 && $0 ~ /^\S\S*:$/ {
+        isnotice = ($0 ~ /.*NOTICE.*[.]txt:$/)
+        currFileName = gensub(/^.*[/]([^/]*)[:]$/, "\\1", "g")
+        currExt = gensub(/^.*[.]([^./]*)[:]$/, "\\1", "g")
+      }
+      ininput != 0 && $1 !~ /^[|][|]?/ {
+        if (include_explicit == "true") {
+          fileName = gensub(/^.*[/]([^/]*)$/, "\\1", "g")
+          print ( \
+              (isnotice && $0 !~ /^\s*build[/]soong[/]scripts[/]/) \
+              || $0 ~ /NOTICE|LICEN[CS]E/ \
+              || $0 ~ /(notice|licen[cs]e)[.]txt/ \
+          )" "(fileName == currFileName||currExt ~ "^(" containers ")$")" "gensub(/^\s*/, "", "g")
+        }
+      }
+      ininput != 0 && $1 == "|" {
+        if (include_implicit == "true") {
+          fileName = gensub(/^.*[/]([^/]*)$/, "\\1", "g")
+          $1 = ""
+          print ( \
+              (isnotice && $0 !~ /^\s*build[/]soong[/]scripts[/]/) \
+              || $0 ~ /NOTICE|LICEN[CS]E/ \
+              || $0 ~ /(notice|licen[cs]e)[.]txt/ \
+          )" "(fileName == currFileName||currExt ~ "^(" containers ")$")" "gensub(/^\s*/, "", "g")
+        }
+      }
+      ininput != 0 && $1 == "||" {
+        if (include_order == "true") {
+          fileName = gensub(/^.*[/]([^/]*)$/, "\\1", "g")
+          $1 = ""
+          print ( \
+              (isnotice && $0 !~ /^\s*build[/]soong[/]scripts[/]/) \
+              || $0 ~ /NOTICE|LICEN[CS]E/ \
+              || $0 ~ /(notice|licen[cs]e)[.]txt/ \
+          )" "(fileName == currFileName||currExt ~ "^(" containers ")$")" "gensub(/^\s*/, "", "g")
+        }
+      }
+      $1 == "input:" {
+        ininput = 1
+      }
+    '
+}
+
+# Reads one input directory per line from stdin; outputs unique git projects.
+function getProjects() {
+    while read d; do
+        while [ "${d}" != '.' ] && [ "${d}" != '/' ]; do
+            if [ -d "${d}/.git/" ]; then
+                echo "${d}"
+                break
+            fi
+            d=$(dirname "${d}")
+        done
+    done | sort -u
+}
+
+
+if [ -z "${ANDROID_BUILD_TOP}" ]; then
+    die "${me}: Run 'lunch' to configure the build environment"
+fi
+
+if [ -z "${TARGET_PRODUCT}" ]; then
+    die "${me}: Run 'lunch' to configure the build environment"
+fi
+
+readonly ninja_file="${ANDROID_BUILD_TOP}/out/combined-${TARGET_PRODUCT}.ninja"
+if [ ! -f "${ninja_file}" ]; then
+    die "${me}: Run 'm nothing' to build the dependency graph"
+fi
+
+readonly ninja_bin="${ANDROID_BUILD_TOP}/prebuilts/build-tools/linux-x86/bin/ninja"
+if [ ! -x "${ninja_bin}" ]; then
+    die "${me}: Cannot find ninja executable expected at ${ninja_bin}"
+fi
+
+
+# parse the command-line
+
+declare -a targets # one or more targets to evaluate
+
+include_order_deps=false    # whether to trace through || "order dependencies"
+include_implicit_deps=true  # whether to trace through | "implicit deps"
+include_deps=true           # whether to trace through regular explicit deps
+quiet=false                 # whether to suppress progress
+
+projects_out=''             # where to output the list of projects
+directories_out=''          # where to output the list of directories
+targets_out=''              # where to output the list of targets/source files
+notices_out=''              # where to output the list of license/notice files
+
+sep=" "                     # separator between md5sum and notice filename
+
+nofollow=''                 # regularexp must fully match targets to skip
+
+container_types=''          # regularexp must full match file extension
+                            # defaults to 'apex|apk|zip|jar|tar|tgz' below.
+
+use_stdin=false             # whether to read targets from stdin i.e. target -
+
+while [ $# -gt 0 ]; do
+    case "${1:-}" in
+      -)
+        use_stdin=true
+      ;;
+      -*)
+        flag=$(expr "${1}" : '^-*\(.*\)$')
+        case "${flag:-}" in
+          order_deps)
+            include_order_deps=true;;
+          noorder_deps)
+            include_order_deps=false;;
+          implicit)
+            include_implicit_deps=true;;
+          noimplicit)
+            include_implicit_deps=false;;
+          explicit)
+            include_deps=true;;
+          noexplicit)
+            include_deps=false;;
+          csv)
+            sep=",";;
+          sep)
+            sep="${2?"${usage}"}"; shift;;
+          sep=)
+            sep=$(expr "${flag}" : '^sep=\(.*\)$');;
+          q) ;&
+          quiet)
+            quiet=true;;
+          noq) ;&
+          noquiet)
+            quiet=false;;
+          nofollow)
+            case "${nofollow}" in
+              '')
+                nofollow="${2?"${usage}"}";;
+              *)
+                nofollow="${nofollow}|${2?"${usage}"}";;
+            esac
+            shift
+          ;;
+          nofollow=*)
+            case "${nofollow}" in
+              '')
+                nofollow=$(expr "${flag}" : '^nofollow=\(.*\)$');;
+              *)
+                nofollow="${nofollow}|"$(expr "${flag}" : '^nofollow=\(.*\)$');;
+            esac
+          ;;
+          container)
+            container_types="${container_types}|${2?"${usage}"}";;
+          container=*)
+            container_types="${container_types}|"$(expr "${flag}" : '^container=\(.*\)$');;
+          p) ;&
+          projects)
+            projects_out="${2?"${usage}"}"; shift;;
+          p=*) ;&
+          projects=*)
+            projects_out=$(expr "${flag}" : '^.*=\(.*\)$');;
+          d) ;&
+          directores)
+            directories_out="${2?"${usage}"}"; shift;;
+          d=*) ;&
+          directories=*)
+            directories_out=$(expr "${flag}" : '^.*=\(.*\)$');;
+          t) ;&
+          targets)
+            targets_out="${2?"${usage}"}"; shift;;
+          t=*) ;&
+          targets=)
+            targets_out=$(expr "${flag}" : '^.*=\(.*\)$');;
+          n) ;&
+          notices)
+            notices_out="${2?"${usage}"}"; shift;;
+          n=*) ;&
+          notices=)
+            notices_out=$(expr "${flag}" : '^.*=\(.*\)$');;
+          *)
+            die "Unknown flag ${1}";;
+        esac
+      ;;
+      *)
+        targets+=("${1:-}")
+      ;;
+    esac
+    shift
+done
+
+
+# fail fast if command-line arguments are invalid
+
+if [ ! -v targets[0] ] && ! ${use_stdin}; then
+    die "${usage}\n\nNo target specified."
+fi
+
+if [ -z "${projects_out}" ] \
+  && [ -z "${directories_out}" ] \
+  && [ -z "${targets_out}" ] \
+  && [ -z "${notices_out}" ]
+then
+    targets_out='/dev/stdout'
+fi
+
+if [ -z "${container_types}" ]; then
+  container_types='apex|apk|zip|jar|tar|tgz'
+fi
+
+# showProgress when stderr is a tty
+if [ -t 2 ] && ! ${quiet}; then
+    showProgress=true
+else
+    showProgress=false
+fi
+
+# interactive when both stderr and stdin are tty
+if ${showProgress} && [ -t 0 ]; then
+    interactive=true
+else
+    interactive=false
+fi
+
+
+readonly tmpFiles=$(mktemp -d "${TMPDIR}.tdeps.XXXXXXXXX")
+if [ -z "${tmpFiles}" ]; then
+    die "${me}: unable to create temporary directory"
+fi
+
+# The deps files contain unique (isnotice target) tuples where
+# isnotice in {0,1} with 1 when ninja target 'target' is a license or notice.
+readonly oldDeps="${tmpFiles}/old"
+readonly newDeps="${tmpFiles}/new"
+readonly allDeps="${tmpFiles}/all"
+
+if ${use_stdin}; then # start deps by reading 1 target per line from stdin
+  awk '
+    NF > 0 {
+      print ( \
+          $0 ~ /NOTICE|LICEN[CS]E/ \
+          || $0 ~ /(notice|licen[cs]e)[.]txt/ \
+      )" "gensub(/\s*$/, "", "g", gensub(/^\s*/, "", "g"))
+    }
+  ' > "${newDeps}"
+else # start with no deps by clearing file
+  : > "${newDeps}"
+fi
+
+# extend deps by appending targets from command-line
+for idx in "${!targets[*]}"; do
+    isnotice='0'
+    case "${targets[${idx}]}" in
+      *NOTICE*) ;&
+      *LICEN[CS]E*) ;&
+      *notice.txt) ;&
+      *licen[cs]e.txt)
+        isnotice='1';;
+    esac
+    echo "${isnotice} 1 ${targets[${idx}]}" >> "${newDeps}"
+done
+
+# remove duplicates and start with new, old and all the same
+sort -u < "${newDeps}" > "${allDeps}"
+cp "${allDeps}" "${newDeps}"
+cp "${allDeps}" "${oldDeps}"
+
+# report depth of dependenciens when showProgress
+depth=0
+
+# 1st iteration always unfiltered
+filter='cat'
+while [ $(wc -l < "${newDeps}") -gt 0 ]; do
+    if ${showProgress}; then
+        echo "depth ${depth} has "$(wc -l < "${newDeps}")" targets" >&2
+        depth=$(expr ${depth} + 1)
+    fi
+    ( # recalculate dependencies by combining unique inputs of new deps w. old
+        set +e
+        sh -c "${filter}" < "${newDeps}" | cut -d\  -f3- | getDeps
+        set -e
+        cat "${oldDeps}"
+    ) | sort -u > "${allDeps}"
+    # recalculate new dependencies as net additions to old dependencies
+    set +e
+    diff "${oldDeps}" "${allDeps}" --old-line-format='' --new-line-format='%L' \
+      --unchanged-line-format='' > "${newDeps}"
+    set -e
+    # apply filters on subsequent iterations
+    case "${nofollow}" in
+      '')
+        filter='cat';;
+      *)
+        filter="egrep -v '^[01] 0 (${nofollow})$'"
+      ;;
+    esac
+    # recalculate old dependencies for next iteration
+    cp "${allDeps}" "${oldDeps}"
+done
+
+# found all deps -- clean up last iteration of old and new
+rm -f "${oldDeps}"
+rm -f "${newDeps}"
+
+if ${showProgress}; then
+    echo $(wc -l < "${allDeps}")" targets" >&2
+fi
+
+if [ -n "${targets_out}" ]; then
+    cut -d\  -f3- "${allDeps}" | sort -u > "${targets_out}"
+fi
+
+if [ -n "${directories_out}" ] \
+  || [ -n "${projects_out}" ] \
+  || [ -n "${notices_out}" ]
+then
+    readonly allDirs="${tmpFiles}/dirs"
+    (
+        cut -d\  -f3- "${allDeps}" | tr '\n' '\0' | xargs -0 dirname
+    ) | sort -u > "${allDirs}"
+    if ${showProgress}; then
+        echo $(wc -l < "${allDirs}")" directories" >&2
+    fi
+
+    case "${directories_out}" in
+      '')        : do nothing;;
+      *)
+        cat "${allDirs}" > "${directories_out}"
+      ;;
+    esac
+fi
+
+if [ -n "${projects_out}" ] \
+  || [ -n "${notices_out}" ]
+then
+    readonly allProj="${tmpFiles}/projects"
+    set +e
+    egrep -v '^out[/]' "${allDirs}" | getProjects > "${allProj}"
+    set -e
+    if ${showProgress}; then
+        echo $(wc -l < "${allProj}")" projects" >&2
+    fi
+
+    case "${projects_out}" in
+      '')        : do nothing;;
+      *)
+        cat "${allProj}" > "${projects_out}"
+      ;;
+    esac
+fi
+
+case "${notices_out}" in
+  '')        : do nothing;;
+  *)
+    readonly allNotice="${tmpFiles}/notices"
+    set +e
+    egrep '^1' "${allDeps}" | cut -d\  -f3- | egrep -v '^out/' > "${allNotice}"
+    set -e
+    cat "${allProj}" | while read proj; do
+        for f in LICENSE LICENCE NOTICE license.txt notice.txt; do
+            if [ -f "${proj}/${f}" ]; then
+                echo "${proj}/${f}"
+            fi
+        done
+    done >> "${allNotice}"
+    if ${showProgress}; then
+      echo $(cat "${allNotice}" | sort -u | wc -l)" notice targets" >&2
+    fi
+    readonly hashedNotice="${tmpFiles}/hashednotices"
+    ( # md5sum outputs checksum space indicator(space or *) filename newline
+        set +e
+        sort -u "${allNotice}" | tr '\n' '\0' | xargs -0 -r md5sum 2>/dev/null
+        set -e
+      # use sed to replace space and indicator with separator
+    ) > "${hashedNotice}"
+    if ${showProgress}; then
+        echo $(cut -d\  -f2- "${hashedNotice}" | sort -u | wc -l)" notice files" >&2
+        echo $(cut -d\  -f1 "${hashedNotice}" | sort -u | wc -l)" distinct notices" >&2
+    fi
+    sed 's/^\([^ ]*\) [* ]/\1'"${sep}"'/g' "${hashedNotice}" | sort > "${notices_out}"
+  ;;
+esac
+
+if ${interactive}; then
+    echo -n "$(date '+%F %-k:%M:%S') Delete ${tmpFiles} ? [n] " >&2
+    read answer
+    case "${answer}" in [yY]*) rm -fr "${tmpFiles}";; esac
+else
+    rm -fr "${tmpFiles}"
+fi
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 9b735e0..bbd6384 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1386,3 +1386,70 @@
 		),
 	)
 }
+
+func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib",
+			apex_available: ["//apex_available:anyapex"],
+			srcs: ["Test.java"],
+			sdk_version: "current",
+			naming_scheme: "framework-modules",
+			public: {
+				enabled: true,
+			},
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    apex_available: ["//apex_available:anyapex"],
+    naming_scheme: "framework-modules",
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    apex_available: ["//apex_available:anyapex"],
+    naming_scheme: "framework-modules",
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib-stubs-publicapi/android_common/javac/myjavalib-stubs-publicapi.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_api.txt -> sdk_library/public/myjavalib-removed.txt
+`),
+		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+		),
+	)
+}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index ae1a492..56be741 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -103,6 +103,7 @@
 				"myjavalib",
 				"mypublicjavalib",
 				"mydefaultedjavalib",
+				"myprivatejavalib",
 			],
 		}
 
@@ -140,6 +141,14 @@
 			system_modules: "none",
 			sdk_version: "none",
 		}
+
+		java_library {
+			name: "myprivatejavalib",
+			srcs: ["Test.java"],
+			visibility: ["//visibility:private"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
 	`
 
 	result := testSdkWithFs(t, ``,
@@ -155,14 +164,20 @@
 java_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
-    visibility: ["//other/foo:__pkg__"],
+    visibility: [
+        "//other/foo",
+        "//package",
+    ],
     jars: ["java/myjavalib.jar"],
 }
 
 java_import {
     name: "myjavalib",
     prefer: false,
-    visibility: ["//other/foo:__pkg__"],
+    visibility: [
+        "//other/foo",
+        "//package",
+    ],
     jars: ["java/myjavalib.jar"],
 }
 
@@ -183,27 +198,48 @@
 java_import {
     name: "mysdk_mydefaultedjavalib@current",
     sdk_member_name: "mydefaultedjavalib",
-    visibility: ["//other/bar:__pkg__"],
+    visibility: [
+        "//other/bar",
+        "//package",
+    ],
     jars: ["java/mydefaultedjavalib.jar"],
 }
 
 java_import {
     name: "mydefaultedjavalib",
     prefer: false,
-    visibility: ["//other/bar:__pkg__"],
+    visibility: [
+        "//other/bar",
+        "//package",
+    ],
     jars: ["java/mydefaultedjavalib.jar"],
 }
 
+java_import {
+    name: "mysdk_myprivatejavalib@current",
+    sdk_member_name: "myprivatejavalib",
+    visibility: ["//package"],
+    jars: ["java/myprivatejavalib.jar"],
+}
+
+java_import {
+    name: "myprivatejavalib",
+    prefer: false,
+    visibility: ["//package"],
+    jars: ["java/myprivatejavalib.jar"],
+}
+
 sdk_snapshot {
     name: "mysdk@current",
     visibility: [
-        "//other/foo:__pkg__",
+        "//other/foo",
         "//package:__subpackages__",
     ],
     java_header_libs: [
         "mysdk_myjavalib@current",
         "mysdk_mypublicjavalib@current",
         "mysdk_mydefaultedjavalib@current",
+        "mysdk_myprivatejavalib@current",
     ],
 }
 `))
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 14fab68..9f27647 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"io"
 	"path"
+	"sync"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -161,8 +162,21 @@
 var (
 	pctx         = android.NewPackageContext("android/soong/sysprop")
 	syspropCcTag = dependencyTag{name: "syspropCc"}
+
+	syspropLibrariesKey  = android.NewOnceKey("syspropLibraries")
+	syspropLibrariesLock sync.Mutex
 )
 
+func syspropLibraries(config android.Config) *[]string {
+	return config.Once(syspropLibrariesKey, func() interface{} {
+		return &[]string{}
+	}).(*[]string)
+}
+
+func SyspropLibraries(config android.Config) []string {
+	return append([]string{}, *syspropLibraries(config)...)
+}
+
 func init() {
 	android.RegisterModuleType("sysprop_library", syspropLibraryFactory)
 }
@@ -202,6 +216,10 @@
 	return proptools.Bool(m.properties.Public_stub)
 }
 
+func (m *syspropLibrary) CurrentSyspropApiFile() android.Path {
+	return m.currentApiFile
+}
+
 func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	baseModuleName := m.BaseModuleName()
 
@@ -475,6 +493,14 @@
 			Stem:        proptools.StringPtr(m.BaseModuleName()),
 		})
 	}
+
+	if m.ExportedToMake() {
+		syspropLibrariesLock.Lock()
+		defer syspropLibrariesLock.Unlock()
+
+		libraries := syspropLibraries(ctx.Config())
+		*libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName())
+	}
 }
 
 func syspropDepsMutator(ctx android.BottomUpMutatorContext) {
diff --git a/ui/build/build.go b/ui/build/build.go
index f3feac2..1122733 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -53,7 +53,6 @@
 {{end -}}
 pool highmem_pool
  depth = {{.HighmemParallel}}
-build _kati_always_build_: phony
 {{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
 subninja {{.KatiPackageNinjaFile}}
 {{end -}}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 0bcdccb..e1123e0 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -104,6 +104,7 @@
 		productOut("*.img"),
 		productOut("*.zip"),
 		productOut("android-info.txt"),
+		productOut("misc_info.txt"),
 		productOut("apex"),
 		productOut("kernel"),
 		productOut("data"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 5b9d10a..7fcc471 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -517,6 +517,9 @@
 				ctx.Fatalln("Unknown option:", arg)
 			}
 		} else if k, v, ok := decodeKeyValue(arg); ok && len(k) > 0 {
+			if k == "OUT_DIR" {
+				ctx.Fatalln("OUT_DIR may only be set in the environment, not as a command line option.")
+			}
 			c.environ.Set(k, v)
 		} else if arg == "dist" {
 			c.dist = true
@@ -611,10 +614,6 @@
 		product = "aosp_arm"
 	case "arm64":
 		product = "aosm_arm64"
-	case "mips":
-		product = "aosp_mips"
-	case "mips64":
-		product = "aosp_mips64"
 	case "x86":
 		product = "aosp_x86"
 	case "x86_64":
@@ -787,48 +786,6 @@
 	return false
 }
 
-func (c *configImpl) UseRBEJAVAC() bool {
-	if !c.UseRBE() {
-		return false
-	}
-
-	if v, ok := c.environ.Get("RBE_JAVAC"); ok {
-		v = strings.TrimSpace(v)
-		if v != "" && v != "false" {
-			return true
-		}
-	}
-	return false
-}
-
-func (c *configImpl) UseRBER8() bool {
-	if !c.UseRBE() {
-		return false
-	}
-
-	if v, ok := c.environ.Get("RBE_R8"); ok {
-		v = strings.TrimSpace(v)
-		if v != "" && v != "false" {
-			return true
-		}
-	}
-	return false
-}
-
-func (c *configImpl) UseRBED8() bool {
-	if !c.UseRBE() {
-		return false
-	}
-
-	if v, ok := c.environ.Get("RBE_D8"); ok {
-		v = strings.TrimSpace(v)
-		if v != "" && v != "false" {
-			return true
-		}
-	}
-	return false
-}
-
 func (c *configImpl) StartRBE() bool {
 	if !c.UseRBE() {
 		return false
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index a3234e3..a559330 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -229,8 +229,6 @@
 		"DEFAULT_ERROR_BUILD_MODULE_TYPES",
 		"BUILD_BROKEN_PREBUILT_ELF_FILES",
 		"BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW",
-		"BUILD_BROKEN_USES_BUILD_AUX_EXECUTABLE",
-		"BUILD_BROKEN_USES_BUILD_AUX_STATIC_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_COPY_HEADERS",
 		"BUILD_BROKEN_USES_BUILD_EXECUTABLE",
 		"BUILD_BROKEN_USES_BUILD_FUZZ_TEST",
@@ -238,17 +236,12 @@
 		"BUILD_BROKEN_USES_BUILD_HOST_DALVIK_JAVA_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE",
-		"BUILD_BROKEN_USES_BUILD_HOST_FUZZ_TEST",
 		"BUILD_BROKEN_USES_BUILD_HOST_JAVA_LIBRARY",
-		"BUILD_BROKEN_USES_BUILD_HOST_NATIVE_TEST",
 		"BUILD_BROKEN_USES_BUILD_HOST_PREBUILT",
 		"BUILD_BROKEN_USES_BUILD_HOST_SHARED_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_HOST_STATIC_LIBRARY",
-		"BUILD_BROKEN_USES_BUILD_HOST_STATIC_TEST_LIBRARY",
-		"BUILD_BROKEN_USES_BUILD_HOST_TEST_CONFIG",
 		"BUILD_BROKEN_USES_BUILD_JAVA_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_MULTI_PREBUILT",
-		"BUILD_BROKEN_USES_BUILD_NATIVE_BENCHMARK",
 		"BUILD_BROKEN_USES_BUILD_NATIVE_TEST",
 		"BUILD_BROKEN_USES_BUILD_NOTICE_FILE",
 		"BUILD_BROKEN_USES_BUILD_PACKAGE",
@@ -258,8 +251,6 @@
 		"BUILD_BROKEN_USES_BUILD_SHARED_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_STATIC_JAVA_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY",
-		"BUILD_BROKEN_USES_BUILD_STATIC_TEST_LIBRARY",
-		"BUILD_BROKEN_USES_BUILD_TARGET_TEST_CONFIG",
 	}, exportEnvVars...), BannerVars...)
 
 	make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true)
diff --git a/ui/build/exec.go b/ui/build/exec.go
index e435c53..053bbae 100644
--- a/ui/build/exec.go
+++ b/ui/build/exec.go
@@ -19,6 +19,8 @@
 	"io"
 	"os/exec"
 	"strings"
+	"syscall"
+	"time"
 )
 
 // Cmd is a wrapper of os/exec.Cmd that integrates with the build context for
@@ -33,6 +35,8 @@
 	ctx    Context
 	config Config
 	name   string
+
+	started time.Time
 }
 
 func Command(ctx Context, config Config, name string, executable string, args ...string) *Cmd {
@@ -57,7 +61,20 @@
 		c.wrapSandbox()
 	}
 
-	c.ctx.Verboseln(c.Path, c.Args)
+	c.ctx.Verbosef("%q executing %q %v\n", c.name, c.Path, c.Args)
+	c.started = time.Now()
+}
+
+func (c *Cmd) report() {
+	if c.Cmd.ProcessState != nil {
+		rusage := c.Cmd.ProcessState.SysUsage().(*syscall.Rusage)
+		c.ctx.Verbosef("%q finished with exit code %d (%s real, %s user, %s system, %dMB maxrss)",
+			c.name, c.Cmd.ProcessState.ExitCode(),
+			time.Since(c.started).Round(time.Millisecond),
+			c.Cmd.ProcessState.UserTime().Round(time.Millisecond),
+			c.Cmd.ProcessState.SystemTime().Round(time.Millisecond),
+			rusage.Maxrss/1024)
+	}
 }
 
 func (c *Cmd) Start() error {
@@ -68,21 +85,30 @@
 func (c *Cmd) Run() error {
 	c.prepare()
 	err := c.Cmd.Run()
+	c.report()
 	return err
 }
 
 func (c *Cmd) Output() ([]byte, error) {
 	c.prepare()
 	bytes, err := c.Cmd.Output()
+	c.report()
 	return bytes, err
 }
 
 func (c *Cmd) CombinedOutput() ([]byte, error) {
 	c.prepare()
 	bytes, err := c.Cmd.CombinedOutput()
+	c.report()
 	return bytes, err
 }
 
+func (c *Cmd) Wait() error {
+	err := c.Cmd.Wait()
+	c.report()
+	return err
+}
+
 // StartOrFatal is equivalent to Start, but handles the error with a call to ctx.Fatal
 func (c *Cmd) StartOrFatal() {
 	if err := c.Start(); err != nil {
diff --git a/ui/build/kati.go b/ui/build/kati.go
index a845c5b..2eb7850 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -67,6 +67,7 @@
 		"--ninja_dir=" + config.OutDir(),
 		"--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
 		"--no_ninja_prelude",
+		"--use_ninja_phony_output",
 		"--regen",
 		"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
 		"--detect_android_echo",
@@ -104,17 +105,20 @@
 	envFunc(cmd.Environment)
 
 	if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
-		u, err := user.Current()
-		if err != nil {
-			ctx.Println("Failed to get current user")
+		username := "unknown"
+		if u, err := user.Current(); err == nil {
+			username = u.Username
+		} else {
+			ctx.Println("Failed to get current user:", err)
 		}
-		cmd.Environment.Set("BUILD_USERNAME", u.Username)
+		cmd.Environment.Set("BUILD_USERNAME", username)
 	}
 
 	if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
 		hostname, err := os.Hostname()
 		if err != nil {
-			ctx.Println("Failed to read hostname")
+			ctx.Println("Failed to read hostname:", err)
+			hostname = "unknown"
 		}
 		cmd.Environment.Set("BUILD_HOSTNAME", hostname)
 	}
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 4fc1f01..1b13e5d 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -58,6 +58,7 @@
 	args = append(args, "-f", config.CombinedNinjaFile())
 
 	args = append(args,
+		"-o", "usesphonyoutputs=yes",
 		"-w", "dupbuild=err",
 		"-w", "missingdepfile=err")
 
@@ -128,6 +129,16 @@
 			"GOMA_USE_LOCAL",
 
 			// RBE client
+			"RBE_compare",
+			"RBE_exec_root",
+			"RBE_exec_strategy",
+			"RBE_invocation_id",
+			"RBE_log_dir",
+			"RBE_platform",
+			"RBE_remote_accept_cache",
+			"RBE_remote_update_cache",
+			"RBE_server_address",
+			// TODO: remove old FLAG_ variables.
 			"FLAG_compare",
 			"FLAG_exec_root",
 			"FLAG_exec_strategy",
diff --git a/ui/build/path.go b/ui/build/path.go
index c34ba1b..7122927 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -177,9 +177,12 @@
 		execs = append(execs, parsePathDir(pathEntry)...)
 	}
 
-	allowAllSymlinks := config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS")
+	if config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS") {
+		ctx.Fatalln("TEMPORARY_DISABLE_PATH_RESTRICTIONS was a temporary migration method, and is now obsolete.")
+	}
+
 	for _, name := range execs {
-		if !paths.GetConfig(name).Symlink && !allowAllSymlinks {
+		if !paths.GetConfig(name).Symlink {
 			continue
 		}
 
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 2de772b..dab0e75 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -19,6 +19,7 @@
 	"os"
 	"os/exec"
 	"os/user"
+	"path/filepath"
 	"strings"
 	"sync"
 )
@@ -54,6 +55,9 @@
 
 	working bool
 	group   string
+	srcDir  string
+	outDir  string
+	distDir string
 }
 
 func (c *Cmd) sandboxSupported() bool {
@@ -72,15 +76,45 @@
 			sandboxConfig.group = "nobody"
 		}
 
-		cmd := exec.CommandContext(c.ctx.Context, nsjailPath,
+		// These directories will be bind mounted
+		// so we need full non-symlink paths
+		sandboxConfig.srcDir = absPath(c.ctx, ".")
+		if derefPath, err := filepath.EvalSymlinks(sandboxConfig.srcDir); err == nil {
+			sandboxConfig.srcDir = absPath(c.ctx, derefPath)
+		}
+		sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir())
+		if derefPath, err := filepath.EvalSymlinks(sandboxConfig.outDir); err == nil {
+			sandboxConfig.outDir = absPath(c.ctx, derefPath)
+		}
+		sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir())
+		if derefPath, err := filepath.EvalSymlinks(sandboxConfig.distDir); err == nil {
+			sandboxConfig.distDir = absPath(c.ctx, derefPath)
+		}
+
+		sandboxArgs := []string{
 			"-H", "android-build",
 			"-e",
 			"-u", "nobody",
 			"-g", sandboxConfig.group,
-			"-B", "/",
+			"-R", "/",
+			"-B", sandboxConfig.srcDir,
+			"-B", "/tmp",
+			"-B", sandboxConfig.outDir,
+		}
+
+		if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
+			//Mount dist dir as read-write if it already exists
+			sandboxArgs = append(sandboxArgs, "-B",
+				sandboxConfig.distDir)
+		}
+
+		sandboxArgs = append(sandboxArgs,
 			"--disable_clone_newcgroup",
 			"--",
 			"/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
+
+		cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...)
+
 		cmd.Env = c.config.Environment().Environ()
 
 		c.ctx.Verboseln(cmd.Args)
@@ -144,8 +178,17 @@
 		"--rlimit_fsize", "soft",
 		"--rlimit_nofile", "soft",
 
-		// For now, just map everything. Eventually we should limit this, especially to make most things readonly.
-		"-B", "/",
+		// For now, just map everything. Make most things readonly.
+		"-R", "/",
+
+		// Mount a writable tmp dir
+		"-B", "/tmp",
+
+		// Mount source are read-write
+		"-B", sandboxConfig.srcDir,
+
+		//Mount out dir as read-write
+		"-B", sandboxConfig.outDir,
 
 		// Disable newcgroup for now, since it may require newer kernels
 		// TODO: try out cgroups
@@ -155,6 +198,11 @@
 		"-q",
 	}
 
+	if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
+		//Mount dist dir as read-write if it already exists
+		sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
+	}
+
 	if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
 		c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
 		c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
diff --git a/ui/build/soong.go b/ui/build/soong.go
index afbc073..2fbf381 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,11 +15,15 @@
 package build
 
 import (
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strconv"
 	"strings"
 
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+
+	"github.com/golang/protobuf/proto"
 	"github.com/google/blueprint/microfactory"
 
 	"android/soong/ui/metrics"
@@ -115,7 +119,11 @@
 		cmd := Command(ctx, config, "soong "+name,
 			config.PrebuiltBuildTool("ninja"),
 			"-d", "keepdepfile",
+			"-o", "usesphonyoutputs=yes",
+			"-o", "preremoveoutputs=yes",
 			"-w", "dupbuild=err",
+			"-w", "outputdir=err",
+			"-w", "missingoutfile=err",
 			"-j", strconv.Itoa(config.Parallel()),
 			"--frontend_file", fifo,
 			"-f", filepath.Join(config.SoongOutDir(), file))
@@ -126,4 +134,35 @@
 
 	ninja("minibootstrap", ".minibootstrap/build.ninja")
 	ninja("bootstrap", ".bootstrap/build.ninja")
+
+	soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
+	logSoongBuildMetrics(ctx, soongBuildMetrics)
+
+	if ctx.Metrics != nil {
+		ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
+	}
+}
+
+func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
+	soongBuildMetricsFile := filepath.Join(config.OutDir(), "soong", "soong_build_metrics.pb")
+	buf, err := ioutil.ReadFile(soongBuildMetricsFile)
+	if err != nil {
+		ctx.Fatalf("Failed to load %s: %s", soongBuildMetricsFile, err)
+	}
+	soongBuildMetrics := &soong_metrics_proto.SoongBuildMetrics{}
+	err = proto.Unmarshal(buf, soongBuildMetrics)
+	if err != nil {
+		ctx.Fatalf("Failed to unmarshal %s: %s", soongBuildMetricsFile, err)
+	}
+	return soongBuildMetrics
+}
+
+func logSoongBuildMetrics(ctx Context, metrics *soong_metrics_proto.SoongBuildMetrics) {
+	ctx.Verbosef("soong_build metrics:")
+	ctx.Verbosef(" modules: %v", metrics.GetModules())
+	ctx.Verbosef(" variants: %v", metrics.GetVariants())
+	ctx.Verbosef(" max heap size: %v MB", metrics.GetMaxHeapSize()/1e6)
+	ctx.Verbosef(" total allocation count: %v", metrics.GetTotalAllocCount())
+	ctx.Verbosef(" total allocation size: %v MB", metrics.GetTotalAllocSize()/1e6)
+
 }
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 5109465..4ff9483 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -50,10 +50,10 @@
 	// Get a list of leaf nodes in the dependency graph from ninja
 	executable := config.PrebuiltBuildTool("ninja")
 
-	args := []string{}
-	args = append(args, config.NinjaArgs()...)
-	args = append(args, "-f", config.CombinedNinjaFile())
-	args = append(args, "-t", "targets", "rule")
+	common_args := []string{}
+	common_args = append(common_args, config.NinjaArgs()...)
+	common_args = append(common_args, "-f", config.CombinedNinjaFile())
+	args := append(common_args, "-t", "targets", "rule")
 
 	cmd := Command(ctx, config, "ninja", executable, args...)
 	stdout, err := cmd.StdoutPipe()
@@ -96,9 +96,31 @@
 		sb := &strings.Builder{}
 		title := "Dependencies in out found with no rule to create them:"
 		fmt.Fprintln(sb, title)
-		for _, dep := range danglingRulesList {
-			fmt.Fprintln(sb, "  ", dep)
+
+		report_lines := 1
+		for i, dep := range danglingRulesList {
+			if report_lines > 20 {
+				fmt.Fprintf(sb, "  ... and %d more\n", len(danglingRulesList)-i)
+				break
+			}
+			// It's helpful to see the reverse dependencies. ninja -t query is the
+			// best tool we got for that. Its output starts with the dependency
+			// itself.
+			query_cmd := Command(ctx, config, "ninja", executable,
+				append(common_args, "-t", "query", dep)...)
+			query_stdout, err := query_cmd.StdoutPipe()
+			if err != nil {
+				ctx.Fatal(err)
+			}
+			query_cmd.StartOrFatal()
+			scanner := bufio.NewScanner(query_stdout)
+			for scanner.Scan() {
+				report_lines++
+				fmt.Fprintln(sb, " ", scanner.Text())
+			}
+			query_cmd.WaitOrFatal()
 		}
+
 		ts.FinishAction(status.ActionResult{
 			Action: action,
 			Error:  fmt.Errorf(title),
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 529639d..3596e10 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -17,6 +17,7 @@
     pkgPath: "android/soong/ui/metrics",
     deps: [
         "golang-protobuf-proto",
+        "soong-ui-metrics_upload_proto",
         "soong-ui-metrics_proto",
         "soong-ui-tracer",
     ],
@@ -35,3 +36,11 @@
     ],
 }
 
+bootstrap_go_package {
+    name: "soong-ui-metrics_upload_proto",
+    pkgPath: "android/soong/ui/metrics/upload_proto",
+    deps: ["golang-protobuf-proto"],
+    srcs: [
+        "upload_proto/upload.pb.go",
+    ],
+}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 8254e4a..3e76d37 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -145,6 +145,10 @@
 	return writeMessageToFile(&m.metrics, outputPath)
 }
 
+func (m *Metrics) SetSoongBuildMetrics(metrics *soong_metrics_proto.SoongBuildMetrics) {
+	m.metrics.SoongBuildMetrics = metrics
+}
+
 type CriticalUserJourneysMetrics struct {
 	cujs soong_metrics_proto.CriticalUserJourneysMetrics
 }
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 3986d0e..a39d1a8 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -197,10 +197,11 @@
 	// The metrics for calling Ninja.
 	NinjaRuns []*PerfInfo `protobuf:"bytes,20,rep,name=ninja_runs,json=ninjaRuns" json:"ninja_runs,omitempty"`
 	// The metrics for the whole build
-	Total                *PerfInfo `protobuf:"bytes,21,opt,name=total" json:"total,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}  `json:"-"`
-	XXX_unrecognized     []byte    `json:"-"`
-	XXX_sizecache        int32     `json:"-"`
+	Total                *PerfInfo          `protobuf:"bytes,21,opt,name=total" json:"total,omitempty"`
+	SoongBuildMetrics    *SoongBuildMetrics `protobuf:"bytes,22,opt,name=soong_build_metrics,json=soongBuildMetrics" json:"soong_build_metrics,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
+	XXX_unrecognized     []byte             `json:"-"`
+	XXX_sizecache        int32              `json:"-"`
 }
 
 func (m *MetricsBase) Reset()         { *m = MetricsBase{} }
@@ -380,6 +381,13 @@
 	return nil
 }
 
+func (m *MetricsBase) GetSoongBuildMetrics() *SoongBuildMetrics {
+	if m != nil {
+		return m.SoongBuildMetrics
+	}
+	return nil
+}
+
 type PerfInfo struct {
 	// The description for the phase/action/part while the tool running.
 	Desc *string `protobuf:"bytes,1,opt,name=desc" json:"desc,omitempty"`
@@ -607,6 +615,82 @@
 	return nil
 }
 
+type SoongBuildMetrics struct {
+	// The number of modules handled by soong_build.
+	Modules *uint32 `protobuf:"varint,1,opt,name=modules" json:"modules,omitempty"`
+	// The total number of variants handled by soong_build.
+	Variants *uint32 `protobuf:"varint,2,opt,name=variants" json:"variants,omitempty"`
+	// The total number of allocations in soong_build.
+	TotalAllocCount *uint64 `protobuf:"varint,3,opt,name=total_alloc_count,json=totalAllocCount" json:"total_alloc_count,omitempty"`
+	// The total size of allocations in soong_build in bytes.
+	TotalAllocSize *uint64 `protobuf:"varint,4,opt,name=total_alloc_size,json=totalAllocSize" json:"total_alloc_size,omitempty"`
+	// The approximate maximum size of the heap in soong_build in bytes.
+	MaxHeapSize          *uint64  `protobuf:"varint,5,opt,name=max_heap_size,json=maxHeapSize" json:"max_heap_size,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *SoongBuildMetrics) Reset()         { *m = SoongBuildMetrics{} }
+func (m *SoongBuildMetrics) String() string { return proto.CompactTextString(m) }
+func (*SoongBuildMetrics) ProtoMessage()    {}
+func (*SoongBuildMetrics) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6039342a2ba47b72, []int{5}
+}
+
+func (m *SoongBuildMetrics) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SoongBuildMetrics.Unmarshal(m, b)
+}
+func (m *SoongBuildMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SoongBuildMetrics.Marshal(b, m, deterministic)
+}
+func (m *SoongBuildMetrics) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SoongBuildMetrics.Merge(m, src)
+}
+func (m *SoongBuildMetrics) XXX_Size() int {
+	return xxx_messageInfo_SoongBuildMetrics.Size(m)
+}
+func (m *SoongBuildMetrics) XXX_DiscardUnknown() {
+	xxx_messageInfo_SoongBuildMetrics.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SoongBuildMetrics proto.InternalMessageInfo
+
+func (m *SoongBuildMetrics) GetModules() uint32 {
+	if m != nil && m.Modules != nil {
+		return *m.Modules
+	}
+	return 0
+}
+
+func (m *SoongBuildMetrics) GetVariants() uint32 {
+	if m != nil && m.Variants != nil {
+		return *m.Variants
+	}
+	return 0
+}
+
+func (m *SoongBuildMetrics) GetTotalAllocCount() uint64 {
+	if m != nil && m.TotalAllocCount != nil {
+		return *m.TotalAllocCount
+	}
+	return 0
+}
+
+func (m *SoongBuildMetrics) GetTotalAllocSize() uint64 {
+	if m != nil && m.TotalAllocSize != nil {
+		return *m.TotalAllocSize
+	}
+	return 0
+}
+
+func (m *SoongBuildMetrics) GetMaxHeapSize() uint64 {
+	if m != nil && m.MaxHeapSize != nil {
+		return *m.MaxHeapSize
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterEnum("soong_build_metrics.MetricsBase_BuildVariant", MetricsBase_BuildVariant_name, MetricsBase_BuildVariant_value)
 	proto.RegisterEnum("soong_build_metrics.MetricsBase_Arch", MetricsBase_Arch_name, MetricsBase_Arch_value)
@@ -616,63 +700,72 @@
 	proto.RegisterType((*ModuleTypeInfo)(nil), "soong_build_metrics.ModuleTypeInfo")
 	proto.RegisterType((*CriticalUserJourneyMetrics)(nil), "soong_build_metrics.CriticalUserJourneyMetrics")
 	proto.RegisterType((*CriticalUserJourneysMetrics)(nil), "soong_build_metrics.CriticalUserJourneysMetrics")
+	proto.RegisterType((*SoongBuildMetrics)(nil), "soong_build_metrics.SoongBuildMetrics")
 }
 
 func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
 
 var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 847 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xdd, 0x6e, 0xdb, 0x36,
-	0x14, 0xae, 0x12, 0x25, 0xb6, 0x8e, 0x62, 0x57, 0x65, 0x52, 0x54, 0x5d, 0x11, 0xcc, 0x10, 0xd6,
-	0x21, 0x17, 0xab, 0x5b, 0x78, 0x45, 0x50, 0x18, 0xc5, 0x80, 0xc4, 0x31, 0x8a, 0x2e, 0xb0, 0x5d,
-	0x28, 0x71, 0x57, 0x6c, 0x17, 0x02, 0x23, 0xd1, 0x8d, 0x3a, 0x4b, 0x14, 0x48, 0xaa, 0x98, 0x1f,
-	0x62, 0x0f, 0xb9, 0x8b, 0xbd, 0xc7, 0xc0, 0x43, 0xc9, 0x51, 0x00, 0x0f, 0x09, 0x7a, 0x47, 0x9d,
-	0xef, 0x87, 0xdf, 0xa1, 0xc4, 0x23, 0xe8, 0x64, 0x4c, 0x89, 0x34, 0x96, 0xfd, 0x42, 0x70, 0xc5,
-	0xc9, 0xbe, 0xe4, 0x3c, 0xff, 0x1c, 0x5d, 0x95, 0xe9, 0x32, 0x89, 0x2a, 0x28, 0xf8, 0xc7, 0x01,
-	0x77, 0x62, 0xd6, 0xa7, 0x54, 0x32, 0xf2, 0x0a, 0x0e, 0x0c, 0x21, 0xa1, 0x8a, 0x45, 0x2a, 0xcd,
-	0x98, 0x54, 0x34, 0x2b, 0x7c, 0xab, 0x67, 0x1d, 0x6d, 0x87, 0x04, 0xb1, 0x33, 0xaa, 0xd8, 0x65,
-	0x8d, 0x90, 0xa7, 0xd0, 0x36, 0x8a, 0x34, 0xf1, 0xb7, 0x7a, 0xd6, 0x91, 0x13, 0xb6, 0xf0, 0xf9,
-	0x7d, 0x42, 0x86, 0xf0, 0xb4, 0x58, 0x52, 0xb5, 0xe0, 0x22, 0x8b, 0xbe, 0x32, 0x21, 0x53, 0x9e,
-	0x47, 0x31, 0x4f, 0x58, 0x4e, 0x33, 0xe6, 0x6f, 0x23, 0xf7, 0x49, 0x4d, 0xf8, 0x68, 0xf0, 0x51,
-	0x05, 0x93, 0xe7, 0xd0, 0x55, 0x54, 0x7c, 0x66, 0x2a, 0x2a, 0x04, 0x4f, 0xca, 0x58, 0xf9, 0x36,
-	0x0a, 0x3a, 0xa6, 0xfa, 0xc1, 0x14, 0x49, 0x02, 0x07, 0x15, 0xcd, 0x84, 0xf8, 0x4a, 0x45, 0x4a,
-	0x73, 0xe5, 0xef, 0xf4, 0xac, 0xa3, 0xee, 0xe0, 0x45, 0x7f, 0x43, 0xcf, 0xfd, 0x46, 0xbf, 0xfd,
-	0x53, 0x8d, 0x7c, 0x34, 0xa2, 0xe1, 0xf6, 0x78, 0xfa, 0x2e, 0x24, 0xc6, 0xaf, 0x09, 0x90, 0x19,
-	0xb8, 0xd5, 0x2e, 0x54, 0xc4, 0xd7, 0xfe, 0x2e, 0x9a, 0x3f, 0xbf, 0xd3, 0xfc, 0x44, 0xc4, 0xd7,
-	0xc3, 0xd6, 0x7c, 0x7a, 0x3e, 0x9d, 0xfd, 0x36, 0x0d, 0xc1, 0x58, 0xe8, 0x22, 0xe9, 0xc3, 0x7e,
-	0xc3, 0x70, 0x9d, 0xba, 0x85, 0x2d, 0x3e, 0xba, 0x21, 0xd6, 0x01, 0x7e, 0x82, 0x2a, 0x56, 0x14,
-	0x17, 0xe5, 0x9a, 0xde, 0x46, 0xba, 0x67, 0x90, 0x51, 0x51, 0xd6, 0xec, 0x73, 0x70, 0xae, 0xb9,
-	0xac, 0xc2, 0x3a, 0xdf, 0x14, 0xb6, 0xad, 0x0d, 0x30, 0x6a, 0x08, 0x1d, 0x34, 0x1b, 0xe4, 0x89,
-	0x31, 0x84, 0x6f, 0x32, 0x74, 0xb5, 0xc9, 0x20, 0x4f, 0xd0, 0xf3, 0x09, 0xb4, 0xd0, 0x93, 0x4b,
-	0xdf, 0xc5, 0x1e, 0x76, 0xf5, 0xe3, 0x4c, 0x92, 0xa0, 0xda, 0x8c, 0xcb, 0x88, 0xfd, 0xa5, 0x04,
-	0xf5, 0xf7, 0x10, 0x76, 0x0d, 0x3c, 0xd6, 0xa5, 0x35, 0x27, 0x16, 0x5c, 0x4a, 0x6d, 0xd1, 0xb9,
-	0xe1, 0x8c, 0x74, 0x6d, 0x26, 0xc9, 0x8f, 0xf0, 0xb0, 0xc1, 0xc1, 0xd8, 0x5d, 0xf3, 0xf9, 0xac,
-	0x59, 0x18, 0xe4, 0x05, 0xec, 0x37, 0x78, 0xeb, 0x16, 0x1f, 0x9a, 0x83, 0x5d, 0x73, 0x1b, 0xb9,
-	0x79, 0xa9, 0xa2, 0x24, 0x15, 0xbe, 0x67, 0x72, 0xf3, 0x52, 0x9d, 0xa5, 0x82, 0xfc, 0x02, 0xae,
-	0x64, 0xaa, 0x2c, 0x22, 0xc5, 0xf9, 0x52, 0xfa, 0x8f, 0x7a, 0xdb, 0x47, 0xee, 0xe0, 0x70, 0xe3,
-	0x11, 0x7d, 0x60, 0x62, 0xf1, 0x3e, 0x5f, 0xf0, 0x10, 0x50, 0x71, 0xa9, 0x05, 0x64, 0x08, 0xce,
-	0x9f, 0x54, 0xa5, 0x91, 0x28, 0x73, 0xe9, 0x93, 0xfb, 0xa8, 0xdb, 0x9a, 0x1f, 0x96, 0xb9, 0x24,
-	0x6f, 0x01, 0x0c, 0x13, 0xc5, 0xfb, 0xf7, 0x11, 0x3b, 0x88, 0xd6, 0xea, 0x3c, 0xcd, 0xbf, 0x50,
-	0xa3, 0x3e, 0xb8, 0x97, 0x1a, 0x05, 0xa8, 0xfe, 0x19, 0x76, 0x14, 0x57, 0x74, 0xe9, 0x3f, 0xee,
-	0x59, 0x77, 0x0b, 0x0d, 0x37, 0x78, 0x05, 0x7b, 0xb7, 0x6e, 0x57, 0x1b, 0xec, 0xf9, 0xc5, 0x38,
-	0xf4, 0x1e, 0x90, 0x0e, 0x38, 0x7a, 0x75, 0x36, 0x3e, 0x9d, 0xbf, 0xf3, 0x2c, 0xd2, 0x02, 0x7d,
-	0x23, 0xbd, 0xad, 0xe0, 0x2d, 0xd8, 0x78, 0xfe, 0x2e, 0xd4, 0xdf, 0x93, 0xf7, 0x40, 0xa3, 0x27,
-	0xe1, 0xc4, 0xb3, 0x88, 0x03, 0x3b, 0x27, 0xe1, 0xe4, 0xf8, 0xb5, 0xb7, 0xa5, 0x6b, 0x9f, 0xde,
-	0x1c, 0x7b, 0xdb, 0x04, 0x60, 0xf7, 0xd3, 0x9b, 0xe3, 0xe8, 0xf8, 0xb5, 0x67, 0x07, 0x7f, 0x5b,
-	0xd0, 0xae, 0x33, 0x10, 0x02, 0x76, 0xc2, 0x64, 0x8c, 0x03, 0xcd, 0x09, 0x71, 0xad, 0x6b, 0x38,
-	0x92, 0xcc, 0xf8, 0xc2, 0x35, 0x39, 0x04, 0x90, 0x8a, 0x0a, 0x85, 0x33, 0x10, 0x87, 0x95, 0x1d,
-	0x3a, 0x58, 0xd1, 0xa3, 0x8f, 0x3c, 0x03, 0x47, 0x30, 0xba, 0x34, 0xa8, 0x8d, 0x68, 0x5b, 0x17,
-	0x10, 0x3c, 0x04, 0xc8, 0x58, 0xc6, 0xc5, 0x2a, 0x2a, 0x25, 0xc3, 0x51, 0x64, 0x87, 0x8e, 0xa9,
-	0xcc, 0x25, 0x0b, 0xfe, 0xb5, 0xa0, 0x3b, 0xe1, 0x49, 0xb9, 0x64, 0x97, 0xab, 0x82, 0x61, 0xaa,
-	0x3f, 0x60, 0xcf, 0x9c, 0x99, 0x5c, 0x49, 0xc5, 0x32, 0x4c, 0xd7, 0x1d, 0xbc, 0xdc, 0x7c, 0xc7,
-	0x6e, 0x49, 0xcd, 0x04, 0xbb, 0x40, 0x59, 0xe3, 0xb6, 0x5d, 0xdd, 0x54, 0xc9, 0xf7, 0xe0, 0x66,
-	0xa8, 0x89, 0xd4, 0xaa, 0xa8, 0xbb, 0x84, 0x6c, 0x6d, 0x43, 0x7e, 0x80, 0x6e, 0x5e, 0x66, 0x11,
-	0x5f, 0x44, 0xa6, 0x28, 0xb1, 0xdf, 0x4e, 0xb8, 0x97, 0x97, 0xd9, 0x6c, 0x61, 0xf6, 0x93, 0xc1,
-	0x4b, 0x70, 0x1b, 0x7b, 0xdd, 0x7e, 0x17, 0x0e, 0xec, 0x5c, 0xcc, 0x66, 0x53, 0xfd, 0xd2, 0xda,
-	0x60, 0x4f, 0x4e, 0xce, 0xc7, 0xde, 0x56, 0xb0, 0x84, 0xef, 0x46, 0x22, 0x55, 0x69, 0x4c, 0x97,
-	0x73, 0xc9, 0xc4, 0xaf, 0xbc, 0x14, 0x39, 0x5b, 0x55, 0x23, 0x62, 0x7d, 0xe8, 0x56, 0xe3, 0xd0,
-	0x87, 0xd0, 0xaa, 0xba, 0xc4, 0x94, 0xee, 0xa0, 0x77, 0xd7, 0x94, 0x09, 0x6b, 0x41, 0x70, 0x05,
-	0xcf, 0x36, 0xec, 0x26, 0xeb, 0xed, 0x46, 0x60, 0xc7, 0xe5, 0x17, 0xe9, 0x5b, 0xf8, 0x85, 0x6f,
-	0x3e, 0xd9, 0xff, 0x4f, 0x1b, 0xa2, 0xf8, 0xf4, 0xf1, 0xef, 0xd5, 0x4f, 0xb4, 0x52, 0x44, 0xf8,
-	0x67, 0xfd, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xc4, 0xbd, 0xe2, 0xb1, 0x69, 0x07, 0x00, 0x00,
+	// 962 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xef, 0x4e, 0xdc, 0x46,
+	0x10, 0x8f, 0xe1, 0xe0, 0xce, 0x63, 0xee, 0x30, 0x0b, 0x69, 0x9c, 0x44, 0xa8, 0x27, 0xab, 0x89,
+	0x50, 0xd5, 0x90, 0x88, 0x46, 0x28, 0x42, 0x51, 0x25, 0x38, 0x50, 0x9a, 0x22, 0xb8, 0xc8, 0xfc,
+	0x69, 0xd4, 0x7e, 0x58, 0x2d, 0xf6, 0x12, 0x9c, 0xda, 0x5e, 0x6b, 0x77, 0x1d, 0x41, 0xde, 0xa1,
+	0x0f, 0xd4, 0xcf, 0x7d, 0x96, 0xbe, 0x47, 0xb5, 0xb3, 0xf6, 0x61, 0xda, 0x8b, 0x40, 0xf9, 0x66,
+	0xcf, 0xef, 0xcf, 0xce, 0xac, 0x67, 0xe6, 0x0e, 0xfa, 0x39, 0xd7, 0x32, 0x8d, 0xd5, 0x7a, 0x29,
+	0x85, 0x16, 0x64, 0x59, 0x09, 0x51, 0x7c, 0xa0, 0x67, 0x55, 0x9a, 0x25, 0xb4, 0x86, 0xc2, 0xbf,
+	0x00, 0xbc, 0x03, 0xfb, 0xbc, 0xc3, 0x14, 0x27, 0x2f, 0x60, 0xc5, 0x12, 0x12, 0xa6, 0x39, 0xd5,
+	0x69, 0xce, 0x95, 0x66, 0x79, 0x19, 0x38, 0x43, 0x67, 0x6d, 0x36, 0x22, 0x88, 0xed, 0x32, 0xcd,
+	0x8f, 0x1b, 0x84, 0x3c, 0x84, 0x9e, 0x55, 0xa4, 0x49, 0x30, 0x33, 0x74, 0xd6, 0xdc, 0xa8, 0x8b,
+	0xef, 0x6f, 0x13, 0xb2, 0x05, 0x0f, 0xcb, 0x8c, 0xe9, 0x73, 0x21, 0x73, 0xfa, 0x89, 0x4b, 0x95,
+	0x8a, 0x82, 0xc6, 0x22, 0xe1, 0x05, 0xcb, 0x79, 0x30, 0x8b, 0xdc, 0x07, 0x0d, 0xe1, 0xd4, 0xe2,
+	0xa3, 0x1a, 0x26, 0x4f, 0x60, 0xa0, 0x99, 0xfc, 0xc0, 0x35, 0x2d, 0xa5, 0x48, 0xaa, 0x58, 0x07,
+	0x1d, 0x14, 0xf4, 0x6d, 0xf4, 0x9d, 0x0d, 0x92, 0x04, 0x56, 0x6a, 0x9a, 0x4d, 0xe2, 0x13, 0x93,
+	0x29, 0x2b, 0x74, 0x30, 0x37, 0x74, 0xd6, 0x06, 0x1b, 0xcf, 0xd6, 0xa7, 0xd4, 0xbc, 0xde, 0xaa,
+	0x77, 0x7d, 0xc7, 0x20, 0xa7, 0x56, 0xb4, 0x35, 0xbb, 0x77, 0xf8, 0x26, 0x22, 0xd6, 0xaf, 0x0d,
+	0x90, 0x31, 0x78, 0xf5, 0x29, 0x4c, 0xc6, 0x17, 0xc1, 0x3c, 0x9a, 0x3f, 0xb9, 0xd5, 0x7c, 0x5b,
+	0xc6, 0x17, 0x5b, 0xdd, 0x93, 0xc3, 0xfd, 0xc3, 0xf1, 0xaf, 0x87, 0x11, 0x58, 0x0b, 0x13, 0x24,
+	0xeb, 0xb0, 0xdc, 0x32, 0x9c, 0x64, 0xdd, 0xc5, 0x12, 0x97, 0xae, 0x89, 0x4d, 0x02, 0x3f, 0x40,
+	0x9d, 0x16, 0x8d, 0xcb, 0x6a, 0x42, 0xef, 0x21, 0xdd, 0xb7, 0xc8, 0xa8, 0xac, 0x1a, 0xf6, 0x3e,
+	0xb8, 0x17, 0x42, 0xd5, 0xc9, 0xba, 0x5f, 0x95, 0x6c, 0xcf, 0x18, 0x60, 0xaa, 0x11, 0xf4, 0xd1,
+	0x6c, 0xa3, 0x48, 0xac, 0x21, 0x7c, 0x95, 0xa1, 0x67, 0x4c, 0x36, 0x8a, 0x04, 0x3d, 0x1f, 0x40,
+	0x17, 0x3d, 0x85, 0x0a, 0x3c, 0xac, 0x61, 0xde, 0xbc, 0x8e, 0x15, 0x09, 0xeb, 0xc3, 0x84, 0xa2,
+	0xfc, 0x52, 0x4b, 0x16, 0x2c, 0x20, 0xec, 0x59, 0x78, 0xcf, 0x84, 0x26, 0x9c, 0x58, 0x0a, 0xa5,
+	0x8c, 0x45, 0xff, 0x9a, 0x33, 0x32, 0xb1, 0xb1, 0x22, 0x4f, 0x61, 0xb1, 0xc5, 0xc1, 0xb4, 0x07,
+	0xb6, 0x7d, 0x26, 0x2c, 0x4c, 0xe4, 0x19, 0x2c, 0xb7, 0x78, 0x93, 0x12, 0x17, 0xed, 0xc5, 0x4e,
+	0xb8, 0xad, 0xbc, 0x45, 0xa5, 0x69, 0x92, 0xca, 0xc0, 0xb7, 0x79, 0x8b, 0x4a, 0xef, 0xa6, 0x92,
+	0xfc, 0x04, 0x9e, 0xe2, 0xba, 0x2a, 0xa9, 0x16, 0x22, 0x53, 0xc1, 0xd2, 0x70, 0x76, 0xcd, 0xdb,
+	0x58, 0x9d, 0x7a, 0x45, 0xef, 0xb8, 0x3c, 0x7f, 0x5b, 0x9c, 0x8b, 0x08, 0x50, 0x71, 0x6c, 0x04,
+	0x64, 0x0b, 0xdc, 0x3f, 0x98, 0x4e, 0xa9, 0xac, 0x0a, 0x15, 0x90, 0xbb, 0xa8, 0x7b, 0x86, 0x1f,
+	0x55, 0x85, 0x22, 0xaf, 0x01, 0x2c, 0x13, 0xc5, 0xcb, 0x77, 0x11, 0xbb, 0x88, 0x36, 0xea, 0x22,
+	0x2d, 0x3e, 0x32, 0xab, 0x5e, 0xb9, 0x93, 0x1a, 0x05, 0xa8, 0xfe, 0x11, 0xe6, 0xb4, 0xd0, 0x2c,
+	0x0b, 0xee, 0x0f, 0x9d, 0xdb, 0x85, 0x96, 0x4b, 0x4e, 0x61, 0xda, 0x2a, 0x0a, 0xbe, 0x41, 0x8b,
+	0xa7, 0x53, 0x2d, 0x8e, 0x4c, 0x0c, 0x47, 0xb2, 0xee, 0xb0, 0x68, 0x49, 0xfd, 0x37, 0x14, 0xbe,
+	0x80, 0x85, 0x1b, 0x53, 0xdb, 0x83, 0xce, 0xc9, 0xd1, 0x5e, 0xe4, 0xdf, 0x23, 0x7d, 0x70, 0xcd,
+	0xd3, 0xee, 0xde, 0xce, 0xc9, 0x1b, 0xdf, 0x21, 0x5d, 0x30, 0x93, 0xee, 0xcf, 0x84, 0xaf, 0xa1,
+	0x83, 0xdf, 0xd5, 0x83, 0xa6, 0x4f, 0xfd, 0x7b, 0x06, 0xdd, 0x8e, 0x0e, 0x7c, 0x87, 0xb8, 0x30,
+	0xb7, 0x1d, 0x1d, 0x6c, 0xbe, 0xf4, 0x67, 0x4c, 0xec, 0xfd, 0xab, 0x4d, 0x7f, 0x96, 0x00, 0xcc,
+	0xbf, 0x7f, 0xb5, 0x49, 0x37, 0x5f, 0xfa, 0x9d, 0xf0, 0x4f, 0x07, 0x7a, 0x4d, 0x6d, 0x84, 0x40,
+	0x27, 0xe1, 0x2a, 0xc6, 0x45, 0xe9, 0x46, 0xf8, 0x6c, 0x62, 0xb8, 0xea, 0xec, 0x5a, 0xc4, 0x67,
+	0xb2, 0x0a, 0xa0, 0x34, 0x93, 0x1a, 0x77, 0x2b, 0x2e, 0xc1, 0x4e, 0xe4, 0x62, 0xc4, 0xac, 0x54,
+	0xf2, 0x18, 0x5c, 0xc9, 0x59, 0x66, 0xd1, 0x0e, 0xa2, 0x3d, 0x13, 0x40, 0x70, 0x15, 0x20, 0xe7,
+	0xb9, 0x90, 0x57, 0xb4, 0x52, 0x1c, 0x57, 0x5c, 0x27, 0x72, 0x6d, 0xe4, 0x44, 0xf1, 0xf0, 0x1f,
+	0x07, 0x06, 0x07, 0x22, 0xa9, 0x32, 0x7e, 0x7c, 0x55, 0x72, 0xcc, 0xea, 0x77, 0x58, 0xb0, 0x17,
+	0xa9, 0xae, 0x94, 0xe6, 0x39, 0x66, 0x37, 0xd8, 0x78, 0x3e, 0x7d, 0x76, 0x6f, 0x48, 0xed, 0x66,
+	0x3c, 0x42, 0x59, 0x6b, 0x8a, 0xcf, 0xae, 0xa3, 0xe4, 0x5b, 0xf0, 0x72, 0xd4, 0x50, 0x7d, 0x55,
+	0x36, 0x55, 0x42, 0x3e, 0xb1, 0x21, 0xdf, 0xc1, 0xa0, 0xa8, 0x72, 0x2a, 0xce, 0xa9, 0x0d, 0x2a,
+	0xac, 0xb7, 0x1f, 0x2d, 0x14, 0x55, 0x3e, 0x3e, 0xb7, 0xe7, 0xa9, 0xf0, 0x39, 0x78, 0xad, 0xb3,
+	0x6e, 0x7e, 0x0b, 0x17, 0xe6, 0x8e, 0xc6, 0xe3, 0x43, 0xf3, 0xd1, 0x7a, 0xd0, 0x39, 0xd8, 0xde,
+	0xdf, 0xf3, 0x67, 0xc2, 0x0c, 0x1e, 0x8d, 0x64, 0xaa, 0xd3, 0x98, 0x65, 0x27, 0x8a, 0xcb, 0x5f,
+	0x44, 0x25, 0x0b, 0x7e, 0x55, 0x77, 0xc1, 0xe4, 0xd2, 0x9d, 0xd6, 0xa5, 0x6f, 0x41, 0xb7, 0xe9,
+	0xb2, 0x19, 0xec, 0xb2, 0xe1, 0x6d, 0xdb, 0x2b, 0x6a, 0x04, 0xe1, 0x19, 0x3c, 0x9e, 0x72, 0x9a,
+	0x6a, 0x8e, 0x1b, 0x41, 0x27, 0xae, 0x3e, 0xaa, 0xc0, 0xc1, 0xc9, 0x99, 0x7e, 0xb3, 0x5f, 0xce,
+	0x36, 0x42, 0x71, 0xf8, 0xb7, 0x03, 0x4b, 0xff, 0x6b, 0x71, 0x12, 0x40, 0xb7, 0xb9, 0x37, 0x07,
+	0xef, 0xad, 0x79, 0x25, 0x8f, 0xa0, 0x57, 0xff, 0x06, 0xd8, 0x82, 0xfa, 0xd1, 0xe4, 0x9d, 0x7c,
+	0x0f, 0x4b, 0x38, 0x66, 0x94, 0x65, 0x99, 0x88, 0x69, 0x2c, 0xaa, 0x42, 0xd7, 0x7d, 0xb6, 0x88,
+	0xc0, 0xb6, 0x89, 0x8f, 0x4c, 0x98, 0xac, 0x81, 0xdf, 0xe6, 0xaa, 0xf4, 0x73, 0xd3, 0x74, 0x83,
+	0x6b, 0xea, 0x51, 0xfa, 0x99, 0x9b, 0xa5, 0x9b, 0xb3, 0x4b, 0x7a, 0xc1, 0x59, 0x69, 0x69, 0xb6,
+	0xfb, 0xbc, 0x9c, 0x5d, 0xfe, 0xcc, 0x59, 0x69, 0x38, 0x3b, 0xf7, 0x7f, 0xab, 0xe7, 0xba, 0xae,
+	0x9b, 0xe2, 0xff, 0x8e, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x2a, 0x36, 0xe3, 0x87, 0x08,
+	0x00, 0x00,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 194aa6b..50810eb 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -92,6 +92,8 @@
 
   // The metrics for the whole build
   optional PerfInfo total = 21;
+
+  optional SoongBuildMetrics soong_build_metrics = 22;
 }
 
 message PerfInfo {
@@ -140,4 +142,21 @@
 message CriticalUserJourneysMetrics {
   // A set of metrics from a run of the critical user journey tests.
   repeated CriticalUserJourneyMetrics cujs = 1;
+}
+
+message SoongBuildMetrics {
+  // The number of modules handled by soong_build.
+  optional uint32 modules = 1;
+
+  // The total number of variants handled by soong_build.
+  optional uint32 variants = 2;
+
+  // The total number of allocations in soong_build.
+  optional uint64 total_alloc_count = 3;
+
+  // The total size of allocations in soong_build in bytes.
+  optional uint64 total_alloc_size = 4;
+
+  // The approximate maximum size of the heap in soong_build in bytes.
+  optional uint64 max_heap_size = 5;
 }
\ No newline at end of file
diff --git a/ui/metrics/upload_proto/regen.sh b/ui/metrics/upload_proto/regen.sh
new file mode 100755
index 0000000..4521df7
--- /dev/null
+++ b/ui/metrics/upload_proto/regen.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# Generates the golang source file of upload.proto file.
+
+set -e
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+  die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. upload.proto; then
+  die "build failed. ${error_msg}"
+fi
diff --git a/ui/metrics/upload_proto/upload.pb.go b/ui/metrics/upload_proto/upload.pb.go
new file mode 100644
index 0000000..1b1e5e8
--- /dev/null
+++ b/ui/metrics/upload_proto/upload.pb.go
@@ -0,0 +1,122 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: upload.proto
+
+package soong_metrics_upload_proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Upload struct {
+	// The timestamp in milliseconds that the build was created.
+	CreationTimestampMs *uint64 `protobuf:"varint,1,opt,name=creation_timestamp_ms,json=creationTimestampMs" json:"creation_timestamp_ms,omitempty"`
+	// The timestamp in milliseconds when the build was completed.
+	CompletionTimestampMs *uint64 `protobuf:"varint,2,opt,name=completion_timestamp_ms,json=completionTimestampMs" json:"completion_timestamp_ms,omitempty"`
+	// The branch name.
+	BranchName *string `protobuf:"bytes,3,opt,name=branch_name,json=branchName" json:"branch_name,omitempty"`
+	// The target name.
+	TargetName *string `protobuf:"bytes,4,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
+	// A list of metrics filepaths to upload.
+	MetricsFiles         []string `protobuf:"bytes,5,rep,name=metrics_files,json=metricsFiles" json:"metrics_files,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Upload) Reset()         { *m = Upload{} }
+func (m *Upload) String() string { return proto.CompactTextString(m) }
+func (*Upload) ProtoMessage()    {}
+func (*Upload) Descriptor() ([]byte, []int) {
+	return fileDescriptor_91b94b655bd2a7e5, []int{0}
+}
+
+func (m *Upload) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Upload.Unmarshal(m, b)
+}
+func (m *Upload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Upload.Marshal(b, m, deterministic)
+}
+func (m *Upload) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Upload.Merge(m, src)
+}
+func (m *Upload) XXX_Size() int {
+	return xxx_messageInfo_Upload.Size(m)
+}
+func (m *Upload) XXX_DiscardUnknown() {
+	xxx_messageInfo_Upload.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Upload proto.InternalMessageInfo
+
+func (m *Upload) GetCreationTimestampMs() uint64 {
+	if m != nil && m.CreationTimestampMs != nil {
+		return *m.CreationTimestampMs
+	}
+	return 0
+}
+
+func (m *Upload) GetCompletionTimestampMs() uint64 {
+	if m != nil && m.CompletionTimestampMs != nil {
+		return *m.CompletionTimestampMs
+	}
+	return 0
+}
+
+func (m *Upload) GetBranchName() string {
+	if m != nil && m.BranchName != nil {
+		return *m.BranchName
+	}
+	return ""
+}
+
+func (m *Upload) GetTargetName() string {
+	if m != nil && m.TargetName != nil {
+		return *m.TargetName
+	}
+	return ""
+}
+
+func (m *Upload) GetMetricsFiles() []string {
+	if m != nil {
+		return m.MetricsFiles
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Upload)(nil), "soong_metrics_upload.Upload")
+}
+
+func init() {
+	proto.RegisterFile("upload.proto", fileDescriptor_91b94b655bd2a7e5)
+}
+
+var fileDescriptor_91b94b655bd2a7e5 = []byte{
+	// 201 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x2d, 0xc8, 0xc9,
+	0x4f, 0x4c, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x29, 0xce, 0xcf, 0xcf, 0x4b, 0x8f,
+	0xcf, 0x4d, 0x2d, 0x29, 0xca, 0x4c, 0x2e, 0x8e, 0x87, 0xc8, 0x29, 0xdd, 0x66, 0xe4, 0x62, 0x0b,
+	0x05, 0x33, 0x85, 0x8c, 0xb8, 0x44, 0x93, 0x8b, 0x52, 0x13, 0x4b, 0x32, 0xf3, 0xf3, 0xe2, 0x4b,
+	0x32, 0x73, 0x53, 0x8b, 0x4b, 0x12, 0x73, 0x0b, 0xe2, 0x73, 0x8b, 0x25, 0x18, 0x15, 0x18, 0x35,
+	0x58, 0x82, 0x84, 0x61, 0x92, 0x21, 0x30, 0x39, 0xdf, 0x62, 0x21, 0x33, 0x2e, 0xf1, 0xe4, 0xfc,
+	0xdc, 0x82, 0x9c, 0x54, 0x4c, 0x5d, 0x4c, 0x60, 0x5d, 0xa2, 0x08, 0x69, 0x64, 0x7d, 0xf2, 0x5c,
+	0xdc, 0x49, 0x45, 0x89, 0x79, 0xc9, 0x19, 0xf1, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0xcc, 0x0a, 0x8c,
+	0x1a, 0x9c, 0x41, 0x5c, 0x10, 0x21, 0xbf, 0xc4, 0xdc, 0x54, 0x90, 0x82, 0x92, 0xc4, 0xa2, 0xf4,
+	0xd4, 0x12, 0x88, 0x02, 0x16, 0x88, 0x02, 0x88, 0x10, 0x58, 0x81, 0x32, 0x17, 0x2f, 0xcc, 0x2b,
+	0x69, 0x99, 0x39, 0xa9, 0xc5, 0x12, 0xac, 0x0a, 0xcc, 0x1a, 0x9c, 0x41, 0x3c, 0x50, 0x41, 0x37,
+	0x90, 0x98, 0x93, 0x4c, 0x94, 0x14, 0x36, 0x5f, 0xc7, 0x83, 0x43, 0x04, 0x10, 0x00, 0x00, 0xff,
+	0xff, 0xe2, 0x01, 0x74, 0x65, 0x20, 0x01, 0x00, 0x00,
+}
diff --git a/ui/metrics/upload_proto/upload.proto b/ui/metrics/upload_proto/upload.proto
new file mode 100644
index 0000000..7a9f080
--- /dev/null
+++ b/ui/metrics/upload_proto/upload.proto
@@ -0,0 +1,35 @@
+// 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.
+
+syntax = "proto2";
+
+package soong_metrics_upload;
+option go_package = "soong_metrics_upload_proto";
+
+message Upload {
+  // The timestamp in milliseconds that the build was created.
+  optional uint64 creation_timestamp_ms = 1;
+
+  // The timestamp in milliseconds when the build was completed.
+  optional uint64 completion_timestamp_ms = 2;
+
+  // The branch name.
+  optional string branch_name = 3;
+
+  // The target name.
+  optional string target_name = 4;
+
+  // A list of metrics filepaths to upload.
+  repeated string metrics_files = 5;
+}
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
index ec929b3..19e5a2a 100644
--- a/ui/status/Android.bp
+++ b/ui/status/Android.bp
@@ -20,6 +20,7 @@
         "soong-ui-logger",
         "soong-ui-status-ninja_frontend",
         "soong-ui-status-build_error_proto",
+        "soong-ui-status-build_progress_proto",
     ],
     srcs: [
         "critical_path.go",
@@ -53,3 +54,12 @@
         "build_error_proto/build_error.pb.go",
     ],
 }
+
+bootstrap_go_package {
+    name: "soong-ui-status-build_progress_proto",
+    pkgPath: "android/soong/ui/status/build_progress_proto",
+    deps: ["golang-protobuf-proto"],
+    srcs: [
+        "build_progress_proto/build_progress.pb.go",
+    ],
+}
diff --git a/ui/status/build_progress_proto/build_progress.pb.go b/ui/status/build_progress_proto/build_progress.pb.go
new file mode 100644
index 0000000..f63c157
--- /dev/null
+++ b/ui/status/build_progress_proto/build_progress.pb.go
@@ -0,0 +1,115 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: build_progress.proto
+
+package soong_build_progress_proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type BuildProgress struct {
+	// Total number of actions in a build. The total actions will increase
+	// and might decrease during the course of a build.
+	TotalActions *uint64 `protobuf:"varint,1,opt,name=total_actions,json=totalActions" json:"total_actions,omitempty"`
+	// Total number of completed build actions. This value will never decrease
+	// and finished_actions <= total_actions. At one point of the build, the
+	// finished_actions will be equal to total_actions. This may not represent
+	// that the build is completed as the total_actions may be increased for
+	// additional counted work or is doing non-counted work.
+	FinishedActions *uint64 `protobuf:"varint,2,opt,name=finished_actions,json=finishedActions" json:"finished_actions,omitempty"`
+	// Total number of current actions being executed during a course of a
+	// build and current_actions + finished_actions <= total_actions.
+	CurrentActions *uint64 `protobuf:"varint,3,opt,name=current_actions,json=currentActions" json:"current_actions,omitempty"`
+	// Total number of actions that reported as a failure.
+	FailedActions        *uint64  `protobuf:"varint,4,opt,name=failed_actions,json=failedActions" json:"failed_actions,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *BuildProgress) Reset()         { *m = BuildProgress{} }
+func (m *BuildProgress) String() string { return proto.CompactTextString(m) }
+func (*BuildProgress) ProtoMessage()    {}
+func (*BuildProgress) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a8a463f8e30dab2e, []int{0}
+}
+
+func (m *BuildProgress) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_BuildProgress.Unmarshal(m, b)
+}
+func (m *BuildProgress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_BuildProgress.Marshal(b, m, deterministic)
+}
+func (m *BuildProgress) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_BuildProgress.Merge(m, src)
+}
+func (m *BuildProgress) XXX_Size() int {
+	return xxx_messageInfo_BuildProgress.Size(m)
+}
+func (m *BuildProgress) XXX_DiscardUnknown() {
+	xxx_messageInfo_BuildProgress.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildProgress proto.InternalMessageInfo
+
+func (m *BuildProgress) GetTotalActions() uint64 {
+	if m != nil && m.TotalActions != nil {
+		return *m.TotalActions
+	}
+	return 0
+}
+
+func (m *BuildProgress) GetFinishedActions() uint64 {
+	if m != nil && m.FinishedActions != nil {
+		return *m.FinishedActions
+	}
+	return 0
+}
+
+func (m *BuildProgress) GetCurrentActions() uint64 {
+	if m != nil && m.CurrentActions != nil {
+		return *m.CurrentActions
+	}
+	return 0
+}
+
+func (m *BuildProgress) GetFailedActions() uint64 {
+	if m != nil && m.FailedActions != nil {
+		return *m.FailedActions
+	}
+	return 0
+}
+
+func init() {
+	proto.RegisterType((*BuildProgress)(nil), "soong_build_progress.BuildProgress")
+}
+
+func init() { proto.RegisterFile("build_progress.proto", fileDescriptor_a8a463f8e30dab2e) }
+
+var fileDescriptor_a8a463f8e30dab2e = []byte{
+	// 165 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x49, 0x2a, 0xcd, 0xcc,
+	0x49, 0x89, 0x2f, 0x28, 0xca, 0x4f, 0x2f, 0x4a, 0x2d, 0x2e, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9,
+	0x17, 0x12, 0x29, 0xce, 0xcf, 0xcf, 0x4b, 0x8f, 0x47, 0x95, 0x53, 0x5a, 0xcf, 0xc8, 0xc5, 0xeb,
+	0x04, 0x12, 0x0a, 0x80, 0x8a, 0x08, 0x29, 0x73, 0xf1, 0x96, 0xe4, 0x97, 0x24, 0xe6, 0xc4, 0x27,
+	0x26, 0x97, 0x64, 0xe6, 0xe7, 0x15, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x04, 0xf1, 0x80, 0x05,
+	0x1d, 0x21, 0x62, 0x42, 0x9a, 0x5c, 0x02, 0x69, 0x99, 0x79, 0x99, 0xc5, 0x19, 0xa9, 0x29, 0x70,
+	0x75, 0x4c, 0x60, 0x75, 0xfc, 0x30, 0x71, 0x98, 0x52, 0x75, 0x2e, 0xfe, 0xe4, 0xd2, 0xa2, 0xa2,
+	0xd4, 0xbc, 0x12, 0xb8, 0x4a, 0x66, 0xb0, 0x4a, 0x3e, 0xa8, 0x30, 0x4c, 0xa1, 0x2a, 0x17, 0x5f,
+	0x5a, 0x62, 0x66, 0x0e, 0x92, 0x89, 0x2c, 0x60, 0x75, 0xbc, 0x10, 0x51, 0xa8, 0x32, 0x27, 0x99,
+	0x28, 0x29, 0x6c, 0x3e, 0x89, 0x07, 0xfb, 0x12, 0x10, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x6e, 0xc1,
+	0xef, 0xfc, 0x00, 0x00, 0x00,
+}
diff --git a/ui/status/build_progress_proto/build_progress.proto b/ui/status/build_progress_proto/build_progress.proto
new file mode 100644
index 0000000..d78060a
--- /dev/null
+++ b/ui/status/build_progress_proto/build_progress.proto
@@ -0,0 +1,38 @@
+// 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.
+
+syntax = "proto2";
+
+package soong_build_progress;
+option go_package = "soong_build_progress_proto";
+
+message BuildProgress {
+  // Total number of actions in a build. The total actions will increase
+  // and might decrease during the course of a build.
+  optional uint64 total_actions = 1;
+
+  // Total number of completed build actions. This value will never decrease
+  // and finished_actions <= total_actions. At one point of the build, the
+  // finished_actions will be equal to total_actions. This may not represent
+  // that the build is completed as the total_actions may be increased for
+  // additional counted work or is doing non-counted work.
+  optional uint64 finished_actions = 2;
+
+  // Total number of current actions being executed during a course of a
+  // build and current_actions + finished_actions <= total_actions.
+  optional uint64 current_actions = 3;
+
+  // Total number of actions that reported as a failure.
+  optional uint64 failed_actions = 4;
+}
diff --git a/ui/status/build_progress_proto/regen.sh b/ui/status/build_progress_proto/regen.sh
new file mode 100755
index 0000000..572785d
--- /dev/null
+++ b/ui/status/build_progress_proto/regen.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# Generates the golang source file of build_completion.proto file.
+
+set -e
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+  die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. build_progress.proto; then
+  die "build failed. ${error_msg}"
+fi
diff --git a/ui/status/log.go b/ui/status/log.go
index d407248..4a08acb 100644
--- a/ui/status/log.go
+++ b/ui/status/log.go
@@ -20,12 +20,14 @@
 	"fmt"
 	"io"
 	"io/ioutil"
+	"os"
 	"strings"
 
 	"github.com/golang/protobuf/proto"
 
 	"android/soong/ui/logger"
 	"android/soong/ui/status/build_error_proto"
+	"android/soong/ui/status/build_progress_proto"
 )
 
 type verboseLog struct {
@@ -154,6 +156,7 @@
 }
 
 func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput {
+	os.Remove(filename)
 	return &errorProtoLog{
 		errorProto: soong_build_error_proto.BuildError{},
 		filename:   filename,
@@ -175,20 +178,17 @@
 		Artifacts:   result.Outputs,
 		Error:       proto.String(result.Error.Error()),
 	})
-}
 
-func (e *errorProtoLog) Flush() {
-	data, err := proto.Marshal(&e.errorProto)
-	if err != nil {
-		e.log.Printf("Failed to marshal build status proto: %v\n", err)
-		return
-	}
-	err = ioutil.WriteFile(e.filename, []byte(data), 0644)
+	err := writeToFile(&e.errorProto, e.filename)
 	if err != nil {
 		e.log.Printf("Failed to write file %s: %v\n", e.filename, err)
 	}
 }
 
+func (e *errorProtoLog) Flush() {
+	//Not required.
+}
+
 func (e *errorProtoLog) Message(level MsgLevel, message string) {
 	if level > ErrorLvl {
 		e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message)
@@ -198,3 +198,75 @@
 func (e *errorProtoLog) Write(p []byte) (int, error) {
 	return 0, errors.New("not supported")
 }
+
+type buildProgressLog struct {
+	filename      string
+	log           logger.Logger
+	failedActions uint64
+}
+
+func NewBuildProgressLog(log logger.Logger, filename string) StatusOutput {
+	return &buildProgressLog{
+		filename:      filename,
+		log:           log,
+		failedActions: 0,
+	}
+}
+
+func (b *buildProgressLog) StartAction(action *Action, counts Counts) {
+	b.updateCounters(counts)
+}
+
+func (b *buildProgressLog) FinishAction(result ActionResult, counts Counts) {
+	if result.Error != nil {
+		b.failedActions++
+	}
+	b.updateCounters(counts)
+}
+
+func (b *buildProgressLog) Flush() {
+	//Not required.
+}
+
+func (b *buildProgressLog) Message(level MsgLevel, message string) {
+	// Not required.
+}
+
+func (b *buildProgressLog) Write(p []byte) (int, error) {
+	return 0, errors.New("not supported")
+}
+
+func (b *buildProgressLog) updateCounters(counts Counts) {
+	err := writeToFile(
+		&soong_build_progress_proto.BuildProgress{
+			CurrentActions:  proto.Uint64(uint64(counts.RunningActions)),
+			FinishedActions: proto.Uint64(uint64(counts.FinishedActions)),
+			TotalActions:    proto.Uint64(uint64(counts.TotalActions)),
+			FailedActions:   proto.Uint64(b.failedActions),
+		},
+		b.filename,
+	)
+	if err != nil {
+		b.log.Printf("Failed to write file %s: %v\n", b.filename, err)
+	}
+}
+
+func writeToFile(pb proto.Message, outputPath string) (err error) {
+	data, err := proto.Marshal(pb)
+	if err != nil {
+		return err
+	}
+
+	tempPath := outputPath + ".tmp"
+	err = ioutil.WriteFile(tempPath, []byte(data), 0644)
+	if err != nil {
+		return err
+	}
+
+	err = os.Rename(tempPath, outputPath)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/vnames.go.json b/vnames.go.json
index 5842097..7ce2d4b 100644
--- a/vnames.go.json
+++ b/vnames.go.json
@@ -3,7 +3,7 @@
         "pattern": "(.*)",
         "vname": {
             "corpus": "android.googlesource.com/platform/superproject",
-            "path": "build/soong/@1@"
+            "path": "@1@"
         }
     }
 ]