Merge "Include car project in grey list removed apis" into rvc-dev am: 50cea1a76e

Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/11746426

Change-Id: Ie39738a277df3af9bdae6c59ad39b4bf07b16287
diff --git a/Android.bp b/Android.bp
index 2df1afb..0b44198 100644
--- a/Android.bp
+++ b/Android.bp
@@ -101,7 +101,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..adf2b4c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,13 @@
-per-file * = asmundak@google.com,ccross@android.com,dwillemsen@google.com,jungjw@google.com,paulduffin@google.com
+per-file * = asmundak@google.com
+per-file * = ccross@android.com
+per-file * = dwillemsen@google.com
+per-file * = eakammer@google.com
+per-file * = jungjw@google.com
+per-file * = patricearruda@google.com
+per-file * = paulduffin@google.com
 
 per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com
 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/Android.bp b/android/Android.bp
index 6541106..47dbc5d 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -8,6 +8,7 @@
         "soong-android-soongconfig",
         "soong-env",
         "soong-shared",
+        "soong-ui-metrics_proto",
     ],
     srcs: [
         "androidmk.go",
@@ -23,6 +24,7 @@
         "hooks.go",
         "image.go",
         "makevars.go",
+        "metrics.go",
         "module.go",
         "mutator.go",
         "namespace.go",
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/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..59118ce 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 {
@@ -906,22 +897,31 @@
 }
 
 // Expected format for apexJarValue = <apex name>:<jar name>
-func SplitApexJarPair(apexJarValue string) (string, string) {
-	var apexJarPair []string = strings.SplitN(apexJarValue, ":", 2)
-	if apexJarPair == nil || len(apexJarPair) != 2 {
-		panic(fmt.Errorf("malformed apexJarValue: %q, expected format: <apex>:<jar>",
-			apexJarValue))
+func SplitApexJarPair(ctx PathContext, str string) (string, string) {
+	pair := strings.SplitN(str, ":", 2)
+	if len(pair) == 2 {
+		return pair[0], pair[1]
+	} else {
+		reportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
+		return "error-apex", "error-jar"
 	}
-	return apexJarPair[0], apexJarPair[1]
+}
+
+func GetJarsFromApexJarPairs(ctx PathContext, apexJarPairs []string) []string {
+	modules := make([]string, len(apexJarPairs))
+	for i, p := range apexJarPairs {
+		_, jar := SplitApexJarPair(ctx, p)
+		modules[i] = jar
+	}
+	return modules
 }
 
 func (c *config) BootJars() []string {
-	jars := c.productVariables.BootJars
-	for _, p := range c.productVariables.UpdatableBootJars {
-		_, jar := SplitApexJarPair(p)
-		jars = append(jars, jar)
-	}
-	return jars
+	ctx := NullPathContext{Config{
+		config: c,
+	}}
+	return append(GetJarsFromApexJarPairs(ctx, c.productVariables.BootJars),
+		GetJarsFromApexJarPairs(ctx, c.productVariables.UpdatableBootJars)...)
 }
 
 func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
diff --git a/android/env.go b/android/env.go
index 46bd3d6..c7c96d5 100644
--- a/android/env.go
+++ b/android/env.go
@@ -15,9 +15,11 @@
 package android
 
 import (
+	"fmt"
 	"os"
 	"os/exec"
 	"strings"
+	"syscall"
 
 	"android/soong/env"
 )
@@ -30,28 +32,59 @@
 // a manifest regeneration.
 
 var originalEnv map[string]string
-var SoongDelveListen string
-var SoongDelvePath string
+var soongDelveListen string
+var soongDelvePath string
+var soongDelveEnv []string
 
 func init() {
 	// Delve support needs to read this environment variable very early, before NewConfig has created a way to
 	// access originalEnv with dependencies.  Store the value where soong_build can find it, it will manually
 	// ensure the dependencies are created.
-	SoongDelveListen = os.Getenv("SOONG_DELVE")
-	SoongDelvePath, _ = exec.LookPath("dlv")
+	soongDelveListen = os.Getenv("SOONG_DELVE")
+	soongDelvePath, _ = exec.LookPath("dlv")
 
 	originalEnv = make(map[string]string)
+	soongDelveEnv = []string{}
 	for _, env := range os.Environ() {
 		idx := strings.IndexRune(env, '=')
 		if idx != -1 {
 			originalEnv[env[:idx]] = env[idx+1:]
+			if env[:idx] != "SOONG_DELVE" {
+				soongDelveEnv = append(soongDelveEnv, env)
+			}
 		}
 	}
+
 	// Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment
 	// variable values.  The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc.
 	os.Clearenv()
 }
 
+func ReexecWithDelveMaybe() {
+	if soongDelveListen == "" {
+		return
+	}
+
+	if soongDelvePath == "" {
+		fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
+		os.Exit(1)
+	}
+	dlvArgv := []string{
+		soongDelvePath,
+		"--listen=:" + soongDelveListen,
+		"--headless=true",
+		"--api-version=2",
+		"exec",
+		os.Args[0],
+		"--",
+	}
+	dlvArgv = append(dlvArgv, os.Args[1:]...)
+	os.Chdir(absSrcDir)
+	syscall.Exec(soongDelvePath, dlvArgv, soongDelveEnv)
+	fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
+	os.Exit(1)
+}
+
 // getenv checks either os.Getenv or originalEnv so that it works before or after the init()
 // function above.  It doesn't add any dependencies on the environment variable, so it should
 // only be used for values that won't change.  For values that might change use ctx.Config().Getenv.
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..82321f4 100644
--- a/android/module.go
+++ b/android/module.go
@@ -19,6 +19,7 @@
 	"os"
 	"path"
 	"path/filepath"
+	"regexp"
 	"strings"
 	"text/scanner"
 
@@ -103,11 +104,15 @@
 type BaseModuleContext interface {
 	EarlyModuleContext
 
+	blueprintBaseModuleContext() blueprint.BaseModuleContext
+
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
 	OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 	OtherModuleExists(name string) bool
+	OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool
+	OtherModuleReverseDependencyVariantExists(name string) bool
 	OtherModuleType(m blueprint.Module) string
 
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
@@ -135,6 +140,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 +194,7 @@
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
+	InstallForceOS() *OsType
 
 	RequiredModuleNames() []string
 	HostRequiredModuleNames() []string
@@ -224,12 +237,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 +264,8 @@
 	RequiredModuleNames() []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
+
+	filesToInstall() InstallPaths
 }
 
 // Qualified id for a module
@@ -703,9 +719,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 +966,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 +1015,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 +1077,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 +1097,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 +1283,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)
@@ -1404,6 +1435,12 @@
 	return b.bp.OtherModuleDependencyTag(m)
 }
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
+func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
+	return b.bp.OtherModuleDependencyVariantExists(variations, name)
+}
+func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool {
+	return b.bp.OtherModuleReverseDependencyVariantExists(name)
+}
 func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
 	return b.bp.OtherModuleType(m)
 }
@@ -1412,11 +1449,15 @@
 	return b.bp.GetDirectDepWithTag(name, tag)
 }
 
+func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext {
+	return b.bp
+}
+
 type moduleContext struct {
 	bp blueprint.ModuleContext
 	baseModuleContext
-	installDeps     Paths
-	installFiles    Paths
+	installDeps     InstallPaths
+	installFiles    InstallPaths
 	checkbuildFiles Paths
 	module          Module
 
@@ -1712,6 +1753,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 +1912,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 +1959,7 @@
 
 	if !m.skipInstall(fullInstallPath) {
 
-		deps = append(deps, m.installDeps...)
+		deps = append(deps, m.installDeps.Paths()...)
 
 		var implicitDeps, orderOnlyDeps Paths
 
@@ -1960,20 +2040,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 {
@@ -2308,4 +2374,10 @@
 	Classes           []string `json:"class,omitempty"`
 	Installed_paths   []string `json:"installed,omitempty"`
 	SrcJars           []string `json:"srcjars,omitempty"`
+	Paths             []string `json:"path,omitempty"`
+}
+
+func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents string) []error {
+	bpctx := ctx.blueprintBaseModuleContext()
+	return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents)
 }
diff --git a/android/neverallow.go b/android/neverallow.go
index 9e075b7..a5e641c 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -187,6 +187,11 @@
 		// derive_sdk_prefer32 suppress the platform installation rules, but fails when
 		// the APEX modules contain the SDK variant and the platform variant still exists.
 		"frameworks/base/apex/sdkextensions/derive_sdk",
+		// These are for apps and shouldn't be used by non-SDK variant modules.
+		"prebuilts/ndk",
+		"tools/test/graphicsbenchmark/apps/sample_app",
+		"tools/test/graphicsbenchmark/functional_tests/java",
+		"vendor/xts/gts-tests/hostsidetests/gamedevicecert/apps/javatests",
 	}
 
 	platformVariantPropertiesWhitelist := []string{
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/paths.go b/android/paths.go
index 1d8d92a..3ad27ac 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -43,6 +43,14 @@
 var _ PathContext = SingletonContext(nil)
 var _ PathContext = ModuleContext(nil)
 
+// "Null" path context is a minimal path context for a given config.
+type NullPathContext struct {
+	config Config
+}
+
+func (NullPathContext) AddNinjaFileDeps(...string) {}
+func (ctx NullPathContext) Config() Config         { return ctx.config }
+
 type ModuleInstallPathContext interface {
 	BaseModuleContext
 
@@ -53,6 +61,7 @@
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
+	InstallForceOS() *OsType
 }
 
 var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -469,6 +478,23 @@
 // 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)
+}
+
+// SortedUniquePaths returns what its name says
+func SortedUniquePaths(list Paths) Paths {
+	unique := FirstUniquePaths(list)
+	sort.Slice(unique, func(i, j int) bool {
+		return unique[i].String() < unique[j].String()
+	})
+	return unique
+}
+
+func firstUniquePathsList(list Paths) Paths {
 	k := 0
 outer:
 	for i := 0; i < len(list); i++ {
@@ -483,13 +509,18 @@
 	return list[:k]
 }
 
-// SortedUniquePaths returns what its name says
-func SortedUniquePaths(list Paths) Paths {
-	unique := FirstUniquePaths(list)
-	sort.Slice(unique, func(i, j int) bool {
-		return unique[i].String() < unique[j].String()
-	})
-	return unique
+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
@@ -1214,22 +1245,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...)
@@ -1240,9 +1289,6 @@
 	}
 
 	ret := InstallPath{basePath{path, ctx.Config(), ""}, ""}
-	if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
-		ret = ret.ToMakePath()
-	}
 
 	return ret
 }
@@ -1270,47 +1316,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/prebuilt.go b/android/prebuilt.go
index ee4a13a..a29ec91 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -52,6 +52,9 @@
 
 	SourceExists bool `blueprint:"mutated"`
 	UsePrebuilt  bool `blueprint:"mutated"`
+
+	// Set if the module has been renamed to remove the "prebuilt_" prefix.
+	PrebuiltRenamedToSource bool `blueprint:"mutated"`
 }
 
 type Prebuilt struct {
@@ -188,25 +191,38 @@
 }
 
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilts", PrebuiltMutator).Parallel()
+	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
 }
 
 func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel()
 	ctx.TopDown("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
 	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
 }
 
-// PrebuiltMutator ensures that there is always a module with an undecorated name, and marks
-// prebuilt modules that have both a prebuilt and a source module.
-func PrebuiltMutator(ctx BottomUpMutatorContext) {
+// PrebuiltRenameMutator ensures that there always is a module with an
+// undecorated name.
+func PrebuiltRenameMutator(ctx BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+		name := m.base().BaseModuleName()
+		if !ctx.OtherModuleExists(name) {
+			ctx.Rename(name)
+			m.Prebuilt().properties.PrebuiltRenamedToSource = true
+		}
+	}
+}
+
+// PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the
+// corresponding source module, if one exists for the same variant.
+func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
 	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
 		p := m.Prebuilt()
-		name := m.base().BaseModuleName()
-		if ctx.OtherModuleExists(name) {
-			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
-			p.properties.SourceExists = true
-		} else {
-			ctx.Rename(name)
+		if !p.properties.PrebuiltRenamedToSource {
+			name := m.base().BaseModuleName()
+			if ctx.OtherModuleReverseDependencyVariantExists(name) {
+				ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
+				p.properties.SourceExists = true
+			}
 		}
 	}
 }
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 8ff5c40..b568f78 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -24,7 +24,7 @@
 var prebuiltsTests = []struct {
 	name     string
 	modules  string
-	prebuilt bool
+	prebuilt []OsClass
 }{
 	{
 		name: "no prebuilt",
@@ -32,7 +32,7 @@
 			source {
 				name: "bar",
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "no source prebuilt not preferred",
@@ -42,7 +42,7 @@
 				prefer: false,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "no source prebuilt preferred",
@@ -52,7 +52,7 @@
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "prebuilt not preferred",
@@ -60,13 +60,13 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: false,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt preferred",
@@ -74,13 +74,13 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "prebuilt no file not preferred",
@@ -88,12 +88,12 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: false,
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt no file preferred",
@@ -101,12 +101,12 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: true,
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt file from filegroup preferred",
@@ -120,7 +120,40 @@
 				prefer: true,
 				srcs: [":fg"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
+	},
+	{
+		name: "prebuilt module for device only",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				host_supported: false,
+				prefer: true,
+				srcs: ["prebuilt_file"],
+			}`,
+		prebuilt: []OsClass{Device},
+	},
+	{
+		name: "prebuilt file for host only",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				prefer: true,
+				target: {
+					host: {
+						srcs: ["prebuilt_file"],
+					},
+				},
+			}`,
+		prebuilt: []OsClass{Host},
 	},
 }
 
@@ -138,9 +171,9 @@
 					deps: [":bar"],
 				}
 				` + test.modules
-			config := TestConfig(buildDir, nil, bp, fs)
+			config := TestArchConfig(buildDir, nil, bp, fs)
 
-			ctx := NewTestContext()
+			ctx := NewTestArchContext()
 			registerTestPrebuiltBuildComponents(ctx)
 			ctx.RegisterModuleType("filegroup", FileGroupFactory)
 			ctx.Register(config)
@@ -150,61 +183,71 @@
 			_, errs = ctx.PrepareBuildActions(config)
 			FailIfErrored(t, errs)
 
-			foo := ctx.ModuleForTests("foo", "")
+			for _, variant := range ctx.ModuleVariantsForTests("foo") {
+				foo := ctx.ModuleForTests("foo", variant)
+				t.Run(foo.Module().Target().Os.Class.String(), func(t *testing.T) {
+					var dependsOnSourceModule, dependsOnPrebuiltModule bool
+					ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
+						if _, ok := m.(*sourceModule); ok {
+							dependsOnSourceModule = true
+						}
+						if p, ok := m.(*prebuiltModule); ok {
+							dependsOnPrebuiltModule = true
+							if !p.Prebuilt().properties.UsePrebuilt {
+								t.Errorf("dependency on prebuilt module not marked used")
+							}
+						}
+					})
 
-			var dependsOnSourceModule, dependsOnPrebuiltModule bool
-			ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
-				if _, ok := m.(*sourceModule); ok {
-					dependsOnSourceModule = true
-				}
-				if p, ok := m.(*prebuiltModule); ok {
-					dependsOnPrebuiltModule = true
-					if !p.Prebuilt().properties.UsePrebuilt {
-						t.Errorf("dependency on prebuilt module not marked used")
+					deps := foo.Module().(*sourceModule).deps
+					if deps == nil || len(deps) != 1 {
+						t.Errorf("deps does not have single path, but is %v", deps)
 					}
-				}
-			})
+					var usingSourceFile, usingPrebuiltFile bool
+					if deps[0].String() == "source_file" {
+						usingSourceFile = true
+					}
+					if deps[0].String() == "prebuilt_file" {
+						usingPrebuiltFile = true
+					}
 
-			deps := foo.Module().(*sourceModule).deps
-			if deps == nil || len(deps) != 1 {
-				t.Errorf("deps does not have single path, but is %v", deps)
-			}
-			var usingSourceFile, usingPrebuiltFile bool
-			if deps[0].String() == "source_file" {
-				usingSourceFile = true
-			}
-			if deps[0].String() == "prebuilt_file" {
-				usingPrebuiltFile = true
-			}
+					prebuilt := false
+					for _, os := range test.prebuilt {
+						if os == foo.Module().Target().Os.Class {
+							prebuilt = true
+						}
+					}
 
-			if test.prebuilt {
-				if !dependsOnPrebuiltModule {
-					t.Errorf("doesn't depend on prebuilt module")
-				}
-				if !usingPrebuiltFile {
-					t.Errorf("doesn't use prebuilt_file")
-				}
+					if prebuilt {
+						if !dependsOnPrebuiltModule {
+							t.Errorf("doesn't depend on prebuilt module")
+						}
+						if !usingPrebuiltFile {
+							t.Errorf("doesn't use prebuilt_file")
+						}
 
-				if dependsOnSourceModule {
-					t.Errorf("depends on source module")
-				}
-				if usingSourceFile {
-					t.Errorf("using source_file")
-				}
-			} else {
-				if dependsOnPrebuiltModule {
-					t.Errorf("depends on prebuilt module")
-				}
-				if usingPrebuiltFile {
-					t.Errorf("using prebuilt_file")
-				}
+						if dependsOnSourceModule {
+							t.Errorf("depends on source module")
+						}
+						if usingSourceFile {
+							t.Errorf("using source_file")
+						}
+					} else {
+						if dependsOnPrebuiltModule {
+							t.Errorf("depends on prebuilt module")
+						}
+						if usingPrebuiltFile {
+							t.Errorf("using prebuilt_file")
+						}
 
-				if !dependsOnSourceModule {
-					t.Errorf("doesn't depend on source module")
-				}
-				if !usingSourceFile {
-					t.Errorf("doesn't use source_file")
-				}
+						if !dependsOnSourceModule {
+							t.Errorf("doesn't depend on source module")
+						}
+						if !usingSourceFile {
+							t.Errorf("doesn't use source_file")
+						}
+					}
+				})
 			}
 		})
 	}
@@ -221,7 +264,7 @@
 	ModuleBase
 	prebuilt   Prebuilt
 	properties struct {
-		Srcs []string `android:"path"`
+		Srcs []string `android:"path,arch_variant"`
 	}
 	src Path
 }
@@ -230,7 +273,7 @@
 	m := &prebuiltModule{}
 	m.AddProperties(&m.properties)
 	InitPrebuiltModule(m, &m.properties.Srcs)
-	InitAndroidModule(m)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
 	return m
 }
 
@@ -260,7 +303,7 @@
 type sourceModule struct {
 	ModuleBase
 	properties struct {
-		Deps []string `android:"path"`
+		Deps []string `android:"path,arch_variant"`
 	}
 	dependsOnSourceModule, dependsOnPrebuiltModule bool
 	deps                                           Paths
@@ -270,7 +313,7 @@
 func newSourceModule() Module {
 	m := &sourceModule{}
 	m.AddProperties(&m.properties)
-	InitAndroidModule(m)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
 	return m
 }
 
diff --git a/android/testing.go b/android/testing.go
index 90989ef..696efb6 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"strings"
 	"testing"
 
@@ -122,9 +123,10 @@
 		ctx.VisitAllModules(func(m blueprint.Module) {
 			allModuleNames = append(allModuleNames, m.(Module).Name()+"("+ctx.ModuleSubDir(m)+")")
 		})
+		sort.Strings(allModuleNames)
 
-		panic(fmt.Errorf("failed to find module %q variant %q."+
-			"\nall modules: %v", name, variant, allModuleNames))
+		panic(fmt.Errorf("failed to find module %q variant %q. All modules:\n  %s",
+			name, variant, strings.Join(allModuleNames, "\n  ")))
 	}
 
 	return TestingModule{module}
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/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 8860984..eaf06c5 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -40,26 +40,31 @@
 	append  bool
 }
 
+var trueValue = &bpparser.Bool{
+	Value: true,
+}
+
 var rewriteProperties = map[string](func(variableAssignmentContext) error){
 	// custom functions
-	"LOCAL_32_BIT_ONLY":           local32BitOnly,
-	"LOCAL_AIDL_INCLUDES":         localAidlIncludes,
-	"LOCAL_ASSET_DIR":             localizePathList("asset_dirs"),
-	"LOCAL_C_INCLUDES":            localIncludeDirs,
-	"LOCAL_EXPORT_C_INCLUDE_DIRS": exportIncludeDirs,
-	"LOCAL_JARJAR_RULES":          localizePath("jarjar_rules"),
-	"LOCAL_LDFLAGS":               ldflags,
-	"LOCAL_MODULE_CLASS":          prebuiltClass,
-	"LOCAL_MODULE_STEM":           stem,
-	"LOCAL_MODULE_HOST_OS":        hostOs,
-	"LOCAL_RESOURCE_DIR":          localizePathList("resource_dirs"),
-	"LOCAL_SANITIZE":              sanitize(""),
-	"LOCAL_SANITIZE_DIAG":         sanitize("diag."),
-	"LOCAL_STRIP_MODULE":          strip(),
-	"LOCAL_CFLAGS":                cflags,
-	"LOCAL_UNINSTALLABLE_MODULE":  invert("installable"),
-	"LOCAL_PROGUARD_ENABLED":      proguardEnabled,
-	"LOCAL_MODULE_PATH":           prebuiltModulePath,
+	"LOCAL_32_BIT_ONLY":                    local32BitOnly,
+	"LOCAL_AIDL_INCLUDES":                  localAidlIncludes,
+	"LOCAL_ASSET_DIR":                      localizePathList("asset_dirs"),
+	"LOCAL_C_INCLUDES":                     localIncludeDirs,
+	"LOCAL_EXPORT_C_INCLUDE_DIRS":          exportIncludeDirs,
+	"LOCAL_JARJAR_RULES":                   localizePath("jarjar_rules"),
+	"LOCAL_LDFLAGS":                        ldflags,
+	"LOCAL_MODULE_CLASS":                   prebuiltClass,
+	"LOCAL_MODULE_STEM":                    stem,
+	"LOCAL_MODULE_HOST_OS":                 hostOs,
+	"LOCAL_RESOURCE_DIR":                   localizePathList("resource_dirs"),
+	"LOCAL_SANITIZE":                       sanitize(""),
+	"LOCAL_SANITIZE_DIAG":                  sanitize("diag."),
+	"LOCAL_STRIP_MODULE":                   strip(),
+	"LOCAL_CFLAGS":                         cflags,
+	"LOCAL_UNINSTALLABLE_MODULE":           invert("installable"),
+	"LOCAL_PROGUARD_ENABLED":               proguardEnabled,
+	"LOCAL_MODULE_PATH":                    prebuiltModulePath,
+	"LOCAL_REPLACE_PREBUILT_APK_INSTALLED": prebuiltPreprocessed,
 
 	// composite functions
 	"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
@@ -111,6 +116,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 +389,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
 }
@@ -490,10 +500,6 @@
 		Value: false,
 	}
 
-	trueValue := &bpparser.Bool{
-		Value: true,
-	}
-
 	if inList("windows") {
 		err = setVariable(ctx.file, ctx.append, "target.windows", "enabled", trueValue, true)
 	}
@@ -699,6 +705,11 @@
 	return nil
 }
 
+func prebuiltPreprocessed(ctx variableAssignmentContext) error {
+	ctx.mkvalue = ctx.mkvalue.Clone()
+	return setVariable(ctx.file, false, ctx.prefix, "preprocessed", trueValue, true)
+}
+
 func cflags(ctx variableAssignmentContext) error {
 	// The Soong replacement for CFLAGS doesn't need the same extra escaped quotes that were present in Make
 	ctx.mkvalue = ctx.mkvalue.Clone()
@@ -825,8 +836,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 +932,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..54bd586 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1341,6 +1341,73 @@
 }
 `,
 	},
+	{
+		desc: "android_test_import prebuilt",
+		in: `
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := foo
+		LOCAL_SRC_FILES := foo.apk
+		LOCAL_MODULE_CLASS := APPS
+		LOCAL_MODULE_TAGS := tests
+		LOCAL_MODULE_SUFFIX := .apk
+		LOCAL_CERTIFICATE := PRESIGNED
+		LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/foo.apk
+		LOCAL_COMPATIBILITY_SUITE := cts
+		include $(BUILD_PREBUILT)
+		`,
+		expected: `
+android_test_import {
+	name: "foo",
+	srcs: ["foo.apk"],
+
+	certificate: "PRESIGNED",
+	preprocessed: true,
+	test_suites: ["cts"],
+}
+`,
+	},
+	{
+		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 d1c2a64..1b3a4ba 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -78,8 +78,10 @@
 		}
 	}
 
+	seenDataOutPaths := make(map[string]bool)
+
 	for _, fi := range a.filesInfo {
-		if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
+		if ccMod, ok := fi.module.(*cc.Module); ok && ccMod.Properties.HideFromMake {
 			continue
 		}
 
@@ -112,17 +114,28 @@
 		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
 		if apexType == flattenedApex {
 			// /system/apex/<name>/{lib|framework|...}
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join(a.installDir.ToMakePath().String(),
-				apexBundleName, fi.installDir))
+			modulePath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
 			if a.primaryApexType && !symbolFilesNotNeeded {
 				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
 			}
 			if len(fi.symlinks) > 0 {
 				fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
 			}
+			newDataPaths := []android.Path{}
+			for _, path := range fi.dataPaths {
+				dataOutPath := modulePath + ":" + path.Rel()
+				if ok := seenDataOutPaths[dataOutPath]; !ok {
+					newDataPaths = append(newDataPaths, path)
+					seenDataOutPaths[dataOutPath] = true
+				}
+			}
+			if len(newDataPaths) > 0 {
+				fmt.Fprintln(w, "LOCAL_TEST_DATA :=", strings.Join(cc.AndroidMkDataPaths(newDataPaths), " "))
+			}
 
-			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)
@@ -165,7 +178,8 @@
 		if fi.jacocoReportClassesFile != nil {
 			fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
 		}
-		if fi.class == javaSharedLib {
+		switch fi.class {
+		case javaSharedLib:
 			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
@@ -176,7 +190,7 @@
 			fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", fi.builtFile.String())
 			fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
-		} else if fi.class == app {
+		case app:
 			fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", fi.certificate.AndroidMkString())
 			// soong_app_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .apk  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
@@ -186,19 +200,26 @@
 				fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(app.JniCoverageOutputs().Strings(), " "))
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_app_prebuilt.mk")
-		} else if fi.class == nativeSharedLib || fi.class == nativeExecutable || fi.class == nativeTest {
+		case appSet:
+			as, ok := fi.module.(*java.AndroidAppSet)
+			if !ok {
+				panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
+			}
+			fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE :=", as.MasterFile())
+			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
+		case nativeSharedLib, nativeExecutable, nativeTest:
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
-			if cc, ok := fi.module.(*cc.Module); ok {
-				if cc.UnstrippedOutputFile() != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", cc.UnstrippedOutputFile().String())
+			if ccMod, ok := fi.module.(*cc.Module); ok {
+				if ccMod.UnstrippedOutputFile() != nil {
+					fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
 				}
-				cc.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
-				if cc.CoverageOutputFile().Valid() {
-					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", cc.CoverageOutputFile().String())
+				ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
+				if ccMod.CoverageOutputFile().Valid() {
+					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
 				}
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
-		} else {
+		default:
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
 			if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
 				if a.primaryApexType {
@@ -308,7 +329,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 b974bcd..42b7e40 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -18,7 +18,6 @@
 	"fmt"
 	"path"
 	"path/filepath"
-	"regexp"
 	"sort"
 	"strings"
 	"sync"
@@ -55,6 +54,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}
@@ -63,6 +63,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)
@@ -932,10 +933,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
 
@@ -945,19 +949,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 {
@@ -979,11 +983,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
@@ -991,9 +991,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
 
@@ -1060,7 +1057,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
 }
 
@@ -1092,6 +1089,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
@@ -1150,6 +1150,7 @@
 	javaSharedLib
 	nativeTest
 	app
+	appSet
 )
 
 func (class apexFileClass) NameInMake() string {
@@ -1164,7 +1165,7 @@
 		return "JAVA_LIBRARIES"
 	case nativeTest:
 		return "NATIVE_TESTS"
-	case app:
+	case app, appSet:
 		// b/142537672 Why isn't this APP? We want to have full control over
 		// the paths and file names of the apk file under the flattend APEX.
 		// If this is set to APP, then the paths and file names are modified
@@ -1188,6 +1189,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
 
@@ -1198,6 +1200,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 {
@@ -1241,7 +1245,7 @@
 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
 }
@@ -1321,7 +1325,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
@@ -1331,16 +1335,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) {
@@ -1373,41 +1383,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))
 		}
@@ -1416,48 +1424,28 @@
 		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))
 			}
-
-			if strings.HasPrefix(ctx.ModuleName(), "com.android.runtime") && target.Os.Class == android.Device {
-				for _, sanitizer := range ctx.Config().SanitizeDevice() {
-					if sanitizer == "hwaddress" {
-						addDependenciesForNativeModules(ctx,
-							[]string{"libclang_rt.hwasan-aarch64-android"},
-							nil, nil, target, a.getImageVariation(config))
-						break
-					}
-				}
-			}
 		}
-
 	}
 
 	// For prebuilt_etc, use the first variant (64 on 64/32bit device,
@@ -1510,6 +1498,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 {
@@ -1587,6 +1577,23 @@
 	return android.InList(sanitizerName, globalSanitizerNames)
 }
 
+func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) {
+	if ctx.Device() && sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") {
+		for _, target := range ctx.MultiTargets() {
+			if target.Arch.ArchType.Multilib == "lib64" {
+				ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
+					{Mutator: "image", Variation: a.getImageVariation(ctx.DeviceConfig())},
+					{Mutator: "link", Variation: "shared"},
+					{Mutator: "version", Variation: ""}, // "" is the non-stub variant
+				}...), sharedLibTag, "libclang_rt.hwasan-aarch64-android")
+				break
+			}
+		}
+	}
+}
+
+var _ cc.Coverage = (*apexBundle)(nil)
+
 func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
 	return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
 }
@@ -1603,6 +1610,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
@@ -1614,10 +1623,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
@@ -1637,13 +1646,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
 }
 
@@ -1675,14 +1686,14 @@
 }
 
 type javaDependency interface {
-	DexJar() android.Path
+	DexJarBuildPath() android.Path
 	JacocoReportClassesFile() android.Path
 	Stem() string
 }
 
 func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaDependency, module android.Module) apexFile {
 	dirInApex := "javalib"
-	fileToCopy := lib.DexJar()
+	fileToCopy := lib.DexJarBuildPath()
 	af := newApexFile(ctx, fileToCopy, module.Name(), dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = lib.JacocoReportClassesFile()
 	af.stem = lib.Stem() + ".jar"
@@ -1727,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
@@ -1776,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
-}
-
 func (a *apexBundle) Updatable() bool {
 	return proptools.Bool(a.properties.Updatable)
 }
@@ -1834,14 +1842,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
 	})
@@ -1857,6 +1858,44 @@
 	}
 }
 
+// Ensures that a lib providing stub isn't statically linked
+func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
+	// Practically, we only care about regular APEXes on the device.
+	if ctx.Host() || a.testApex || a.vndkApex {
+		return
+	}
+
+	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+		if ccm, ok := to.(*cc.Module); ok {
+			apexName := ctx.ModuleName()
+			fromName := ctx.OtherModuleName(from)
+			toName := ctx.OtherModuleName(to)
+
+			// If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
+			// do any of its dependencies.
+			if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+				// As soon as the dependency graph crosses the APEX boundary, don't go further.
+				return false
+			}
+
+			// The dynamic linker and crash_dump tool in the runtime APEX is the only exception to this rule.
+			// It can't make the static dependencies dynamic because it can't
+			// do the dynamic linking for itself.
+			if apexName == "com.android.runtime" && (fromName == "linker" || fromName == "crash_dump") {
+				return false
+			}
+
+			isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !android.DirectlyInApex(apexName, toName)
+			if isStubLibraryFromOtherApex && !externalDep {
+				ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
+					"It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
+			}
+
+		}
+		return true
+	})
+}
+
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
 	switch a.properties.ApexType {
@@ -1894,6 +1933,7 @@
 
 	a.checkApexAvailability(ctx)
 	a.checkUpdatable(ctx)
+	a.checkStaticLinkingToStubLibraries(ctx)
 
 	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
 
@@ -1935,9 +1975,11 @@
 		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 {
 					fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
+					fi.isJniLib = isJniLib
 					filesInfo = append(filesInfo, fi)
 					// bootstrap bionic libs are treated as provided by system
 					if c.HasStubsVariants() && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
@@ -1945,7 +1987,11 @@
 					}
 					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 {
@@ -1981,9 +2027,24 @@
 					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
 				} else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
 					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+				} else if ap, ok := child.(*java.AndroidAppSet); ok {
+					appDir := "app"
+					if ap.Privileged() {
+						appDir = "priv-app"
+					}
+					af := newApexFile(ctx, ap.OutputFile(), ap.Name(),
+						filepath.Join(appDir, ap.BaseModuleName()), appSet, ap)
+					af.certificate = java.PresignedCertificate
+					filesInfo = append(filesInfo, af)
 				} 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.(prebuilt_etc.PrebuiltEtcModule); ok {
 					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
@@ -2001,13 +2062,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)
 				}
@@ -2055,8 +2116,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, af.Stem())
 							// Don't track further
@@ -2086,7 +2147,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 a98f6c6..d6a5d09 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -91,21 +91,39 @@
 	}
 }
 
+// withNativeBridgeTargets sets configuration with targets including:
+// - X86_64 (primary)
+// - X86 (secondary)
+// - Arm64 on X86_64 (native bridge)
+// - Arm on X86 (native bridge)
+func withNativeBridgeEnabled(_ 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
 	}
 }
 
-func withBinder32bit(fs map[string][]byte, config android.Config) {
+func withBinder32bit(_ map[string][]byte, config android.Config) {
 	config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
 }
 
-func withUnbundledBuild(fs map[string][]byte, config android.Config) {
+func withUnbundledBuild(_ map[string][]byte, config android.Config) {
 	config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
 }
 
-func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
+func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
 	android.ClearApexDependency()
 
 	bp = bp + `
@@ -166,6 +184,10 @@
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
 		"dummy.txt":                                  nil,
+		"baz":                                        nil,
+		"bar/baz":                                    nil,
+		"testdata/baz":                               nil,
+		"AppSet.apks":                                nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -237,7 +259,15 @@
 }
 
 func tearDown() {
-	os.RemoveAll(buildDir)
+	_ = 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'
@@ -248,6 +278,15 @@
 	}
 }
 
+// ensure that 'result' contains 'expected' exactly one time
+func ensureContainsOnce(t *testing.T, result string, expected string) {
+	t.Helper()
+	count := strings.Count(result, expected)
+	if count != 1 {
+		t.Errorf("%q is found %d times (expected 1 time) in %q", expected, count, result)
+	}
+}
+
 // ensures that 'result' does not contain 'notExpected'
 func ensureNotContains(t *testing.T, result string, notExpected string) {
 	t.Helper()
@@ -256,6 +295,17 @@
 	}
 }
 
+func ensureMatches(t *testing.T, result string, expectedRex string) {
+	ok, err := regexp.MatchString(expectedRex, result)
+	if err != nil {
+		t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", result, expectedRex, err)
+		return
+	}
+	if !ok {
+		t.Errorf("%s does not match regular expession %s", result, expectedRex)
+	}
+}
+
 func ensureListContains(t *testing.T, result []string, expected string) {
 	t.Helper()
 	if !android.InList(expected, result) {
@@ -522,6 +572,7 @@
 			native_shared_libs: ["mylib"],
 			java_libs: ["myjar"],
 			apps: ["AppFoo"],
+			rros: ["rro"],
 		}
 
 		prebuilt_etc {
@@ -562,12 +613,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",
 	})
 }
 
@@ -891,6 +949,130 @@
 
 }
 
+func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
+	ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
+		bp := `
+		apex {
+			name: "com.android.runtime",
+			key: "com.android.runtime.key",
+			native_shared_libs: ["libc"],
+		}
+
+		apex_key {
+			name: "com.android.runtime.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libc",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			stubs: { versions: ["1"] },
+			apex_available: ["com.android.runtime"],
+
+			sanitize: {
+				hwaddress: true,
+			}
+		}
+
+		cc_prebuilt_library_shared {
+			name: "libclang_rt.hwasan-aarch64-android",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			srcs: [""],
+			stubs: { versions: ["1"] },
+
+			sanitize: {
+				never: true,
+			},
+		}
+		`
+		// override bp to use hard-coded names: com.android.runtime and libc
+		fs["Android.bp"] = []byte(bp)
+		fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil
+	})
+
+	ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{
+		"lib64/bionic/libc.so",
+		"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
+	})
+
+	hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+
+	installed := hwasan.Description("install libclang_rt.hwasan")
+	ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
+
+	symlink := hwasan.Description("install symlink libclang_rt.hwasan")
+	ensureEquals(t, symlink.Args["fromPath"], "/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so")
+	ensureContains(t, symlink.Output.String(), "/system/lib64/libclang_rt.hwasan-aarch64-android.so")
+}
+
+func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) {
+	ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
+		bp := `
+		apex {
+			name: "com.android.runtime",
+			key: "com.android.runtime.key",
+			native_shared_libs: ["libc"],
+		}
+
+		apex_key {
+			name: "com.android.runtime.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libc",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			stubs: { versions: ["1"] },
+			apex_available: ["com.android.runtime"],
+		}
+
+		cc_prebuilt_library_shared {
+			name: "libclang_rt.hwasan-aarch64-android",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			srcs: [""],
+			stubs: { versions: ["1"] },
+
+			sanitize: {
+				never: true,
+			},
+		}
+		`
+		// override bp to use hard-coded names: com.android.runtime and libc
+		fs["Android.bp"] = []byte(bp)
+		fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil
+
+		config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
+	})
+
+	ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{
+		"lib64/bionic/libc.so",
+		"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
+	})
+
+	hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+
+	installed := hwasan.Description("install libclang_rt.hwasan")
+	ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
+
+	symlink := hwasan.Description("install symlink libclang_rt.hwasan")
+	ensureEquals(t, symlink.Args["fromPath"], "/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so")
+	ensureContains(t, symlink.Output.String(), "/system/lib64/libclang_rt.hwasan-aarch64-android.so")
+}
+
 func TestApexDependsOnLLNDKTransitively(t *testing.T) {
 	testcases := []struct {
 		name          string
@@ -1586,6 +1768,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 {
@@ -2459,15 +2699,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",
@@ -2562,7 +2794,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: ""},
 			},
 		}),
 	)
@@ -3202,14 +3435,38 @@
 			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",
+		}
+
+		filegroup {
+			name: "fg2",
+			srcs: [
+				"testdata/baz"
+			],
 		}
 
 		cc_test {
@@ -3225,14 +3482,23 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			data: [
+				":fg",
+				":fg2",
+			],
 		}
 	`)
 
 	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")
@@ -3240,9 +3506,9 @@
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest3")
 
 	// Ensure the module is correctly translated.
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
-	name := apexBundle.BaseModuleName()
+	bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", bundle)
+	name := bundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
@@ -3254,6 +3520,13 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
+
+	flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
+	data = android.AndroidMkDataForTest(t, config, "", flatBundle)
+	data.Custom(&builder, name, prefix, "", data)
+	flatAndroidMk := builder.String()
+	ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :baz :bar/baz\n")
+	ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :testdata/baz\n")
 }
 
 func TestInstallExtraFlattenedApexes(t *testing.T) {
@@ -3552,6 +3825,7 @@
 			dex_preopt: {
 				enabled: false,
 			},
+			apex_available: ["myapex"],
 		}
 
 		android_app_import {
@@ -3563,6 +3837,7 @@
 				enabled: false,
 			},
 			filename: "AwesomePrebuiltAppFooPriv.apk",
+			apex_available: ["myapex"],
 		}
 	`)
 
@@ -3604,6 +3879,7 @@
 			filename: "AppFooPrebuilt.apk",
 			presigned: true,
 			prefer: true,
+			apex_available: ["myapex"],
 		}
 	`, withFiles(map[string][]byte{
 		"AppFooPrebuilt.apk": nil,
@@ -4393,6 +4669,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 {
@@ -4414,6 +4731,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 {
@@ -4444,10 +4784,110 @@
 	ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo/AppFoo.apk"}]}`)
 }
 
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func TestAppSetBundle(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: ["AppSet"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_app_set {
+			name: "AppSet",
+			set: "AppSet.apks",
+		}`)
+	mod := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	bundleConfigRule := mod.Description("Bundle Config")
+	content := bundleConfigRule.Args["content"]
+	ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
+	s := mod.Rule("apexRule").Args["copy_commands"]
+	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
+	if len(copyCmds) != 3 {
+		t.Fatalf("Expected 3 commands, got %d in:\n%s", len(copyCmds), s)
+	}
+	ensureMatches(t, copyCmds[0], "^rm -rf .*/app/AppSet$")
+	ensureMatches(t, copyCmds[1], "^mkdir -p .*/app/AppSet$")
+	ensureMatches(t, copyCmds[2], "^unzip .*-d .*/app/AppSet .*/AppSet.zip$")
+}
+
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
 	t.Helper()
 
-	bp = bp + `
+	bp := `
+		java_library {
+			name: "some-updatable-apex-lib",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			apex_available: [
+				"some-updatable-apex",
+			],
+		}
+
+		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",
+			installable: true,
+		}
+
+		java_library {
+			name: "some-art-lib",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			apex_available: [
+				"com.android.art.something",
+			],
+			hostdex: true,
+		}
+
+		apex {
+			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 {
+			name: "com.android.art.something.key",
+		}
+
 		filegroup {
 			name: "some-updatable-apex-file_contexts",
 			srcs: [
@@ -4534,145 +4974,86 @@
 }
 
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
-	bp := `
-		java_library {
-			name: "some-updatable-apex-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			apex_available: [
-				"some-updatable-apex",
-			],
-		}
 
-		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",
-			installable: true,
-		}
-
-		java_library {
-			name: "some-art-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			apex_available: [
-				"com.android.art.something",
-			],
-			hostdex: true,
-		}
-
-		apex {
-			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 {
-			name: "com.android.art.something.key",
-		}
-	`
-
-	var error string
+	var err string
 	var transform func(*dexpreopt.GlobalConfig)
 
-	// updatable jar from ART apex in the ART boot image => ok
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"some-art-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"com.android.art.something:some-art-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, "", 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"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
+		err = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"com.android.art.something:some-art-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, err, 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"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
+		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, err, 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-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
+		err = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, err, 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"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
+		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, err, 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-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, "", 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"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
+		err = "failed to find a dex jar path for module 'nonexistent'"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"platform:nonexistent"}
+		}
+		testNoUpdatableJarsInBootImage(t, err, 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"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
+		err = "failed to find a dex jar path for module 'nonexistent'"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"platform:nonexistent"}
+		}
+		testNoUpdatableJarsInBootImage(t, err, transform)
+	})
 
-	// platform jar in the ART boot image => error
-	error = "module 'some-platform-lib' is not allowed in the ART boot image"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"some-platform-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
+		err = "module 'some-platform-lib' is not allowed in the ART boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"platform:some-platform-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, err, transform)
+	})
 
-	// platform jar in the framework boot image => ok
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"some-platform-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"platform:some-platform-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, "", transform)
+	})
 }
 
 func TestTestFor(t *testing.T) {
@@ -4769,6 +5150,42 @@
 	}
 }
 
+func TestNoStaticLinkingToStubsLib(t *testing.T) {
+	testApexError(t, `.*required by "mylib" is a native library providing stub.*`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			static_libs: ["otherlib"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "otherlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1", "2", "3"],
+			},
+			apex_available: [ "myapex" ],
+		}
+	`)
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/builder.go b/apex/builder.go
index fce2503..3078cb9 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -69,11 +69,14 @@
 	// by default set to (uid/gid/mode) = (1000/1000/0644)
 	// TODO(b/113082813) make this configurable using config.fs syntax
 	generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
-		Command: `echo '/ 1000 1000 0755' > ${out} && ` +
-			`echo ${ro_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 1000 1000 0644"}' >> ${out} && ` +
-			`echo ${exec_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 0 2000 0755"}' >> ${out}`,
-		Description: "fs_config ${out}",
-	}, "ro_paths", "exec_paths")
+		Command: `( echo '/ 1000 1000 0755' ` +
+			`&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` +
+			`&& for i in  ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` +
+			`&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`,
+		Description:    "fs_config ${out}",
+		Rspfile:        "$out.apklist",
+		RspfileContent: "$in",
+	}, "ro_paths", "exec_paths", "apk_paths")
 
 	apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
 		Command: `rm -f $out && ${jsonmodify} $in ` +
@@ -186,6 +189,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.Stem())
+		}
+	}
+	if len(jniLibs) > 0 {
+		optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
+	}
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
 		Input:  manifestSrc,
@@ -226,10 +240,8 @@
 			return false
 		}
 
-		notice := to.NoticeFile()
-		if notice.Valid() {
-			noticeFiles = append(noticeFiles, notice.Path())
-		}
+		notices := to.NoticeFiles()
+		noticeFiles = append(noticeFiles, notices...)
 
 		return true
 	})
@@ -278,7 +290,7 @@
 	// collect the manifest names and paths of android apps
 	// if their manifest names are overridden
 	for _, fi := range a.filesInfo {
-		if fi.class != app {
+		if fi.class != app && fi.class != appSet {
 			continue
 		}
 		packageName := fi.overriddenPackageName
@@ -328,13 +340,22 @@
 	var copyCommands []string
 	for _, fi := range a.filesInfo {
 		destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.Path()).String()
-		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(destPath))
+		destPathDir := filepath.Dir(destPath)
+		if fi.class == appSet {
+			copyCommands = append(copyCommands, "rm -rf "+destPathDir)
+		}
+		copyCommands = append(copyCommands, "mkdir -p "+destPathDir)
 		if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
 			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
 			pathOnDevice := filepath.Join("/system", fi.Path())
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
-			copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+			if fi.class == appSet {
+				copyCommands = append(copyCommands,
+					fmt.Sprintf("unzip -q -d %s %s", destPathDir, fi.builtFile.String()))
+			} else {
+				copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+			}
 			implicitInputs = append(implicitInputs, fi.builtFile)
 		}
 		// create additional symlinks pointing the file inside the APEX
@@ -342,6 +363,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
@@ -394,13 +428,21 @@
 		// files and dirs that will be created in APEX
 		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 		var executablePaths []string // this also includes dirs
+		var extractedAppSetPaths android.Paths
+		var extractedAppSetDirs []string
 		for _, f := range a.filesInfo {
 			pathInApex := f.Path()
 			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))
 				}
+			} else if f.class == appSet {
+				extractedAppSetPaths = append(extractedAppSetPaths, f.builtFile)
+				extractedAppSetDirs = append(extractedAppSetDirs, f.installDir)
 			} else {
 				readOnlyPaths = append(readOnlyPaths, pathInApex)
 			}
@@ -421,9 +463,11 @@
 			Rule:        generateFsConfig,
 			Output:      cannedFsConfig,
 			Description: "generate fs config",
+			Inputs:      extractedAppSetPaths,
 			Args: map[string]string{
 				"ro_paths":   strings.Join(readOnlyPaths, " "),
 				"exec_paths": strings.Join(executablePaths, " "),
+				"apk_paths":  strings.Join(extractedAppSetDirs, " "),
 			},
 		})
 
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/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index a1c5de1..e731750 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -408,6 +408,8 @@
 			switch mod.Type {
 			case "android_app":
 				mod.Type = "android_test"
+			case "android_app_import":
+				mod.Type = "android_test_import"
 			case "java_library", "java_library_installable":
 				mod.Type = "java_test"
 			case "java_library_host":
@@ -951,7 +953,8 @@
 			case strings.Contains(mod.Type, "cc_test"),
 				strings.Contains(mod.Type, "cc_library_static"),
 				strings.Contains(mod.Type, "java_test"),
-				mod.Type == "android_test":
+				mod.Type == "android_test",
+				mod.Type == "android_test_import":
 				continue
 			case strings.Contains(mod.Type, "cc_lib"):
 				replaceStr += `// WARNING: Module tags are not supported in Soong.
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 fede601..52480ea 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/binary_sdk_member.go b/cc/binary_sdk_member.go
index 88ac513..372a72e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -20,6 +20,7 @@
 	"android/soong/android"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -65,7 +66,15 @@
 }
 
 func (mt *binarySdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
-	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "cc_prebuilt_binary")
+	pbm := ctx.SnapshotBuilder().AddPrebuiltModule(member, "cc_prebuilt_binary")
+
+	ccModule := member.Variants()[0].(*Module)
+
+	if stl := ccModule.stl.Properties.Stl; stl != nil {
+		pbm.AddProperty("stl", proptools.String(stl))
+	}
+
+	return pbm
 }
 
 func (mt *binarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
@@ -105,6 +114,10 @@
 	//
 	// This field is exported as its contents may not be arch specific.
 	SystemSharedLibs []string
+
+	// Arch specific flags.
+	StaticExecutable bool
+	Nocrt            bool
 }
 
 func (p *nativeBinaryInfoProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -113,6 +126,10 @@
 	p.archType = ccModule.Target().Arch.ArchType.String()
 	p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
 
+	binaryLinker := ccModule.linker.(*binaryDecorator)
+	p.StaticExecutable = binaryLinker.static()
+	p.Nocrt = Bool(binaryLinker.baseLinker.Properties.Nocrt)
+
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
 		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
@@ -143,4 +160,11 @@
 	if p.SystemSharedLibs != nil {
 		propertySet.AddPropertyWithTag("system_shared_libs", p.SystemSharedLibs, builder.SdkMemberReferencePropertyTag(false))
 	}
+
+	if p.StaticExecutable {
+		propertySet.AddProperty("static_executable", p.StaticExecutable)
+	}
+	if p.Nocrt {
+		propertySet.AddProperty("nocrt", p.Nocrt)
+	}
 }
diff --git a/cc/builder.go b/cc/builder.go
index 37fbf4e..e571e5a 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},
@@ -90,9 +83,8 @@
 			Command:     "$reTemplate$ldCmd -fuse-ld=lld -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
 			CommandDeps: []string{"$ldCmd"},
 		}, &remoteexec.REParams{
-			Labels:          map[string]string{"type": "link", "tool": "clang"},
-			ExecStrategy:    "${config.RECXXLinksExecStrategy}",
-			Inputs:          []string{"$inCommaList"},
+			Labels:       map[string]string{"type": "link", "tool": "clang"},
+			ExecStrategy: "${config.RECXXLinksExecStrategy}", Inputs: []string{"$inCommaList"},
 			OutputFiles:     []string{"${out}", "$implicitOutputs"},
 			ToolchainInputs: []string{"$ldCmd"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
@@ -107,6 +99,15 @@
 		},
 		"arCmd", "arFlags")
 
+	arWithLibs = pctx.AndroidStaticRule("arWithLibs",
+		blueprint.RuleParams{
+			Command:        "rm -f ${out} && $arCmd $arObjFlags $out @${out}.rsp && $arCmd $arLibFlags $out $arLibs",
+			CommandDeps:    []string{"$arCmd"},
+			Rspfile:        "${out}.rsp",
+			RspfileContent: "${arObjs}",
+		},
+		"arCmd", "arObjFlags", "arObjs", "arLibFlags", "arLibs")
+
 	darwinStrip = pctx.AndroidStaticRule("darwinStrip",
 		blueprint.RuleParams{
 			Command:     "${config.MacStripPath} -u -r -o $out $in",
@@ -215,22 +216,29 @@
 		}, []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)"
@@ -239,7 +247,7 @@
 				CommandDeps: []string{"$sAbiDiffer"},
 			}
 		},
-		"allowFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
+		"extraFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
 
 	unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
 		blueprint.RuleParams{
@@ -264,7 +272,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"},
 		},
@@ -606,26 +618,45 @@
 }
 
 // Generate a rule for compiling multiple .o files to a static library (.a)
-func TransformObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
+func TransformObjToStaticLib(ctx android.ModuleContext,
+	objFiles android.Paths, wholeStaticLibs android.Paths,
 	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
 
 	arCmd := "${config.ClangBin}/llvm-ar"
-	arFlags := "crsPD"
+	arFlags := ""
 	if !ctx.Darwin() {
 		arFlags += " -format=gnu"
 	}
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        ar,
-		Description: "static link " + outputFile.Base(),
-		Output:      outputFile,
-		Inputs:      objFiles,
-		Implicits:   deps,
-		Args: map[string]string{
-			"arFlags": arFlags,
-			"arCmd":   arCmd,
-		},
-	})
+	if len(wholeStaticLibs) == 0 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        ar,
+			Description: "static link " + outputFile.Base(),
+			Output:      outputFile,
+			Inputs:      objFiles,
+			Implicits:   deps,
+			Args: map[string]string{
+				"arFlags": "crsPD" + arFlags,
+				"arCmd":   arCmd,
+			},
+		})
+
+	} else {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        arWithLibs,
+			Description: "static link " + outputFile.Base(),
+			Output:      outputFile,
+			Inputs:      append(objFiles, wholeStaticLibs...),
+			Implicits:   deps,
+			Args: map[string]string{
+				"arCmd":      arCmd,
+				"arObjFlags": "crsPD" + arFlags,
+				"arObjs":     strings.Join(objFiles.Strings(), " "),
+				"arLibFlags": "cqsL" + arFlags,
+				"arLibs":     strings.Join(wholeStaticLibs.Strings(), " "),
+			},
+		})
+	}
 }
 
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
@@ -729,17 +760,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)
 }
@@ -756,27 +800,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{
@@ -789,7 +842,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 3d3a841..8eabff5 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -124,10 +124,16 @@
 	StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
 
 	// Paths to .o files
-	Objs               Objects
+	Objs Objects
+	// Paths to .o files in dependencies that provide them. Note that these lists
+	// aren't complete since prebuilt modules don't provide the .o files.
 	StaticLibObjs      Objects
 	WholeStaticLibObjs Objects
 
+	// Paths to .a files in prebuilts. Complements WholeStaticLibObjs to contain
+	// the libs from all whole_static_lib dependencies.
+	WholeStaticLibsFromPrebuilts android.Paths
+
 	// Paths to generated source files
 	GeneratedSources android.Paths
 	GeneratedHeaders android.Paths
@@ -223,6 +229,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"`
@@ -438,7 +445,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"}
 )
 
@@ -746,6 +752,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 {
@@ -1055,7 +1070,7 @@
 
 func InstallToBootstrap(name string, config android.Config) bool {
 	if name == "libclang_rt.hwasan-aarch64-android" {
-		return inList("hwaddress", config.SanitizeDevice())
+		return true
 	}
 	return isBionic(name)
 }
@@ -1206,6 +1221,11 @@
 		return false
 	}
 
+	// Coverage builds have extra symbols.
+	if ctx.mod.isCoverageVariant() {
+		return false
+	}
+
 	if ctx.ctx.Fuchsia() {
 		return false
 	}
@@ -1385,6 +1405,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.
@@ -2465,7 +2494,11 @@
 					}
 					ctx.AddMissingDependencies(missingDeps)
 				}
-				depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
+				if _, ok := ccWholeStaticLib.linker.(prebuiltLinkerInterface); ok {
+					depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
+				} else {
+					depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
+				}
 			} else {
 				ctx.ModuleErrorf(
 					"non-cc.Modules cannot be included as whole static libraries.", depName)
@@ -2493,13 +2526,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()...)
 			}
 		}
 
@@ -2611,6 +2647,9 @@
 		case wholeStaticDepTag:
 			c.Properties.AndroidMkWholeStaticLibs = append(
 				c.Properties.AndroidMkWholeStaticLibs, makeLibName(depName))
+		case headerDepTag:
+			c.Properties.AndroidMkHeaderLibs = append(
+				c.Properties.AndroidMkHeaderLibs, makeLibName(depName))
 		}
 	})
 
@@ -2866,6 +2905,9 @@
 				return false
 			}
 		}
+	} else if ctx.OtherModuleDependencyTag(dep) == llndkImplDep {
+		// We don't track beyond LLNDK
+		return false
 	}
 	return true
 }
diff --git a/cc/compiler.go b/cc/compiler.go
index 681b1ab..e7495da 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -300,8 +300,7 @@
 	if !(ctx.useSdk() || ctx.useVndk()) || ctx.Host() {
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 			"${config.CommonGlobalIncludes}",
-			tc.IncludeFlags(),
-			"${config.CommonNativehelperInclude}")
+			tc.IncludeFlags())
 	}
 
 	if ctx.useSdk() {
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 7edb0c9..6275064 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -15,8 +15,6 @@
         "arm_device.go",
         "arm64_device.go",
         "arm64_fuchsia_device.go",
-        "mips_device.go",
-        "mips64_device.go",
         "x86_device.go",
         "x86_64_device.go",
         "x86_64_fuchsia_device.go",
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 aaffcbc..1dd8a2d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -261,20 +261,11 @@
 	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 3deb173..ba8b0f4 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.
@@ -895,7 +899,7 @@
 		}
 	}
 
-	TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
+	TransformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, objs.tidyFiles)
 
 	library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
@@ -1097,7 +1101,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 b7c0bf2..0751f1c 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..aaaf694 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
 	}
@@ -989,6 +989,7 @@
 	android.Module
 	IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool
 	EnableSanitizer(sanitizerName string)
+	AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
 }
 
 // Create sanitized variants for modules that need them
@@ -1075,6 +1076,7 @@
 			c.sanitize.Properties.SanitizeDep = false
 		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
 			// APEX modules fall here
+			sanitizeable.AddSanitizerDependencies(mctx, t.name())
 			mctx.CreateVariations(t.variationName())
 		}
 	}
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 d92309f..edbb24d 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 2e2a779..ea94544 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -775,14 +775,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 ef33c1a..04b865f 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -310,7 +310,13 @@
 		panic(err)
 	}
 
-	if m.HasStubsVariants() {
+	if m.HasStubsVariants() && name != "libz" {
+		// b/155456180 libz is the ONLY exception here. We don't want to make
+		// libz an LLNDK library because we in general can't guarantee that
+		// libz will behave consistently especially about the compression.
+		// i.e. the compressed output might be different across releases.
+		// As the library is an external one, it's risky to keep the compatibility
+		// promise if it becomes an LLNDK.
 		mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
 	}
 
@@ -661,13 +667,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/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_inject/host_bionic_inject.go
index f7163d7..629f6cc 100644
--- a/cmd/host_bionic_inject/host_bionic_inject.go
+++ b/cmd/host_bionic_inject/host_bionic_inject.go
@@ -105,7 +105,9 @@
 
 	err = checkLinker(file, linker, symbols)
 	if err != nil {
-		return 0, err
+		return 0, fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
+			"linker might not be in sync with crtbegin_dynamic.o.",
+			err)
 	}
 
 	start, err := findSymbol(symbols, "_start")
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..905f206 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -18,12 +18,7 @@
 	"flag"
 	"fmt"
 	"os"
-	"os/exec"
 	"path/filepath"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
 
 	"github.com/google/blueprint/bootstrap"
 
@@ -55,42 +50,7 @@
 }
 
 func main() {
-	if android.SoongDelveListen != "" {
-		if android.SoongDelvePath == "" {
-			fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
-			os.Exit(1)
-		}
-		pid := strconv.Itoa(os.Getpid())
-		cmd := []string{android.SoongDelvePath,
-			"attach", pid,
-			"--headless",
-			"-l", android.SoongDelveListen,
-			"--api-version=2",
-			"--accept-multiclient",
-			"--log",
-		}
-
-		fmt.Println("Starting", strings.Join(cmd, " "))
-		dlv := exec.Command(cmd[0], cmd[1:]...)
-		dlv.Stdout = os.Stdout
-		dlv.Stderr = os.Stderr
-		dlv.Stdin = nil
-
-		// Put dlv into its own process group so we can kill it and the child process it starts.
-		dlv.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
-
-		err := dlv.Start()
-		if err != nil {
-			// Print the error starting dlv and continue.
-			fmt.Println(err)
-		} else {
-			// Kill the process group for dlv when soong_build exits.
-			defer syscall.Kill(-dlv.Process.Pid, syscall.SIGKILL)
-			// Wait to give dlv a chance to connect and pause the process.
-			time.Sleep(time.Second)
-		}
-	}
-
+	android.ReexecWithDelveMaybe()
 	flag.Parse()
 
 	// The top-level Blueprints file is passed as the first argument.
@@ -131,4 +91,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..d0cad78 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -117,6 +117,8 @@
 // Command is the type of soong_ui execution. Only one type of
 // execution is specified. The args are specific to the command.
 func main() {
+	buildStartedMilli := time.Now().UnixNano() / int64(time.Millisecond)
+
 	c, args := getCommand(os.Args)
 	if c == nil {
 		fmt.Fprintf(os.Stderr, "The `soong` native UI is not yet available.\n")
@@ -166,19 +168,25 @@
 		logsDir = filepath.Join(config.DistDir(), "logs")
 	}
 
+	buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
+	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
+	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
+	defer build.UploadMetrics(buildCtx, config, buildStartedMilli, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+
 	os.MkdirAll(logsDir, 0777)
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
 	trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
 	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
 	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.NewProtoErrorLog(log, buildErrorFile))
 	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",
 		config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
 
-	defer met.Dump(filepath.Join(logsDir, c.logsPrefix+"soong_metrics"))
+	defer met.Dump(soongMetricsFile)
 
 	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
 		if !strings.HasSuffix(start, "N") {
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/config.go b/dexpreopt/config.go
index 98850e5..3440f8e 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -50,6 +50,8 @@
 	UpdatableSystemServerJars []string // jars within apex that are loaded into system server
 	SpeedApps                 []string // apps that should be speed optimized
 
+	BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
+
 	PreoptFlags []string // global dex2oat flags that should be used if no module-specific dex2oat flags are specified
 
 	DefaultCompilerFilter      string // default compiler filter to pass to dex2oat, overridden by --compiler-filter= in module-specific dex2oat flags
@@ -98,6 +100,15 @@
 	ConstructContext android.Path
 }
 
+// LibraryPath contains paths to the library DEX jar on host and on device.
+type LibraryPath struct {
+	Host   android.Path
+	Device string
+}
+
+// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
+type LibraryPaths map[string]*LibraryPath
+
 type ModuleConfig struct {
 	Name            string
 	DexLocation     string // dex location on device
@@ -115,7 +126,7 @@
 	EnforceUsesLibraries         bool
 	PresentOptionalUsesLibraries []string
 	UsesLibraries                []string
-	LibraryPaths                 map[string]android.Path
+	LibraryPaths                 LibraryPaths
 
 	Archs                   []android.ArchType
 	DexPreoptImages         []android.Path
@@ -163,14 +174,6 @@
 	return ret
 }
 
-func constructPathMap(ctx android.PathContext, paths map[string]string) map[string]android.Path {
-	ret := map[string]android.Path{}
-	for key, path := range paths {
-		ret[key] = constructPath(ctx, path)
-	}
-	return ret
-}
-
 func constructWritablePath(ctx android.PathContext, path string) android.WritablePath {
 	if path == "" {
 		return nil
@@ -262,6 +265,13 @@
 // from Make to read the module dexpreopt.config written in the Make config
 // stage.
 func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) {
+	type jsonLibraryPath struct {
+		Host   string
+		Device string
+	}
+
+	type jsonLibraryPaths map[string]jsonLibraryPath
+
 	type ModuleJSONConfig struct {
 		*ModuleConfig
 
@@ -271,12 +281,24 @@
 		DexPath                     string
 		ManifestPath                string
 		ProfileClassListing         string
-		LibraryPaths                map[string]string
+		LibraryPaths                jsonLibraryPaths
 		DexPreoptImages             []string
 		DexPreoptImageLocations     []string
 		PreoptBootClassPathDexFiles []string
 	}
 
+	// convert JSON map of library paths to LibraryPaths
+	constructLibraryPaths := func(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
+		m := LibraryPaths{}
+		for lib, path := range paths {
+			m[lib] = &LibraryPath{
+				constructPath(ctx, path.Host),
+				path.Device,
+			}
+		}
+		return m
+	}
+
 	config := ModuleJSONConfig{}
 
 	err := json.Unmarshal(data, &config)
@@ -289,7 +311,7 @@
 	config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
 	config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
 	config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
-	config.ModuleConfig.LibraryPaths = constructPathMap(ctx, config.LibraryPaths)
+	config.ModuleConfig.LibraryPaths = constructLibraryPaths(ctx, config.LibraryPaths)
 	config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
 	config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
 	config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index f984966..57a9250 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -37,6 +37,7 @@
 	"fmt"
 	"path/filepath"
 	"runtime"
+	"sort"
 	"strings"
 
 	"android/soong/android"
@@ -82,7 +83,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(ctx, global.BootJars), module.Name) {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
@@ -104,7 +105,7 @@
 
 	// Don't preopt system server jars that are updatable.
 	for _, p := range global.UpdatableSystemServerJars {
-		if _, jar := android.SplitApexJarPair(p); jar == module.Name {
+		if _, jar := android.SplitApexJarPair(ctx, p); jar == module.Name {
 			return true
 		}
 	}
@@ -113,7 +114,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(ctx, global.BootJars), module.Name) &&
 		!contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
@@ -192,6 +193,56 @@
 	return profilePath
 }
 
+type classLoaderContext struct {
+	// The class loader context using paths in the build.
+	Host android.Paths
+
+	// The class loader context using paths as they will be on the device.
+	Target []string
+}
+
+// A map of class loader contexts for each SDK version.
+// A map entry for "any" version contains libraries that are unconditionally added to class loader
+// context. Map entries for existing versions contains libraries that were in the default classpath
+// until that API version, and should be added to class loader context if and only if the
+// targetSdkVersion in the manifest or APK is less than that API version.
+type classLoaderContextMap map[int]*classLoaderContext
+
+const anySdkVersion int = -1
+
+func (m classLoaderContextMap) getSortedKeys() []int {
+	keys := make([]int, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+	sort.Ints(keys)
+	return keys
+}
+
+func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
+	if _, ok := m[sdkVer]; !ok {
+		m[sdkVer] = &classLoaderContext{}
+	}
+	return m[sdkVer]
+}
+
+func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) {
+	clc := m.getValue(sdkVer)
+	for _, lib := range libs {
+		p := pathForLibrary(module, lib)
+		clc.Host = append(clc.Host, p.Host)
+		clc.Target = append(clc.Target, p.Device)
+	}
+}
+
+func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
+	clc := m.getValue(sdkVer)
+	for _, lib := range libs {
+		clc.Host = append(clc.Host, SystemServerDexJarHostPath(ctx, lib))
+		clc.Target = append(clc.Target, filepath.Join("/system/framework", lib+".jar"))
+	}
+}
+
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
 	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
 	appImage bool, generateDM bool) {
@@ -227,77 +278,45 @@
 
 	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
 
-	// The class loader context using paths in the build
-	var classLoaderContextHost android.Paths
-
-	// The class loader context using paths as they will be on the device
-	var classLoaderContextTarget []string
-
-	// Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 28
-	var conditionalClassLoaderContextHost28 android.Paths
-	var conditionalClassLoaderContextTarget28 []string
-
-	// Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 29
-	var conditionalClassLoaderContextHost29 android.Paths
-	var conditionalClassLoaderContextTarget29 []string
+	classLoaderContexts := make(classLoaderContextMap)
 
 	// A flag indicating if the '&' class loader context is used.
 	unknownClassLoaderContext := false
 
 	if module.EnforceUsesLibraries {
+		// Unconditional class loader context.
 		usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...)
+		classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...)
 
-		// Create class loader context for dex2oat from uses libraries and filtered optional libraries
-		for _, l := range usesLibs {
-
-			classLoaderContextHost = append(classLoaderContextHost,
-				pathForLibrary(module, l))
-			classLoaderContextTarget = append(classLoaderContextTarget,
-				filepath.Join("/system/framework", l+".jar"))
-		}
-
+		// Conditional class loader context for API version < 28.
 		const httpLegacy = "org.apache.http.legacy"
-		const httpLegacyImpl = "org.apache.http.legacy.impl"
-
-		// org.apache.http.legacy contains classes that were in the default classpath until API 28.  If the
-		// targetSdkVersion in the manifest or APK is < 28, and the module does not explicitly depend on
-		// org.apache.http.legacy, then implicitly add the classes to the classpath for dexpreopt.  One the
-		// device the classes will be in a file called org.apache.http.legacy.impl.jar.
-		module.LibraryPaths[httpLegacyImpl] = module.LibraryPaths[httpLegacy]
-
-		if !contains(module.UsesLibraries, httpLegacy) && !contains(module.PresentOptionalUsesLibraries, httpLegacy) {
-			conditionalClassLoaderContextHost28 = append(conditionalClassLoaderContextHost28,
-				pathForLibrary(module, httpLegacyImpl))
-			conditionalClassLoaderContextTarget28 = append(conditionalClassLoaderContextTarget28,
-				filepath.Join("/system/framework", httpLegacyImpl+".jar"))
+		if !contains(usesLibs, httpLegacy) {
+			classLoaderContexts.addLibs(28, module, httpLegacy)
 		}
 
-		const hidlBase = "android.hidl.base-V1.0-java"
-		const hidlManager = "android.hidl.manager-V1.0-java"
+		// Conditional class loader context for API version < 29.
+		usesLibs29 := []string{
+			"android.hidl.base-V1.0-java",
+			"android.hidl.manager-V1.0-java",
+		}
+		classLoaderContexts.addLibs(29, module, usesLibs29...)
 
-		// android.hidl.base-V1.0-java and android.hidl.manager-V1.0 contain classes that were in the default
-		// classpath until API 29.  If the targetSdkVersion in the manifest or APK is < 29 then implicitly add
-		// the classes to the classpath for dexpreopt.
-		conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
-			pathForLibrary(module, hidlManager))
-		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
-			filepath.Join("/system/framework", hidlManager+".jar"))
-		conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
-			pathForLibrary(module, hidlBase))
-		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
-			filepath.Join("/system/framework", hidlBase+".jar"))
+		// Conditional class loader context for API version < 30.
+		const testBase = "android.test.base"
+		if !contains(usesLibs, testBase) {
+			classLoaderContexts.addLibs(30, module, testBase)
+		}
 	} else if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
 		// System server jars should be dexpreopted together: class loader context of each jar
 		// should include all preceding jars on the system server classpath.
-		for _, otherJar := range systemServerJars[:jarIndex] {
-			classLoaderContextHost = append(classLoaderContextHost, SystemServerDexJarHostPath(ctx, otherJar))
-			classLoaderContextTarget = append(classLoaderContextTarget, "/system/framework/"+otherJar+".jar")
-		}
+		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
 
 		// Copy the system server jar to a predefined location where dex2oat will find it.
 		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
 		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
 		rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
+
+		checkSystemServerOrder(ctx, jarIndex)
 	} else {
 		// Pass special class loader context to skip the classpath and collision check.
 		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
@@ -314,10 +333,11 @@
 			Text(`class_loader_context_arg=--class-loader-context=\&`).
 			Text(`stored_class_loader_context_arg=""`)
 	} else {
+		clc := classLoaderContexts[anySdkVersion]
 		rule.Command().
-			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(classLoaderContextHost.Strings(), ":") + "]").
-			Implicits(classLoaderContextHost).
-			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(classLoaderContextTarget, ":") + "]")
+			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
+			Implicits(clc.Host).
+			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
 	}
 
 	if module.EnforceUsesLibraries {
@@ -336,21 +356,19 @@
 				Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
 				Text(`)"`)
 		}
-		rule.Command().Textf(`dex_preopt_host_libraries="%s"`,
-			strings.Join(classLoaderContextHost.Strings(), " ")).
-			Implicits(classLoaderContextHost)
-		rule.Command().Textf(`dex_preopt_target_libraries="%s"`,
-			strings.Join(classLoaderContextTarget, " "))
-		rule.Command().Textf(`conditional_host_libs_28="%s"`,
-			strings.Join(conditionalClassLoaderContextHost28.Strings(), " ")).
-			Implicits(conditionalClassLoaderContextHost28)
-		rule.Command().Textf(`conditional_target_libs_28="%s"`,
-			strings.Join(conditionalClassLoaderContextTarget28, " "))
-		rule.Command().Textf(`conditional_host_libs_29="%s"`,
-			strings.Join(conditionalClassLoaderContextHost29.Strings(), " ")).
-			Implicits(conditionalClassLoaderContextHost29)
-		rule.Command().Textf(`conditional_target_libs_29="%s"`,
-			strings.Join(conditionalClassLoaderContextTarget29, " "))
+		for _, ver := range classLoaderContexts.getSortedKeys() {
+			clc := classLoaderContexts.getValue(ver)
+			var varHost, varTarget string
+			if ver == anySdkVersion {
+				varHost = "dex_preopt_host_libraries"
+				varTarget = "dex_preopt_target_libraries"
+			} else {
+				varHost = fmt.Sprintf("conditional_host_libs_%d", ver)
+				varTarget = fmt.Sprintf("conditional_target_libs_%d", ver)
+			}
+			rule.Command().Textf(varHost+`="%s"`, strings.Join(clc.Host.Strings(), " ")).Implicits(clc.Host)
+			rule.Command().Textf(varTarget+`="%s"`, strings.Join(clc.Target, " "))
+		}
 		rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath)
 	}
 
@@ -540,7 +558,7 @@
 	return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String()))
 }
 
-func pathForLibrary(module *ModuleConfig, lib string) android.Path {
+func pathForLibrary(module *ModuleConfig, lib string) *LibraryPath {
 	path, ok := module.LibraryPaths[lib]
 	if !ok {
 		panic(fmt.Errorf("unknown library path for %q", lib))
@@ -561,20 +579,11 @@
 }
 
 // Expected format for apexJarValue = <apex name>:<jar name>
-func GetJarLocationFromApexJarPair(apexJarValue string) string {
-	apex, jar := android.SplitApexJarPair(apexJarValue)
+func GetJarLocationFromApexJarPair(ctx android.PathContext, apexJarValue string) string {
+	apex, jar := android.SplitApexJarPair(ctx, apexJarValue)
 	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 +591,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(ctx, global.UpdatableSystemServerJars))
 	}).([]string)
 }
 
@@ -601,6 +610,29 @@
 	}
 }
 
+// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
+// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
+// have the dependency jar in the class loader context, and it won't be able to resolve any
+// references to its classes and methods.
+func checkSystemServerOrder(ctx android.PathContext, jarIndex int) {
+	mctx, isModule := ctx.(android.ModuleContext)
+	if isModule {
+		config := GetGlobalConfig(ctx)
+		jars := NonUpdatableSystemServerJars(ctx, config)
+		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+			depIndex := android.IndexList(dep.Name(), jars)
+			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
+				jar := jars[jarIndex]
+				dep := jars[depIndex]
+				mctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
+					" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
+					" references from '%s' to '%s'.\n", jar, dep, jar, dep)
+			}
+			return true
+		})
+	}
+}
+
 func contains(l []string, s string) bool {
 	for _, e := range l {
 		if e == s {
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/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 842d9ee..d6eb008 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -59,6 +59,9 @@
 
 	// Whether this module is directly installable to one of the partitions. Default: true.
 	Installable *bool
+
+	// Install symlinks to the installed file.
+	Symlinks []string `android:"arch_variant"`
 }
 
 type PrebuiltEtcModule interface {
@@ -211,10 +214,13 @@
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
 				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+				if len(p.properties.Symlinks) > 0 {
+					entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
+				}
 				entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
 				if p.additionalDependencies != nil {
 					for _, path := range *p.additionalDependencies {
-						entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
+						entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
 					}
 				}
 			},
diff --git a/genrule/genrule.go b/genrule/genrule.go
index fe877fe..f6904f1 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -144,6 +144,9 @@
 
 	subName string
 	subDir  string
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	modulePaths []string
 }
 
 type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
@@ -190,6 +193,9 @@
 func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	g.subName = ctx.ModuleSubDir()
 
+	// Collect the module directory for IDE info in java/jdeps.go.
+	g.modulePaths = append(g.modulePaths, ctx.ModuleDir())
+
 	if len(g.properties.Export_include_dirs) > 0 {
 		for _, dir := range g.properties.Export_include_dirs {
 			g.exportedIncludeDirs = append(g.exportedIncludeDirs,
@@ -529,6 +535,7 @@
 			dpInfo.Deps = append(dpInfo.Deps, src)
 		}
 	}
+	dpInfo.Paths = append(dpInfo.Paths, g.modulePaths...)
 }
 
 func (g *Module) AndroidMk() android.AndroidMkData {
diff --git a/java/aar.go b/java/aar.go
index c8daf83..7413c80 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -730,7 +730,7 @@
 	return android.Paths{a.classpathFile}
 }
 
-func (a *AARImport) DexJar() android.Path {
+func (a *AARImport) DexJarBuildPath() android.Path {
 	return nil
 }
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 75fb5fb..62cf169 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -531,14 +531,12 @@
 					fmt.Fprintln(w, ddoc.Name()+"-check-last-released-api:",
 						ddoc.checkLastReleasedApiTimestamp.String())
 
-					if ddoc.Name() == "api-stubs-docs" || ddoc.Name() == "system-api-stubs-docs" {
-						fmt.Fprintln(w, ".PHONY: checkapi")
-						fmt.Fprintln(w, "checkapi:",
-							ddoc.checkLastReleasedApiTimestamp.String())
+					fmt.Fprintln(w, ".PHONY: checkapi")
+					fmt.Fprintln(w, "checkapi:",
+						ddoc.checkLastReleasedApiTimestamp.String())
 
-						fmt.Fprintln(w, ".PHONY: droidcore")
-						fmt.Fprintln(w, "droidcore: checkapi")
-					}
+					fmt.Fprintln(w, ".PHONY: droidcore")
+					fmt.Fprintln(w, "droidcore: checkapi")
 				}
 			},
 		},
@@ -655,6 +653,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 5bc09ff..c1396a6 100755
--- a/java/app.go
+++ b/java/app.go
@@ -28,6 +28,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/dexpreopt"
 	"android/soong/tradefed"
 )
 
@@ -96,6 +97,14 @@
 	return Bool(as.properties.Privileged)
 }
 
+func (as *AndroidAppSet) OutputFile() android.Path {
+	return as.packedOutput
+}
+
+func (as *AndroidAppSet) MasterFile() string {
+	return as.masterFile
+}
+
 var TargetCpuAbi = map[string]string{
 	"arm":    "ARMEABI_V7A",
 	"arm64":  "ARM64_V8A",
@@ -120,7 +129,7 @@
 }
 
 func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	as.packedOutput = android.PathForModuleOut(ctx, "extracted.zip")
+	as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
 	// We are assuming here that the master file in the APK
 	// set has `.apk` suffix. If it doesn't the build will fail.
 	// APK sets containing APEX files are handled elsewhere.
@@ -145,26 +154,17 @@
 				"stem":              ctx.ModuleName(),
 			},
 		})
-	// TODO(asmundak): add this (it's wrong now, will cause copying extracted.zip)
-	/*
-		var installDir android.InstallPath
-		if Bool(as.properties.Privileged) {
-			installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName())
-		} else if ctx.InstallInTestcases() {
-			installDir = android.PathForModuleInstall(ctx, as.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
-		} else {
-			installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName())
-		}
-		ctx.InstallFile(installDir, as.masterFile", as.packedOutput)
-	*/
 }
 
 // android_app_set extracts a set of APKs based on the target device
 // configuration and installs this set as "split APKs".
-// The set will always contain `base-master.apk` and every APK built
-// to the target device. All density-specific APK will be included, too,
-// unless PRODUCT_APPT_PREBUILT_DPI is defined (should contain comma-sepearated
-// list of density names (LDPI, MDPI, HDPI, etc.)
+// The extracted set always contains 'master' APK whose name is
+// _module_name_.apk and every split APK matching target device.
+// The extraction of the density-specific splits depends on
+// PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
+// be a list density names: LDPI, MDPI, HDPI, etc.), only listed
+// splits will be extracted. Otherwise all density-specific splits
+// will be extracted.
 func AndroidApkSetFactory() android.Module {
 	module := &AndroidAppSet{}
 	module.AddProperties(&module.properties)
@@ -335,7 +335,7 @@
 	presigned bool
 }
 
-var presignedCertificate = Certificate{presigned: true}
+var PresignedCertificate = Certificate{presigned: true}
 
 func (c Certificate) AndroidMkString() string {
 	if c.presigned {
@@ -494,6 +494,14 @@
 		!a.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs
 }
 
+func generateAaptRenamePackageFlags(packageName string) []string {
+	aaptFlags := []string{}
+	aaptFlags = append(aaptFlags, "--rename-manifest-package "+packageName)
+	// Required to rename the package name in the resources table.
+	aaptFlags = append(aaptFlags, "--rename-resources-package "+packageName)
+	return aaptFlags
+}
+
 func (a *AndroidApp) OverriddenManifestPackageName() string {
 	return a.overriddenManifestPackageName
 }
@@ -530,7 +538,7 @@
 		if !overridden {
 			manifestPackageName = *a.overridableAppProperties.Package_name
 		}
-		aaptLinkFlags = append(aaptLinkFlags, "--rename-manifest-package "+manifestPackageName)
+		aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName)...)
 		a.overriddenManifestPackageName = manifestPackageName
 	}
 
@@ -635,16 +643,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 {
@@ -959,6 +971,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.
@@ -994,6 +1008,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.
@@ -1196,7 +1211,7 @@
 	android.OverrideModuleBase
 }
 
-func (i *OverrideAndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (i *OverrideAndroidApp) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 	// TODO(jungjw): Check the base module type.
 }
@@ -1217,7 +1232,7 @@
 	android.OverrideModuleBase
 }
 
-func (i *OverrideAndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (i *OverrideAndroidTest) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 	// TODO(jungjw): Check the base module type.
 }
@@ -1239,7 +1254,7 @@
 	android.OverrideModuleBase
 }
 
-func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 	// TODO(jungjw): Check the base module type.
 }
@@ -1258,6 +1273,7 @@
 type AndroidAppImport struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.ApexModuleBase
 	prebuilt android.Prebuilt
 
 	properties   AndroidAppImportProperties
@@ -1483,7 +1499,7 @@
 	// Sign or align the package if package has not been preprocessed
 	if a.preprocessed {
 		a.outputFile = srcApk
-		a.certificate = presignedCertificate
+		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
 		// If the certificate property is empty at this point, default_dev_cert must be set to true.
 		// Which makes processMainCert's behavior for the empty cert string WAI.
@@ -1503,12 +1519,14 @@
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
 		TransformZipAlign(ctx, alignedApk, dexOutput)
 		a.outputFile = alignedApk
-		a.certificate = presignedCertificate
+		a.certificate = PresignedCertificate
 	}
 
 	// 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
 }
@@ -1559,6 +1577,13 @@
 	return Bool(a.properties.Privileged)
 }
 
+func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ 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("")
 }
@@ -1613,6 +1638,7 @@
 		module.processVariants(ctx)
 	})
 
+	android.InitApexModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
@@ -1663,6 +1689,7 @@
 
 	module.dexpreopter.isTest = true
 
+	android.InitApexModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
@@ -1719,6 +1746,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() {
@@ -1789,6 +1825,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 {
@@ -1840,6 +1888,7 @@
 				"org.apache.http.legacy",
 				"android.hidl.base-V1.0-java",
 				"android.hidl.manager-V1.0-java")
+			ctx.AddVariationDependencies(nil, usesLibTag, optionalUsesLibs...)
 		}
 	}
 }
@@ -1851,24 +1900,30 @@
 	return optionalUsesLibs
 }
 
-// usesLibraryPaths returns a map of module names of shared library dependencies to the paths to their dex jars.
-func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) map[string]android.Path {
-	usesLibPaths := make(map[string]android.Path)
+// usesLibraryPaths returns a map of module names of shared library dependencies to the paths
+// to their dex jars on host and on device.
+func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) dexpreopt.LibraryPaths {
+	usesLibPaths := make(dexpreopt.LibraryPaths)
 
 	if !ctx.Config().UnbundledBuild() {
 		ctx.VisitDirectDepsWithTag(usesLibTag, func(m android.Module) {
+			dep := ctx.OtherModuleName(m)
 			if lib, ok := m.(Dependency); ok {
-				if dexJar := lib.DexJar(); dexJar != nil {
-					usesLibPaths[ctx.OtherModuleName(m)] = dexJar
+				if dexJar := lib.DexJarBuildPath(); dexJar != nil {
+					usesLibPaths[dep] = &dexpreopt.LibraryPath{
+						dexJar,
+						// TODO(b/132357300): propagate actual install paths here.
+						filepath.Join("/system/framework", dep+".jar"),
+					}
 				} else {
-					ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must produce a dex jar, does it have installable: true?",
-						ctx.OtherModuleName(m))
+					ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must"+
+						" produce a dex jar, does it have installable: true?", dep)
 				}
 			} else if ctx.Config().AllowMissingDependencies() {
-				ctx.AddMissingDependencies([]string{ctx.OtherModuleName(m)})
+				ctx.AddMissingDependencies([]string{dep})
 			} else {
-				ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library",
-					ctx.OtherModuleName(m))
+				ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be "+
+					"a java library", dep)
 			}
 		})
 	}
diff --git a/java/app_test.go b/java/app_test.go
index e686f27..1a86e02 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -149,7 +149,7 @@
 			prerelease: true,
         }`)
 	module := ctx.ModuleForTests("foo", "android_common")
-	const packedSplitApks = "extracted.zip"
+	const packedSplitApks = "foo.zip"
 	params := module.Output(packedSplitApks)
 	if params.Rule == nil {
 		t.Errorf("expected output %s is missing", packedSplitApks)
@@ -218,7 +218,7 @@
 		ctx := testContext()
 		run(t, ctx, config)
 		module := ctx.ModuleForTests("foo", "android_common")
-		const packedSplitApks = "extracted.zip"
+		const packedSplitApks = "foo.zip"
 		params := module.Output(packedSplitApks)
 		for k, v := range test.expected {
 			if actual := params.Args[k]; actual != v {
@@ -1748,7 +1748,7 @@
 		certFlag       string
 		lineageFlag    string
 		overrides      []string
-		aaptFlag       string
+		packageFlag    string
 		logging_parent string
 	}{
 		{
@@ -1758,7 +1758,7 @@
 			certFlag:       "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			lineageFlag:    "",
 			overrides:      []string{"qux"},
-			aaptFlag:       "",
+			packageFlag:    "",
 			logging_parent: "",
 		},
 		{
@@ -1768,7 +1768,7 @@
 			certFlag:       "cert/new_cert.x509.pem cert/new_cert.pk8",
 			lineageFlag:    "--lineage lineage.bin",
 			overrides:      []string{"qux", "foo"},
-			aaptFlag:       "",
+			packageFlag:    "",
 			logging_parent: "bah",
 		},
 		{
@@ -1778,7 +1778,7 @@
 			certFlag:       "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			lineageFlag:    "",
 			overrides:      []string{"qux", "foo"},
-			aaptFlag:       "--rename-manifest-package org.dandroid.bp",
+			packageFlag:    "org.dandroid.bp",
 			logging_parent: "",
 		},
 	}
@@ -1826,12 +1826,11 @@
 				expected.logging_parent, logging_parent)
 		}
 
-		// Check the package renaming flag, if exists.
+		// Check the package renaming flags, if exists.
 		res := variant.Output("package-res.apk")
 		aapt2Flags := res.Args["flags"]
-		if !strings.Contains(aapt2Flags, expected.aaptFlag) {
-			t.Errorf("package renaming flag, %q is missing in aapt2 link flags, %q", expected.aaptFlag, aapt2Flags)
-		}
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", expected.packageFlag)
 	}
 }
 
@@ -1968,6 +1967,7 @@
 		res := variant.Output("package-res.apk")
 		aapt2Flags := res.Args["flags"]
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", expected.packageFlag)
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-instrumentation-target-package", expected.targetPackageFlag)
 	}
 }
@@ -3180,6 +3180,7 @@
 		res := variant.Output("package-res.apk")
 		aapt2Flags := res.Args["flags"]
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
 	}
 }
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index b40ab93..1ffb13f 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -150,7 +150,7 @@
 	return d.implementationAndResourceJars
 }
 
-func (d *DeviceHostConverter) DexJar() android.Path {
+func (d *DeviceHostConverter) DexJarBuildPath() android.Path {
 	return nil
 }
 
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 28a2c8a..2911fd9 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -37,7 +37,7 @@
 	usesLibs         []string
 	optionalUsesLibs []string
 	enforceUsesLibs  bool
-	libraryPaths     map[string]android.Path
+	libraryPaths     dexpreopt.LibraryPaths
 
 	builtInstalled string
 }
@@ -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 2f0cbdb..9d93838 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,21 +99,31 @@
 	return nil
 }
 
-func (image bootImageConfig) moduleName(idx int) string {
+// 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(ctx android.PathContext, 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(ctx, image.modules[idx])
 	name := image.stem
-	if idx != 0 || image.extension {
+	if idx != 0 || image.extends != nil {
 		name += "-" + stemOf(m)
 	}
 	return name
 }
 
-func (image bootImageConfig) firstModuleNameOrStem() string {
+func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
 	if len(image.modules) > 0 {
-		return image.moduleName(0)
+		return image.moduleName(ctx, 0)
 	} else {
 		return image.stem
 	}
@@ -144,7 +132,7 @@
 func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
 	ret := make(android.OutputPaths, 0, len(image.modules)*len(exts))
 	for i := range image.modules {
-		name := image.moduleName(i)
+		name := image.moduleName(ctx, i)
 		for _, ext := range exts {
 			ret = append(ret, dir.Join(ctx, name+ext))
 		}
@@ -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
 }
@@ -251,13 +255,13 @@
 		return -1, nil
 	}
 
-	jar, hasJar := module.(interface{ DexJar() android.Path })
+	jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
 	if !hasJar {
 		return -1, nil
 	}
 
 	name := ctx.ModuleName(module)
-	index := android.IndexList(name, image.modules)
+	index := android.IndexList(name, android.GetJarsFromApexJarPairs(ctx, image.modules))
 	if index == -1 {
 		return -1, nil
 	}
@@ -292,7 +296,7 @@
 		panic("unknown boot image: " + image.name)
 	}
 
-	return index, jar.DexJar()
+	return index, jar.DexJarBuildPath()
 }
 
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
@@ -310,13 +314,13 @@
 	// Ensure all modules were converted to paths
 	for i := range bootDexJars {
 		if bootDexJars[i] == nil {
+			_, m := android.SplitApexJarPair(ctx, 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)
 			}
 		}
 	}
@@ -363,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")
@@ -411,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(), ":").
@@ -433,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)
 	}
@@ -451,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
@@ -470,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") {
@@ -485,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()
@@ -535,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")
@@ -586,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")
@@ -606,7 +614,7 @@
 
 	return ctx.Config().Once(updatableBcpPackagesRuleKey, func() interface{} {
 		global := dexpreopt.GetGlobalConfig(ctx)
-		updatableModules := dexpreopt.GetJarsFromApexJarPairs(global.UpdatableBootJars)
+		updatableModules := android.GetJarsFromApexJarPairs(ctx, global.UpdatableBootJars)
 
 		// Collect `permitted_packages` for updatable boot jars.
 		var updatablePackages []string
@@ -658,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)
 	}
@@ -716,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..e9704dc 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -24,7 +24,7 @@
 	"android/soong/dexpreopt"
 )
 
-func TestDexpreoptBootJars(t *testing.T) {
+func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOutputs []string) {
 	bp := `
 		java_sdk_library {
 			name: "foo",
@@ -48,67 +48,88 @@
 
 	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()
-
 	RegisterDexpreoptBootJarsComponents(ctx)
-
 	run(t, ctx, config)
 
 	dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
-
-	bootArt := dexpreoptBootJars.Output("boot-foo.art")
-
-	expectedInputs := []string{
-		"dex_artjars/apex/com.android.art/javalib/arm64/boot.art",
-		"dex_bootjars_input/foo.jar",
-		"dex_bootjars_input/bar.jar",
-		"dex_bootjars_input/baz.jar",
-	}
+	rule := dexpreoptBootJars.Output(ruleFile)
 
 	for i := range expectedInputs {
 		expectedInputs[i] = filepath.Join(buildDir, "test_device", expectedInputs[i])
 	}
 
-	inputs := bootArt.Implicits.Strings()
-	sort.Strings(inputs)
-	sort.Strings(expectedInputs)
-
-	if !reflect.DeepEqual(inputs, expectedInputs) {
-		t.Errorf("want inputs %q\n got inputs %q", expectedInputs, inputs)
-	}
-
-	expectedOutputs := []string{
-		"dex_bootjars/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/system/framework/arm64/boot-foo.oat",
-		"dex_bootjars/system/framework/arm64/boot-bar.oat",
-		"dex_bootjars/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_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",
-	}
-
 	for i := range expectedOutputs {
 		expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
 	}
 
-	outputs := append(android.WritablePaths{bootArt.Output}, bootArt.ImplicitOutputs...).Strings()
+	inputs := rule.Implicits.Strings()
+	sort.Strings(inputs)
+	sort.Strings(expectedInputs)
+
+	outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Strings()
 	sort.Strings(outputs)
 	sort.Strings(expectedOutputs)
 
+	if !reflect.DeepEqual(inputs, expectedInputs) {
+		t.Errorf("want inputs %q\n got inputs %q", expectedInputs, inputs)
+	}
+
 	if !reflect.DeepEqual(outputs, expectedOutputs) {
 		t.Errorf("want outputs %q\n got outputs %q", expectedOutputs, outputs)
 	}
 }
+
+func TestDexpreoptBootJars(t *testing.T) {
+	ruleFile := "boot-foo.art"
+
+	expectedInputs := []string{
+		"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",
+	}
+
+	expectedOutputs := []string{
+		"dex_bootjars/android/system/framework/arm64/boot.invocation",
+		"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/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/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/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",
+	}
+
+	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
+}
+
+// Changes to the boot.zip structure may break the ART APK scanner.
+func TestDexpreoptBootZip(t *testing.T) {
+	ruleFile := "boot.zip"
+
+	ctx := android.PathContextForTesting(testConfig(nil, "", nil))
+	expectedInputs := []string{}
+	for _, target := range dexpreoptTargets(ctx) {
+		for _, ext := range []string{".art", ".oat", ".vdex"} {
+			for _, jar := range []string{"foo", "bar", "baz"} {
+				expectedInputs = append(expectedInputs,
+					filepath.Join("dex_bootjars", target.Os.String(), "system/framework", target.Arch.ArchType.String(), "boot-"+jar+ext))
+			}
+		}
+	}
+
+	expectedOutputs := []string{
+		"dex_bootjars/boot.zip",
+	}
+
+	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
+}
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index f8356d1..f13d9f2 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -39,7 +39,7 @@
 		// 2) The jars that are from an updatable apex.
 		for _, m := range global.UpdatableSystemServerJars {
 			systemServerClasspathLocations = append(systemServerClasspathLocations,
-				dexpreopt.GetJarLocationFromApexJarPair(m))
+				dexpreopt.GetJarLocationFromApexJarPair(ctx, m))
 		}
 		if len(systemServerClasspathLocations) != len(global.SystemServerJars)+len(global.UpdatableSystemServerJars) {
 			panic(fmt.Errorf("Wrong number of system server jars, got %d, expected %d",
@@ -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(ctx, 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{
@@ -143,9 +156,7 @@
 			c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped")
 
 			// 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()}
+			imageName := c.firstModuleNameOrStem(ctx) + ".art"
 
 			// The path to bootclasspath dex files needs to be known at module
 			// GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled.
@@ -153,20 +164,25 @@
 			// 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(ctx, 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)
@@ -199,10 +215,10 @@
 
 		updatableBootclasspath := make([]string, len(global.UpdatableBootJars))
 		for i, p := range global.UpdatableBootJars {
-			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(p)
+			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(ctx, 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 cf4c892..75ef271 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() {
@@ -375,6 +376,7 @@
 	srcFiles    android.Paths
 	sourcepaths android.Paths
 	argFiles    android.Paths
+	implicits   android.Paths
 
 	args string
 
@@ -574,6 +576,7 @@
 	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
 	// may contain filegroup or genrule.
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+	j.implicits = append(j.implicits, srcFiles...)
 
 	filterByPackage := func(srcs []android.Path, filterPackages []string) []android.Path {
 		if filterPackages == nil {
@@ -599,6 +602,24 @@
 	}
 	srcFiles = filterByPackage(srcFiles, j.properties.Filter_packages)
 
+	// While metalava needs package html files, it does not need them to be explicit on the command
+	// line. More importantly, the metalava rsp file is also used by the subsequent jdiff action if
+	// jdiff_enabled=true. javadoc complains if it receives html files on the command line. The filter
+	// below excludes html files from the rsp file for both metalava and jdiff. Note that the html
+	// files are still included as implicit inputs for successful remote execution and correct
+	// incremental builds.
+	filterHtml := func(srcs []android.Path) []android.Path {
+		filtered := []android.Path{}
+		for _, src := range srcs {
+			if src.Ext() == ".html" {
+				continue
+			}
+			filtered = append(filtered, src)
+		}
+		return filtered
+	}
+	srcFiles = filterHtml(srcFiles)
+
 	flags := j.collectAidlFlags(ctx, deps)
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
@@ -1398,10 +1419,51 @@
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
-	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicits 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()
+
+	rspFile := ""
+	if len(implicits) > 0 {
+		implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
+		rspFile = implicitsRsp.String()
+		impRule := android.NewRuleBuilder()
+		impCmd := impRule.Command()
+		// A dummy action that copies the ninja generated rsp file to a new location. This allows us to
+		// add a large number of inputs to a file without exceeding bash command length limits (which
+		// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
+		// rsp file to be ${output}.rsp.
+		impCmd.Text("cp").FlagWithRspFileInputList("", implicits).Output(implicitsRsp)
+		impRule.Build(pctx, ctx, "implicitsGen", "implicits generation")
+		cmd.Implicits(implicits)
+		cmd.Implicit(implicitsRsp)
+	}
+	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()}
+		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,
+			RSPFile:         rspFile,
+			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()).
@@ -1453,7 +1515,7 @@
 	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
 
 	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
-		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, d.Javadoc.implicits)
 
 	d.stubsFlags(ctx, cmd, stubsDir)
 
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 95dd0bb..87dc01e 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -147,7 +147,7 @@
 			name := ctx.ModuleName(module)
 			for moduleList, pathList := range moduleListToPathList {
 				if i := android.IndexList(name, *moduleList); i != -1 {
-					pathList[i] = j.DexJar()
+					pathList[i] = j.DexJarBuildPath()
 				}
 			}
 		}
diff --git a/java/java.go b/java/java.go
index b086504..e1190d3 100644
--- a/java/java.go
+++ b/java/java.go
@@ -480,6 +480,9 @@
 	kytheFiles android.Paths
 
 	distFile android.Path
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	modulePaths []string
 }
 
 func (j *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -502,7 +505,7 @@
 	ImplementationJars() android.Paths
 	ResourceJars() android.Paths
 	ImplementationAndResourcesJars() android.Paths
-	DexJar() android.Path
+	DexJarBuildPath() android.Path
 	AidlIncludeDirs() android.Paths
 	ExportedSdkLibs() []string
 	ExportedPlugins() (android.Paths, []string)
@@ -1745,7 +1748,7 @@
 	return android.Paths{j.implementationJarFile}
 }
 
-func (j *Module) DexJar() android.Path {
+func (j *Module) DexJarBuildPath() android.Path {
 	return j.dexJarFile
 }
 
@@ -1796,6 +1799,7 @@
 	if j.expandJarjarRules != nil {
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
 	}
+	dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...)
 }
 
 func (j *Module) CompilerDeps() []string {
@@ -1890,6 +1894,9 @@
 	j.dexpreopter.uncompressedDex = *j.deviceProperties.Uncompress_dex
 	j.compile(ctx, nil)
 
+	// Collect the module directory for IDE info in java/jdeps.go.
+	j.modulePaths = append(j.modulePaths, ctx.ModuleDir())
+
 	exclusivelyForApex := android.InAnyApex(ctx.ModuleName()) && !j.IsForPlatform()
 	if (Bool(j.properties.Installable) || ctx.Host()) && !exclusivelyForApex {
 		var extraInstallDeps android.Paths
@@ -2430,6 +2437,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 {
@@ -2446,6 +2459,7 @@
 
 	combinedClasspathFile android.Path
 	exportedSdkLibs       []string
+	exportAidlIncludeDirs android.Paths
 }
 
 func (j *Import) sdkVersion() sdkSpec {
@@ -2529,6 +2543,8 @@
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			jarName, outputFile)
 	}
+
+	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 }
 
 var _ Dependency = (*Import)(nil)
@@ -2558,12 +2574,12 @@
 	return android.Paths{j.combinedClasspathFile}
 }
 
-func (j *Import) DexJar() android.Path {
+func (j *Import) DexJarBuildPath() android.Path {
 	return nil
 }
 
 func (j *Import) AidlIncludeDirs() android.Paths {
-	return nil
+	return j.exportAidlIncludeDirs
 }
 
 func (j *Import) ExportedSdkLibs() []string {
@@ -2747,7 +2763,7 @@
 	}
 }
 
-func (j *DexImport) DexJar() android.Path {
+func (j *DexImport) DexJarBuildPath() android.Path {
 	return j.dexJarFile
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index 0471723..8ea34d9 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1038,7 +1038,7 @@
 	for _, i := range metalavaRule.Implicits {
 		systemJars = append(systemJars, i.Base())
 	}
-	if len(systemJars) != 1 || systemJars[0] != systemJar {
+	if len(systemJars) < 1 || systemJars[0] != systemJar {
 		t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
 	}
 }
@@ -1754,3 +1754,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/jdeps.go b/java/jdeps.go
index 49e3de3..9f43887 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -72,6 +72,7 @@
 		dpInfo.Jarjar_rules = android.FirstUniqueStrings(dpInfo.Jarjar_rules)
 		dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars)
 		dpInfo.SrcJars = android.FirstUniqueStrings(dpInfo.SrcJars)
+		dpInfo.Paths = android.FirstUniqueStrings(dpInfo.Paths)
 		moduleInfos[name] = dpInfo
 
 		mkProvider, ok := module.(android.AndroidMkDataProvider)
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/testing.go b/java/testing.go
index d6a2446..faf4d32 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,
 
@@ -228,6 +230,22 @@
 			system_modules: "core-platform-api-stubs-system-modules",
 			installable: true,
 		}
+
+		java_library {
+			name: "android.test.base",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "core-platform-api-stubs-system-modules",
+			installable: true,
+		}
+  
+		java_library {
+			name: "android.test.mock",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "core-platform-api-stubs-system-modules",
+			installable: true,
+		}
 	`
 
 	systemModules := []string{
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index c7d518e..d6e2c0a 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -75,8 +75,8 @@
 	// OutputFiles is a list of output file paths or ninja variables as placeholders for rule
 	// outputs.
 	OutputFiles []string
-	// OutputDirectories is a list of output directory paths or ninja variables as placeholders
-	// for rule outputs.
+	// OutputDirectories is a list of output directories or ninja variables as placeholders for
+	// rule output directories.
 	OutputDirectories []string
 	// ToolchainInputs is a list of paths or ninja variables pointing to the location of
 	// toolchain binaries used by the rule.
@@ -85,17 +85,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 generates 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 {
@@ -105,7 +119,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 {
@@ -119,36 +133,36 @@
 	}
 	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.OutputDirectories) > 0 {
-		template += " --output_directories=" + strings.Join(r.OutputDirectories, ",")
+		args += " --output_directories=" + strings.Join(r.OutputDirectories, ",")
 	}
 
 	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
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/Android.bp b/rust/Android.bp
index 24fd830..684db0b 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -10,6 +10,7 @@
     srcs: [
         "androidmk.go",
         "compiler.go",
+        "coverage.go",
         "binary.go",
         "builder.go",
         "library.go",
@@ -22,6 +23,7 @@
     testSrcs: [
         "binary_test.go",
         "compiler_test.go",
+        "coverage_test.go",
         "library_test.go",
         "rust_test.go",
         "test_test.go",
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/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index 7cfc59c..4c16693 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -62,7 +62,11 @@
 	return "x86_64-apple-darwin"
 }
 
-func (t *toolchainDarwin) ShlibSuffix() string {
+func (t *toolchainDarwin) SharedLibSuffix() string {
+	return ".dylib"
+}
+
+func (t *toolchainDarwin) ProcMacroSuffix() string {
 	return ".dylib"
 }
 
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 9e97a60..dd0966f 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1,2 +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_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/cc_sdk_test.go b/sdk/cc_sdk_test.go
index dded153..4a09081 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -401,7 +401,6 @@
 				"Test.cpp",
 			],
 			compile_multilib: "both",
-			stl: "none",
 		}
 	`)
 
@@ -494,6 +493,7 @@
     device_supported: false,
     host_supported: true,
     installable: false,
+    stl: "none",
     target: {
         linux_glibc: {
             compile_multilib: "both",
@@ -518,6 +518,7 @@
     prefer: false,
     device_supported: false,
     host_supported: true,
+    stl: "none",
     target: {
         linux_glibc: {
             compile_multilib: "both",
@@ -557,6 +558,90 @@
 	)
 }
 
+// Test that we support the necessary flags for the linker binary, which is
+// special in several ways.
+func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		module_exports {
+			name: "mymodule_exports",
+			host_supported: true,
+			device_supported: false,
+			native_binaries: ["linker"],
+		}
+
+		cc_binary {
+			name: "linker",
+			host_supported: true,
+			static_executable: true,
+			nocrt: true,
+			stl: "none",
+			srcs: [
+				"Test.cpp",
+			],
+			compile_multilib: "both",
+		}
+	`)
+
+	result.CheckSnapshot("mymodule_exports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+    name: "mymodule_exports_linker@current",
+    sdk_member_name: "linker",
+    device_supported: false,
+    host_supported: true,
+    installable: false,
+    stl: "none",
+    static_executable: true,
+    nocrt: true,
+    compile_multilib: "both",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/bin/linker"],
+        },
+        x86: {
+            srcs: ["x86/bin/linker"],
+        },
+    },
+}
+
+cc_prebuilt_binary {
+    name: "linker",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    static_executable: true,
+    nocrt: true,
+    compile_multilib: "both",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/bin/linker"],
+        },
+        x86: {
+            srcs: ["x86/bin/linker"],
+        },
+    },
+}
+
+module_exports_snapshot {
+    name: "mymodule_exports@current",
+    device_supported: false,
+    host_supported: true,
+    native_binaries: ["mymodule_exports_linker@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/linker/linux_glibc_x86_64/linker -> x86_64/bin/linker
+.intermediates/linker/linux_glibc_x86/linker -> x86/bin/linker
+`),
+	)
+}
+
 func TestSnapshotWithCcSharedLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
diff --git a/sdk/update.go b/sdk/update.go
index 476a4a5..1ba5806 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -350,6 +350,9 @@
 	bp = newGeneratedFile(ctx, "snapshot", "Android.bp")
 	generateBpContents(&bp.generatedContents, bpFile)
 
+	contents := bp.content.String()
+	syntaxCheckSnapshotBpFile(ctx, contents)
+
 	bp.build(pctx, ctx, nil)
 
 	filesToZip := builder.filesToZip
@@ -394,6 +397,36 @@
 	return outputZipFile
 }
 
+// Check the syntax of the generated Android.bp file contents and if they are
+// invalid then log an error with the contents (tagged with line numbers) and the
+// errors that were found so that it is easy to see where the problem lies.
+func syntaxCheckSnapshotBpFile(ctx android.ModuleContext, contents string) {
+	errs := android.CheckBlueprintSyntax(ctx, "Android.bp", contents)
+	if len(errs) != 0 {
+		message := &strings.Builder{}
+		_, _ = fmt.Fprint(message, `errors in generated Android.bp snapshot:
+
+Generated Android.bp contents
+========================================================================
+`)
+		for i, line := range strings.Split(contents, "\n") {
+			_, _ = fmt.Fprintf(message, "%6d:    %s\n", i+1, line)
+		}
+
+		_, _ = fmt.Fprint(message, `
+========================================================================
+
+Errors found:
+`)
+
+		for _, err := range errs {
+			_, _ = fmt.Fprintf(message, "%s\n", err.Error())
+		}
+
+		ctx.ModuleErrorf("%s", message.String())
+	}
+}
+
 func extractCommonProperties(ctx android.ModuleContext, extractor *commonValueExtractor, commonProperties interface{}, inputPropertiesSlice interface{}) {
 	err := extractor.extractCommonProperties(commonProperties, inputPropertiesSlice)
 	if err != nil {
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/Android.bp b/ui/build/Android.bp
index 2a5a51a..0a0bb16 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -56,12 +56,14 @@
         "signal.go",
         "soong.go",
         "test_build.go",
+        "upload.go",
         "util.go",
     ],
     testSrcs: [
         "cleanbuild_test.go",
         "config_test.go",
         "environment_test.go",
+        "upload_test.go",
         "util_test.go",
         "proc_sync_test.go",
     ],
diff --git a/ui/build/build.go b/ui/build/build.go
index 349a7de..89c3fad 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 3a1188b..49f506e 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":
@@ -962,3 +961,14 @@
 func (c *configImpl) IsPdkBuild() bool {
 	return c.pdkBuild
 }
+
+func (c *configImpl) BuildDateTime() string {
+	return c.buildDateTime
+}
+
+func (c *configImpl) MetricsUploaderApp() string {
+	if p, ok := c.environ.Get("ANDROID_ENABLE_METRICS_UPLOAD"); ok {
+		return p
+	}
+	return ""
+}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index df618c4..7b14c47 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -26,6 +26,7 @@
 	"testing"
 
 	"android/soong/ui/logger"
+	"android/soong/ui/status"
 )
 
 func testContext() Context {
@@ -33,6 +34,7 @@
 		Context: context.Background(),
 		Logger:  logger.New(&bytes.Buffer{}),
 		Writer:  &bytes.Buffer{},
+		Status:  &status.Status{},
 	}}
 }
 
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index a3234e3..7dc4915 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -54,18 +54,16 @@
 
 	var ret map[string]string
 	if len(makeVars) > 0 {
+		// It's not safe to use the same TMPDIR as the build, as that can be removed.
 		tmpDir, err := ioutil.TempDir("", "dumpvars")
 		if err != nil {
 			return nil, err
 		}
 		defer os.RemoveAll(tmpDir)
 
-		// It's not safe to use the same TMPDIR as the build, as that can be removed.
-		config.Environment().Set("TMPDIR", tmpDir)
+		SetupLitePath(ctx, config, tmpDir)
 
-		SetupLitePath(ctx, config)
-
-		ret, err = dumpMakeVars(ctx, config, goals, makeVars, false)
+		ret, err = dumpMakeVars(ctx, config, goals, makeVars, false, tmpDir)
 		if err != nil {
 			return ret, err
 		}
@@ -82,7 +80,7 @@
 	return ret, nil
 }
 
-func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool) (map[string]string, error) {
+func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool, tmpDir string) (map[string]string, error) {
 	ctx.BeginTrace(metrics.RunKati, "dumpvars")
 	defer ctx.EndTrace()
 
@@ -98,6 +96,9 @@
 		cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true")
 	}
 	cmd.Environment.Set("DUMP_MANY_VARS", strings.Join(vars, " "))
+	if tmpDir != "" {
+		cmd.Environment.Set("TMPDIR", tmpDir)
+	}
 	cmd.Sandbox = dumpvarsSandbox
 	output := bytes.Buffer{}
 	cmd.Stdout = &output
@@ -229,8 +230,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 +237,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,11 +252,9 @@
 		"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)
+	make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
 	if err != nil {
 		ctx.Fatalln("Error dumping make vars:", err)
 	}
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..fa44cb1 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -39,6 +39,7 @@
 	args := []string{
 		"-d", "keepdepfile",
 		"-d", "keeprsp",
+		"-d", "stats",
 		"--frontend_file", fifo,
 	}
 
@@ -58,6 +59,7 @@
 	args = append(args, "-f", config.CombinedNinjaFile())
 
 	args = append(args,
+		"-o", "usesphonyoutputs=yes",
 		"-w", "dupbuild=err",
 		"-w", "missingdepfile=err")
 
@@ -128,6 +130,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..6f5cf78 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -55,8 +55,9 @@
 }
 
 // A "lite" version of SetupPath used for dumpvars, or other places that need
-// minimal overhead (but at the expense of logging).
-func SetupLitePath(ctx Context, config Config) {
+// minimal overhead (but at the expense of logging). If tmpDir is empty, the
+// default TMPDIR is used from config.
+func SetupLitePath(ctx Context, config Config, tmpDir string) {
 	if config.pathReplaced {
 		return
 	}
@@ -65,8 +66,11 @@
 	defer ctx.EndTrace()
 
 	origPath, _ := config.Environment().Get("PATH")
-	myPath, _ := config.Environment().Get("TMPDIR")
-	myPath = filepath.Join(myPath, "path")
+
+	if tmpDir == "" {
+		tmpDir, _ = config.Environment().Get("TMPDIR")
+	}
+	myPath := filepath.Join(tmpDir, "path")
 	ensureEmptyDirectoriesExist(ctx, myPath)
 
 	os.Setenv("PATH", origPath)
@@ -177,9 +181,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..6a12add 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,12 @@
 		cmd := Command(ctx, config, "soong "+name,
 			config.PrebuiltBuildTool("ninja"),
 			"-d", "keepdepfile",
+			"-d", "stats",
+			"-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 +135,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/build/upload.go b/ui/build/upload.go
new file mode 100644
index 0000000..75a5e2f
--- /dev/null
+++ b/ui/build/upload.go
@@ -0,0 +1,80 @@
+// 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 build
+
+// This file contains the functionality to upload data from one location to
+// another.
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+
+	upload_proto "android/soong/ui/metrics/upload_proto"
+)
+
+const (
+	uploadPbFilename = ".uploader.pb"
+)
+
+// UploadMetrics uploads a set of metrics files to a server for analysis. An
+// uploader full path is required to be specified in order to upload the set
+// of metrics files. This is accomplished by defining the ANDROID_ENABLE_METRICS_UPLOAD
+// environment variable.
+func UploadMetrics(ctx Context, config Config, buildStartedMilli int64, files ...string) {
+	uploader := config.MetricsUploaderApp()
+	// No metrics to upload if the path to the uploader was not specified.
+	if uploader == "" {
+		return
+	}
+
+	// Some files may not exist. For example, build errors protobuf file
+	// may not exist since the build was successful.
+	var metricsFiles []string
+	for _, f := range files {
+		if _, err := os.Stat(f); err == nil {
+			metricsFiles = append(metricsFiles, f)
+		}
+	}
+
+	if len(metricsFiles) == 0 {
+		return
+	}
+
+	// For platform builds, the branch and target name is hardcoded to specific
+	// values for later extraction of the metrics in the data metrics pipeline.
+	data, err := proto.Marshal(&upload_proto.Upload{
+		CreationTimestampMs:   proto.Uint64(uint64(buildStartedMilli)),
+		CompletionTimestampMs: proto.Uint64(uint64(time.Now().UnixNano() / int64(time.Millisecond))),
+		BranchName:            proto.String("developer-metrics"),
+		TargetName:            proto.String("platform-build-systems-metrics"),
+		MetricsFiles:          metricsFiles,
+	})
+	if err != nil {
+		ctx.Fatalf("failed to marshal metrics upload proto buffer message: %v\n", err)
+	}
+
+	pbFile := filepath.Join(config.OutDir(), uploadPbFilename)
+	if err := ioutil.WriteFile(pbFile, data, 0644); err != nil {
+		ctx.Fatalf("failed to write the marshaled metrics upload protobuf to %q: %v\n", pbFile, err)
+	}
+	// Remove the upload file as it's not longer needed after it has been processed by the uploader.
+	defer os.Remove(pbFile)
+
+	Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile).RunAndStreamOrFatal()
+}
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
new file mode 100644
index 0000000..adaa08d
--- /dev/null
+++ b/ui/build/upload_test.go
@@ -0,0 +1,120 @@
+// 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 build
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"testing"
+	"time"
+
+	"android/soong/ui/logger"
+)
+
+func TestUploadMetrics(t *testing.T) {
+	ctx := testContext()
+	tests := []struct {
+		description string
+		uploader    string
+		createFiles bool
+		files       []string
+	}{{
+		description: "ANDROID_ENABLE_METRICS_UPLOAD not set",
+	}, {
+		description: "no metrics files to upload",
+		uploader:    "fake",
+	}, {
+		description: "non-existent metrics files no upload",
+		uploader:    "fake",
+		files:       []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+	}, {
+		description: "trigger upload",
+		uploader:    "echo",
+		createFiles: true,
+		files:       []string{"metrics_file_1", "metrics_file_2"},
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			defer logger.Recover(func(err error) {
+				t.Fatalf("got unexpected error: %v", err)
+			})
+
+			outDir, err := ioutil.TempDir("", "")
+			if err != nil {
+				t.Fatalf("failed to create out directory: %v", outDir)
+			}
+			defer os.RemoveAll(outDir)
+
+			var metricsFiles []string
+			if tt.createFiles {
+				for _, f := range tt.files {
+					filename := filepath.Join(outDir, f)
+					metricsFiles = append(metricsFiles, filename)
+					if err := ioutil.WriteFile(filename, []byte("test file"), 0644); err != nil {
+						t.Fatalf("failed to create a fake metrics file %q for uploading: %v", filename, err)
+					}
+				}
+			}
+
+			config := Config{&configImpl{
+				environ: &Environment{
+					"OUT_DIR=" + outDir,
+					"ANDROID_ENABLE_METRICS_UPLOAD=" + tt.uploader,
+				},
+				buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
+			}}
+
+			UploadMetrics(ctx, config, 1591031903, metricsFiles...)
+
+			if _, err := os.Stat(filepath.Join(outDir, uploadPbFilename)); err == nil {
+				t.Error("got true, want false for upload protobuf file to exist")
+			}
+		})
+	}
+}
+
+func TestUploadMetricsErrors(t *testing.T) {
+	expectedErr := "failed to write the marshaled"
+	defer logger.Recover(func(err error) {
+		got := err.Error()
+		if !strings.Contains(got, expectedErr) {
+			t.Errorf("got %q, want %q to be contained in error", got, expectedErr)
+		}
+	})
+
+	outDir, err := ioutil.TempDir("", "")
+	if err != nil {
+		t.Fatalf("failed to create out directory: %v", outDir)
+	}
+	defer os.RemoveAll(outDir)
+
+	metricsFile := filepath.Join(outDir, "metrics_file_1")
+	if err := ioutil.WriteFile(metricsFile, []byte("test file"), 0644); err != nil {
+		t.Fatalf("failed to create a fake metrics file %q for uploading: %v", metricsFile, err)
+	}
+
+	config := Config{&configImpl{
+		environ: &Environment{
+			"ANDROID_ENABLE_METRICS_UPLOAD=fake",
+			"OUT_DIR=/bad",
+		}}}
+
+	UploadMetrics(testContext(), config, 1591031903, metricsFile)
+	t.Errorf("got nil, expecting %q as a failure", expectedErr)
+}
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/ui/status/ninja.go b/ui/status/ninja.go
index 9cf2f6a..a11774c 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -174,6 +174,8 @@
 				n.status.Print("warning: " + message)
 			case ninja_frontend.Status_Message_ERROR:
 				n.status.Error(message)
+			case ninja_frontend.Status_Message_DEBUG:
+				n.status.Verbose(message)
 			default:
 				n.status.Print(message)
 			}
diff --git a/ui/status/ninja_frontend/frontend.pb.go b/ui/status/ninja_frontend/frontend.pb.go
index 7c05eed..7ba9de2 100644
--- a/ui/status/ninja_frontend/frontend.pb.go
+++ b/ui/status/ninja_frontend/frontend.pb.go
@@ -3,9 +3,11 @@
 
 package ninja_frontend
 
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
+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
@@ -16,7 +18,7 @@
 // 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.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type Status_Message_Level int32
 
@@ -24,17 +26,21 @@
 	Status_Message_INFO    Status_Message_Level = 0
 	Status_Message_WARNING Status_Message_Level = 1
 	Status_Message_ERROR   Status_Message_Level = 2
+	Status_Message_DEBUG   Status_Message_Level = 3
 )
 
 var Status_Message_Level_name = map[int32]string{
 	0: "INFO",
 	1: "WARNING",
 	2: "ERROR",
+	3: "DEBUG",
 }
+
 var Status_Message_Level_value = map[string]int32{
 	"INFO":    0,
 	"WARNING": 1,
 	"ERROR":   2,
+	"DEBUG":   3,
 }
 
 func (x Status_Message_Level) Enum() *Status_Message_Level {
@@ -42,9 +48,11 @@
 	*p = x
 	return p
 }
+
 func (x Status_Message_Level) String() string {
 	return proto.EnumName(Status_Message_Level_name, int32(x))
 }
+
 func (x *Status_Message_Level) UnmarshalJSON(data []byte) error {
 	value, err := proto.UnmarshalJSONEnum(Status_Message_Level_value, data, "Status_Message_Level")
 	if err != nil {
@@ -53,8 +61,9 @@
 	*x = Status_Message_Level(value)
 	return nil
 }
+
 func (Status_Message_Level) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5, 0}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 5, 0}
 }
 
 type Status struct {
@@ -73,16 +82,17 @@
 func (m *Status) String() string { return proto.CompactTextString(m) }
 func (*Status) ProtoMessage()    {}
 func (*Status) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0}
+	return fileDescriptor_eca3873955a29cfe, []int{0}
 }
+
 func (m *Status) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status.Unmarshal(m, b)
 }
 func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status.Marshal(b, m, deterministic)
 }
-func (dst *Status) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status.Merge(dst, src)
+func (m *Status) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status.Merge(m, src)
 }
 func (m *Status) XXX_Size() int {
 	return xxx_messageInfo_Status.Size(m)
@@ -147,16 +157,17 @@
 func (m *Status_TotalEdges) String() string { return proto.CompactTextString(m) }
 func (*Status_TotalEdges) ProtoMessage()    {}
 func (*Status_TotalEdges) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 0}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 0}
 }
+
 func (m *Status_TotalEdges) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_TotalEdges.Unmarshal(m, b)
 }
 func (m *Status_TotalEdges) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_TotalEdges.Marshal(b, m, deterministic)
 }
-func (dst *Status_TotalEdges) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_TotalEdges.Merge(dst, src)
+func (m *Status_TotalEdges) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_TotalEdges.Merge(m, src)
 }
 func (m *Status_TotalEdges) XXX_Size() int {
 	return xxx_messageInfo_Status_TotalEdges.Size(m)
@@ -188,16 +199,17 @@
 func (m *Status_BuildStarted) String() string { return proto.CompactTextString(m) }
 func (*Status_BuildStarted) ProtoMessage()    {}
 func (*Status_BuildStarted) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 1}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 1}
 }
+
 func (m *Status_BuildStarted) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_BuildStarted.Unmarshal(m, b)
 }
 func (m *Status_BuildStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_BuildStarted.Marshal(b, m, deterministic)
 }
-func (dst *Status_BuildStarted) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_BuildStarted.Merge(dst, src)
+func (m *Status_BuildStarted) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_BuildStarted.Merge(m, src)
 }
 func (m *Status_BuildStarted) XXX_Size() int {
 	return xxx_messageInfo_Status_BuildStarted.Size(m)
@@ -232,16 +244,17 @@
 func (m *Status_BuildFinished) String() string { return proto.CompactTextString(m) }
 func (*Status_BuildFinished) ProtoMessage()    {}
 func (*Status_BuildFinished) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 2}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 2}
 }
+
 func (m *Status_BuildFinished) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_BuildFinished.Unmarshal(m, b)
 }
 func (m *Status_BuildFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_BuildFinished.Marshal(b, m, deterministic)
 }
-func (dst *Status_BuildFinished) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_BuildFinished.Merge(dst, src)
+func (m *Status_BuildFinished) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_BuildFinished.Merge(m, src)
 }
 func (m *Status_BuildFinished) XXX_Size() int {
 	return xxx_messageInfo_Status_BuildFinished.Size(m)
@@ -276,16 +289,17 @@
 func (m *Status_EdgeStarted) String() string { return proto.CompactTextString(m) }
 func (*Status_EdgeStarted) ProtoMessage()    {}
 func (*Status_EdgeStarted) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 3}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 3}
 }
+
 func (m *Status_EdgeStarted) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_EdgeStarted.Unmarshal(m, b)
 }
 func (m *Status_EdgeStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_EdgeStarted.Marshal(b, m, deterministic)
 }
-func (dst *Status_EdgeStarted) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_EdgeStarted.Merge(dst, src)
+func (m *Status_EdgeStarted) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_EdgeStarted.Merge(m, src)
 }
 func (m *Status_EdgeStarted) XXX_Size() int {
 	return xxx_messageInfo_Status_EdgeStarted.Size(m)
@@ -353,7 +367,11 @@
 	// Exit status (0 for success).
 	Status *int32 `protobuf:"zigzag32,3,opt,name=status" json:"status,omitempty"`
 	// Edge output, may contain ANSI codes.
-	Output               *string  `protobuf:"bytes,4,opt,name=output" json:"output,omitempty"`
+	Output *string `protobuf:"bytes,4,opt,name=output" json:"output,omitempty"`
+	// Number of milliseconds spent executing in user mode
+	UserTime *uint32 `protobuf:"varint,5,opt,name=user_time,json=userTime" json:"user_time,omitempty"`
+	// Number of milliseconds spent executing in kernel mode
+	SystemTime           *uint32  `protobuf:"varint,6,opt,name=system_time,json=systemTime" json:"system_time,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -363,16 +381,17 @@
 func (m *Status_EdgeFinished) String() string { return proto.CompactTextString(m) }
 func (*Status_EdgeFinished) ProtoMessage()    {}
 func (*Status_EdgeFinished) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 4}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 4}
 }
+
 func (m *Status_EdgeFinished) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_EdgeFinished.Unmarshal(m, b)
 }
 func (m *Status_EdgeFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_EdgeFinished.Marshal(b, m, deterministic)
 }
-func (dst *Status_EdgeFinished) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_EdgeFinished.Merge(dst, src)
+func (m *Status_EdgeFinished) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_EdgeFinished.Merge(m, src)
 }
 func (m *Status_EdgeFinished) XXX_Size() int {
 	return xxx_messageInfo_Status_EdgeFinished.Size(m)
@@ -411,8 +430,22 @@
 	return ""
 }
 
+func (m *Status_EdgeFinished) GetUserTime() uint32 {
+	if m != nil && m.UserTime != nil {
+		return *m.UserTime
+	}
+	return 0
+}
+
+func (m *Status_EdgeFinished) GetSystemTime() uint32 {
+	if m != nil && m.SystemTime != nil {
+		return *m.SystemTime
+	}
+	return 0
+}
+
 type Status_Message struct {
-	// Message priority level (INFO, WARNING, or ERROR).
+	// Message priority level (DEBUG, INFO, WARNING, ERROR).
 	Level *Status_Message_Level `protobuf:"varint,1,opt,name=level,enum=ninja.Status_Message_Level,def=0" json:"level,omitempty"`
 	// Info/warning/error message from Ninja.
 	Message              *string  `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
@@ -425,16 +458,17 @@
 func (m *Status_Message) String() string { return proto.CompactTextString(m) }
 func (*Status_Message) ProtoMessage()    {}
 func (*Status_Message) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 5}
 }
+
 func (m *Status_Message) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_Message.Unmarshal(m, b)
 }
 func (m *Status_Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_Message.Marshal(b, m, deterministic)
 }
-func (dst *Status_Message) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_Message.Merge(dst, src)
+func (m *Status_Message) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_Message.Merge(m, src)
 }
 func (m *Status_Message) XXX_Size() int {
 	return xxx_messageInfo_Status_Message.Size(m)
@@ -462,6 +496,7 @@
 }
 
 func init() {
+	proto.RegisterEnum("ninja.Status_Message_Level", Status_Message_Level_name, Status_Message_Level_value)
 	proto.RegisterType((*Status)(nil), "ninja.Status")
 	proto.RegisterType((*Status_TotalEdges)(nil), "ninja.Status.TotalEdges")
 	proto.RegisterType((*Status_BuildStarted)(nil), "ninja.Status.BuildStarted")
@@ -469,42 +504,46 @@
 	proto.RegisterType((*Status_EdgeStarted)(nil), "ninja.Status.EdgeStarted")
 	proto.RegisterType((*Status_EdgeFinished)(nil), "ninja.Status.EdgeFinished")
 	proto.RegisterType((*Status_Message)(nil), "ninja.Status.Message")
-	proto.RegisterEnum("ninja.Status_Message_Level", Status_Message_Level_name, Status_Message_Level_value)
 }
 
-func init() { proto.RegisterFile("frontend.proto", fileDescriptor_frontend_5a49d9b15a642005) }
+func init() {
+	proto.RegisterFile("frontend.proto", fileDescriptor_eca3873955a29cfe)
+}
 
-var fileDescriptor_frontend_5a49d9b15a642005 = []byte{
-	// 496 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0xd1, 0x6e, 0xd3, 0x30,
-	0x14, 0xa5, 0x69, 0xd3, 0x34, 0x37, 0x6d, 0x28, 0x96, 0x40, 0x59, 0x10, 0xa2, 0xda, 0xd3, 0x78,
-	0x20, 0x48, 0xbc, 0x20, 0x10, 0x12, 0xa2, 0xd2, 0x06, 0x43, 0xd0, 0x49, 0xde, 0x24, 0x24, 0x5e,
-	0xaa, 0x74, 0xf6, 0x86, 0x51, 0xe2, 0x54, 0xb1, 0xbb, 0x5f, 0xe0, 0x7f, 0x78, 0xe0, 0xfb, 0x90,
-	0xaf, 0xed, 0x2c, 0x65, 0x7b, 0xcb, 0xf1, 0x3d, 0xe7, 0xde, 0x73, 0x8f, 0x1d, 0x48, 0xaf, 0xda,
-	0x46, 0x6a, 0x2e, 0x59, 0xb1, 0x6d, 0x1b, 0xdd, 0x90, 0x50, 0x0a, 0xf9, 0xab, 0x3c, 0xfc, 0x13,
-	0xc1, 0xf8, 0x5c, 0x97, 0x7a, 0xa7, 0xc8, 0x5b, 0x48, 0x74, 0xa3, 0xcb, 0x6a, 0xcd, 0xd9, 0x35,
-	0x57, 0xd9, 0x60, 0x31, 0x38, 0x4a, 0x5e, 0x67, 0x05, 0xf2, 0x0a, 0xcb, 0x29, 0x2e, 0x0c, 0xe1,
-	0xd8, 0xd4, 0x29, 0xe8, 0xee, 0x9b, 0x7c, 0x80, 0xd9, 0x66, 0x27, 0x2a, 0xb6, 0x56, 0xba, 0x6c,
-	0x35, 0x67, 0x59, 0x80, 0xe2, 0x7c, 0x5f, 0xbc, 0x34, 0x94, 0x73, 0xcb, 0xa0, 0xd3, 0x4d, 0x0f,
-	0x91, 0x25, 0xa4, 0xb6, 0xc1, 0x95, 0x90, 0x42, 0xfd, 0xe4, 0x2c, 0x1b, 0x62, 0x87, 0xa7, 0xf7,
-	0x74, 0x38, 0x71, 0x14, 0x6a, 0x67, 0x7a, 0x48, 0xde, 0xc3, 0xd4, 0x38, 0xef, 0x3c, 0x8c, 0xb0,
-	0xc3, 0xc1, 0x7e, 0x07, 0xe3, 0xd7, 0x5b, 0x48, 0xf8, 0x2d, 0x30, 0x2b, 0xa0, 0xba, 0x33, 0x10,
-	0xde, 0xb7, 0x82, 0x91, 0x77, 0xf3, 0x71, 0x5c, 0x37, 0xfe, 0x15, 0x44, 0x35, 0x57, 0xaa, 0xbc,
-	0xe6, 0xd9, 0x18, 0xa5, 0x8f, 0xf7, 0xa5, 0xdf, 0x6c, 0x91, 0x7a, 0x56, 0xfe, 0x12, 0xe0, 0x36,
-	0x4e, 0xf2, 0xfc, 0x6e, 0xfa, 0xb3, 0x7e, 0xc6, 0xf9, 0x17, 0x98, 0xf6, 0x03, 0x24, 0x0b, 0x48,
-	0xb6, 0x65, 0x5b, 0x56, 0x15, 0xaf, 0x84, 0xaa, 0x9d, 0xa0, 0x7f, 0x44, 0x32, 0x88, 0x6e, 0x78,
-	0xbb, 0x69, 0x14, 0xc7, 0xfb, 0x98, 0x50, 0x0f, 0xf3, 0x87, 0x30, 0xdb, 0x8b, 0x32, 0xff, 0x3b,
-	0x80, 0xa4, 0x17, 0x0d, 0x49, 0x21, 0x10, 0xcc, 0xf5, 0x0c, 0x04, 0x23, 0xcf, 0x00, 0x30, 0xd6,
-	0xb5, 0x16, 0xb5, 0xed, 0x36, 0xa3, 0x31, 0x9e, 0x5c, 0x88, 0x9a, 0x93, 0x27, 0x30, 0x16, 0x72,
-	0xbb, 0xd3, 0x2a, 0x1b, 0x2e, 0x86, 0x47, 0x31, 0x75, 0xc8, 0x38, 0x68, 0x76, 0x1a, 0x0b, 0x23,
-	0x2c, 0x78, 0x48, 0x08, 0x8c, 0x18, 0x57, 0x97, 0x98, 0x72, 0x4c, 0xf1, 0xdb, 0xb0, 0x2f, 0x9b,
-	0xba, 0x2e, 0x25, 0xc3, 0x04, 0x63, 0xea, 0xa1, 0xad, 0x48, 0xd5, 0x54, 0x3c, 0x8b, 0xec, 0x26,
-	0x0e, 0xe6, 0x02, 0xa6, 0xfd, 0x3b, 0xb9, 0x63, 0xfc, 0x00, 0x26, 0x5c, 0xb2, 0xbe, 0xed, 0x88,
-	0x4b, 0xe6, 0x4d, 0x2b, 0xbc, 0x1a, 0x7c, 0x6b, 0x8f, 0xa8, 0x43, 0xe6, 0xdc, 0xba, 0xc4, 0x17,
-	0x14, 0x53, 0x87, 0xf2, 0xdf, 0x03, 0x88, 0xdc, 0x25, 0x92, 0x37, 0x10, 0x56, 0xfc, 0x86, 0x57,
-	0x38, 0x29, 0xfd, 0xff, 0x99, 0x3a, 0x56, 0xf1, 0xd5, 0x50, 0xde, 0x8d, 0x4e, 0x57, 0x27, 0x67,
-	0xd4, 0xf2, 0xcd, 0x26, 0xfe, 0x95, 0x04, 0x76, 0x47, 0x07, 0x0f, 0x5f, 0x40, 0x88, 0x7c, 0x32,
-	0x01, 0x54, 0xcc, 0x1f, 0x90, 0x04, 0xa2, 0xef, 0x1f, 0xe9, 0xea, 0x74, 0xf5, 0x69, 0x3e, 0x20,
-	0x31, 0x84, 0xc7, 0x94, 0x9e, 0xd1, 0x79, 0xb0, 0x24, 0x9f, 0x87, 0x3f, 0x52, 0x9c, 0xb8, 0xf6,
-	0x7f, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x8c, 0xef, 0xcb, 0xe0, 0x03, 0x00, 0x00,
+var fileDescriptor_eca3873955a29cfe = []byte{
+	// 539 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x54, 0xd1, 0x6e, 0xd3, 0x4a,
+	0x10, 0xbd, 0x4e, 0xe2, 0x38, 0x1e, 0x27, 0xb9, 0x61, 0x25, 0x90, 0xeb, 0x0a, 0x35, 0xea, 0x53,
+	0x5f, 0x08, 0x12, 0x42, 0x42, 0x20, 0x24, 0x44, 0x44, 0x5a, 0x8a, 0x20, 0x95, 0xb6, 0x45, 0x48,
+	0xbc, 0x44, 0x4e, 0x77, 0x5a, 0x8c, 0xec, 0x75, 0xe4, 0xdd, 0x54, 0xe2, 0x37, 0xf8, 0x09, 0xfe,
+	0x80, 0xaf, 0xe3, 0x01, 0xed, 0xec, 0xda, 0x75, 0x68, 0xdf, 0x7c, 0x76, 0xce, 0x9c, 0x39, 0x7b,
+	0x76, 0x64, 0x18, 0x5f, 0x55, 0xa5, 0xd4, 0x28, 0xc5, 0x6c, 0x53, 0x95, 0xba, 0x64, 0xbe, 0xcc,
+	0xe4, 0xf7, 0xf4, 0xf0, 0x4f, 0x00, 0xfd, 0x73, 0x9d, 0xea, 0xad, 0x62, 0x2f, 0x21, 0xd2, 0xa5,
+	0x4e, 0xf3, 0x15, 0x8a, 0x6b, 0x54, 0xb1, 0x37, 0xf5, 0x8e, 0xa2, 0x67, 0xf1, 0x8c, 0x78, 0x33,
+	0xcb, 0x99, 0x5d, 0x18, 0xc2, 0xc2, 0xd4, 0x39, 0xe8, 0xe6, 0x9b, 0xbd, 0x81, 0xd1, 0x7a, 0x9b,
+	0xe5, 0x62, 0xa5, 0x74, 0x5a, 0x69, 0x14, 0x71, 0x87, 0x9a, 0x93, 0xdd, 0xe6, 0xb9, 0xa1, 0x9c,
+	0x5b, 0x06, 0x1f, 0xae, 0x5b, 0x88, 0xcd, 0x61, 0x6c, 0x05, 0xae, 0x32, 0x99, 0xa9, 0x6f, 0x28,
+	0xe2, 0x2e, 0x29, 0xec, 0xdf, 0xa3, 0x70, 0xec, 0x28, 0xdc, 0xce, 0xac, 0x21, 0x7b, 0x0d, 0x43,
+	0xe3, 0xbc, 0xf1, 0xd0, 0x23, 0x85, 0xbd, 0x5d, 0x05, 0xe3, 0xb7, 0xb6, 0x10, 0xe1, 0x2d, 0x30,
+	0x57, 0xa0, 0xee, 0xc6, 0x80, 0x7f, 0xdf, 0x15, 0x4c, 0x7b, 0x33, 0x9f, 0xc6, 0x35, 0xe3, 0x9f,
+	0x42, 0x50, 0xa0, 0x52, 0xe9, 0x35, 0xc6, 0x7d, 0x6a, 0x7d, 0xb8, 0xdb, 0xfa, 0xc9, 0x16, 0x79,
+	0xcd, 0x4a, 0x9e, 0x00, 0xdc, 0xc6, 0xc9, 0x0e, 0xee, 0xa6, 0x3f, 0x6a, 0x67, 0x9c, 0x7c, 0x80,
+	0x61, 0x3b, 0x40, 0x36, 0x85, 0x68, 0x93, 0x56, 0x69, 0x9e, 0x63, 0x9e, 0xa9, 0xc2, 0x35, 0xb4,
+	0x8f, 0x58, 0x0c, 0xc1, 0x0d, 0x56, 0xeb, 0x52, 0x21, 0xbd, 0xc7, 0x80, 0xd7, 0x30, 0xf9, 0x1f,
+	0x46, 0x3b, 0x51, 0x26, 0xbf, 0x3d, 0x88, 0x5a, 0xd1, 0xb0, 0x31, 0x74, 0x32, 0xe1, 0x34, 0x3b,
+	0x99, 0x60, 0x8f, 0x01, 0x28, 0xd6, 0x95, 0xce, 0x0a, 0xab, 0x36, 0xe2, 0x21, 0x9d, 0x5c, 0x64,
+	0x05, 0xb2, 0x47, 0xd0, 0xcf, 0xe4, 0x66, 0xab, 0x55, 0xdc, 0x9d, 0x76, 0x8f, 0x42, 0xee, 0x90,
+	0x71, 0x50, 0x6e, 0x35, 0x15, 0x7a, 0x54, 0xa8, 0x21, 0x63, 0xd0, 0x13, 0xa8, 0x2e, 0x29, 0xe5,
+	0x90, 0xd3, 0xb7, 0x61, 0x5f, 0x96, 0x45, 0x91, 0x4a, 0x41, 0x09, 0x86, 0xbc, 0x86, 0xb6, 0x22,
+	0x55, 0x99, 0x63, 0x1c, 0xd8, 0x9b, 0x38, 0x98, 0xfc, 0xf2, 0x60, 0xd8, 0x7e, 0x94, 0x3b, 0xce,
+	0xf7, 0x60, 0x80, 0x52, 0xb4, 0x7d, 0x07, 0x28, 0x45, 0xed, 0x5a, 0xd1, 0xdb, 0xd0, 0xb2, 0x3d,
+	0xe0, 0x0e, 0x99, 0x73, 0x6b, 0x93, 0x56, 0x28, 0xe4, 0x0e, 0xb1, 0x7d, 0x08, 0xb7, 0x0a, 0x2b,
+	0xab, 0xe5, 0x93, 0xd6, 0xc0, 0x1c, 0x90, 0xd8, 0x01, 0x44, 0xea, 0x87, 0xd2, 0x58, 0xd8, 0x72,
+	0xdf, 0xbe, 0x9f, 0x3d, 0x32, 0x84, 0xe4, 0xa7, 0x07, 0x81, 0xdb, 0x01, 0xf6, 0x02, 0xfc, 0x1c,
+	0x6f, 0x30, 0x27, 0x9f, 0xe3, 0x7f, 0xb7, 0xdc, 0xb1, 0x66, 0x1f, 0x0d, 0xe5, 0x55, 0xef, 0x74,
+	0x79, 0x7c, 0xc6, 0x2d, 0xdf, 0x04, 0x51, 0x2f, 0x59, 0xc7, 0x46, 0xe4, 0xe0, 0xe1, 0x73, 0xf0,
+	0x89, 0xcf, 0x06, 0x40, 0x1d, 0x93, 0xff, 0x58, 0x04, 0xc1, 0x97, 0xb7, 0x7c, 0x79, 0xba, 0x3c,
+	0x99, 0x78, 0x2c, 0x04, 0x7f, 0xc1, 0xf9, 0x19, 0x9f, 0x74, 0xcc, 0xe7, 0xbb, 0xc5, 0xfc, 0xf3,
+	0xc9, 0xa4, 0x3b, 0x67, 0xef, 0xbb, 0x5f, 0xc7, 0x34, 0x7c, 0x55, 0xff, 0x1f, 0xfe, 0x06, 0x00,
+	0x00, 0xff, 0xff, 0xaf, 0x93, 0x48, 0xcf, 0x2a, 0x04, 0x00, 0x00,
 }
diff --git a/ui/status/ninja_frontend/frontend.proto b/ui/status/ninja_frontend/frontend.proto
index 13fd535..baa0046 100644
--- a/ui/status/ninja_frontend/frontend.proto
+++ b/ui/status/ninja_frontend/frontend.proto
@@ -61,6 +61,10 @@
     optional sint32 status = 3;
     // Edge output, may contain ANSI codes.
     optional string output = 4;
+    // Number of milliseconds spent executing in user mode
+    optional uint32 user_time = 5;
+    // Number of milliseconds spent executing in kernel mode
+    optional uint32 system_time = 6;
   }
 
   message Message {
@@ -68,8 +72,9 @@
       INFO = 0;
       WARNING = 1;
       ERROR = 2;
+      DEBUG = 3;
     }
-    // Message priority level (INFO, WARNING, or ERROR).
+    // Message priority level (DEBUG, INFO, WARNING, ERROR).
     optional Level level = 1 [default = INFO];
     // Info/warning/error message from Ninja.
     optional string message = 2;
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@"
         }
     }
 ]