Merge "Use merge_csv python_binary."
diff --git a/Android.bp b/Android.bp
index 0382ee2..9b55c8c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,6 +36,7 @@
         "blueprint",
         "blueprint-bootstrap",
         "soong",
+        "soong-android-soongconfig",
         "soong-env",
         "soong-shared",
     ],
@@ -73,6 +74,7 @@
         "android/sdk.go",
         "android/sh_binary.go",
         "android/singleton.go",
+        "android/soong_config_modules.go",
         "android/testing.go",
         "android/util.go",
         "android/variable.go",
@@ -101,6 +103,7 @@
         "android/prebuilt_test.go",
         "android/prebuilt_etc_test.go",
         "android/rule_builder_test.go",
+        "android/soong_config_modules_test.go",
         "android/util_test.go",
         "android/variable_test.go",
         "android/visibility_test.go",
@@ -109,6 +112,20 @@
 }
 
 bootstrap_go_package {
+    name: "soong-android-soongconfig",
+    pkgPath: "android/soong/android/soongconfig",
+    deps: [
+        "blueprint",
+        "blueprint-parser",
+        "blueprint-proptools",
+    ],
+    srcs: [
+        "android/soongconfig/config.go",
+        "android/soongconfig/modules.go",
+    ],
+}
+
+bootstrap_go_package {
     name: "soong-cc-config",
     pkgPath: "android/soong/cc/config",
     deps: [
@@ -473,6 +490,7 @@
     ],
     testSrcs: [
         "apex/apex_test.go",
+        "apex/vndk_test.go",
     ],
     pluginFor: ["soong_build"],
 }
@@ -548,6 +566,7 @@
     name: "libatomic",
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
@@ -594,8 +613,10 @@
     name: "libgcc_stripped",
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
+    sdk_version: "current",
 
     arch: {
         arm: {
diff --git a/README.md b/README.md
index b6fda50..b1bb425 100644
--- a/README.md
+++ b/README.md
@@ -376,36 +376,14 @@
 be resolved by hand to a single module with any differences inside
 `target: { android: { }, host: { } }` blocks.
 
-## Build logic
+### Conditionals
 
-The build logic is written in Go using the
-[blueprint](http://godoc.org/github.com/google/blueprint) framework.  Build
-logic receives module definitions parsed into Go structures using reflection
-and produces build rules.  The build rules are collected by blueprint and
-written to a [ninja](http://ninja-build.org) build file.
-
-## Other documentation
-
-* [Best Practices](docs/best_practices.md)
-* [Build Performance](docs/perf.md)
-* [Generating CLion Projects](docs/clion.md)
-* [Generating YouCompleteMe/VSCode compile\_commands.json file](docs/compdb.md)
-* Make-specific documentation: [build/make/README.md](https://android.googlesource.com/platform/build/+/master/README.md)
-
-## FAQ
-
-### How do I write conditionals?
-
-Soong deliberately does not support conditionals in Android.bp files.  We
+Soong deliberately does not support most conditionals in Android.bp files.  We
 suggest removing most conditionals from the build.  See
 [Best Practices](docs/best_practices.md#removing-conditionals) for some
 examples on how to remove conditionals.
 
-In cases where build time conditionals are unavoidable, complexity in build
-rules that would require conditionals are handled in Go through Soong plugins.
-This allows Go language features to be used for better readability and
-testability, and implicit dependencies introduced by conditionals can be
-tracked.  Most conditionals supported natively by Soong are converted to a map
+Most conditionals supported natively by Soong are converted to a map
 property.  When building the module one of the properties in the map will be
 selected, and its values appended to the property with the same name at the
 top level of the module.
@@ -430,6 +408,106 @@
 be built.  When building for x86 the `generic.cpp` and 'x86.cpp' sources will
 be built.
 
+#### Soong Config Variables
+
+When converting vendor modules that contain conditionals, simple conditionals
+can be supported through Soong config variables using `soong_config_*`
+modules that describe the module types, variables and possible values:
+
+```
+soong_config_module_type {
+    name: "acme_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "acme",
+    variables: ["board", "feature"],
+    properties: ["cflags", "srcs"],
+}
+
+soong_config_string_variable {
+    name: "board",
+    values: ["soc_a", "soc_b"],
+}
+
+soong_config_bool_variable {
+    name: "feature",
+}
+```
+
+This example describes a new `acme_cc_defaults` module type that extends the
+`cc_defaults` module type, with two additional conditionals based on variables
+`board` and `feature`, which can affect properties `cflags` and `srcs`.
+
+The values of the variables can be set from a product's `BoardConfig.mk` file:
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+    board \
+    feature \
+
+SOONG_CONFIG_acme_board := soc_a
+SOONG_CONFIG_acme_feature := true
+```
+
+The `acme_cc_defaults` module type can be used anywhere after the definition in
+the file where it is defined, or can be imported into another file with:
+```
+soong_config_module_type_import {
+    from: "device/acme/Android.bp",
+    module_types: ["acme_cc_defaults"],
+}
+```
+
+It can used like any other module type:
+```
+acme_cc_defaults {
+    name: "acme_defaults",
+    cflags: ["-DGENERIC"],
+    soong_config_variables: {
+        board: {
+            soc_a: {
+                cflags: ["-DSOC_A"],
+            },
+            soc_b: {
+                cflags: ["-DSOC_B"],
+            },
+        },
+        feature: {
+            cflags: ["-DFEATURE"],
+        },
+    },
+}
+
+cc_library {
+    name: "libacme_foo",
+    defaults: ["acme_defaults"],
+    srcs: ["*.cpp"],
+}
+```
+
+With the `BoardConfig.mk` snippet above, libacme_foo would build with
+cflags "-DGENERIC -DSOC_A -DFEATURE".
+
+`soong_config_module_type` modules will work best when used to wrap defaults
+modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
+by all of the vendor's other modules using the normal namespace and visibility
+rules.
+
+## Build logic
+
+The build logic is written in Go using the
+[blueprint](http://godoc.org/github.com/google/blueprint) framework.  Build
+logic receives module definitions parsed into Go structures using reflection
+and produces build rules.  The build rules are collected by blueprint and
+written to a [ninja](http://ninja-build.org) build file.
+
+## Other documentation
+
+* [Best Practices](docs/best_practices.md)
+* [Build Performance](docs/perf.md)
+* [Generating CLion Projects](docs/clion.md)
+* [Generating YouCompleteMe/VSCode compile\_commands.json file](docs/compdb.md)
+* Make-specific documentation: [build/make/README.md](https://android.googlesource.com/platform/build/+/master/README.md)
+
 ## Developing for Soong
 
 To load Soong code in a Go-aware IDE, create a directory outside your android tree and then:
diff --git a/android/apex.go b/android/apex.go
index 8482dc2..a4b6956 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -82,7 +82,10 @@
 }
 
 type ApexProperties struct {
-	// Availability of this module in APEXes. Only the listed APEXes can include this module.
+	// Availability of this module in APEXes. Only the listed APEXes can contain
+	// this module. If the module has stubs then other APEXes and the platform may
+	// access it through them (subject to visibility).
+	//
 	// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
 	// "//apex_available:platform" refers to non-APEX partitions like "system.img".
 	// Default is ["//apex_available:platform", "//apex_available:anyapex"].
@@ -144,9 +147,9 @@
 
 func CheckAvailableForApex(what string, apex_available []string) bool {
 	if len(apex_available) == 0 {
-		// apex_available defaults to ["//apex_available:platform", "//apex_available:anyapex"],
-		// which means 'available to everybody'.
-		return true
+		// apex_available defaults to ["//apex_available:platform"],
+		// which means 'available to the platform but no apexes'.
+		return what == AvailableToPlatform
 	}
 	return InList(what, apex_available) ||
 		(what != AvailableToPlatform && InList(availableToAnyApex, apex_available))
diff --git a/android/arch.go b/android/arch.go
index b5b8a8f..65833a8 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -838,8 +838,8 @@
 		osTargets = targets
 	}
 
-	// only the primary arch in the recovery partition
-	if os == Android && module.InstallInRecovery() {
+	// only the primary arch in the ramdisk / recovery partition
+	if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk()) {
 		osTargets = []Target{osTargets[0]}
 	}
 
@@ -926,16 +926,31 @@
 	return targets
 }
 
-// createArchType takes a reflect.Type that is either a struct or a pointer to a struct, and returns a list of
-// reflect.Type that contains the arch-variant properties inside structs for each architecture, os, target, multilib,
-// etc.
-func createArchType(props reflect.Type) []reflect.Type {
-	propShards, _ := proptools.FilterPropertyStructSharded(props, filterArchStruct)
+type archPropTypeDesc struct {
+	arch, multilib, target reflect.Type
+}
+
+type archPropRoot struct {
+	Arch, Multilib, Target interface{}
+}
+
+// createArchPropTypeDesc takes a reflect.Type that is either a struct or a pointer to a struct, and
+// returns lists of reflect.Types that contains the arch-variant properties inside structs for each
+// arch, multilib and target property.
+func createArchPropTypeDesc(props reflect.Type) []archPropTypeDesc {
+	// Each property struct shard will be nested many times under the runtime generated arch struct,
+	// which can hit the limit of 64kB for the name of runtime generated structs.  They are nested
+	// 97 times now, which may grow in the future, plus there is some overhead for the containing
+	// type.  This number may need to be reduced if too many are added, but reducing it too far
+	// could cause problems if a single deeply nested property no longer fits in the name.
+	const maxArchTypeNameSize = 500
+
+	propShards, _ := proptools.FilterPropertyStructSharded(props, maxArchTypeNameSize, filterArchStruct)
 	if len(propShards) == 0 {
 		return nil
 	}
 
-	var ret []reflect.Type
+	var ret []archPropTypeDesc
 	for _, props := range propShards {
 
 		variantFields := func(names []string) []reflect.StructField {
@@ -1011,20 +1026,12 @@
 		}
 
 		targetType := reflect.StructOf(variantFields(targets))
-		ret = append(ret, reflect.StructOf([]reflect.StructField{
-			{
-				Name: "Arch",
-				Type: archType,
-			},
-			{
-				Name: "Multilib",
-				Type: multilibType,
-			},
-			{
-				Name: "Target",
-				Type: targetType,
-			},
-		}))
+
+		ret = append(ret, archPropTypeDesc{
+			arch:     reflect.PtrTo(archType),
+			multilib: reflect.PtrTo(multilibType),
+			target:   reflect.PtrTo(targetType),
+		})
 	}
 	return ret
 }
@@ -1036,11 +1043,20 @@
 		// 16-bit limit on structure name length. The name is constructed
 		// based on the Go source representation of the structure, so
 		// the tag names count towards that length.
-		//
-		// TODO: handle the uncommon case of other tags being involved
-		if field.Tag == `android:"arch_variant"` {
-			field.Tag = ""
+
+		androidTag := field.Tag.Get("android")
+		values := strings.Split(androidTag, ",")
+
+		if string(field.Tag) != `android:"`+strings.Join(values, ",")+`"` {
+			panic(fmt.Errorf("unexpected tag format %q", field.Tag))
 		}
+		// these tags don't need to be present in the runtime generated struct type.
+		values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path"})
+		if len(values) > 0 {
+			panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
+		}
+
+		field.Tag = ""
 		return true, field
 	}
 	return false, field
@@ -1069,12 +1085,16 @@
 		}
 
 		archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
-			return createArchType(t)
-		}).([]reflect.Type)
+			return createArchPropTypeDesc(t)
+		}).([]archPropTypeDesc)
 
 		var archProperties []interface{}
 		for _, t := range archPropTypes {
-			archProperties = append(archProperties, reflect.New(t).Interface())
+			archProperties = append(archProperties, &archPropRoot{
+				Arch:     reflect.Zero(t.arch).Interface(),
+				Multilib: reflect.Zero(t.multilib).Interface(),
+				Target:   reflect.Zero(t.target).Interface(),
+			})
 		}
 		base.archProperties = append(base.archProperties, archProperties)
 		m.AddProperties(archProperties...)
@@ -1088,6 +1108,13 @@
 func (m *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
 	dst interface{}, src reflect.Value, field, srcPrefix string) reflect.Value {
 
+	if src.Kind() == reflect.Ptr {
+		if src.IsNil() {
+			return src
+		}
+		src = src.Elem()
+	}
+
 	src = src.FieldByName(field)
 	if !src.IsValid() {
 		ctx.ModuleErrorf("field %q does not exist", srcPrefix)
@@ -1134,7 +1161,7 @@
 		for _, archProperties := range m.archProperties[i] {
 			archPropValues := reflect.ValueOf(archProperties).Elem()
 
-			targetProp := archPropValues.FieldByName("Target")
+			targetProp := archPropValues.FieldByName("Target").Elem()
 
 			// Handle host-specific properties in the form:
 			// target: {
@@ -1229,9 +1256,9 @@
 		for _, archProperties := range m.archProperties[i] {
 			archPropValues := reflect.ValueOf(archProperties).Elem()
 
-			archProp := archPropValues.FieldByName("Arch")
-			multilibProp := archPropValues.FieldByName("Multilib")
-			targetProp := archPropValues.FieldByName("Target")
+			archProp := archPropValues.FieldByName("Arch").Elem()
+			multilibProp := archPropValues.FieldByName("Multilib").Elem()
+			targetProp := archPropValues.FieldByName("Target").Elem()
 
 			// Handle arch-specific properties in the form:
 			// arch: {
diff --git a/android/arch_test.go b/android/arch_test.go
index 98b0534..b987d56 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -55,6 +55,24 @@
 			filtered: true,
 		},
 		{
+			name: "tags",
+			in: &struct {
+				A *string `android:"arch_variant"`
+				B *string `android:"arch_variant,path"`
+				C *string `android:"arch_variant,path,variant_prepend"`
+				D *string `android:"path,variant_prepend,arch_variant"`
+				E *string `android:"path"`
+				F *string
+			}{},
+			out: &struct {
+				A *string
+				B *string
+				C *string
+				D *string
+			}{},
+			filtered: true,
+		},
+		{
 			name: "all filtered",
 			in: &struct {
 				A *string
diff --git a/android/config.go b/android/config.go
index 3c49c1a..3dae6e2 100644
--- a/android/config.go
+++ b/android/config.go
@@ -29,11 +29,14 @@
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android/soongconfig"
 )
 
 var Bool = proptools.Bool
 var String = proptools.String
-var FutureApiLevel = 10000
+
+const FutureApiLevel = 10000
 
 // The configuration file name
 const configFileName = "soong.config"
@@ -66,19 +69,7 @@
 	*deviceConfig
 }
 
-type VendorConfig interface {
-	// Bool interprets the variable named `name` as a boolean, returning true if, after
-	// lowercasing, it matches one of "1", "y", "yes", "on", or "true". Unset, or any other
-	// value will return false.
-	Bool(name string) bool
-
-	// String returns the string value of `name`. If the variable was not set, it will
-	// return the empty string.
-	String(name string) string
-
-	// IsSet returns whether the variable `name` was set by Make.
-	IsSet(name string) bool
-}
+type VendorConfig soongconfig.SoongConfig
 
 type config struct {
 	FileConfigurableOptions
@@ -128,8 +119,6 @@
 	OncePer
 }
 
-type vendorConfig map[string]string
-
 type jsonConfigurable interface {
 	SetDefaultConfig()
 }
@@ -827,6 +816,22 @@
 	return Bool(c.productVariables.UseRBE)
 }
 
+func (c *config) UseRBEJAVAC() bool {
+	return Bool(c.productVariables.UseRBEJAVAC)
+}
+
+func (c *config) UseRBER8() bool {
+	return Bool(c.productVariables.UseRBER8)
+}
+
+func (c *config) UseRBED8() bool {
+	return Bool(c.productVariables.UseRBED8)
+}
+
+func (c *config) UseRemoteBuild() bool {
+	return c.UseGoma() || c.UseRBE()
+}
+
 func (c *config) RunErrorProne() bool {
 	return c.IsEnvTrue("RUN_ERROR_PRONE")
 }
@@ -835,6 +840,14 @@
 	return c.Getenv("XREF_CORPUS")
 }
 
+// Returns Compilation Unit encoding to use. Can be 'json' (default), 'proto' or 'all'.
+func (c *config) XrefCuEncoding() string {
+	if enc := c.Getenv("KYTHE_KZIP_ENCODING"); enc != "" {
+		return enc
+	}
+	return "json"
+}
+
 func (c *config) EmitXrefRules() bool {
 	return c.XrefCorpusName() != ""
 }
@@ -1019,6 +1032,10 @@
 	return Bool(c.config.productVariables.NativeCoverage)
 }
 
+func (c *deviceConfig) ClangCoverageEnabled() bool {
+	return Bool(c.config.productVariables.ClangCoverage)
+}
+
 func (c *deviceConfig) CoverageEnabledForPath(path string) bool {
 	coverage := false
 	if c.config.productVariables.CoveragePaths != nil {
@@ -1125,21 +1142,7 @@
 }
 
 func (c *config) VendorConfig(name string) VendorConfig {
-	return vendorConfig(c.productVariables.VendorVars[name])
-}
-
-func (c vendorConfig) Bool(name string) bool {
-	v := strings.ToLower(c[name])
-	return v == "1" || v == "y" || v == "yes" || v == "on" || v == "true"
-}
-
-func (c vendorConfig) String(name string) string {
-	return c[name]
-}
-
-func (c vendorConfig) IsSet(name string) bool {
-	_, ok := c[name]
-	return ok
+	return soongconfig.Config(c.productVariables.VendorVars[name])
 }
 
 func (c *config) NdkAbis() bool {
@@ -1225,3 +1228,7 @@
 func (c *deviceConfig) DeviceSecondaryArchVariant() string {
 	return String(c.config.productVariables.DeviceSecondaryArchVariant)
 }
+
+func (c *deviceConfig) BoardUsesRecoveryAsBoot() bool {
+	return Bool(c.config.productVariables.BoardUsesRecoveryAsBoot)
+}
diff --git a/android/defs.go b/android/defs.go
index 4890c66..5c815e6 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -99,6 +99,9 @@
 
 	// Used only when USE_GOMA=true is set, to restrict non-goma jobs to the local parallelism value
 	localPool = blueprint.NewBuiltinPool("local_pool")
+
+	// Used for processes that need significant RAM to ensure there are not too many running in parallel.
+	highmemPool = blueprint.NewBuiltinPool("highmem_pool")
 )
 
 func init() {
diff --git a/android/hooks.go b/android/hooks.go
index 04ba69e..47f69d1 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -34,6 +34,9 @@
 	AppendProperties(...interface{})
 	PrependProperties(...interface{})
 	CreateModule(ModuleFactory, ...interface{}) Module
+
+	registerScopedModuleType(name string, factory blueprint.ModuleFactory)
+	moduleFactories() map[string]blueprint.ModuleFactory
 }
 
 func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) {
@@ -52,6 +55,10 @@
 	module Module
 }
 
+func (l *loadHookContext) moduleFactories() map[string]blueprint.ModuleFactory {
+	return l.bp.ModuleFactories()
+}
+
 func (l *loadHookContext) AppendProperties(props ...interface{}) {
 	for _, p := range props {
 		err := proptools.AppendMatchingProperties(l.Module().base().customizableProperties,
@@ -90,7 +97,7 @@
 			module.base().variableProperties,
 			// Put an empty copy of the src properties into dst so that properties in src that are not in dst
 			// don't cause a "failed to find property to extend" error.
-			proptools.CloneEmptyProperties(reflect.ValueOf(src).Elem()).Interface(),
+			proptools.CloneEmptyProperties(reflect.ValueOf(src)).Interface(),
 		}
 		err := proptools.AppendMatchingProperties(dst, src, nil)
 		if err != nil {
@@ -101,6 +108,10 @@
 	return module
 }
 
+func (l *loadHookContext) registerScopedModuleType(name string, factory blueprint.ModuleFactory) {
+	l.bp.RegisterScopedModuleType(name, factory)
+}
+
 type InstallHookContext interface {
 	ModuleContext
 	Path() InstallPath
diff --git a/android/image.go b/android/image.go
index 5291ce3..061bfa5 100644
--- a/android/image.go
+++ b/android/image.go
@@ -22,6 +22,10 @@
 	// CoreVariantNeeded should return true if the module needs a core variant (installed on the system image).
 	CoreVariantNeeded(ctx BaseModuleContext) bool
 
+	// RamdiskVariantNeeded should return true if the module needs a ramdisk variant (installed on the
+	// ramdisk partition).
+	RamdiskVariantNeeded(ctx BaseModuleContext) bool
+
 	// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
 	// recovery partition).
 	RecoveryVariantNeeded(ctx BaseModuleContext) bool
@@ -46,6 +50,9 @@
 
 	// RecoveryVariation means a module to be installed to recovery image.
 	RecoveryVariation string = "recovery"
+
+	// RamdiskVariation means a module to be installed to ramdisk image.
+	RamdiskVariation string = "ramdisk"
 )
 
 // imageMutator creates variants for modules that implement the ImageInterface that
@@ -63,6 +70,9 @@
 		if m.CoreVariantNeeded(ctx) {
 			variations = append(variations, CoreVariation)
 		}
+		if m.RamdiskVariantNeeded(ctx) {
+			variations = append(variations, RamdiskVariation)
+		}
 		if m.RecoveryVariantNeeded(ctx) {
 			variations = append(variations, RecoveryVariation)
 		}
diff --git a/android/module.go b/android/module.go
index 67d1f12..96c2e1e 100644
--- a/android/module.go
+++ b/android/module.go
@@ -62,6 +62,7 @@
 	ModuleName() string
 	ModuleDir() string
 	ModuleType() string
+	BlueprintsFile() string
 
 	ContainsProperty(name string) bool
 	Errorf(pos scanner.Position, fmt string, args ...interface{})
@@ -167,6 +168,7 @@
 	InstallInData() bool
 	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
+	InstallInRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -207,6 +209,7 @@
 	InstallInData() bool
 	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
+	InstallInRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -384,6 +387,9 @@
 	// Whether this module is installed to recovery partition
 	Recovery *bool
 
+	// Whether this module is installed to ramdisk
+	Ramdisk *bool
+
 	// Whether this module is built for non-native architecures (also known as native bridge binary)
 	Native_bridge_supported *bool `android:"arch_variant"`
 
@@ -519,9 +525,13 @@
 	}
 }
 
+func initAndroidModuleBase(m Module) {
+	m.base().module = m
+}
+
 func InitAndroidModule(m Module) {
+	initAndroidModuleBase(m)
 	base := m.base()
-	base.module = m
 
 	m.AddProperties(
 		&base.nameProperties,
@@ -867,6 +877,10 @@
 	return false
 }
 
+func (m *ModuleBase) InstallInRamdisk() bool {
+	return Bool(m.commonProperties.Ramdisk)
+}
+
 func (m *ModuleBase) InstallInRecovery() bool {
 	return Bool(m.commonProperties.Recovery)
 }
@@ -898,6 +912,10 @@
 	}
 }
 
+func (m *ModuleBase) InRamdisk() bool {
+	return m.base().commonProperties.ImageVariation == RamdiskVariation
+}
+
 func (m *ModuleBase) InRecovery() bool {
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
@@ -1338,7 +1356,7 @@
 func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
 
-	if (m.config.UseGoma() || m.config.UseRBE()) && params.Pool == nil {
+	if m.config.UseRemoteBuild() && params.Pool == nil {
 		// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
 		// jobs to the local parallelism value
 		params.Pool = localPool
@@ -1649,6 +1667,10 @@
 	return m.module.InstallInSanitizerDir()
 }
 
+func (m *moduleContext) InstallInRamdisk() bool {
+	return m.module.InstallInRamdisk()
+}
+
 func (m *moduleContext) InstallInRecovery() bool {
 	return m.module.InstallInRecovery()
 }
@@ -1746,7 +1768,7 @@
 			Rule:        Symlink,
 			Description: "install symlink " + fullInstallPath.Base(),
 			Output:      fullInstallPath,
-			OrderOnly:   Paths{srcPath},
+			Input:       srcPath,
 			Default:     !m.Config().EmbeddedInMake(),
 			Args: map[string]string{
 				"fromPath": relPath,
diff --git a/android/mutator.go b/android/mutator.go
index f2f9663..a46d4be 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -27,6 +27,7 @@
 //   run Pre-deps mutators
 //   run depsMutator
 //   run PostDeps mutators
+//   run FinalDeps mutators (CreateVariations disallowed in this phase)
 //   continue on to GenerateAndroidBuildActions
 
 func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) {
@@ -43,7 +44,7 @@
 	}
 }
 
-func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps []RegisterMutatorFunc) {
+func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
 	mctx := &registerMutatorsContext{}
 
 	register := func(funcs []RegisterMutatorFunc) {
@@ -60,11 +61,15 @@
 
 	register(postDeps)
 
+	mctx.finalPhase = true
+	register(finalDeps)
+
 	registerMutatorsToContext(ctx, mctx.mutators)
 }
 
 type registerMutatorsContext struct {
-	mutators []*mutator
+	mutators   []*mutator
+	finalPhase bool
 }
 
 type RegisterMutatorsContext interface {
@@ -102,6 +107,8 @@
 	RegisterOverridePostDepsMutators,
 }
 
+var finalDeps = []RegisterMutatorFunc{}
+
 func PreArchMutators(f RegisterMutatorFunc) {
 	preArch = append(preArch, f)
 }
@@ -114,6 +121,10 @@
 	postDeps = append(postDeps, f)
 }
 
+func FinalDepsMutators(f RegisterMutatorFunc) {
+	finalDeps = append(finalDeps, f)
+}
+
 type TopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
@@ -156,14 +167,17 @@
 type bottomUpMutatorContext struct {
 	bp blueprint.BottomUpMutatorContext
 	baseModuleContext
+	finalPhase bool
 }
 
 func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
+	finalPhase := x.finalPhase
 	f := func(ctx blueprint.BottomUpMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			actx := &bottomUpMutatorContext{
 				bp:                ctx,
 				baseModuleContext: a.base().baseModuleContextFactory(ctx),
+				finalPhase:        finalPhase,
 			}
 			m(actx)
 		}
@@ -256,7 +270,7 @@
 			module.base().variableProperties,
 			// Put an empty copy of the src properties into dst so that properties in src that are not in dst
 			// don't cause a "failed to find property to extend" error.
-			proptools.CloneEmptyProperties(reflect.ValueOf(src).Elem()).Interface(),
+			proptools.CloneEmptyProperties(reflect.ValueOf(src)).Interface(),
 		}
 		err := proptools.AppendMatchingProperties(dst, src, nil)
 		if err != nil {
@@ -285,6 +299,10 @@
 }
 
 func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []Module {
+	if b.finalPhase {
+		panic("CreateVariations not allowed in FinalDepsMutators")
+	}
+
 	modules := b.bp.CreateVariations(variations...)
 
 	aModules := make([]Module, len(modules))
@@ -299,6 +317,10 @@
 }
 
 func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []Module {
+	if b.finalPhase {
+		panic("CreateLocalVariations not allowed in FinalDepsMutators")
+	}
+
 	modules := b.bp.CreateLocalVariations(variations...)
 
 	aModules := make([]Module, len(modules))
diff --git a/android/mutator_test.go b/android/mutator_test.go
index d179f9d..191b535 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -15,9 +15,12 @@
 package android
 
 import (
+	"fmt"
 	"reflect"
+	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -185,3 +188,110 @@
 		t.Errorf("want module String() values:\n%q\ngot:\n%q", want, moduleStrings)
 	}
 }
+
+func TestFinalDepsPhase(t *testing.T) {
+	ctx := NewTestContext()
+
+	finalGot := map[string]int{}
+
+	dep1Tag := struct {
+		blueprint.BaseDependencyTag
+	}{}
+	dep2Tag := struct {
+		blueprint.BaseDependencyTag
+	}{}
+
+	ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("far_deps_1", func(ctx BottomUpMutatorContext) {
+			if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+				ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
+			}
+		})
+		ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
+			ctx.CreateLocalVariations("a", "b")
+		})
+	})
+
+	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("far_deps_2", func(ctx BottomUpMutatorContext) {
+			if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+				ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep2Tag, "common_dep_2")
+			}
+		})
+		ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
+			finalGot[ctx.Module().String()] += 1
+			ctx.VisitDirectDeps(func(mod Module) {
+				finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1
+			})
+		})
+	})
+
+	ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+
+	bp := `
+		test {
+			name: "common_dep_1",
+		}
+		test {
+			name: "common_dep_2",
+		}
+		test {
+			name: "foo",
+		}
+	`
+
+	config := TestConfig(buildDir, nil, bp, nil)
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+
+	finalWant := map[string]int{
+		"common_dep_1{variant:a}":                   1,
+		"common_dep_1{variant:b}":                   1,
+		"common_dep_2{variant:a}":                   1,
+		"common_dep_2{variant:b}":                   1,
+		"foo{variant:a}":                            1,
+		"foo{variant:a} -> common_dep_1{variant:a}": 1,
+		"foo{variant:a} -> common_dep_2{variant:a}": 1,
+		"foo{variant:b}":                            1,
+		"foo{variant:b} -> common_dep_1{variant:b}": 1,
+		"foo{variant:b} -> common_dep_2{variant:a}": 1,
+	}
+
+	if !reflect.DeepEqual(finalWant, finalGot) {
+		t.Errorf("want:\n%q\ngot:\n%q", finalWant, finalGot)
+	}
+}
+
+func TestNoCreateVariationsInFinalDeps(t *testing.T) {
+	ctx := NewTestContext()
+
+	checkErr := func() {
+		if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
+			panic("Expected FinalDepsMutators consistency check to fail")
+		}
+	}
+
+	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
+			defer checkErr()
+			ctx.CreateVariations("a", "b")
+		})
+		ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
+			defer checkErr()
+			ctx.CreateLocalVariations("a", "b")
+		})
+	})
+
+	ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+	config := TestConfig(buildDir, nil, `test {name: "foo"}`, nil)
+	ctx.Register(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+}
diff --git a/android/namespace.go b/android/namespace.go
index 64ad7e9..9d7e8ac 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -162,6 +162,12 @@
 	return namespace
 }
 
+// A NamelessModule can never be looked up by name.  It must still implement Name(), but the return
+// value doesn't have to be unique.
+type NamelessModule interface {
+	Nameless()
+}
+
 func (r *NameResolver) NewModule(ctx blueprint.NamespaceContext, moduleGroup blueprint.ModuleGroup, module blueprint.Module) (namespace blueprint.Namespace, errs []error) {
 	// if this module is a namespace, then save it to our list of namespaces
 	newNamespace, ok := module.(*NamespaceModule)
@@ -173,6 +179,10 @@
 		return nil, nil
 	}
 
+	if _, ok := module.(NamelessModule); ok {
+		return nil, nil
+	}
+
 	// if this module is not a namespace, then save it into the appropriate namespace
 	ns := r.findNamespaceFromCtx(ctx)
 
diff --git a/android/package.go b/android/package.go
index ed604c6..077c4a4 100644
--- a/android/package.go
+++ b/android/package.go
@@ -22,7 +22,19 @@
 )
 
 func init() {
-	RegisterModuleType("package", PackageFactory)
+	RegisterPackageBuildComponents(InitRegistrationContext)
+}
+
+// Register the package module type and supporting mutators.
+//
+// This must be called in the correct order (relative to other methods that also
+// register mutators) to match the order of mutator registration in mutator.go.
+// Failing to do so will result in an unrealistic test environment.
+func RegisterPackageBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("package", PackageFactory)
+
+	// Register mutators that are hard coded in to mutator.go.
+	ctx.HardCodedPreArchMutators(RegisterPackageRenamer)
 }
 
 // The information maintained about each package.
diff --git a/android/package_ctx.go b/android/package_ctx.go
index a228910..5a43ea9 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -109,7 +109,7 @@
 		if len(ctx.errors) > 0 {
 			return params, ctx.errors[0]
 		}
-		if (ctx.Config().UseGoma() || ctx.Config().UseRBE()) && params.Pool == nil {
+		if ctx.Config().UseRemoteBuild() && params.Pool == nil {
 			// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by
 			// goma/RBE, restrict jobs to the local parallelism value
 			params.Pool = localPool
@@ -232,16 +232,32 @@
 	}, argNames...)
 }
 
-// RemoteRuleSupports selects if a AndroidRemoteStaticRule supports goma, RBE, or both.
-type RemoteRuleSupports int
+// RBEExperimentalFlag indicates which flag should be set for the AndroidRemoteStaticRule
+// to use RBE.
+type RBEExperimentalFlag int
 
 const (
-	SUPPORTS_NONE = 0
-	SUPPORTS_GOMA = 1 << iota
-	SUPPORTS_RBE  = 1 << iota
-	SUPPORTS_BOTH = SUPPORTS_GOMA | SUPPORTS_RBE
+	// RBE_NOT_EXPERIMENTAL indicates the rule should use RBE in every build that has
+	// UseRBE set.
+	RBE_NOT_EXPERIMENTAL RBEExperimentalFlag = iota
+	// RBE_JAVAC indicates the rule should use RBE only if the RBE_JAVAC variable is
+	// set in an RBE enabled build.
+	RBE_JAVAC
+	// RBE_R8 indicates the rule should use RBE only if the RBE_R8 variable is set in
+	// an RBE enabled build.
+	RBE_R8
+	// RBE_D8 indicates the rule should use RBE only if the RBE_D8 variable is set in
+	// an RBE enabled build.
+	RBE_D8
 )
 
+// RemoteRuleSupports configures rules with whether they have Goma and/or RBE support.
+type RemoteRuleSupports struct {
+	Goma    bool
+	RBE     bool
+	RBEFlag RBEExperimentalFlag
+}
+
 // AndroidRemoteStaticRule wraps blueprint.StaticRule but uses goma or RBE's parallelism if goma or RBE are enabled
 // and the appropriate SUPPORTS_* flag is set.
 func (p PackageContext) AndroidRemoteStaticRule(name string, supports RemoteRuleSupports, params blueprint.RuleParams,
@@ -249,18 +265,30 @@
 
 	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
 		ctx := &configErrorWrapper{p, config.(Config), nil}
-		if ctx.Config().UseGoma() && supports&SUPPORTS_GOMA == 0 {
+		if ctx.Config().UseGoma() && !supports.Goma {
 			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
 			// local parallelism value
 			params.Pool = localPool
 		}
 
-		if ctx.Config().UseRBE() && supports&SUPPORTS_RBE == 0 {
+		if ctx.Config().UseRBE() && !supports.RBE {
 			// When USE_RBE=true is set and the rule is not supported by RBE, restrict jobs to the
 			// local parallelism value
 			params.Pool = localPool
 		}
 
+		if ctx.Config().UseRBE() && supports.RBE {
+			if supports.RBEFlag == RBE_JAVAC && !ctx.Config().UseRBEJAVAC() {
+				params.Pool = localPool
+			}
+			if supports.RBEFlag == RBE_R8 && !ctx.Config().UseRBER8() {
+				params.Pool = localPool
+			}
+			if supports.RBEFlag == RBE_D8 && !ctx.Config().UseRBED8() {
+				params.Pool = localPool
+			}
+		}
+
 		return params, nil
 	}, argNames...)
 }
diff --git a/android/package_test.go b/android/package_test.go
index bc66928..4710e39 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -87,8 +87,7 @@
 	config := TestArchConfig(buildDir, nil, "", fs)
 
 	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("package", PackageFactory)
-	ctx.PreArchMutators(RegisterPackageRenamer)
+	RegisterPackageBuildComponents(ctx)
 	ctx.Register(config)
 
 	_, errs := ctx.ParseBlueprintsFiles(".")
diff --git a/android/paths.go b/android/paths.go
index 02f56d0..66725c6 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -49,6 +49,7 @@
 	InstallInData() bool
 	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
+	InstallInRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -842,10 +843,12 @@
 // OutputPath is a Path representing an intermediates file path rooted from the build directory
 type OutputPath struct {
 	basePath
+	fullPath string
 }
 
 func (p OutputPath) withRel(rel string) OutputPath {
 	p.basePath = p.basePath.withRel(rel)
+	p.fullPath = filepath.Join(p.fullPath, rel)
 	return p
 }
 
@@ -869,7 +872,9 @@
 	if err != nil {
 		reportPathError(ctx, err)
 	}
-	return OutputPath{basePath{path, ctx.Config(), ""}}
+	fullPath := filepath.Join(ctx.Config().buildDir, path)
+	path = fullPath[len(fullPath)-len(path):]
+	return OutputPath{basePath{path, ctx.Config(), ""}, fullPath}
 }
 
 // PathsForOutput returns Paths rooted from buildDir
@@ -884,7 +889,7 @@
 func (p OutputPath) writablePath() {}
 
 func (p OutputPath) String() string {
-	return filepath.Join(p.config.buildDir, p.path)
+	return p.fullPath
 }
 
 // Join creates a new OutputPath with paths... joined with the current path. The
@@ -1254,6 +1259,15 @@
 		partition = "data"
 	} else if ctx.InstallInTestcases() {
 		partition = "testcases"
+	} 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"
diff --git a/android/paths_test.go b/android/paths_test.go
index 46e3e1f..7a32026 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -202,6 +202,7 @@
 	inData         bool
 	inTestcases    bool
 	inSanitizerDir bool
+	inRamdisk      bool
 	inRecovery     bool
 	inRoot         bool
 }
@@ -224,6 +225,10 @@
 	return m.inSanitizerDir
 }
 
+func (m moduleInstallPathContextImpl) InstallInRamdisk() bool {
+	return m.inRamdisk
+}
+
 func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
 	return m.inRecovery
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 2c99f1f..c780cb2 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -36,6 +36,9 @@
 
 var PrebuiltDepTag prebuiltDependencyTag
 
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t prebuiltDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
 type PrebuiltProperties struct {
 	// When prefer is set to true the prebuilt will be used instead of any source module with
 	// a matching name.
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 388d17f..3dea6d8 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -41,6 +41,9 @@
 	// is the same as the file name of the source file.
 	Filename_from_src *bool `android:"arch_variant"`
 
+	// Make this module available when building for ramdisk.
+	Ramdisk_available *bool
+
 	// Make this module available when building for recovery.
 	Recovery_available *bool
 
@@ -69,6 +72,18 @@
 	additionalDependencies *Paths
 }
 
+func (p *PrebuiltEtc) inRamdisk() bool {
+	return p.ModuleBase.InRamdisk() || p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) onlyInRamdisk() bool {
+	return p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) InstallInRamdisk() bool {
+	return p.inRamdisk()
+}
+
 func (p *PrebuiltEtc) inRecovery() bool {
 	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
 }
@@ -86,7 +101,11 @@
 func (p *PrebuiltEtc) ImageMutatorBegin(ctx BaseModuleContext) {}
 
 func (p *PrebuiltEtc) CoreVariantNeeded(ctx BaseModuleContext) bool {
-	return !p.ModuleBase.InstallInRecovery()
+	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx BaseModuleContext) bool {
+	return Bool(p.properties.Ramdisk_available) || p.ModuleBase.InstallInRamdisk()
 }
 
 func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx BaseModuleContext) bool {
@@ -167,6 +186,9 @@
 
 func (p *PrebuiltEtc) AndroidMkEntries() []AndroidMkEntries {
 	nameSuffix := ""
+	if p.inRamdisk() && !p.onlyInRamdisk() {
+		nameSuffix = ".ramdisk"
+	}
 	if p.inRecovery() && !p.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 8648b93..8ff5c40 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -141,10 +141,8 @@
 			config := TestConfig(buildDir, nil, bp, fs)
 
 			ctx := NewTestContext()
-			RegisterPrebuiltMutators(ctx)
+			registerTestPrebuiltBuildComponents(ctx)
 			ctx.RegisterModuleType("filegroup", FileGroupFactory)
-			ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
-			ctx.RegisterModuleType("source", newSourceModule)
 			ctx.Register(config)
 
 			_, errs := ctx.ParseBlueprintsFiles("Android.bp")
@@ -212,6 +210,13 @@
 	}
 }
 
+func registerTestPrebuiltBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
+	ctx.RegisterModuleType("source", newSourceModule)
+
+	RegisterPrebuiltMutators(ctx)
+}
+
 type prebuiltModule struct {
 	ModuleBase
 	prebuilt   Prebuilt
diff --git a/android/register.go b/android/register.go
index b48d3d1..ccfe01e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -102,7 +102,7 @@
 		ctx.RegisterSingletonType(t.name, t.factory)
 	}
 
-	registerMutators(ctx.Context, preArch, preDeps, postDeps)
+	registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
 
 	// Register makevars after other singletons so they can export values through makevars
 	ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc))
@@ -127,8 +127,15 @@
 	RegisterModuleType(name string, factory ModuleFactory)
 	RegisterSingletonType(name string, factory SingletonFactory)
 	PreArchMutators(f RegisterMutatorFunc)
+
+	// Register pre arch mutators that are hard coded into mutator.go.
+	//
+	// Only registers mutators for testing, is a noop on the InitRegistrationContext.
+	HardCodedPreArchMutators(f RegisterMutatorFunc)
+
 	PreDepsMutators(f RegisterMutatorFunc)
 	PostDepsMutators(f RegisterMutatorFunc)
+	FinalDepsMutators(f RegisterMutatorFunc)
 }
 
 // Used to register build components from an init() method, e.g.
@@ -180,6 +187,10 @@
 	PreArchMutators(f)
 }
 
+func (ctx *initRegistrationContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
+	// Nothing to do as the mutators are hard code in preArch in mutator.go
+}
+
 func (ctx *initRegistrationContext) PreDepsMutators(f RegisterMutatorFunc) {
 	PreDepsMutators(f)
 }
@@ -187,3 +198,7 @@
 func (ctx *initRegistrationContext) PostDepsMutators(f RegisterMutatorFunc) {
 	PostDepsMutators(f)
 }
+
+func (ctx *initRegistrationContext) FinalDepsMutators(f RegisterMutatorFunc) {
+	FinalDepsMutators(f)
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 6f04672..b4f144a 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -33,6 +33,8 @@
 	temporariesSet map[WritablePath]bool
 	restat         bool
 	sbox           bool
+	highmem        bool
+	remoteable     RemoteRuleSupports
 	sboxOutDir     WritablePath
 	missingDeps    []string
 }
@@ -87,6 +89,19 @@
 	return r
 }
 
+// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
+// rules.
+func (r *RuleBuilder) HighMem() *RuleBuilder {
+	r.highmem = true
+	return r
+}
+
+// Remoteable marks the rule as supporting remote execution.
+func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
+	r.remoteable = supports
+	return r
+}
+
 // Sbox marks the rule as needing to be wrapped by sbox. The WritablePath should point to the output
 // directory that sbox will wipe. It should not be written to by any other rule. sbox will ensure
 // that all outputs have been written, and will discard any output files that were not specified.
@@ -401,6 +416,17 @@
 		rspFileContent = "$in"
 	}
 
+	var pool blueprint.Pool
+	if ctx.Config().UseGoma() && r.remoteable.Goma {
+		// When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
+	} else if ctx.Config().UseRBE() && r.remoteable.RBE {
+		// When USE_RBE=true is set and the rule is supported by RBE, allow jobs to run outside the local pool.
+	} else if r.highmem {
+		pool = highmemPool
+	} else if ctx.Config().UseRemoteBuild() {
+		pool = localPool
+	}
+
 	ctx.Build(pctx, BuildParams{
 		Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
 			Command:        commandString,
@@ -408,6 +434,7 @@
 			Restat:         r.restat,
 			Rspfile:        rspFile,
 			RspfileContent: rspFileContent,
+			Pool:           pool,
 		}),
 		Inputs:          rspFileInputs,
 		Implicits:       r.Inputs(),
diff --git a/android/singleton.go b/android/singleton.go
index 91268ad..45a9b82 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -128,7 +128,7 @@
 }
 
 func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
-	if (s.Config().UseGoma() || s.Config().UseRBE()) && params.Pool == nil {
+	if s.Config().UseRemoteBuild() && params.Pool == nil {
 		// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
 		// jobs to the local parallelism value
 		params.Pool = localPool
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
new file mode 100644
index 0000000..3e55958
--- /dev/null
+++ b/android/soong_config_modules.go
@@ -0,0 +1,369 @@
+// Copyright 2019 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
+
+// This file provides module types that implement wrapper module types that add conditionals on
+// Soong config variables.
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+	"text/scanner"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android/soongconfig"
+)
+
+func init() {
+	RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
+	RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+	RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+	RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+}
+
+type soongConfigModuleTypeImport struct {
+	ModuleBase
+	properties soongConfigModuleTypeImportProperties
+}
+
+type soongConfigModuleTypeImportProperties struct {
+	From         string
+	Module_types []string
+}
+
+// soong_config_module_type_import imports module types with conditionals on Soong config
+// variables from another Android.bp file.  The imported module type will exist for all
+// modules after the import in the Android.bp file.
+//
+// For example, an Android.bp file could have:
+//
+//     soong_config_module_type_import {
+//         from: "device/acme/Android.bp.bp",
+//         module_types: ["acme_cc_defaults"],
+//     }
+//
+//     acme_cc_defaults {
+//         name: "acme_defaults",
+//         cflags: ["-DGENERIC"],
+//         soong_config_variables: {
+//             board: {
+//                 soc_a: {
+//                     cflags: ["-DSOC_A"],
+//                 },
+//                 soc_b: {
+//                     cflags: ["-DSOC_B"],
+//                 },
+//             },
+//             feature: {
+//                 cflags: ["-DFEATURE"],
+//             },
+//         },
+//     }
+//
+//     cc_library {
+//         name: "libacme_foo",
+//         defaults: ["acme_defaults"],
+//         srcs: ["*.cpp"],
+//     }
+//
+// And device/acme/Android.bp could have:
+//
+//     soong_config_module_type {
+//         name: "acme_cc_defaults",
+//         module_type: "cc_defaults",
+//         config_namespace: "acme",
+//         variables: ["board", "feature"],
+//         properties: ["cflags", "srcs"],
+//     }
+//
+//     soong_config_string_variable {
+//         name: "board",
+//         values: ["soc_a", "soc_b"],
+//     }
+//
+//     soong_config_bool_variable {
+//         name: "feature",
+//     }
+//
+// If an acme BoardConfig.mk file contained:
+//
+//     SOONG_CONFIG_NAMESPACES += acme
+//     SOONG_CONFIG_acme += \
+//         board \
+//         feature \
+//
+//     SOONG_CONFIG_acme_board := soc_a
+//     SOONG_CONFIG_acme_feature := true
+//
+// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
+func soongConfigModuleTypeImportFactory() Module {
+	module := &soongConfigModuleTypeImport{}
+
+	module.AddProperties(&module.properties)
+	AddLoadHook(module, func(ctx LoadHookContext) {
+		importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
+	})
+
+	initAndroidModuleBase(module)
+	return module
+}
+
+func (m *soongConfigModuleTypeImport) Name() string {
+	return "soong_config_module_type_import_" + soongconfig.CanonicalizeToProperty(m.properties.From)
+}
+
+func (*soongConfigModuleTypeImport) Nameless()                                 {}
+func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
+
+// Create dummy modules for soong_config_module_type and soong_config_*_variable
+
+type soongConfigModuleTypeModule struct {
+	ModuleBase
+	properties soongconfig.ModuleTypeProperties
+}
+
+// soong_config_module_type defines module types with conditionals on Soong config
+// variables from another Android.bp file.  The new module type will exist for all
+// modules after the definition in an Android.bp file, and can be imported into other
+// Android.bp files using soong_config_module_type_import.
+//
+// For example, an Android.bp file could have:
+//
+//     soong_config_module_type {
+//         name: "acme_cc_defaults",
+//         module_type: "cc_defaults",
+//         config_namespace: "acme",
+//         variables: ["board", "feature"],
+//         properties: ["cflags", "srcs"],
+//     }
+//
+//     soong_config_string_variable {
+//         name: "board",
+//         values: ["soc_a", "soc_b"],
+//     }
+//
+//     soong_config_bool_variable {
+//         name: "feature",
+//     }
+//
+//     acme_cc_defaults {
+//         name: "acme_defaults",
+//         cflags: ["-DGENERIC"],
+//         soong_config_variables: {
+//             board: {
+//                 soc_a: {
+//                     cflags: ["-DSOC_A"],
+//                 },
+//                 soc_b: {
+//                     cflags: ["-DSOC_B"],
+//                 },
+//             },
+//             feature: {
+//                 cflags: ["-DFEATURE"],
+//             },
+//         },
+//     }
+//
+//     cc_library {
+//         name: "libacme_foo",
+//         defaults: ["acme_defaults"],
+//         srcs: ["*.cpp"],
+//     }
+//
+// And device/acme/Android.bp could have:
+//
+// If an acme BoardConfig.mk file contained:
+//
+//     SOONG_CONFIG_NAMESPACES += acme
+//     SOONG_CONFIG_acme += \
+//         board \
+//         feature \
+//
+//     SOONG_CONFIG_acme_board := soc_a
+//     SOONG_CONFIG_acme_feature := true
+//
+// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
+func soongConfigModuleTypeFactory() Module {
+	module := &soongConfigModuleTypeModule{}
+
+	module.AddProperties(&module.properties)
+
+	AddLoadHook(module, func(ctx LoadHookContext) {
+		// A soong_config_module_type module should implicitly import itself.
+		importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
+	})
+
+	initAndroidModuleBase(module)
+
+	return module
+}
+
+func (m *soongConfigModuleTypeModule) Name() string {
+	return m.properties.Name
+}
+func (*soongConfigModuleTypeModule) Nameless()                                     {}
+func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+type soongConfigStringVariableDummyModule struct {
+	ModuleBase
+	properties       soongconfig.VariableProperties
+	stringProperties soongconfig.StringVariableProperties
+}
+
+type soongConfigBoolVariableDummyModule struct {
+	ModuleBase
+	properties soongconfig.VariableProperties
+}
+
+// soong_config_string_variable defines a variable and a set of possible string values for use
+// in a soong_config_module_type definition.
+func soongConfigStringVariableDummyFactory() Module {
+	module := &soongConfigStringVariableDummyModule{}
+	module.AddProperties(&module.properties, &module.stringProperties)
+	initAndroidModuleBase(module)
+	return module
+}
+
+// soong_config_string_variable defines a variable with true or false values for use
+// in a soong_config_module_type definition.
+func soongConfigBoolVariableDummyFactory() Module {
+	module := &soongConfigBoolVariableDummyModule{}
+	module.AddProperties(&module.properties)
+	initAndroidModuleBase(module)
+	return module
+}
+
+func (m *soongConfigStringVariableDummyModule) Name() string {
+	return m.properties.Name
+}
+func (*soongConfigStringVariableDummyModule) Nameless()                                     {}
+func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+func (m *soongConfigBoolVariableDummyModule) Name() string {
+	return m.properties.Name
+}
+func (*soongConfigBoolVariableDummyModule) Nameless()                                     {}
+func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
+	from = filepath.Clean(from)
+	if filepath.Ext(from) != ".bp" {
+		ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
+		return
+	}
+
+	if strings.HasPrefix(from, "../") {
+		ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
+			from)
+		return
+	}
+
+	moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
+	if moduleTypeDefinitions == nil {
+		return
+	}
+	for _, moduleType := range moduleTypes {
+		if factory, ok := moduleTypeDefinitions[moduleType]; ok {
+			ctx.registerScopedModuleType(moduleType, factory)
+		} else {
+			ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
+				moduleType, from)
+		}
+	}
+}
+
+// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file.  It caches the
+// result so each file is only parsed once.
+func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
+	type onceKeyType string
+	key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
+
+	reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
+		for _, err := range errs {
+			if parseErr, ok := err.(*parser.ParseError); ok {
+				ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
+			} else {
+				ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
+			}
+		}
+	}
+
+	return ctx.Config().Once(key, func() interface{} {
+		r, err := ctx.Config().fs.Open(from)
+		if err != nil {
+			ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
+			return (map[string]blueprint.ModuleFactory)(nil)
+		}
+
+		mtDef, errs := soongconfig.Parse(r, from)
+
+		if len(errs) > 0 {
+			reportErrors(ctx, from, errs...)
+			return (map[string]blueprint.ModuleFactory)(nil)
+		}
+
+		globalModuleTypes := ctx.moduleFactories()
+
+		factories := make(map[string]blueprint.ModuleFactory)
+
+		for name, moduleType := range mtDef.ModuleTypes {
+			factory := globalModuleTypes[moduleType.BaseModuleType]
+			if factory != nil {
+				factories[name] = soongConfigModuleFactory(factory, moduleType)
+			} else {
+				reportErrors(ctx, from,
+					fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
+			}
+		}
+
+		if ctx.Failed() {
+			return (map[string]blueprint.ModuleFactory)(nil)
+		}
+
+		return factories
+	}).(map[string]blueprint.ModuleFactory)
+}
+
+// soongConfigModuleFactory takes an existing soongConfigModuleFactory and a ModuleType and returns
+// a new soongConfigModuleFactory that wraps the existing soongConfigModuleFactory and adds conditional on Soong config
+// variables.
+func soongConfigModuleFactory(factory blueprint.ModuleFactory,
+	moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
+
+	conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
+	if conditionalFactoryProps.IsValid() {
+		return func() (blueprint.Module, []interface{}) {
+			module, props := factory()
+
+			conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
+			props = append(props, conditionalProps.Interface())
+
+			AddLoadHook(module, func(ctx LoadHookContext) {
+				config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
+				for _, ps := range soongconfig.PropertiesToApply(moduleType, conditionalProps, config) {
+					ctx.AppendProperties(ps)
+				}
+			})
+
+			return module, props
+		}
+	} else {
+		return factory
+	}
+}
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
new file mode 100644
index 0000000..66feba8
--- /dev/null
+++ b/android/soong_config_modules_test.go
@@ -0,0 +1,141 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"reflect"
+	"testing"
+)
+
+type soongConfigTestModule struct {
+	ModuleBase
+	props soongConfigTestModuleProperties
+}
+
+type soongConfigTestModuleProperties struct {
+	Cflags []string
+}
+
+func soongConfigTestModuleFactory() Module {
+	m := &soongConfigTestModule{}
+	m.AddProperties(&m.props)
+	InitAndroidModule(m)
+	return m
+}
+
+func (t soongConfigTestModule) GenerateAndroidBuildActions(ModuleContext) {}
+
+func TestSoongConfigModule(t *testing.T) {
+	configBp := `
+		soong_config_module_type {
+			name: "acme_test_defaults",
+			module_type: "test_defaults",
+			config_namespace: "acme",
+			variables: ["board", "feature1", "feature2", "feature3"],
+			properties: ["cflags", "srcs"],
+		}
+
+		soong_config_string_variable {
+			name: "board",
+			values: ["soc_a", "soc_b"],
+		}
+
+		soong_config_bool_variable {
+			name: "feature1",
+		}
+
+		soong_config_bool_variable {
+			name: "feature2",
+		}
+
+		soong_config_bool_variable {
+			name: "feature3",
+		}
+	`
+
+	importBp := `
+		soong_config_module_type_import {
+			from: "SoongConfig.bp",
+			module_types: ["acme_test_defaults"],
+		}
+	`
+
+	bp := `
+		acme_test_defaults {
+			name: "foo",
+			cflags: ["-DGENERIC"],
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+					soc_b: {
+						cflags: ["-DSOC_B"],
+					},
+				},
+				feature1: {
+					cflags: ["-DFEATURE1"],
+				},
+				feature2: {
+					cflags: ["-DFEATURE2"],
+				},
+				feature3: {
+					cflags: ["-DFEATURE3"],
+				},
+			},
+		}
+    `
+
+	run := func(t *testing.T, bp string, fs map[string][]byte) {
+		config := TestConfig(buildDir, nil, bp, fs)
+
+		config.TestProductVariables.VendorVars = map[string]map[string]string{
+			"acme": map[string]string{
+				"board":    "soc_a",
+				"feature1": "true",
+				"feature2": "false",
+				// FEATURE3 unset
+			},
+		}
+
+		ctx := NewTestContext()
+		ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
+		ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+		ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+		ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+		ctx.RegisterModuleType("test_defaults", soongConfigTestModuleFactory)
+		ctx.Register(config)
+
+		_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+		FailIfErrored(t, errs)
+		_, errs = ctx.PrepareBuildActions(config)
+		FailIfErrored(t, errs)
+
+		foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
+		if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) {
+			t.Errorf("wanted foo cflags %q, got %q", w, g)
+		}
+	}
+
+	t.Run("single file", func(t *testing.T) {
+		run(t, configBp+bp, nil)
+	})
+
+	t.Run("import", func(t *testing.T) {
+		run(t, importBp+bp, map[string][]byte{
+			"SoongConfig.bp": []byte(configBp),
+		})
+	})
+}
diff --git a/android/soongconfig/config.go b/android/soongconfig/config.go
new file mode 100644
index 0000000..39a776c
--- /dev/null
+++ b/android/soongconfig/config.go
@@ -0,0 +1,51 @@
+// 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 soongconfig
+
+import "strings"
+
+type SoongConfig interface {
+	// Bool interprets the variable named `name` as a boolean, returning true if, after
+	// lowercasing, it matches one of "1", "y", "yes", "on", or "true". Unset, or any other
+	// value will return false.
+	Bool(name string) bool
+
+	// String returns the string value of `name`. If the variable was not set, it will
+	// return the empty string.
+	String(name string) string
+
+	// IsSet returns whether the variable `name` was set by Make.
+	IsSet(name string) bool
+}
+
+func Config(vars map[string]string) SoongConfig {
+	return soongConfig(vars)
+}
+
+type soongConfig map[string]string
+
+func (c soongConfig) Bool(name string) bool {
+	v := strings.ToLower(c[name])
+	return v == "1" || v == "y" || v == "yes" || v == "on" || v == "true"
+}
+
+func (c soongConfig) String(name string) string {
+	return c[name]
+}
+
+func (c soongConfig) IsSet(name string) bool {
+	_, ok := c[name]
+	return ok
+}
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
new file mode 100644
index 0000000..aa4f5c5
--- /dev/null
+++ b/android/soongconfig/modules.go
@@ -0,0 +1,517 @@
+// 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 soongconfig
+
+import (
+	"fmt"
+	"io"
+	"reflect"
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/proptools"
+)
+
+var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
+
+// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file.  It caches the
+// result so each file is only parsed once.
+func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
+	scope := parser.NewScope(nil)
+	file, errs := parser.ParseAndEval(from, r, scope)
+
+	if len(errs) > 0 {
+		return nil, errs
+	}
+
+	mtDef := &SoongConfigDefinition{
+		ModuleTypes: make(map[string]*ModuleType),
+		variables:   make(map[string]soongConfigVariable),
+	}
+
+	for _, def := range file.Defs {
+		switch def := def.(type) {
+		case *parser.Module:
+			newErrs := processImportModuleDef(mtDef, def)
+
+			if len(newErrs) > 0 {
+				errs = append(errs, newErrs...)
+			}
+
+		case *parser.Assignment:
+			// Already handled via Scope object
+		default:
+			panic("unknown definition type")
+		}
+	}
+
+	if len(errs) > 0 {
+		return nil, errs
+	}
+
+	for name, moduleType := range mtDef.ModuleTypes {
+		for _, varName := range moduleType.variableNames {
+			if v, ok := mtDef.variables[varName]; ok {
+				moduleType.Variables = append(moduleType.Variables, v)
+			} else {
+				return nil, []error{
+					fmt.Errorf("unknown variable %q in module type %q", varName, name),
+				}
+			}
+		}
+	}
+
+	return mtDef, nil
+}
+
+func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
+	switch def.Type {
+	case "soong_config_module_type":
+		return processModuleTypeDef(v, def)
+	case "soong_config_string_variable":
+		return processStringVariableDef(v, def)
+	case "soong_config_bool_variable":
+		return processBoolVariableDef(v, def)
+	default:
+		// Unknown module types will be handled when the file is parsed as a normal
+		// Android.bp file.
+	}
+
+	return nil
+}
+
+type ModuleTypeProperties struct {
+	// the name of the new module type.  Unlike most modules, this name does not need to be unique,
+	// although only one module type with any name will be importable into an Android.bp file.
+	Name string
+
+	// the module type that this module type will extend.
+	Module_type string
+
+	// the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
+	// configuration variables from.
+	Config_namespace string
+
+	// the list of SOONG_CONFIG variables that this module type will read
+	Variables []string
+
+	// the list of properties that this module type will extend.
+	Properties []string
+}
+
+func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
+
+	props := &ModuleTypeProperties{}
+
+	_, errs = proptools.UnpackProperties(def.Properties, props)
+	if len(errs) > 0 {
+		return errs
+	}
+
+	if props.Name == "" {
+		errs = append(errs, fmt.Errorf("name property must be set"))
+	}
+
+	if props.Config_namespace == "" {
+		errs = append(errs, fmt.Errorf("config_namespace property must be set"))
+	}
+
+	if props.Module_type == "" {
+		errs = append(errs, fmt.Errorf("module_type property must be set"))
+	}
+
+	if len(errs) > 0 {
+		return errs
+	}
+
+	mt := &ModuleType{
+		affectableProperties: props.Properties,
+		ConfigNamespace:      props.Config_namespace,
+		BaseModuleType:       props.Module_type,
+		variableNames:        props.Variables,
+	}
+	v.ModuleTypes[props.Name] = mt
+
+	return nil
+}
+
+type VariableProperties struct {
+	Name string
+}
+
+type StringVariableProperties struct {
+	Values []string
+}
+
+func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
+	stringProps := &StringVariableProperties{}
+
+	base, errs := processVariableDef(def, stringProps)
+	if len(errs) > 0 {
+		return errs
+	}
+
+	if len(stringProps.Values) == 0 {
+		return []error{fmt.Errorf("values property must be set")}
+	}
+
+	v.variables[base.variable] = &stringVariable{
+		baseVariable: base,
+		values:       CanonicalizeToProperties(stringProps.Values),
+	}
+
+	return nil
+}
+
+func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
+	base, errs := processVariableDef(def)
+	if len(errs) > 0 {
+		return errs
+	}
+
+	v.variables[base.variable] = &boolVariable{
+		baseVariable: base,
+	}
+
+	return nil
+}
+
+func processVariableDef(def *parser.Module,
+	extraProps ...interface{}) (cond baseVariable, errs []error) {
+
+	props := &VariableProperties{}
+
+	allProps := append([]interface{}{props}, extraProps...)
+
+	_, errs = proptools.UnpackProperties(def.Properties, allProps...)
+	if len(errs) > 0 {
+		return baseVariable{}, errs
+	}
+
+	if props.Name == "" {
+		return baseVariable{}, []error{fmt.Errorf("name property must be set")}
+	}
+
+	return baseVariable{
+		variable: props.Name,
+	}, nil
+}
+
+type SoongConfigDefinition struct {
+	ModuleTypes map[string]*ModuleType
+
+	variables map[string]soongConfigVariable
+}
+
+// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
+// property layout for the Soong config variables, with each possible value an interface{} that
+// contains a nil pointer to another newly constructed type that contains the affectable properties.
+// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
+//
+// For example, the acme_cc_defaults example above would
+// produce a reflect.Value whose type is:
+// *struct {
+//     Soong_config_variables struct {
+//         Board struct {
+//             Soc_a interface{}
+//             Soc_b interface{}
+//         }
+//     }
+// }
+// And whose value is:
+// &{
+//     Soong_config_variables: {
+//         Board: {
+//             Soc_a: (*struct{ Cflags []string })(nil),
+//             Soc_b: (*struct{ Cflags []string })(nil),
+//         },
+//     },
+// }
+func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
+	var fields []reflect.StructField
+
+	_, factoryProps := factory()
+	affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
+	if affectablePropertiesType == nil {
+		return reflect.Value{}
+	}
+
+	for _, c := range moduleType.Variables {
+		fields = append(fields, reflect.StructField{
+			Name: proptools.FieldNameForProperty(c.variableProperty()),
+			Type: c.variableValuesType(),
+		})
+	}
+
+	typ := reflect.StructOf([]reflect.StructField{{
+		Name: soongConfigProperty,
+		Type: reflect.StructOf(fields),
+	}})
+
+	props := reflect.New(typ)
+	structConditions := props.Elem().FieldByName(soongConfigProperty)
+
+	for i, c := range moduleType.Variables {
+		c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
+	}
+
+	return props
+}
+
+// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
+// that exists in factoryProps.
+func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
+	affectableProperties = append([]string(nil), affectableProperties...)
+	sort.Strings(affectableProperties)
+
+	var recurse func(prefix string, aps []string) ([]string, reflect.Type)
+	recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
+		var fields []reflect.StructField
+
+		for len(affectableProperties) > 0 {
+			p := affectableProperties[0]
+			if !strings.HasPrefix(affectableProperties[0], prefix) {
+				break
+			}
+			affectableProperties = affectableProperties[1:]
+
+			nestedProperty := strings.TrimPrefix(p, prefix)
+			if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
+				var nestedType reflect.Type
+				nestedPrefix := nestedProperty[:i+1]
+
+				affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
+
+				if nestedType != nil {
+					nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
+
+					fields = append(fields, reflect.StructField{
+						Name: nestedFieldName,
+						Type: nestedType,
+					})
+				}
+			} else {
+				typ := typeForPropertyFromPropertyStructs(factoryProps, p)
+				if typ != nil {
+					fields = append(fields, reflect.StructField{
+						Name: proptools.FieldNameForProperty(nestedProperty),
+						Type: typ,
+					})
+				}
+			}
+		}
+
+		var typ reflect.Type
+		if len(fields) > 0 {
+			typ = reflect.StructOf(fields)
+		}
+		return affectableProperties, typ
+	}
+
+	affectableProperties, typ := recurse("", affectableProperties)
+	if len(affectableProperties) > 0 {
+		panic(fmt.Errorf("didn't handle all affectable properties"))
+	}
+
+	if typ != nil {
+		return reflect.PtrTo(typ)
+	}
+
+	return nil
+}
+
+func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
+	for _, ps := range psList {
+		if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
+			return typ
+		}
+	}
+
+	return nil
+}
+
+func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
+	v := reflect.ValueOf(ps)
+	for len(property) > 0 {
+		if !v.IsValid() {
+			return nil
+		}
+
+		if v.Kind() == reflect.Interface {
+			if v.IsNil() {
+				return nil
+			} else {
+				v = v.Elem()
+			}
+		}
+
+		if v.Kind() == reflect.Ptr {
+			if v.IsNil() {
+				v = reflect.Zero(v.Type().Elem())
+			} else {
+				v = v.Elem()
+			}
+		}
+
+		if v.Kind() != reflect.Struct {
+			return nil
+		}
+
+		if index := strings.IndexRune(property, '.'); index >= 0 {
+			prefix := property[:index]
+			property = property[index+1:]
+
+			v = v.FieldByName(proptools.FieldNameForProperty(prefix))
+		} else {
+			f := v.FieldByName(proptools.FieldNameForProperty(property))
+			if !f.IsValid() {
+				return nil
+			}
+			return f.Type()
+		}
+	}
+	return nil
+}
+
+// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
+// based on SoongConfig values.
+func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) []interface{} {
+	var ret []interface{}
+	props = props.Elem().FieldByName(soongConfigProperty)
+	for i, c := range moduleType.Variables {
+		if ps := c.PropertiesToApply(config, props.Field(i)); ps != nil {
+			ret = append(ret, ps)
+		}
+	}
+	return ret
+}
+
+type ModuleType struct {
+	BaseModuleType  string
+	ConfigNamespace string
+	Variables       []soongConfigVariable
+
+	affectableProperties []string
+	variableNames        []string
+}
+
+type soongConfigVariable interface {
+	// variableProperty returns the name of the variable.
+	variableProperty() string
+
+	// conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
+	variableValuesType() reflect.Type
+
+	// initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
+	// reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
+	// the zero value of the affectable properties type.
+	initializeProperties(v reflect.Value, typ reflect.Type)
+
+	// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
+	// to the module.
+	PropertiesToApply(config SoongConfig, values reflect.Value) interface{}
+}
+
+type baseVariable struct {
+	variable string
+}
+
+func (c *baseVariable) variableProperty() string {
+	return CanonicalizeToProperty(c.variable)
+}
+
+type stringVariable struct {
+	baseVariable
+	values []string
+}
+
+func (s *stringVariable) variableValuesType() reflect.Type {
+	var fields []reflect.StructField
+
+	for _, v := range s.values {
+		fields = append(fields, reflect.StructField{
+			Name: proptools.FieldNameForProperty(v),
+			Type: emptyInterfaceType,
+		})
+	}
+
+	return reflect.StructOf(fields)
+}
+
+func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
+	for i := range s.values {
+		v.Field(i).Set(reflect.Zero(typ))
+	}
+}
+
+func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
+	for j, v := range s.values {
+		if config.String(s.variable) == v {
+			return values.Field(j).Interface()
+		}
+	}
+
+	return nil
+}
+
+type boolVariable struct {
+	baseVariable
+}
+
+func (b boolVariable) variableValuesType() reflect.Type {
+	return emptyInterfaceType
+}
+
+func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
+	v.Set(reflect.Zero(typ))
+}
+
+func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
+	if config.Bool(b.variable) {
+		return values.Interface()
+	}
+
+	return nil
+}
+
+func CanonicalizeToProperty(v string) string {
+	return strings.Map(func(r rune) rune {
+		switch {
+		case r >= 'A' && r <= 'Z',
+			r >= 'a' && r <= 'z',
+			r >= '0' && r <= '9',
+			r == '_':
+			return r
+		default:
+			return '_'
+		}
+	}, v)
+}
+
+func CanonicalizeToProperties(values []string) []string {
+	ret := make([]string, len(values))
+	for i, v := range values {
+		ret[i] = CanonicalizeToProperty(v)
+	}
+	return ret
+}
+
+type emptyInterfaceStruct struct {
+	i interface{}
+}
+
+var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
new file mode 100644
index 0000000..4190016
--- /dev/null
+++ b/android/soongconfig/modules_test.go
@@ -0,0 +1,249 @@
+// 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 soongconfig
+
+import (
+	"reflect"
+	"testing"
+)
+
+func Test_CanonicalizeToProperty(t *testing.T) {
+	tests := []struct {
+		name string
+		arg  string
+		want string
+	}{
+		{
+			name: "lowercase",
+			arg:  "board",
+			want: "board",
+		},
+		{
+			name: "uppercase",
+			arg:  "BOARD",
+			want: "BOARD",
+		},
+		{
+			name: "numbers",
+			arg:  "BOARD123",
+			want: "BOARD123",
+		},
+		{
+			name: "underscore",
+			arg:  "TARGET_BOARD",
+			want: "TARGET_BOARD",
+		},
+		{
+			name: "dash",
+			arg:  "TARGET-BOARD",
+			want: "TARGET_BOARD",
+		},
+		{
+			name: "unicode",
+			arg:  "boardλ",
+			want: "board_",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := CanonicalizeToProperty(tt.arg); got != tt.want {
+				t.Errorf("canonicalizeToProperty() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_typeForPropertyFromPropertyStruct(t *testing.T) {
+	tests := []struct {
+		name     string
+		ps       interface{}
+		property string
+		want     string
+	}{
+		{
+			name: "string",
+			ps: struct {
+				A string
+			}{},
+			property: "a",
+			want:     "string",
+		},
+		{
+			name: "list",
+			ps: struct {
+				A []string
+			}{},
+			property: "a",
+			want:     "[]string",
+		},
+		{
+			name: "missing",
+			ps: struct {
+				A []string
+			}{},
+			property: "b",
+			want:     "",
+		},
+		{
+			name: "nested",
+			ps: struct {
+				A struct {
+					B string
+				}
+			}{},
+			property: "a.b",
+			want:     "string",
+		},
+		{
+			name: "missing nested",
+			ps: struct {
+				A struct {
+					B string
+				}
+			}{},
+			property: "a.c",
+			want:     "",
+		},
+		{
+			name: "not a struct",
+			ps: struct {
+				A string
+			}{},
+			property: "a.b",
+			want:     "",
+		},
+		{
+			name: "nested pointer",
+			ps: struct {
+				A *struct {
+					B string
+				}
+			}{},
+			property: "a.b",
+			want:     "string",
+		},
+		{
+			name: "nested interface",
+			ps: struct {
+				A interface{}
+			}{
+				A: struct {
+					B string
+				}{},
+			},
+			property: "a.b",
+			want:     "string",
+		},
+		{
+			name: "nested interface pointer",
+			ps: struct {
+				A interface{}
+			}{
+				A: &struct {
+					B string
+				}{},
+			},
+			property: "a.b",
+			want:     "string",
+		},
+		{
+			name: "nested interface nil pointer",
+			ps: struct {
+				A interface{}
+			}{
+				A: (*struct {
+					B string
+				})(nil),
+			},
+			property: "a.b",
+			want:     "string",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			typ := typeForPropertyFromPropertyStruct(tt.ps, tt.property)
+			got := ""
+			if typ != nil {
+				got = typ.String()
+			}
+			if got != tt.want {
+				t.Errorf("typeForPropertyFromPropertyStruct() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_createAffectablePropertiesType(t *testing.T) {
+	tests := []struct {
+		name                 string
+		affectableProperties []string
+		factoryProps         interface{}
+		want                 string
+	}{
+		{
+			name:                 "string",
+			affectableProperties: []string{"cflags"},
+			factoryProps: struct {
+				Cflags string
+			}{},
+			want: "*struct { Cflags string }",
+		},
+		{
+			name:                 "list",
+			affectableProperties: []string{"cflags"},
+			factoryProps: struct {
+				Cflags []string
+			}{},
+			want: "*struct { Cflags []string }",
+		},
+		{
+			name:                 "string pointer",
+			affectableProperties: []string{"cflags"},
+			factoryProps: struct {
+				Cflags *string
+			}{},
+			want: "*struct { Cflags *string }",
+		},
+		{
+			name:                 "subset",
+			affectableProperties: []string{"cflags"},
+			factoryProps: struct {
+				Cflags  string
+				Ldflags string
+			}{},
+			want: "*struct { Cflags string }",
+		},
+		{
+			name:                 "none",
+			affectableProperties: []string{"cflags"},
+			factoryProps: struct {
+				Ldflags string
+			}{},
+			want: "",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			typ := createAffectablePropertiesType(tt.affectableProperties, []interface{}{tt.factoryProps})
+			got := ""
+			if typ != nil {
+				got = typ.String()
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("createAffectablePropertiesType() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/android/testing.go b/android/testing.go
index 6663728..9aff039 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -50,15 +50,20 @@
 
 type TestContext struct {
 	*Context
-	preArch, preDeps, postDeps []RegisterMutatorFunc
-	NameResolver               *NameResolver
-	config                     Config
+	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
+	NameResolver                          *NameResolver
+	config                                Config
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
 	ctx.preArch = append(ctx.preArch, f)
 }
 
+func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
+	// Register mutator function as normal for testing.
+	ctx.PreArchMutators(f)
+}
+
 func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) {
 	ctx.preDeps = append(ctx.preDeps, f)
 }
@@ -67,12 +72,16 @@
 	ctx.postDeps = append(ctx.postDeps, f)
 }
 
+func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
+	ctx.finalDeps = append(ctx.finalDeps, f)
+}
+
 func (ctx *TestContext) Register(config Config) {
 	ctx.SetFs(config.fs)
 	if config.mockBpList != "" {
 		ctx.SetModuleListFile(config.mockBpList)
 	}
-	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
+	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
 
 	ctx.RegisterSingletonType("env", EnvSingleton)
 
diff --git a/android/variable.go b/android/variable.go
index 2bf84dd..51306ad 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -43,8 +43,10 @@
 		} `android:"arch_variant"`
 
 		Malloc_not_svelte struct {
-			Cflags      []string `android:"arch_variant"`
-			Shared_libs []string `android:"arch_variant"`
+			Cflags              []string `android:"arch_variant"`
+			Shared_libs         []string `android:"arch_variant"`
+			Whole_static_libs   []string `android:"arch_variant"`
+			Exclude_static_libs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 
 		Safestack struct {
@@ -204,6 +206,9 @@
 	Binder32bit                      *bool `json:",omitempty"`
 	UseGoma                          *bool `json:",omitempty"`
 	UseRBE                           *bool `json:",omitempty"`
+	UseRBEJAVAC                      *bool `json:",omitempty"`
+	UseRBER8                         *bool `json:",omitempty"`
+	UseRBED8                         *bool `json:",omitempty"`
 	Debuggable                       *bool `json:",omitempty"`
 	Eng                              *bool `json:",omitempty"`
 	Treble_linker_namespaces         *bool `json:",omitempty"`
@@ -243,6 +248,7 @@
 	TidyChecks *string `json:",omitempty"`
 
 	NativeCoverage       *bool    `json:",omitempty"`
+	ClangCoverage        *bool    `json:",omitempty"`
 	CoveragePaths        []string `json:",omitempty"`
 	CoverageExcludePaths []string `json:",omitempty"`
 
@@ -316,6 +322,8 @@
 	EnforceProductPartitionInterface *bool `json:",omitempty"`
 
 	InstallExtraFlattenedApexes *bool `json:",omitempty"`
+
+	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/android/variable_test.go b/android/variable_test.go
index 451d43d..cde2b1a 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -148,7 +148,7 @@
 		clonedProps := proptools.CloneProperties(reflect.ValueOf(props)).Interface()
 		m.AddProperties(clonedProps)
 
-		// Set a default variableProperties, this will be used as the input to the property struct filter
+		// Set a default soongConfigVariableProperties, this will be used as the input to the property struct filter
 		// for this test module.
 		m.variableProperties = testProductVariableProperties
 		InitAndroidModule(m)
@@ -159,15 +159,15 @@
 func TestProductVariables(t *testing.T) {
 	ctx := NewTestContext()
 	// A module type that has a srcs property but not a cflags property.
-	ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(struct {
+	ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct {
 		Srcs []string
 	}{}))
 	// A module type that has a cflags property but not a srcs property.
-	ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(struct {
+	ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct {
 		Cflags []string
 	}{}))
 	// A module type that does not have any properties that match product_variables.
-	ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(struct {
+	ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
 		Foo []string
 	}{}))
 	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
diff --git a/android/visibility.go b/android/visibility.go
index c28ec93..a597687 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -19,6 +19,8 @@
 	"regexp"
 	"strings"
 	"sync"
+
+	"github.com/google/blueprint"
 )
 
 // Enforces visibility rules between modules.
@@ -190,6 +192,15 @@
 	}).(*sync.Map)
 }
 
+// Marker interface that identifies dependencies that are excluded from visibility
+// enforcement.
+type ExcludeFromVisibilityEnforcementTag interface {
+	blueprint.DependencyTag
+
+	// Method that differentiates this interface from others.
+	ExcludeFromVisibilityEnforcement()
+}
+
 // The rule checker needs to be registered before defaults expansion to correctly check that
 // //visibility:xxx isn't combined with other packages in the same list in any one module.
 func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) {
@@ -389,6 +400,12 @@
 
 	// Visit all the dependencies making sure that this module has access to them all.
 	ctx.VisitDirectDeps(func(dep Module) {
+		// Ignore dependencies that have an ExcludeFromVisibilityEnforcementTag
+		tag := ctx.OtherModuleDependencyTag(dep)
+		if _, ok := tag.(ExcludeFromVisibilityEnforcementTag); ok {
+			return
+		}
+
 		depName := ctx.OtherModuleName(dep)
 		depDir := ctx.OtherModuleDir(dep)
 		depQualified := qualifiedModuleName{depDir, depName}
diff --git a/android/visibility_test.go b/android/visibility_test.go
index fbf2fb7..6006072 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -853,6 +853,51 @@
 				` not visible to this module`,
 		},
 	},
+	{
+		name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)",
+		fs: map[string][]byte{
+			"prebuilts/Blueprints": []byte(`
+				prebuilt {
+					name: "module",
+					visibility: ["//top/other"],
+				}`),
+			"top/sources/source_file": nil,
+			"top/sources/Blueprints": []byte(`
+				source {
+					name: "module",
+					visibility: ["//top/other"],
+				}`),
+			"top/other/source_file": nil,
+			"top/other/Blueprints": []byte(`
+				source {
+					name: "other",
+					deps: [":module"],
+				}`),
+		},
+	},
+	{
+		name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)",
+		fs: map[string][]byte{
+			"prebuilts/Blueprints": []byte(`
+				prebuilt {
+					name: "module",
+					visibility: ["//top/other"],
+					prefer: true,
+				}`),
+			"top/sources/source_file": nil,
+			"top/sources/Blueprints": []byte(`
+				source {
+					name: "module",
+					visibility: ["//top/other"],
+				}`),
+			"top/other/source_file": nil,
+			"top/other/Blueprints": []byte(`
+				source {
+					name: "other",
+					deps: [":module"],
+				}`),
+		},
+	},
 }
 
 func TestVisibility(t *testing.T) {
@@ -871,10 +916,12 @@
 	config := TestArchConfig(buildDir, nil, "", fs)
 
 	ctx := NewTestArchContext()
-	ctx.RegisterModuleType("package", PackageFactory)
 	ctx.RegisterModuleType("mock_library", newMockLibraryModule)
 	ctx.RegisterModuleType("mock_defaults", defaultsFactory)
-	ctx.PreArchMutators(RegisterPackageRenamer)
+
+	// Order of the following method calls is significant.
+	RegisterPackageBuildComponents(ctx)
+	registerTestPrebuiltBuildComponents(ctx)
 	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
 	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index b2fe8da..8929910 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -52,13 +52,39 @@
 		return moduleNames
 	}
 
+	var postInstallCommands []string
+	for _, fi := range a.filesInfo {
+		if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
+			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
+			linkTarget := filepath.Join("/system", fi.Path())
+			linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexName, fi.Path())
+			mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
+			linkCmd := "ln -sfn " + linkTarget + " " + linkPath
+			postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
+		}
+	}
+
 	for _, fi := range a.filesInfo {
 		if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
 			continue
 		}
 
-		if !android.InList(fi.moduleName, moduleNames) {
-			moduleNames = append(moduleNames, fi.moduleName)
+		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform()
+
+		var moduleName string
+		if linkToSystemLib {
+			moduleName = fi.moduleName
+		} else {
+			moduleName = fi.moduleName + "." + apexName + a.suffix
+		}
+
+		if !android.InList(moduleName, moduleNames) {
+			moduleNames = append(moduleNames, moduleName)
+		}
+
+		if linkToSystemLib {
+			// No need to copy the file since it's linked to the system file
+			continue
 		}
 
 		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
@@ -67,7 +93,7 @@
 		} else {
 			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 		}
-		fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
+		fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
 		// /apex/<apex_name>/{lib|framework|...}
 		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
 		if apexType == flattenedApex {
@@ -86,6 +112,11 @@
 			}
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
+
+			// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
+			// We don't need to have notice file for the individual modules in it. Otherwise,
+			// we will have duplicated notice entries.
+			fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
 		}
 		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
 		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
@@ -132,6 +163,7 @@
 			fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
 		} else if fi.class == 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
 			// we will have foo.apk.apk
@@ -151,18 +183,23 @@
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
-			if a.primaryApexType && fi.builtFile == a.manifestPbOut {
-				// Make apex_manifest.pb module for this APEX to override all other
-				// modules in the APEXes being overridden by this APEX
-				var patterns []string
-				for _, o := range a.overridableProperties.Overrides {
-					patterns = append(patterns, "%."+o+a.suffix)
-				}
-				fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
+			if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
+				if a.primaryApexType {
+					// Make apex_manifest.pb module for this APEX to override all other
+					// modules in the APEXes being overridden by this APEX
+					var patterns []string
+					for _, o := range a.overridableProperties.Overrides {
+						patterns = append(patterns, "%."+o+a.suffix)
+					}
+					fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
 
-				if len(a.compatSymlinks) > 0 {
-					// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
-					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
+					if len(a.compatSymlinks) > 0 {
+						// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
+						postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
+					}
+				}
+				if len(postInstallCommands) > 0 {
+					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
 				}
 			}
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
@@ -240,11 +277,24 @@
 				if len(postInstallCommands) > 0 {
 					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
 				}
+
+				if a.mergedNotices.Merged.Valid() {
+					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
+				}
+
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 
 				if apexType == imageApex {
 					fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
 				}
+
+				if a.installedFilesFile != nil {
+					goal := "droidcore"
+					distFile := name + "-installed-files.txt"
+					fmt.Fprintln(w, ".PHONY:", goal)
+					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+						goal, a.installedFilesFile.String(), distFile)
+				}
 			}
 		}}
 }
diff --git a/apex/apex.go b/apex/apex.go
index 7d6b7f0..48cdedf 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -57,8 +57,249 @@
 	certificateTag = dependencyTag{name: "certificate"}
 	usesTag        = dependencyTag{name: "uses"}
 	androidAppTag  = dependencyTag{name: "androidApp"}
+	apexAvailWl    = makeApexAvailableWhitelist()
 )
 
+// This is a map from apex to modules, which overrides the
+// apex_available setting for that particular module to make
+// it available for the apex regardless of its setting.
+// TODO(b/147364041): remove this
+func makeApexAvailableWhitelist() map[string][]string {
+	// The "Module separator"s below are employed to minimize merge conflicts.
+	m := make(map[string][]string)
+	//
+	// Module separator
+	//
+	m["com.android.adbd"] = []string{"adbd", "libcrypto"}
+	//
+	// Module separator
+	//
+	m["com.android.art"] = []string{
+		"jacocoagent",
+		"libadbconnection_server",
+		"libartd-disassembler",
+		"libbacktrace",
+		"libbase",
+		"libc++",
+		"libcrypto",
+		"libdexfile_support",
+		"libexpat",
+		"libicuuc",
+		"liblzma",
+		"libmeminfo",
+		"libprocinfo",
+		"libunwindstack",
+		"libvixl",
+		"libvixld",
+		"libz",
+		"libziparchive",
+		"prebuilt_libclang_rt",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.bluetooth.updatable"] = []string{
+		"android.hardware.audio.common@5.0",
+		"android.hardware.bluetooth@1.0",
+		"android.hardware.bluetooth@1.1",
+		"android.hardware.bluetooth.a2dp@1.0",
+		"android.hardware.bluetooth.audio@2.0",
+		"android.hidl.safe_union@1.0",
+		"libbase",
+		"libbinderthreadstate",
+		"libbluetooth",
+		"libbluetooth_jni",
+		"libc++",
+		"libchrome",
+		"libcrypto",
+		"libcutils",
+		"libevent",
+		"libfmq",
+		"libhidlbase",
+		"libprocessgroup",
+		"libprotobuf-cpp-lite",
+		"libstatslog",
+		"libtinyxml2",
+		"libutils",
+		"libz",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.conscrypt"] = []string{"boringssl_self_test", "libc++", "libcrypto", "libssl"}
+	//
+	// Module separator
+	//
+	m["com.android.cronet"] = []string{"org.chromium.net.cronet", "prebuilt_libcronet.80.0.3986.0"}
+	//
+	// Module separator
+	//
+	m["com.android.media"] = []string{
+		"android.hardware.cas@1.0",
+		"android.hardware.cas.native@1.0",
+		"android.hidl.allocator@1.0",
+		"android.hidl.memory@1.0",
+		"android.hidl.memory.token@1.0",
+		"android.hidl.token@1.0",
+		"android.hidl.token@1.0-utils",
+		"libaacextractor",
+		"libamrextractor",
+		"libbase",
+		"libbinderthreadstate",
+		"libc++",
+		"libcrypto",
+		"libcutils",
+		"libflacextractor",
+		"libhidlbase",
+		"libhidlmemory",
+		"libmidiextractor",
+		"libmkvextractor",
+		"libmp3extractor",
+		"libmp4extractor",
+		"libmpeg2extractor",
+		"liboggextractor",
+		"libprocessgroup",
+		"libutils",
+		"libwavextractor",
+		"updatable-media",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.media.swcodec"] = []string{
+		"android.frameworks.bufferhub@1.0",
+		"android.hardware.configstore@1.0",
+		"android.hardware.configstore@1.1",
+		"android.hardware.configstore-utils",
+		"android.hardware.graphics.allocator@2.0",
+		"android.hardware.graphics.allocator@3.0",
+		"android.hardware.graphics.bufferqueue@1.0",
+		"android.hardware.graphics.bufferqueue@2.0",
+		"android.hardware.graphics.common@1.0",
+		"android.hardware.graphics.common@1.1",
+		"android.hardware.graphics.common@1.2",
+		"android.hardware.graphics.mapper@2.0",
+		"android.hardware.graphics.mapper@2.1",
+		"android.hardware.graphics.mapper@3.0",
+		"android.hardware.media@1.0",
+		"android.hardware.media.bufferpool@2.0",
+		"android.hardware.media.c2@1.0",
+		"android.hardware.media.omx@1.0",
+		"android.hidl.memory@1.0",
+		"android.hidl.memory.token@1.0",
+		"android.hidl.safe_union@1.0",
+		"android.hidl.token@1.0",
+		"android.hidl.token@1.0-utils",
+		"libavservices_minijail",
+		"libbacktrace",
+		"libbase",
+		"libbinderthreadstate",
+		"libc++",
+		"libcap",
+		"libcodec2",
+		"libcodec2_hidl@1.0",
+		"libcodec2_soft_aacdec",
+		"libcodec2_soft_aacenc",
+		"libcodec2_soft_amrnbdec",
+		"libcodec2_soft_amrnbenc",
+		"libcodec2_soft_amrwbdec",
+		"libcodec2_soft_amrwbenc",
+		"libcodec2_soft_av1dec_gav1",
+		"libcodec2_soft_avcdec",
+		"libcodec2_soft_avcenc",
+		"libcodec2_soft_common",
+		"libcodec2_soft_flacdec",
+		"libcodec2_soft_flacenc",
+		"libcodec2_soft_g711alawdec",
+		"libcodec2_soft_g711mlawdec",
+		"libcodec2_soft_gsmdec",
+		"libcodec2_soft_h263dec",
+		"libcodec2_soft_h263enc",
+		"libcodec2_soft_hevcdec",
+		"libcodec2_soft_hevcenc",
+		"libcodec2_soft_mp3dec",
+		"libcodec2_soft_mpeg2dec",
+		"libcodec2_soft_mpeg4dec",
+		"libcodec2_soft_mpeg4enc",
+		"libcodec2_soft_opusdec",
+		"libcodec2_soft_opusenc",
+		"libcodec2_soft_rawdec",
+		"libcodec2_soft_vorbisdec",
+		"libcodec2_soft_vp8dec",
+		"libcodec2_soft_vp8enc",
+		"libcodec2_soft_vp9dec",
+		"libcodec2_soft_vp9enc",
+		"libcodec2_vndk",
+		"libc_scudo",
+		"libcutils",
+		"libdexfile_support",
+		"libEGL",
+		"libfmq",
+		"libgraphicsenv",
+		"libhardware",
+		"libhidlbase",
+		"libhidlmemory",
+		"libion",
+		"liblzma",
+		"libmedia_codecserviceregistrant",
+		"libminijail",
+		"libnativebridge_lazy",
+		"libnativeloader_lazy",
+		"libopus",
+		"libprocessgroup",
+		"libscudo_wrapper",
+		"libsfplugin_ccodec_utils",
+		"libstagefright_amrnb_common",
+		"libstagefright_bufferpool@2.0.1",
+		"libstagefright_bufferqueue_helper",
+		"libstagefright_enc_common",
+		"libstagefright_flacdec",
+		"libstagefright_foundation",
+		"libsync",
+		"libui",
+		"libunwindstack",
+		"libutils",
+		"libvorbisidec",
+		"libvpx",
+		"mediaswcodec",
+		"prebuilt_libclang_rt",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.runtime"] = []string{
+		"libbase",
+		"libc++",
+		"libdexfile_support",
+		"liblzma",
+		"libunwindstack",
+		"prebuilt_libclang_rt",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.resolv"] = []string{"libcrypto", "libnetd_resolv", "libssl"}
+	//
+	// Module separator
+	//
+	m["com.android.tethering"] = []string{"libbase", "libc++", "libnativehelper_compat_libc++"}
+	//
+	// Module separator
+	//
+	m["com.android.vndk"] = []string{
+		"libbacktrace",
+		"libbinderthreadstate",
+		"libblas",
+		"libcompiler_rt",
+		"libgui",
+		"libunwind",
+	}
+	//
+	// Module separator
+	//
+	return m
+}
+
 func init() {
 	android.RegisterModuleType("apex", BundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
@@ -92,7 +333,7 @@
 // Mark the direct and transitive dependencies of apex bundles so that they
 // can be built for the apex bundles.
 func apexDepsMutator(mctx android.BottomUpMutatorContext) {
-	if a, ok := mctx.Module().(*apexBundle); ok {
+	if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
 		apexBundleName := mctx.ModuleName()
 		mctx.WalkDeps(func(child, parent android.Module) bool {
 			depName := mctx.OtherModuleName(child)
@@ -120,7 +361,7 @@
 func apexMutator(mctx android.BottomUpMutatorContext) {
 	if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
 		am.CreateApexVariations(mctx)
-	} else if _, ok := mctx.Module().(*apexBundle); ok {
+	} else if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
 		// apex bundle itself is mutated so that it and its modules have same
 		// apex variant.
 		apexBundleName := mctx.ModuleName()
@@ -468,7 +709,8 @@
 	targetRequiredModuleNames []string
 	hostRequiredModuleNames   []string
 
-	jacocoReportClassesFile android.Path // only for javalibs and apps
+	jacocoReportClassesFile android.Path     // only for javalibs and apps
+	certificate             java.Certificate // only for apps
 }
 
 func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
@@ -492,6 +734,30 @@
 	return af.builtFile != nil && af.builtFile.String() != ""
 }
 
+// Path() returns path of this apex file relative to the APEX root
+func (af *apexFile) Path() string {
+	return filepath.Join(af.installDir, af.builtFile.Base())
+}
+
+// SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
+func (af *apexFile) SymlinkPaths() []string {
+	var ret []string
+	for _, symlink := range af.symlinks {
+		ret = append(ret, filepath.Join(af.installDir, symlink))
+	}
+	return ret
+}
+
+func (af *apexFile) AvailableToPlatform() bool {
+	if af.module == nil {
+		return false
+	}
+	if am, ok := af.module.(android.ApexModule); ok {
+		return am.AvailableFor(android.AvailableToPlatform)
+	}
+	return false
+}
+
 type apexBundle struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -547,6 +813,15 @@
 	// Suffix of module name in Android.mk
 	// ".flattened", ".apex", ".zipapex", or ""
 	suffix string
+
+	installedFilesFile android.WritablePath
+
+	// Whether to create symlink to the system file instead of having a file
+	// inside the apex or not
+	linkToSystemLib bool
+
+	// Struct holding the merged notice file paths in different formats
+	mergedNotices android.NoticeOutputs
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -813,7 +1088,7 @@
 }
 
 func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
-	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+	return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
 }
 
 func (a *apexBundle) PreventInstall() {
@@ -924,6 +1199,7 @@
 	Privileged() bool
 	OutputFile() android.Path
 	JacocoReportClassesFile() android.Path
+	Certificate() java.Certificate
 }, pkgName string) apexFile {
 	appDir := "app"
 	if aapp.Privileged() {
@@ -933,6 +1209,7 @@
 	fileToCopy := aapp.OutputFile()
 	af := newApexFile(ctx, fileToCopy, aapp.Name(), dirInApex, app, aapp)
 	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
+	af.certificate = aapp.Certificate()
 	return af
 }
 
@@ -1171,7 +1448,8 @@
 						// of the original test module (`depName`, shared by all `test_per_src`
 						// variations of that module).
 						af.moduleName = filepath.Base(af.builtFile.String())
-						af.transitiveDep = true
+						// these are not considered transitive dep
+						af.transitiveDep = false
 						filesInfo = append(filesInfo, af)
 						return true // track transitive dependencies
 					}
@@ -1209,15 +1487,22 @@
 
 	// remove duplicates in filesInfo
 	removeDup := func(filesInfo []apexFile) []apexFile {
-		encountered := make(map[string]bool)
-		result := []apexFile{}
+		encountered := make(map[string]apexFile)
 		for _, f := range filesInfo {
 			dest := filepath.Join(f.installDir, f.builtFile.Base())
-			if !encountered[dest] {
-				encountered[dest] = true
-				result = append(result, f)
+			if e, ok := encountered[dest]; !ok {
+				encountered[dest] = f
+			} else {
+				// If a module is directly included and also transitively depended on
+				// consider it as directly included.
+				e.transitiveDep = e.transitiveDep && f.transitiveDep
+				encountered[dest] = e
 			}
 		}
+		var result []apexFile
+		for _, v := range encountered {
+			result = append(result, v)
+		}
 		return result
 	}
 	filesInfo = removeDup(filesInfo)
@@ -1231,7 +1516,12 @@
 	if !ctx.Host() && !a.testApex {
 		for _, fi := range filesInfo {
 			if am, ok := fi.module.(android.ApexModule); ok {
-				if !am.AvailableFor(ctx.ModuleName()) {
+				// vndk {enabled:true} implies visibility to the vndk apex
+				if ccm, ok := fi.module.(*cc.Module); ok && ccm.IsVndk() && a.vndkApex {
+					continue
+				}
+
+				if !am.AvailableFor(ctx.ModuleName()) && !whitelistedApexAvailable(ctx.ModuleName(), a.vndkApex, fi.module) {
 					ctx.ModuleErrorf("requires %q that is not available for the APEX", fi.module.Name())
 					// don't stop so that we can report other violations in the same run
 				}
@@ -1239,12 +1529,6 @@
 		}
 	}
 
-	// prepend the name of this APEX to the module names. These names will be the names of
-	// modules that will be defined if the APEX is flattened.
-	for i := range filesInfo {
-		filesInfo[i].moduleName = filesInfo[i].moduleName + "." + a.Name() + a.suffix
-	}
-
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
@@ -1264,6 +1548,14 @@
 			return
 		}
 	}
+	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
+	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
+	// the same library in the system partition, thus effectively sharing the same libraries
+	// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
+	// in the APEX.
+	a.linkToSystemLib = !ctx.Config().UnbundledBuild() &&
+		a.installable() &&
+		!proptools.Bool(a.properties.Use_vendor)
 
 	// prepare apex_manifest.json
 	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
@@ -1280,6 +1572,25 @@
 	a.buildApexDependencyInfo(ctx)
 }
 
+func whitelistedApexAvailable(apex string, is_vndk bool, module android.Module) bool {
+	key := apex
+	key = strings.Replace(key, "test_", "", 1)
+	key = strings.Replace(key, "com.android.art.debug", "com.android.art", 1)
+	key = strings.Replace(key, "com.android.art.release", "com.android.art", 1)
+
+	moduleName := module.Name()
+	if strings.Contains(moduleName, "prebuilt_libclang_rt") {
+		// This module has variants that depend on the product being built.
+		moduleName = "prebuilt_libclang_rt"
+	}
+
+	if val, ok := apexAvailWl[key]; ok && android.InList(moduleName, val) {
+		return true
+	}
+
+	return false
+}
+
 func newApexBundle() *apexBundle {
 	module := &apexBundle{}
 	module.AddProperties(&module.properties)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b37674f..c7ecbc9 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -91,6 +91,10 @@
 	config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
 }
 
+func withUnbundledBuild(fs 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) {
 	android.ClearApexDependency()
 
@@ -287,8 +291,10 @@
 	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
 	ctx.RegisterModuleType("override_apex", overrideApexFactory)
 
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
+
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
@@ -300,9 +306,7 @@
 	java.RegisterAppBuildComponents(ctx)
 	ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory)
 
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
 
 	ctx.Register(config)
@@ -403,6 +407,11 @@
 			shared_libs: ["mylib2"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_binary {
@@ -422,6 +431,7 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -430,6 +440,11 @@
 			system_shared_libs: [],
 			stl: "none",
 			notice: "custom_notice",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		java_library {
@@ -440,6 +455,11 @@
 			compile_dex: true,
 			static_libs: ["myotherjar"],
 			libs: ["mysharedjar"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		java_library {
@@ -501,7 +521,7 @@
 	found_foo_link_64 := false
 	found_foo := false
 	for _, cmd := range strings.Split(copyCmds, " && ") {
-		if strings.HasPrefix(cmd, "ln -s foo64") {
+		if strings.HasPrefix(cmd, "ln -sfn foo64") {
 			if strings.HasSuffix(cmd, "bin/foo") {
 				found_foo = true
 			} else if strings.HasSuffix(cmd, "bin/foo_link_64") {
@@ -560,6 +580,7 @@
 			name: "mylib",
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		java_library {
@@ -568,6 +589,7 @@
 			sdk_version: "none",
 			system_modules: "none",
 			compile_dex: true,
+			apex_available: [ "myapex" ],
 		}
 
 		android_app {
@@ -575,9 +597,10 @@
 			srcs: ["foo/bar/MyClass.java"],
 			sdk_version: "none",
 			system_modules: "none",
+			apex_available: [ "myapex" ],
 		}
 	`)
-	ensureExactContents(t, ctx, "myapex", []string{
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"etc/myetc",
 		"javalib/myjar.jar",
 		"lib64/mylib.so",
@@ -627,6 +650,7 @@
 			shared_libs: ["mylib2"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -634,6 +658,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`)
 
@@ -674,6 +699,7 @@
 			shared_libs: ["mylib2", "mylib3"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -696,6 +722,7 @@
 			stubs: {
 				versions: ["10", "11", "12"],
 			},
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -703,6 +730,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`)
 
@@ -737,7 +765,7 @@
 	// Ensure that genstub is invoked with --apex
 	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_3").Rule("genStubSrc").Args["flags"])
 
-	ensureExactContents(t, ctx, "myapex", []string{
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"lib64/mylib.so",
 		"lib64/mylib3.so",
 		"lib64/mylib4.so",
@@ -764,6 +792,7 @@
 			shared_libs: ["libfoo#10"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex2" ],
 		}
 
 		cc_library {
@@ -845,6 +874,7 @@
 			runtime_libs: ["libfoo", "libbar"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -862,6 +892,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 	`)
@@ -906,6 +937,7 @@
 			shared_libs: ["libbar"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -956,6 +988,7 @@
 			srcs: ["mylib.cpp"],
 			shared_libs: ["libdl#27"],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library_shared {
@@ -963,6 +996,7 @@
 			srcs: ["mylib.cpp"],
 			shared_libs: ["libdl#27"],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -985,6 +1019,10 @@
 			stubs: {
 				versions: ["27", "28", "29"],
 			},
+			apex_available: [
+				"//apex_available:platform",
+				"myapex"
+			],
 		}
 
 		cc_library {
@@ -996,6 +1034,10 @@
 			stubs: {
 				versions: ["27", "28", "29"],
 			},
+			apex_available: [
+				"//apex_available:platform",
+				"myapex"
+			],
 		}
 
 		cc_library {
@@ -1087,6 +1129,7 @@
 			relative_install_path: "foo/bar",
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_binary {
@@ -1096,6 +1139,7 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`)
 
@@ -1140,6 +1184,7 @@
 			system_shared_libs: [],
 			vendor_available: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -1148,6 +1193,7 @@
 			system_shared_libs: [],
 			vendor_available: true,
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`, func(fs map[string][]byte, config android.Config) {
 		setUseVendorWhitelistForTest(config, []string{"myapex"})
@@ -1248,6 +1294,10 @@
 			stubs: {
 				versions: ["1", "2", "3"],
 			},
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_binary {
@@ -1281,6 +1331,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex_keytest" ],
 		}
 
 		apex_key {
@@ -1474,6 +1525,12 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+				"otherapex",
+			],
 		}
 	`)
 
@@ -1527,6 +1584,7 @@
 			stubs: {
 				versions: ["1", "2", "3"],
 			},
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -1544,46 +1602,72 @@
 	ensureContains(t, cFlags, "-Imy_include")
 }
 
-func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName string, files []string) {
+type fileInApex struct {
+	path   string // path in apex
+	src    string // src path
+	isLink bool
+}
+
+func getFiles(t *testing.T, ctx *android.TestContext, moduleName, variant string) []fileInApex {
 	t.Helper()
-	apexRule := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(moduleName, variant).Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 	imageApexDir := "/image.apex/"
-	var failed bool
-	var surplus []string
-	filesMatched := make(map[string]bool)
-	addContent := func(content string) {
-		for _, expected := range files {
-			if matched, _ := path.Match(expected, content); matched {
-				filesMatched[expected] = true
-				return
-			}
-		}
-		surplus = append(surplus, content)
-	}
+	var ret []fileInApex
 	for _, cmd := range strings.Split(copyCmds, "&&") {
 		cmd = strings.TrimSpace(cmd)
 		if cmd == "" {
 			continue
 		}
 		terms := strings.Split(cmd, " ")
+		var dst, src string
+		var isLink bool
 		switch terms[0] {
 		case "mkdir":
 		case "cp":
-			if len(terms) != 3 {
+			if len(terms) != 3 && len(terms) != 4 {
 				t.Fatal("copyCmds contains invalid cp command", cmd)
 			}
-			dst := terms[2]
+			dst = terms[len(terms)-1]
+			src = terms[len(terms)-2]
+			isLink = false
+		case "ln":
+			if len(terms) != 3 && len(terms) != 4 {
+				// ln LINK TARGET or ln -s LINK TARGET
+				t.Fatal("copyCmds contains invalid ln command", cmd)
+			}
+			dst = terms[len(terms)-1]
+			src = terms[len(terms)-2]
+			isLink = true
+		default:
+			t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
+		}
+		if dst != "" {
 			index := strings.Index(dst, imageApexDir)
 			if index == -1 {
 				t.Fatal("copyCmds should copy a file to image.apex/", cmd)
 			}
 			dstFile := dst[index+len(imageApexDir):]
-			addContent(dstFile)
-		default:
-			t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
+			ret = append(ret, fileInApex{path: dstFile, src: src, isLink: isLink})
 		}
 	}
+	return ret
+}
+
+func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+	t.Helper()
+	var failed bool
+	var surplus []string
+	filesMatched := make(map[string]bool)
+	for _, file := range getFiles(t, ctx, moduleName, variant) {
+		for _, expected := range files {
+			if matched, _ := path.Match(expected, file.path); matched {
+				filesMatched[expected] = true
+				return
+			}
+		}
+		surplus = append(surplus, file.path)
+	}
 
 	if len(surplus) > 0 {
 		sort.Strings(surplus)
@@ -1629,6 +1713,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -1641,10 +1726,11 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 	`+vndkLibrariesTxtFiles("current"))
 
-	ensureExactContents(t, ctx, "myapex", []string{
+	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
 		"lib/libvndk.so",
 		"lib/libvndksp.so",
 		"lib64/libvndk.so",
@@ -1678,6 +1764,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_prebuilt_library_shared {
@@ -1695,6 +1782,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 		`+vndkLibrariesTxtFiles("current"),
 		withFiles(map[string][]byte{
@@ -1702,7 +1790,7 @@
 			"libvndk.arm.so": nil,
 		}))
 
-	ensureExactContents(t, ctx, "myapex", []string{
+	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
 		"lib/libvndk.so",
 		"lib/libvndk.arm.so",
 		"lib64/libvndk.so",
@@ -1765,6 +1853,7 @@
 					srcs: ["libvndk27_arm64.so"],
 				},
 			},
+			apex_available: [ "myapex_v27" ],
 		}
 
 		vndk_prebuilt_shared {
@@ -1792,7 +1881,7 @@
 			"libvndk27_x86_64.so": nil,
 		}))
 
-	ensureExactContents(t, ctx, "myapex_v27", []string{
+	ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
 		"lib/libvndk27_arm.so",
 		"lib64/libvndk27_arm64.so",
 		"etc/*",
@@ -1865,7 +1954,7 @@
 		}`+vndkLibrariesTxtFiles("28", "current"))
 
 	assertApexName := func(expected, moduleName string) {
-		bundle := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Module().(*apexBundle)
+		bundle := ctx.ModuleForTests(moduleName, "android_common_image").Module().(*apexBundle)
 		actual := proptools.String(bundle.properties.Apex_name)
 		if !reflect.DeepEqual(actual, expected) {
 			t.Errorf("Got '%v', expected '%v'", actual, expected)
@@ -1901,6 +1990,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 		`+vndkLibrariesTxtFiles("current"),
 		withTargets(map[android.OsType][]android.Target{
@@ -1912,7 +2002,7 @@
 			},
 		}))
 
-	ensureExactContents(t, ctx, "myapex", []string{
+	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
 		"lib/libvndk.so",
 		"lib64/libvndk.so",
 		"etc/*",
@@ -1993,6 +2083,7 @@
 					srcs: ["libvndk27binder32.so"],
 				}
 			},
+			apex_available: [ "myapex_v27" ],
 		}
 		`+vndkLibrariesTxtFiles("27"),
 		withFiles(map[string][]byte{
@@ -2007,7 +2098,7 @@
 		}),
 	)
 
-	ensureExactContents(t, ctx, "myapex_v27", []string{
+	ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
 		"lib/libvndk27binder32.so",
 		"etc/*",
 	})
@@ -2058,6 +2149,7 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex_nodep" ],
 		}
 
 		cc_library {
@@ -2066,6 +2158,11 @@
 			shared_libs: ["libfoo"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [
+				"myapex_dep",
+				"myapex_provider",
+				"myapex_selfcontained",
+			],
 		}
 
 		cc_library {
@@ -2076,6 +2173,10 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [
+				"myapex_provider",
+				"myapex_selfcontained",
+			],
 		}
 	`)
 
@@ -2148,6 +2249,10 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [
+					"//apex_available:platform",
+				  "myapex",
+		  ],
 		}
 	`)
 
@@ -2199,6 +2304,11 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 	`)
 
@@ -2267,6 +2377,11 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_library {
@@ -2275,6 +2390,11 @@
 			system_shared_libs: [],
 			stl: "none",
 			compile_multilib: "first",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_library {
@@ -2701,6 +2821,7 @@
 			shared_libs: ["libcommon"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library {
@@ -2708,6 +2829,12 @@
 			srcs: ["mylib_common.cpp"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"commonapex",
+				"myapex",
+			],
 		}
 	`)
 
@@ -2859,6 +2986,7 @@
 			sdk_version: "none",
 			system_modules: "none",
 			jni_libs: ["libjni"],
+			apex_available: [ "myapex" ],
 		}
 
 		android_app {
@@ -2867,6 +2995,7 @@
 			sdk_version: "none",
 			system_modules: "none",
 			privileged: true,
+			apex_available: [ "myapex" ],
 		}
 
 		cc_library_shared {
@@ -2885,7 +3014,7 @@
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv/AppFooPriv.apk")
 
 	// JNI libraries are embedded inside APK
-	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").Rule("zip")
+	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").Description("zip jni lib")
 	libjniOutput := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module).OutputFile()
 	ensureListContains(t, appZipRule.Implicits.Strings(), libjniOutput.String())
 	// ... uncompressed
@@ -2960,6 +3089,7 @@
 		android_test_helper_app {
 			name: "TesterHelpAppFoo",
 			srcs: ["foo/bar/MyClass.java"],
+			apex_available: [ "myapex" ],
 		}
 
 	`)
@@ -3215,6 +3345,7 @@
 			package_name: "foo",
 			sdk_version: "none",
 			system_modules: "none",
+			apex_available: [ "myapex" ],
 		}
 
 		override_android_app {
@@ -3278,6 +3409,7 @@
 	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
 	args := module.Rule("apexRule").Args
 	ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String())
+	ensureNotContains(t, args["opt_flags"], "--no_hashtree")
 }
 
 func TestJavaSDKLibrary(t *testing.T) {
@@ -3298,6 +3430,7 @@
 			name: "foo",
 			srcs: ["a.java"],
 			api_packages: ["foo"],
+			apex_available: [ "myapex" ],
 		}
 	`, withFiles(map[string][]byte{
 		"api/current.txt":        nil,
@@ -3309,7 +3442,7 @@
 	}))
 
 	// java_sdk_library installs both impl jar and permission XML
-	ensureExactContents(t, ctx, "myapex", []string{
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"javalib/foo.jar",
 		"etc/permissions/foo.xml",
 	})
@@ -3363,6 +3496,7 @@
 			required: ["a", "b"],
 			host_required: ["c", "d"],
 			target_required: ["e", "f"],
+			apex_available: [ "myapex" ],
 		}
 	`)
 
@@ -3378,6 +3512,106 @@
 	ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES += e f\n")
 }
 
+func TestSymlinksFromApexToSystem(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			java_libs: ["myjar"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["myotherlib"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+
+		cc_library {
+			name: "myotherlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+
+		java_library {
+			name: "myjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			libs: ["myotherjar"],
+			compile_dex: true,
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+
+		java_library {
+			name: "myotherjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			apex_available: [
+				"myapex",
+				"//apex_available:platform",
+			],
+		}
+	`
+
+	ensureRealfileExists := func(t *testing.T, files []fileInApex, file string) {
+		for _, f := range files {
+			if f.path == file {
+				if f.isLink {
+					t.Errorf("%q is not a real file", file)
+				}
+				return
+			}
+		}
+		t.Errorf("%q is not found", file)
+	}
+
+	ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string) {
+		for _, f := range files {
+			if f.path == file {
+				if !f.isLink {
+					t.Errorf("%q is not a symlink", file)
+				}
+				return
+			}
+		}
+		t.Errorf("%q is not found", file)
+	}
+
+	ctx, _ := testApex(t, bp, withUnbundledBuild)
+	files := getFiles(t, ctx, "myapex", "android_common_myapex_image")
+	ensureRealfileExists(t, files, "javalib/myjar.jar")
+	ensureRealfileExists(t, files, "lib64/mylib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib.so")
+
+	ctx, _ = testApex(t, bp)
+	files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
+	ensureRealfileExists(t, files, "javalib/myjar.jar")
+	ensureRealfileExists(t, files, "lib64/mylib.so")
+	ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/builder.go b/apex/builder.go
index 2f1e453..53f39a6 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -160,7 +160,7 @@
 			`echo -e "New unexpected files were added to ${apex_module_name}." ` +
 			` "To fix the build run following command:" && ` +
 			`echo "system/apex/tools/update_whitelist.sh ${whitelisted_files_file} ${image_content_file}" && ` +
-			`exit 1)`,
+			`exit 1); touch ${out}`,
 		Description: "Diff ${image_content_file} and ${whitelisted_files_file}",
 	}, "image_content_file", "whitelisted_files_file", "apex_module_name")
 )
@@ -211,7 +211,7 @@
 	})
 }
 
-func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
+func (a *apexBundle) buildNoticeFiles(ctx android.ModuleContext, apexFileName string) android.NoticeOutputs {
 	noticeFiles := []android.Path{}
 	for _, f := range a.filesInfo {
 		if f.module != nil {
@@ -227,10 +227,23 @@
 	}
 
 	if len(noticeFiles) == 0 {
-		return android.OptionalPath{}
+		return android.NoticeOutputs{}
 	}
 
-	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)).HtmlGzOutput
+	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles))
+}
+
+func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
+	output := android.PathForModuleOut(ctx, "installed-files.txt")
+	rule := android.NewRuleBuilder()
+	rule.Command().
+		Implicit(builtApex).
+		Text("(cd " + imageDir.String() + " ; ").
+		Text("find . -type f -printf \"%s %p\\n\") ").
+		Text(" | sort -nr > ").
+		Output(output)
+	rule.Build(pctx, ctx, "installed-files."+a.Name(), "Installed files")
+	return output.OutputPath
 }
 
 func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
@@ -245,34 +258,40 @@
 
 	apexType := a.properties.ApexType
 	suffix := apexType.suffix()
+	var implicitInputs []android.Path
 	unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
 
-	filesToCopy := []android.Path{}
-	for _, f := range a.filesInfo {
-		filesToCopy = append(filesToCopy, f.builtFile)
+	// TODO(jiyong): construct the copy rules using RuleBuilder
+	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))
+		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)
+			implicitInputs = append(implicitInputs, fi.builtFile)
+		}
+		// create additional symlinks pointing the file inside the APEX
+		for _, symlinkPath := range fi.SymlinkPaths() {
+			symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
+			copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
+		}
 	}
 
-	copyCommands := []string{}
-	emitCommands := []string{}
-	imageContentFile := android.PathForModuleOut(ctx, a.Name()+"-content.txt")
+	// TODO(jiyong): use RuleBuilder
+	var emitCommands []string
+	imageContentFile := android.PathForModuleOut(ctx, "content.txt")
 	emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
 	if proptools.Bool(a.properties.Legacy_android10_support) {
 		emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
 	}
-	for i, src := range filesToCopy {
-		dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
-		emitCommands = append(emitCommands, "echo './"+dest+"' >> "+imageContentFile.String())
-		dest_path := filepath.Join(android.PathForModuleOut(ctx, "image"+suffix).String(), dest)
-		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
-		copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
-		for _, sym := range a.filesInfo[i].symlinks {
-			symlinkDest := filepath.Join(filepath.Dir(dest_path), sym)
-			copyCommands = append(copyCommands, "ln -s "+filepath.Base(dest)+" "+symlinkDest)
-		}
+	for _, fi := range a.filesInfo {
+		emitCommands = append(emitCommands, "echo './"+fi.Path()+"' >> "+imageContentFile.String())
 	}
 	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
-
-	implicitInputs := append(android.Paths(nil), filesToCopy...)
 	implicitInputs = append(implicitInputs, a.manifestPbOut)
 
 	if a.properties.Whitelisted_files != nil {
@@ -307,6 +326,7 @@
 	outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
+	imageDir := android.PathForModuleOut(ctx, "image"+suffix)
 	if apexType == imageApex {
 		// files and dirs that will be created in APEX
 		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
@@ -362,28 +382,30 @@
 		}
 
 		targetSdkVersion := ctx.Config().DefaultAppTargetSdk()
-		if targetSdkVersion == ctx.Config().PlatformSdkCodename() &&
-			ctx.Config().UnbundledBuild() &&
-			!ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
-			ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
-			apiFingerprint := java.ApiFingerprintPath(ctx)
-			targetSdkVersion += fmt.Sprintf(".$$(cat %s)", apiFingerprint.String())
-			implicitInputs = append(implicitInputs, apiFingerprint)
+		minSdkVersion := ctx.Config().DefaultAppTargetSdk()
+		if java.UseApiFingerprint(ctx, targetSdkVersion) {
+			targetSdkVersion += fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String())
+			implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx))
+		}
+		if java.UseApiFingerprint(ctx, minSdkVersion) {
+			minSdkVersion += fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String())
+			implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx))
 		}
 		optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
+		optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion)
 
-		noticeFile := a.buildNoticeFile(ctx, a.Name()+suffix)
-		if noticeFile.Valid() {
+		a.mergedNotices = a.buildNoticeFiles(ctx, a.Name()+suffix)
+		if a.mergedNotices.HtmlGzOutput.Valid() {
 			// If there's a NOTICE file, embed it as an asset file in the APEX.
-			implicitInputs = append(implicitInputs, noticeFile.Path())
-			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
+			implicitInputs = append(implicitInputs, a.mergedNotices.HtmlGzOutput.Path())
+			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String()))
 		}
 
 		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
 			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
 			return
 		}
-		if (!ctx.Config().UnbundledBuild() && a.installable()) || a.testOnlyShouldSkipHashtreeGeneration() {
+		if !proptools.Bool(a.properties.Legacy_android10_support) || a.testOnlyShouldSkipHashtreeGeneration() {
 			// Apexes which are supposed to be installed in builtin dirs(/system, etc)
 			// don't need hashtree for activation. Therefore, by removing hashtree from
 			// apex bundle (filesystem image in it, to be specific), we can save storage.
@@ -408,7 +430,7 @@
 			Description: "apex (" + apexType.name() + ")",
 			Args: map[string]string{
 				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":        android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"image_dir":        imageDir.String(),
 				"copy_commands":    strings.Join(copyCommands, " && "),
 				"manifest":         a.manifestPbOut.String(),
 				"file_contexts":    a.fileContexts.String(),
@@ -446,7 +468,7 @@
 			Description: "apex (" + apexType.name() + ")",
 			Args: map[string]string{
 				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":     android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"image_dir":     imageDir.String(),
 				"copy_commands": strings.Join(copyCommands, " && "),
 				"manifest":      a.manifestPbOut.String(),
 			},
@@ -474,6 +496,9 @@
 		ctx.InstallFile(a.installDir, a.Name()+suffix, a.outputFile)
 	}
 	a.buildFilesInfo(ctx)
+
+	// installed-files.txt is dist'ed
+	a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
 }
 
 func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
@@ -513,7 +538,7 @@
 	if a.installable() {
 		// For flattened APEX, do nothing but make sure that APEX manifest and apex_pubkey are also copied along
 		// with other ordinary files.
-		a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))
+		a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil))
 
 		// rename to apex_pubkey
 		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -522,7 +547,7 @@
 			Input:  a.public_key_file,
 			Output: copiedPubkey,
 		})
-		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey."+a.Name()+a.suffix, ".", etc, nil))
+		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
 
 		if a.properties.ApexType == flattenedApex {
 			apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
new file mode 100644
index 0000000..391072e
--- /dev/null
+++ b/apex/vndk_test.go
@@ -0,0 +1,94 @@
+package apex
+
+import (
+	"testing"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+func TestVndkApexUsesVendorVariant(t *testing.T) {
+	bp := `
+		apex_vndk {
+			name: "myapex",
+			key: "mykey",
+		}
+		apex_key {
+			name: "mykey",
+		}
+		cc_library {
+			name: "libfoo",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+			notice: "custom_notice",
+		}
+		` + vndkLibrariesTxtFiles("current")
+
+	ensureFileSrc := func(t *testing.T, files []fileInApex, path, src string) {
+		t.Helper()
+		for _, f := range files {
+			if f.path == path {
+				ensureContains(t, f.src, src)
+				return
+			}
+		}
+		t.Fail()
+	}
+
+	t.Run("VNDK lib doesn't have an apex variant", func(t *testing.T) {
+		ctx, _ := testApex(t, bp)
+
+		// libfoo doesn't have apex variants
+		for _, variant := range ctx.ModuleVariantsForTests("libfoo") {
+			ensureNotContains(t, variant, "_myapex")
+		}
+
+		// VNDK APEX doesn't create apex variant
+		files := getFiles(t, ctx, "myapex", "android_common_image")
+		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+	})
+
+	t.Run("VNDK APEX gathers only vendor variants even if product variants are available", func(t *testing.T) {
+		ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
+			// Now product variant is available
+			config.TestProductVariables.ProductVndkVersion = proptools.StringPtr("current")
+		})
+
+		files := getFiles(t, ctx, "myapex", "android_common_image")
+		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+	})
+
+	t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
+		ctx, _ := testApex(t, bp+`
+			cc_library {
+				name: "libprofile-extras",
+				vendor_available: true,
+				native_coverage: false,
+				system_shared_libs: [],
+				stl: "none",
+				notice: "custom_notice",
+			}
+			cc_library {
+				name: "libprofile-clang-extras",
+				vendor_available: true,
+				native_coverage: false,
+				system_shared_libs: [],
+				stl: "none",
+				notice: "custom_notice",
+			}
+		`, func(fs map[string][]byte, config android.Config) {
+			config.TestProductVariables.NativeCoverage = proptools.BoolPtr(true)
+		})
+
+		files := getFiles(t, ctx, "myapex", "android_common_image")
+		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+
+		files = getFiles(t, ctx, "myapex", "android_common_cov_image")
+		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared_cov/libfoo.so")
+	})
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 024fcbc..1d792ef 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -33,7 +33,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/bpf")
 
-	ccRule = pctx.AndroidRemoteStaticRule("ccRule", android.SUPPORTS_GOMA,
+	ccRule = pctx.AndroidRemoteStaticRule("ccRule", android.RemoteRuleSupports{Goma: true},
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 4633aa6..0516279 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -120,6 +120,10 @@
 		Name: "removeEmptyLibDependencies",
 		Fix:  removeEmptyLibDependencies,
 	},
+	{
+		Name: "removeHidlInterfaceTypes",
+		Fix:  removeHidlInterfaceTypes,
+	},
 }
 
 func NewFixRequest() FixRequest {
@@ -698,6 +702,18 @@
 	return nil
 }
 
+// Removes hidl_interface 'types' which are no longer needed
+func removeHidlInterfaceTypes(f *Fixer) error {
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !(ok && mod.Type == "hidl_interface") {
+			continue
+		}
+		removeProperty(mod, "types")
+	}
+	return nil
+}
+
 // Converts the default source list property, 'srcs', to a single source property with a given name.
 // "LOCAL_MODULE" reference is also resolved during the conversion process.
 func convertToSingleSource(mod *parser.Module, srcPropertyName string) {
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 032282f..38cefdd 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -887,3 +887,34 @@
 		})
 	}
 }
+
+func TestRemoveHidlInterfaceTypes(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "remove types",
+			in: `
+				hidl_interface {
+					name: "foo@1.0",
+					types: ["ParcelFooBar"],
+				}
+			`,
+			out: `
+				hidl_interface {
+					name: "foo@1.0",
+
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return removeHidlInterfaceTypes(fixer)
+			})
+		})
+	}
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index 02b346d..008030f 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -6,12 +6,15 @@
 # The following environment variables affect the result:
 #   BUILD_NUMBER          build number, used to generate unique ID (will use UUID if not set)
 #   DIST_DIR              where the resulting all.kzip will be placed
+#   KYTHE_KZIP_ENCODING   proto or json (proto is default)
 #   OUT_DIR               output directory (out if not specified})
 #   TARGET_BUILD_VARIANT  variant, e.g., `userdebug`
 #   TARGET_PRODUCT        target device name, e.g., 'aosp_blueline'
 #   XREF_CORPUS           source code repository URI, e.g., 'android.googlesource.com/platform/superproject'
 
 : ${BUILD_NUMBER:=$(uuidgen)}
+: ${KYTHE_KZIP_ENCODING:=proto}
+export KYTHE_KZIP_ENCODING
 
 # The extraction might fail for some source files, so run with -k and then check that
 # sufficiently many files were generated.
@@ -20,13 +23,14 @@
 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.
 declare -r abspath_out=$(realpath "${out}")
-(cd build/soong;
- ../../prebuilts/build-tools/linux-x86/bin/go_extractor \
-    --goroot="${PWD}/../../prebuilts/go/linux-x86" \
-    --rules=vnames.go.json \
-    --canonicalize_package_corpus \
-    --output "${abspath_out}/soong/all.go.kzip" \
-    ./... )
+declare -r go_extractor=$(realpath prebuilts/build-tools/linux-x86/bin/go_extractor)
+declare -r go_root=$(realpath prebuilts/go/linux-x86)
+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" ./...
+  )
+done
 
 declare -r kzip_count=$(find "$out" -name '*.kzip' | wc -l)
 (($kzip_count>100000)) || { printf "Too few kzip files were generated: %d\n" $kzip_count; exit 1; }
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ff88091..137cb63 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -27,6 +27,7 @@
 	nativeBridgeSuffix = ".native_bridge"
 	productSuffix      = ".product"
 	vendorSuffix       = ".vendor"
+	ramdiskSuffix      = ".ramdisk"
 	recoverySuffix     = ".recovery"
 )
 
@@ -40,6 +41,7 @@
 	UseVndk() bool
 	VndkVersion() string
 	static() bool
+	InRamdisk() bool
 	InRecovery() bool
 }
 
@@ -217,6 +219,9 @@
 			fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
 			fmt.Fprintln(w, "LOCAL_VNDK_DEPEND_ON_CORE_VARIANT := true")
 		}
+		if library.checkSameCoreVariant {
+			fmt.Fprintln(w, "LOCAL_CHECK_SAME_VNDK_VARIANTS := true")
+		}
 	})
 
 	if library.shared() && !library.buildStubs() {
@@ -230,7 +235,7 @@
 		})
 	}
 	if len(library.Properties.Stubs.Versions) > 0 &&
-		android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.InRecovery() && !ctx.UseVndk() &&
+		android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.InRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() &&
 		!ctx.static() {
 		if !library.buildStubs() {
 			ret.SubName = ".bootstrap"
diff --git a/cc/binary.go b/cc/binary.go
index 617d4dd..280d17b 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -56,8 +56,12 @@
 }
 
 func init() {
-	android.RegisterModuleType("cc_binary", BinaryFactory)
-	android.RegisterModuleType("cc_binary_host", binaryHostFactory)
+	RegisterBinaryBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterBinaryBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_binary", BinaryFactory)
+	ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
 }
 
 // cc_binary produces a binary that is runnable on a device.
@@ -260,7 +264,7 @@
 				} else {
 					switch ctx.Os() {
 					case android.Android:
-						if ctx.bootstrap() && !ctx.inRecovery() {
+						if ctx.bootstrap() && !ctx.inRecovery() && !ctx.inRamdisk() {
 							flags.DynamicLinker = "/system/bin/bootstrap/linker"
 						} else {
 							flags.DynamicLinker = "/system/bin/linker"
@@ -454,7 +458,7 @@
 	// The original path becomes a symlink to the corresponding file in the
 	// runtime APEX.
 	translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
-	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRecovery() {
+	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRamdisk() && !ctx.inRecovery() {
 		if ctx.Device() && isBionic(ctx.baseModuleName()) {
 			binary.installSymlinkToRuntimeApex(ctx, file)
 		}
diff --git a/cc/builder.go b/cc/builder.go
index 1ec323f..3ecfe54 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -46,7 +46,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/cc")
 
-	cc = pctx.AndroidRemoteStaticRule("cc", android.SUPPORTS_BOTH,
+	cc = pctx.AndroidRemoteStaticRule("cc", android.RemoteRuleSupports{Goma: true, RBE: true},
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
@@ -55,7 +55,7 @@
 		},
 		"ccCmd", "cFlags")
 
-	ccNoDeps = pctx.AndroidRemoteStaticRule("ccNoDeps", android.SUPPORTS_GOMA,
+	ccNoDeps = pctx.AndroidRemoteStaticRule("ccNoDeps", android.RemoteRuleSupports{Goma: true},
 		blueprint.RuleParams{
 			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -o $out $in",
 			CommandDeps: []string{"$ccCmd"},
@@ -178,7 +178,7 @@
 
 	windres = pctx.AndroidStaticRule("windres",
 		blueprint.RuleParams{
-			Command:     "$windresCmd $flags -I$$(dirname $in) -i $in -o $out",
+			Command:     "$windresCmd $flags -I$$(dirname $in) -i $in -o $out --preprocessor \"${config.ClangBin}/clang -E -xc-header -DRC_INVOKED\"",
 			CommandDeps: []string{"$windresCmd"},
 		},
 		"windresCmd", "flags")
@@ -238,9 +238,13 @@
 	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+	_ = pctx.VariableFunc("kytheCuEncoding",
+		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
 	kytheExtract = pctx.StaticRule("kythe",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out KYTHE_VNAMES=$kytheVnames $cxxExtractor $cFlags $in ",
+			Command: `rm -f $out && ` +
+				`KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out KYTHE_VNAMES=$kytheVnames KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
+				`$cxxExtractor $cFlags $in `,
 			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
 		},
 		"cFlags")
diff --git a/cc/cc.go b/cc/cc.go
index d1b97b4..ce3a2ed 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -223,11 +223,15 @@
 	// file
 	Logtags []string
 
+	// Make this module available when building for ramdisk
+	Ramdisk_available *bool
+
 	// Make this module available when building for recovery
 	Recovery_available *bool
 
 	// Set by imageMutator
 	CoreVariantNeeded     bool     `blueprint:"mutated"`
+	RamdiskVariantNeeded  bool     `blueprint:"mutated"`
 	RecoveryVariantNeeded bool     `blueprint:"mutated"`
 	ExtraVariants         []string `blueprint:"mutated"`
 
@@ -290,6 +294,7 @@
 	isVndkExt() bool
 	inProduct() bool
 	inVendor() bool
+	inRamdisk() bool
 	inRecovery() bool
 	shouldCreateSourceAbiDump() bool
 	selectedStl() string
@@ -878,10 +883,18 @@
 	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
 }
 
+func (c *Module) InRamdisk() bool {
+	return c.ModuleBase.InRamdisk() || c.ModuleBase.InstallInRamdisk()
+}
+
 func (c *Module) InRecovery() bool {
 	return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
 }
 
+func (c *Module) OnlyInRamdisk() bool {
+	return c.ModuleBase.InstallInRamdisk()
+}
+
 func (c *Module) OnlyInRecovery() bool {
 	return c.ModuleBase.InstallInRecovery()
 }
@@ -1018,7 +1031,7 @@
 }
 
 func (ctx *moduleContextImpl) useSdk() bool {
-	if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() {
+	if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() {
 		return String(ctx.mod.Properties.Sdk_version) != ""
 	}
 	return false
@@ -1090,6 +1103,10 @@
 	return ctx.mod.inVendor()
 }
 
+func (ctx *moduleContextImpl) inRamdisk() bool {
+	return ctx.mod.InRamdisk()
+}
+
 func (ctx *moduleContextImpl) inRecovery() bool {
 	return ctx.mod.InRecovery()
 }
@@ -1113,7 +1130,7 @@
 		// Host modules do not need ABI dumps.
 		return false
 	}
-	if ctx.isStubs() {
+	if ctx.isStubs() || ctx.isNDKStubLibrary() {
 		// Stubs do not need ABI dumps.
 		return false
 	}
@@ -1335,6 +1352,8 @@
 		// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
 		// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
 		c.Properties.SubName += vendorSuffix
+	} else if c.InRamdisk() && !c.OnlyInRamdisk() {
+		c.Properties.SubName += ramdiskSuffix
 	} else if c.InRecovery() && !c.OnlyInRecovery() {
 		c.Properties.SubName += recoverySuffix
 	}
@@ -1444,7 +1463,7 @@
 		// (unless it is explicitly referenced via .bootstrap suffix or the
 		// module is marked with 'bootstrap: true').
 		if c.HasStubsVariants() &&
-			android.DirectlyInAnyApex(ctx, ctx.baseModuleName()) &&
+			android.DirectlyInAnyApex(ctx, ctx.baseModuleName()) && !c.InRamdisk() &&
 			!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
 			c.IsStubs() {
 			c.Properties.HideFromMake = false // unhide
@@ -1732,7 +1751,7 @@
 	addSharedLibDependencies := func(depTag DependencyTag, name string, version string) {
 		var variations []blueprint.Variation
 		variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
-		versionVariantAvail := !ctx.useVndk() && !c.InRecovery()
+		versionVariantAvail := !ctx.useVndk() && !c.InRecovery() && !c.InRamdisk()
 		if version != "" && versionVariantAvail {
 			// Version is explicitly specified. i.e. libFoo#30
 			variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
@@ -1863,6 +1882,10 @@
 		// Platform code can link to anything
 		return
 	}
+	if from.InRamdisk() {
+		// Ramdisk code is not NDK
+		return
+	}
 	if from.InRecovery() {
 		// Recovery code is not NDK
 		return
@@ -2123,8 +2146,8 @@
 					// If not building for APEX, use stubs only when it is from
 					// an APEX (and not from platform)
 					useThisDep = (depInPlatform != depIsStubs)
-					if c.InRecovery() || c.bootstrap() {
-						// However, for recovery or bootstrap modules,
+					if c.InRamdisk() || c.InRecovery() || c.bootstrap() {
+						// However, for ramdisk, recovery or bootstrap modules,
 						// always link to non-stub variant
 						useThisDep = !depIsStubs
 					}
@@ -2275,7 +2298,7 @@
 			isVendorPublicLib := inList(libName, *vendorPublicLibraries)
 			bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
 
-			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRecovery() {
+			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRamdisk() && !c.InRecovery() {
 				// The vendor module is a no-vendor-variant VNDK library.  Depend on the
 				// core module instead.
 				return libName
@@ -2285,6 +2308,8 @@
 				return libName + c.getNameSuffixWithVndkVersion(ctx)
 			} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
 				return libName + vendorPublicLibrarySuffix
+			} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
+				return libName + ramdiskSuffix
 			} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
 				return libName + recoverySuffix
 			} else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
@@ -2369,6 +2394,10 @@
 	return c.installer.inSanitizerDir()
 }
 
+func (c *Module) InstallInRamdisk() bool {
+	return c.InRamdisk()
+}
+
 func (c *Module) InstallInRecovery() bool {
 	return c.InRecovery()
 }
@@ -2441,6 +2470,8 @@
 			return "native:product"
 		}
 		return "native:vendor"
+	} else if c.InRamdisk() {
+		return "native:ramdisk"
 	} else if c.InRecovery() {
 		return "native:recovery"
 	} else if c.Target().Os == android.Android && String(c.Properties.Sdk_version) != "" {
@@ -2647,6 +2678,7 @@
 	}
 
 	var coreVariantNeeded bool = false
+	var ramdiskVariantNeeded bool = false
 	var recoveryVariantNeeded bool = false
 
 	var vendorVariants []string
@@ -2729,6 +2761,15 @@
 		productVariants = []string{}
 	}
 
+	if Bool(m.Properties.Ramdisk_available) {
+		ramdiskVariantNeeded = true
+	}
+
+	if m.ModuleBase.InstallInRamdisk() {
+		ramdiskVariantNeeded = true
+		coreVariantNeeded = false
+	}
+
 	if Bool(m.Properties.Recovery_available) {
 		recoveryVariantNeeded = true
 	}
@@ -2746,6 +2787,7 @@
 		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
 	}
 
+	m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
 	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
 	m.Properties.CoreVariantNeeded = coreVariantNeeded
 }
@@ -2754,6 +2796,10 @@
 	return c.Properties.CoreVariantNeeded
 }
 
+func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.RamdiskVariantNeeded
+}
+
 func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return c.Properties.RecoveryVariantNeeded
 }
@@ -2764,7 +2810,9 @@
 
 func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
 	m := module.(*Module)
-	if variant == android.RecoveryVariation {
+	if variant == android.RamdiskVariation {
+		m.MakeAsPlatform()
+	} else if variant == android.RecoveryVariation {
 		m.MakeAsPlatform()
 		squashRecoverySrcs(m)
 	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 4d02f4f..332cc45 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2303,13 +2303,13 @@
 	// Check the shared version of lib2.
 	variant := "android_arm64_armv8-a_shared"
 	module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
-	checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+	checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, module)
 
 	// Check the static version of lib2.
 	variant = "android_arm64_armv8-a_static"
 	module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
 	// libc++_static is linked additionally.
-	checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+	checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, module)
 }
 
 var compilerFlagsTestCases = []struct {
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
index 9b89110..4e23a7b 100644
--- a/cc/ccdeps.go
+++ b/cc/ccdeps.go
@@ -142,7 +142,7 @@
 			compilerParams.HeaderSearchPath =
 				append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I"))
 		case systemHeaderSearchPath:
-			if i < len(params)-1 {
+			if i < len(cparams)-1 {
 				compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1])
 			}
 			i = i + 1
diff --git a/cc/config/OWNERS b/cc/config/OWNERS
new file mode 100644
index 0000000..b2f54e5
--- /dev/null
+++ b/cc/config/OWNERS
@@ -0,0 +1 @@
+per-file vndk.go = smoreland@google.com, victoryang@google.com
diff --git a/cc/config/global.go b/cc/config/global.go
index bae5555..44de4d5 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -88,6 +88,7 @@
 		"-Wl,--no-undefined-version",
 		"-Wl,--exclude-libs,libgcc.a",
 		"-Wl,--exclude-libs,libgcc_stripped.a",
+		"-Wl,--exclude-libs,libunwind_llvm.a",
 	}
 
 	deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
@@ -165,6 +166,8 @@
 			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
 		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=pattern")
+		} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
+			flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
 		}
 
 		return strings.Join(flags, " ")
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 9feb5a3..52bd9f0 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -18,59 +18,24 @@
 // For these libraries, the vendor variants must be installed even if the device
 // has VndkUseCoreVariant set.
 var VndkMustUseVendorVariantList = []string{
-	"android.frameworks.sensorservice@1.0",
-	"android.hardware.atrace@1.0",
-	"android.hardware.audio.common@5.0",
-	"android.hardware.audio.effect@2.0",
-	"android.hardware.audio.effect@4.0",
-	"android.hardware.audio.effect@5.0",
-	"android.hardware.audio@2.0",
-	"android.hardware.audio@4.0",
-	"android.hardware.audio@5.0",
 	"android.hardware.automotive.evs@1.0",
 	"android.hardware.automotive.vehicle@2.0",
-	"android.hardware.bluetooth.audio@2.0",
-	"android.hardware.boot@1.0",
-	"android.hardware.broadcastradio@1.0",
-	"android.hardware.broadcastradio@1.1",
 	"android.hardware.broadcastradio@2.0",
 	"android.hardware.camera.device@1.0",
 	"android.hardware.camera.device@3.2",
 	"android.hardware.camera.device@3.3",
 	"android.hardware.camera.device@3.4",
 	"android.hardware.camera.provider@2.4",
-	"android.hardware.cas.native@1.0",
-	"android.hardware.cas@1.0",
-	"android.hardware.configstore@1.0",
-	"android.hardware.configstore@1.1",
-	"android.hardware.contexthub@1.0",
-	"android.hardware.drm@1.0",
-	"android.hardware.drm@1.1",
 	"android.hardware.fastboot@1.0",
-	"android.hardware.gatekeeper@1.0",
-	"android.hardware.gnss@1.0",
-	"android.hardware.graphics.allocator@2.0",
-	"android.hardware.graphics.bufferqueue@1.0",
-	"android.hardware.graphics.composer@2.1",
-	"android.hardware.graphics.composer@2.2",
-	"android.hardware.health@1.0",
-	"android.hardware.health@2.0",
-	"android.hardware.ir@1.0",
-	"android.hardware.keymaster@3.0",
-	"android.hardware.keymaster@4.0",
-	"android.hardware.light@2.0",
 	"android.hardware.media.bufferpool@1.0",
-	"android.hardware.media.omx@1.0",
-	"android.hardware.memtrack@1.0",
 	"android.hardware.neuralnetworks@1.0",
 	"android.hardware.neuralnetworks@1.1",
 	"android.hardware.neuralnetworks@1.2",
 	"android.hardware.neuralnetworks@1.3",
-	"android.hardware.nfc@1.0",
-	"android.hardware.nfc@1.1",
 	"android.hardware.nfc@1.2",
 	"android.hardware.oemlock@1.0",
 	"android.hardware.power.stats@1.0",
+	"android.hardware.power-ndk_platform",
 	"android.hardware.power@1.0",
 	"android.hardware.power@1.1",
 	"android.hardware.radio@1.4",
@@ -81,56 +46,37 @@
 	"android.hardware.soundtrigger@2.1",
 	"android.hardware.tetheroffload.config@1.0",
 	"android.hardware.tetheroffload.control@1.0",
-	"android.hardware.thermal@1.0",
-	"android.hardware.tv.cec@1.0",
-	"android.hardware.tv.input@1.0",
 	"android.hardware.vibrator-ndk_platform",
-	"android.hardware.vibrator@1.0",
-	"android.hardware.vibrator@1.1",
-	"android.hardware.vibrator@1.2",
 	"android.hardware.weaver@1.0",
 	"android.hardware.wifi.hostapd@1.0",
 	"android.hardware.wifi.offload@1.0",
 	"android.hardware.wifi.supplicant@1.0",
 	"android.hardware.wifi.supplicant@1.1",
-	"android.hardware.wifi@1.0",
 	"android.hardware.wifi@1.1",
 	"android.hardware.wifi@1.2",
 	"android.hardwareundtrigger@2.0",
 	"android.hardwareundtrigger@2.0-core",
 	"android.hardwareundtrigger@2.1",
-	"android.hidl.allocator@1.0",
-	"android.hidl.token@1.0",
-	"android.hidl.token@1.0-utils",
-	"android.system.net.netd@1.0",
-	"android.system.wifi.keystore@1.0",
 	"libaudioroute",
-	"libaudioutils",
 	"libbinder",
 	"libcamera_metadata",
 	"libcrypto",
-	"libdiskconfig",
-	"libdumpstateutil",
 	"libexpat",
-	"libfmq",
 	"libgatekeeper",
 	"libgui",
 	"libhidlcache",
 	"libkeymaster_messages",
 	"libkeymaster_portable",
-	"libmedia_helper",
 	"libmedia_omx",
-	"libmemtrack",
-	"libnetutils",
 	"libprotobuf-cpp-full",
 	"libprotobuf-cpp-lite",
 	"libpuresoftkeymasterdevice",
-	"libradio_metadata",
 	"libselinux",
 	"libsoftkeymasterdevice",
 	"libsqlite",
 	"libssl",
 	"libstagefright_amrnb_common",
+	"libstagefright_bufferpool@2.0",
 	"libstagefright_bufferqueue_helper",
 	"libstagefright_enc_common",
 	"libstagefright_flacdec",
@@ -159,11 +105,8 @@
 	"libstagefright_soft_vpxdec",
 	"libstagefright_soft_vpxenc",
 	"libstagefright_xmlparser",
-	"libsysutils",
-	"libtinyxml2",
 	"libui",
 	"libvorbisidec",
 	"libxml2",
 	"libyuv",
-	"libziparchive",
 }
diff --git a/cc/coverage.go b/cc/coverage.go
index b6451ee..b94b628 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -43,7 +43,7 @@
 	return []interface{}{&cov.Properties}
 }
 
-func getProfileLibraryName(ctx ModuleContextIntf) string {
+func getGcovProfileLibraryName(ctx ModuleContextIntf) string {
 	// This function should only ever be called for a cc.Module, so the
 	// following statement should always succeed.
 	if ctx.useSdk() {
@@ -53,28 +53,47 @@
 	}
 }
 
+func getClangProfileLibraryName(ctx ModuleContextIntf) string {
+	if ctx.useSdk() {
+		return "libprofile-clang-extras_ndk"
+	} else {
+		return "libprofile-clang-extras"
+	}
+}
+
 func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
 	if cov.Properties.NeedCoverageVariant {
 		ctx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, coverageDepTag, getProfileLibraryName(ctx))
+		}, coverageDepTag, getGcovProfileLibraryName(ctx))
+		ctx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, coverageDepTag, getClangProfileLibraryName(ctx))
 	}
 	return deps
 }
 
 func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
-	if !ctx.DeviceConfig().NativeCoverageEnabled() {
+	gcovCoverage := ctx.DeviceConfig().NativeCoverageEnabled()
+	clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
+
+	if !gcovCoverage && !clangCoverage {
 		return flags, deps
 	}
 
 	if cov.Properties.CoverageEnabled {
 		flags.Coverage = true
-		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0")
 		cov.linkCoverage = true
 
-		// Override -Wframe-larger-than and non-default optimization
-		// flags that the module may use.
-		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
+		if gcovCoverage {
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0")
+
+			// Override -Wframe-larger-than and non-default optimization
+			// 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")
+		}
 	}
 
 	// Even if we don't have coverage enabled, if any of our object files were compiled
@@ -112,12 +131,19 @@
 	}
 
 	if cov.linkCoverage {
-		flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
+		if gcovCoverage {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
 
-		coverage := ctx.GetDirectDepWithTag(getProfileLibraryName(ctx), coverageDepTag).(*Module)
-		deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+			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")
+			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)
+			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+		}
 	}
 
 	return flags, deps
@@ -125,7 +151,7 @@
 
 func (cov *coverage) begin(ctx BaseModuleContext) {
 	// Coverage is disabled globally
-	if !ctx.DeviceConfig().NativeCoverageEnabled() {
+	if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
 		return
 	}
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8b84be8..ee24300 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -355,10 +355,10 @@
 			return
 		}
 
-		// Discard vendor-NDK-linked + recovery modules, they're duplicates of
+		// Discard vendor-NDK-linked + ramdisk + recovery modules, they're duplicates of
 		// fuzz targets we're going to package anyway.
 		if !ccModule.Enabled() || ccModule.Properties.PreventInstall ||
-			ccModule.UseVndk() || ccModule.InRecovery() {
+			ccModule.UseVndk() || ccModule.InRamdisk() || ccModule.InRecovery() {
 			return
 		}
 
diff --git a/cc/genrule.go b/cc/genrule.go
index 548d5f2..155e410 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -25,6 +25,7 @@
 
 type GenruleExtraProperties struct {
 	Vendor_available   *bool
+	Ramdisk_available  *bool
 	Recovery_available *bool
 }
 
@@ -62,6 +63,10 @@
 	return Bool(g.Vendor_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
 }
 
+func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return Bool(g.Ramdisk_available)
+}
+
 func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return Bool(g.Recovery_available)
 }
diff --git a/cc/library.go b/cc/library.go
index ae95bc5..6c8f5bf 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -385,7 +385,8 @@
 
 	// If useCoreVariant is true, the vendor variant of a VNDK library is
 	// not installed.
-	useCoreVariant bool
+	useCoreVariant       bool
+	checkSameCoreVariant bool
 
 	// Decorated interafaces
 	*baseCompiler
@@ -756,6 +757,13 @@
 		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
 	}
+	if ctx.inRamdisk() {
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Ramdisk.Exclude_static_libs)
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Ramdisk.Exclude_shared_libs)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Ramdisk.Exclude_static_libs)
+		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Ramdisk.Exclude_shared_libs)
+		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Ramdisk.Exclude_static_libs)
+	}
 
 	return deps
 }
@@ -1039,7 +1047,7 @@
 			isVendor := ctx.useVndk()
 			isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
 
-			if !ctx.inRecovery() && (isProduct || (isOwnerPlatform == isVendor)) {
+			if !ctx.inRamdisk() && !ctx.inRecovery() && (isProduct || (isOwnerPlatform == isVendor)) {
 				dir = android.PathForModuleGen(ctx, "sysprop/public", "include")
 			}
 		}
@@ -1096,8 +1104,11 @@
 			if ctx.isVndkSp() {
 				library.baseInstaller.subDir = "vndk-sp"
 			} else if ctx.isVndk() {
-				if ctx.DeviceConfig().VndkUseCoreVariant() && !ctx.mustUseVendorVariant() {
-					library.useCoreVariant = true
+				if !ctx.mustUseVendorVariant() && !ctx.isVndkExt() {
+					library.checkSameCoreVariant = true
+					if ctx.DeviceConfig().VndkUseCoreVariant() {
+						library.useCoreVariant = true
+					}
 				}
 				library.baseInstaller.subDir = "vndk"
 			}
@@ -1114,7 +1125,7 @@
 			// The original path becomes a symlink to the corresponding file in the
 			// runtime APEX.
 			translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
-			if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && !translatedArch && !ctx.inRecovery() {
+			if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && !translatedArch && !ctx.inRamdisk() && !ctx.inRecovery() {
 				if ctx.Device() {
 					library.installSymlinkToRuntimeApex(ctx, file)
 				}
@@ -1129,7 +1140,7 @@
 	}
 
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
-		!ctx.useVndk() && !ctx.inRecovery() && ctx.Device() &&
+		!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && ctx.Device() &&
 		library.baseLinker.sanitize.isUnsanitizedVariant() &&
 		!library.buildStubs() {
 		installPath := getNdkSysrootBase(ctx).Join(
diff --git a/cc/linkable.go b/cc/linkable.go
index 106092b..3c46d9d 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -38,6 +38,9 @@
 	Shared() bool
 	Toc() android.OptionalPath
 
+	InRamdisk() bool
+	OnlyInRamdisk() bool
+
 	InRecovery() bool
 	OnlyInRecovery() bool
 
diff --git a/cc/linker.go b/cc/linker.go
index 61ae757..5a16cbd 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -141,6 +141,19 @@
 			// of the C/C++ module.
 			Exclude_header_libs []string
 		}
+		Ramdisk struct {
+			// list of static libs that only should be used to build the recovery
+			// variant of the C/C++ module.
+			Static_libs []string
+
+			// list of shared libs that should not be used to build
+			// the ramdisk variant of the C/C++ module.
+			Exclude_shared_libs []string
+
+			// list of static libs that should not be used to build
+			// the ramdisk variant of the C/C++ module.
+			Exclude_static_libs []string
+		}
 	}
 
 	// make android::build:GetBuildNumber() available containing the build ID.
@@ -151,6 +164,9 @@
 
 	// local file name to pass to the linker as --version_script
 	Version_script *string `android:"path,arch_variant"`
+
+	// list of static libs that should not be used to build this module
+	Exclude_static_libs []string
 }
 
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -196,6 +212,8 @@
 	deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, linker.Properties.Export_shared_lib_headers...)
 	deps.ReexportGeneratedHeaders = append(deps.ReexportGeneratedHeaders, linker.Properties.Export_generated_headers...)
 
+	deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Exclude_static_libs)
+
 	if Bool(linker.Properties.Use_version_lib) {
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs, "libbuildversion")
 	}
@@ -223,12 +241,20 @@
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
 	}
 
+	if ctx.inRamdisk() {
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Target.Recovery.Static_libs...)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+	}
+
 	if ctx.toolchain().Bionic() {
-		// libclang_rt.builtins, libgcc and libatomic have to be last on the command line
+		// libclang_rt.builtins and libatomic have to be last on the command line
 		if !Bool(linker.Properties.No_libcrt) {
 			deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 			deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
-			deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc_stripped")
 		}
 
 		systemSharedLibs := linker.Properties.System_shared_libs
@@ -341,6 +367,8 @@
 				flags.Global.LdFlags = append(flags.Global.LdFlags,
 					"-Wl,--pack-dyn-relocs=android+relr",
 					"-Wl,--use-android-relr-tags")
+			} else if CheckSdkVersionAtLeast(ctx, 23) {
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=android")
 			}
 		}
 	} else {
diff --git a/cc/pgo.go b/cc/pgo.go
index 0072355..e341d03 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -177,6 +177,10 @@
 		// if profileFile gets updated
 		flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
+
+		if props.isSampling() {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
+		}
 	}
 	return flags
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index c4aeb96..93c4b41 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -351,8 +351,8 @@
 	}
 
 	// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
-	// Keep libc instrumented so that recovery can run hwasan-instrumented code if necessary.
-	if ctx.inRecovery() && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
+	// Keep libc instrumented so that ramdisk / recovery can run hwasan-instrumented code if necessary.
+	if (ctx.inRamdisk() || ctx.inRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
 		s.Hwaddress = nil
 	}
 
diff --git a/cc/stl.go b/cc/stl.go
index 5ccd44a..af015f9 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -171,11 +171,13 @@
 			deps.StaticLibs = append(deps.StaticLibs, "libc++demangle")
 		}
 		if ctx.toolchain().Bionic() {
-			if ctx.Arch().ArchType == android.Arm {
-				deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
-			}
 			if ctx.staticBinary() {
 				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc")
+				if ctx.Arch().ArchType == android.Arm {
+					deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
+				} else {
+					deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
+				}
 			}
 		}
 	case "":
@@ -196,6 +198,8 @@
 		}
 		if ctx.Arch().ArchType == android.Arm {
 			deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
+		} else {
+			deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
 		}
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
diff --git a/cc/testing.go b/cc/testing.go
index bc31077..ba8ed95 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -23,6 +23,7 @@
 	android.RegisterPrebuiltMutators(ctx)
 
 	RegisterCCBuildComponents(ctx)
+	RegisterBinaryBuildComponents(ctx)
 	RegisterLibraryBuildComponents(ctx)
 
 	ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
@@ -135,6 +136,7 @@
 			name: "libc",
 			no_libcrt: true,
 			nocrt: true,
+			stl: "none",
 			system_shared_libs: [],
 			recovery_available: true,
 		}
@@ -146,6 +148,7 @@
 			name: "libm",
 			no_libcrt: true,
 			nocrt: true,
+			stl: "none",
 			system_shared_libs: [],
 			recovery_available: true,
 		}
@@ -157,6 +160,7 @@
 			name: "libdl",
 			no_libcrt: true,
 			nocrt: true,
+			stl: "none",
 			system_shared_libs: [],
 			recovery_available: true,
 		}
@@ -305,8 +309,6 @@
 
 func CreateTestContext() *android.TestContext {
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("cc_binary", BinaryFactory)
-	ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
 	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
 	ctx.RegisterModuleType("cc_test", TestFactory)
 	ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
diff --git a/cc/vndk.go b/cc/vndk.go
index 872a473..ab73035 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -351,7 +351,7 @@
 	if lib, ok := m.linker.(libraryInterface); ok {
 		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
 			mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
-		return lib.shared() && m.UseVndk() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
+		return lib.shared() && m.inVendor() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
 	}
 	return false
 }
@@ -670,7 +670,7 @@
 		if m.Target().NativeBridge == android.NativeBridgeEnabled {
 			return nil, "", false
 		}
-		if !m.UseVndk() || !m.IsForPlatform() || !m.installable() || !m.inVendor() {
+		if !m.UseVndk() || !m.installable() || !m.inVendor() {
 			return nil, "", false
 		}
 		l, ok := m.linker.(vndkSnapshotLibraryInterface)
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index a9be612..a95aca9 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -667,8 +667,10 @@
 		return nil
 	}
 	var err error
-	fiz.reader, err = zip.OpenReader(fiz.Name())
-	return err
+	if fiz.reader, err = zip.OpenReader(fiz.Name()); err != nil {
+		return fmt.Errorf("%s: %s", fiz.Name(), err.Error())
+	}
+	return nil
 }
 
 func main() {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 974c644..db61fba 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -174,6 +174,10 @@
 	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"build_error")))
 	stat.AddOutput(status.NewCriticalPath(log))
 
+	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"))
 
 	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
diff --git a/docs/perf.md b/docs/perf.md
index c3a2647..538adff 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -8,7 +8,7 @@
 viewed.  Just open `$OUT_DIR/build.trace.gz` in Chrome's <chrome://tracing>, or
 with [catapult's trace viewer][catapult trace_viewer]. The last few traces are
 stored in `build.trace.#.gz` (larger numbers are older). The associated logs
-are stored in `soong.#.log`.
+are stored in `soong.#.log` and `verbose.#.log.gz`.
 
 ![trace example](./trace_example.png)
 
@@ -31,29 +31,29 @@
 
 In most cases, we've found that the fast-path is slow because all of the
 `$(shell)` commands need to be re-executed to determine if their output changed.
-The `$OUT_DIR/soong.log` contains statistics from the regen check:
+The `$OUT_DIR/verbose.log.gz` contains statistics from the regen check:
 
 ```
-.../kati.go:127: *kati*: regen check time: 1.699207
-.../kati.go:127: *kati*: glob time (regen): 0.377193 / 33609
-.../kati.go:127: *kati*: shell time (regen): 1.313529 / 184
-.../kati.go:127: *kati*:  0.217 find device vendor -type f -name \*.pk8 -o -name verifiedboot\* -o -name \*.x509.pem -o -name oem\*.prop | sort
-.../kati.go:127: *kati*:  0.105 cd packages/apps/Dialer ; find -L . -type d -name "res"
-.../kati.go:127: *kati*:  0.035 find device vendor -maxdepth 4 -name '*_aux_variant_config.mk' -o -name '*_aux_os_config.mk' | sort
-.../kati.go:127: *kati*:  0.029 cd frameworks/base ; find -L  core/java graphics/java location/java media/java media/mca/effect/java media/mca/filterfw/java media/mca/filterpacks/java drm/java opengl/java sax/java telecomm/java telephony/java wifi/java lowpan/java keystore/java rs/java ../opt/telephony/src/java/android/telephony ../opt/telephony/src/java/android/telephony/gsm ../opt/net/voip/src/java/android/net/rtp ../opt/net/voip/src/java/android/net/sip   -name "*.html" -and -not -name ".*"
-.../kati.go:127: *kati*:  0.025 test -d device && find -L device -maxdepth 4 -path '*/marlin/BoardConfig.mk'
-.../kati.go:127: *kati*:  0.023 find packages/apps/Settings/tests/robotests -type f -name '*Test.java' | sed -e 's!.*\(com/google.*Test\)\.java!\1!' -e 's!.*\(com/android.*Test\)\.java!\1!' | sed 's!/!\.!g' | cat
-.../kati.go:127: *kati*:  0.022 test -d vendor && find -L vendor -maxdepth 4 -path '*/marlin/BoardConfig.mk'
-.../kati.go:127: *kati*:  0.017 cd cts/tests/tests/shortcutmanager/packages/launchermanifest ; find -L  ../src -name "*.java" -and -not -name ".*"
-.../kati.go:127: *kati*:  0.016 cd cts/tests/tests/shortcutmanager/packages/launchermanifest ; find -L  ../../common/src -name "*.java" -and -not -name ".*"
-.../kati.go:127: *kati*:  0.015 cd libcore && (find luni/src/test/java -name "*.java" 2> /dev/null) | grep -v -f java_tests_blacklist
-.../kati.go:127: *kati*: stat time (regen): 0.250384 / 4405
+verbose: *kati*: regen check time: 0.754030
+verbose: *kati*: glob time (regen): 0.545859 / 43840
+verbose: *kati*: shell time (regen): 0.278095 / 66 (59 unique)
+verbose: *kati*:   0.012 / 1 mkdir -p out/target/product/generic && echo Android/aosp_arm/generic:R/AOSP.MASTER/$(date -d @$(cat out/build_date.txt) +%m%d%H%M):eng/test-keys >out/target/product/generic/build_fingerprint.txt && grep " " out/target/product/generic/build_fingerprint.txt
+verbose: *kati*:   0.010 / 1 echo 'com.android.launcher3.config.FlagOverrideSampleTest com.android.launcher3.logging.FileLogTest com.android.launcher3.model.AddWorkspaceItemsTaskTest com.android.launcher3.model.CacheDataUpdatedTaskTest com.android.launcher3.model.DbDowngradeHelperTest com.android.launcher3.model.GridBackupTableTest com.android.launcher3.model.GridSizeMigrationTaskTest com.android.launcher3.model.PackageInstallStateChangedTaskTest com.android.launcher3.popup.PopupPopulatorTest com.android.launcher3.util.GridOccupancyTest com.android.launcher3.util.IntSetTest' | tr ' ' '\n' | cat
+verbose: *kati*:   0.010 / 1 cd cts/tests/framework/base/windowmanager ; find -L  * -name "Components.java" -and -not -name ".*"
+verbose: *kati*:   0.010 / 1 git -C test/framework/build log -s -n 1 --format="%cd" --date=format:"%Y%m%d_%H%M%S" 2>/dev/null
+verbose: *kati*:   0.009 / 2 cd development/samples/ShortcutDemo/publisher ; find -L  ../common/src -name "*.java" -and -not -name ".*"
+verbose: *kati*:   0.009 / 2 cd development/samples/ShortcutDemo/launcher ; find -L  ../common/src -name "*.java" -and -not -name ".*"
+verbose: *kati*:   0.009 / 1 if ! cmp -s out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk; then mv out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk; else rm out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp; fi
+verbose: *kati*:   0.008 / 1 mkdir -p out/target/product/generic && echo R/AOSP.MASTER/$(cat out/build_number.txt):eng/test-keys >out/target/product/generic/build_thumbprint.txt && grep " " out/target/product/generic/build_thumbprint.txt
+verbose: *kati*:   0.007 / 1 echo 'com.android.customization.model.clock.BaseClockManagerTest com.android.customization.model.clock.ClockManagerTest com.android.customization.model.grid.GridOptionsManagerTest com.android.customization.model.theme.ThemeManagerTest' | tr ' ' '\n' | cat
+verbose: *kati*:   0.007 / 1 uname -sm
+verbose: *kati*: stat time (regen): 0.361907 / 1241
 ```
 
-In this case, the total time spent checking was 1.69 seconds, even though the
+In this case, the total time spent checking was 0.75 seconds, even though the
 other "(regen)" numbers add up to more than that (some parts are parallelized
-where possible). The biggest contributor is the `$(shell)` times -- 184
-executions took a total of 1.31 seconds. The top 10 longest shell functions are
+where possible). Often times, the biggest contributor is the `$(shell)` times
+-- in this case, 66 calls took 0.27s. The top 10 longest shell functions are
 printed.
 
 All the longest commands in this case are all variants of a call to `find`, but
@@ -96,7 +96,8 @@
 $(sort $(shell find device vendor -type -f -a -name \*.pk8 -o -name verifiedboot\* -o -name \*.x509.pem -o -name oem\*.prop))
 ```
 
-Kati is learning about the implicit `-a` in [this change](https://github.com/google/kati/pull/132)
+Kati has now learned about the implicit `-a`, so this particular change is no
+longer necessary, but the basic concept holds.
 
 #### Kati regens too often
 
@@ -113,6 +114,46 @@
 is available when ckati is run with `--regen_debug`, but that can be a lot of
 data to understand.
 
+#### Debugging the slow path
+
+Kati will now dump out information about which Makefiles took the most time to
+execute. This is also in the `verbose.log.gz` file:
+
+```
+verbose: *kati*: included makefiles: 73.640833 / 232810 (1066 unique)
+verbose: *kati*:  18.389 /     1 out/soong/Android-aosp_arm.mk
+verbose: *kati*:  13.137 / 20144 build/make/core/soong_cc_prebuilt.mk
+verbose: *kati*:  11.743 / 27666 build/make/core/base_rules.mk
+verbose: *kati*:   2.289 /     1 art/Android.mk
+verbose: *kati*:   2.054 /     1 art/build/Android.cpplint.mk
+verbose: *kati*:   1.955 / 28269 build/make/core/clear_vars.mk
+verbose: *kati*:   1.795 /   283 build/make/core/package.mk
+verbose: *kati*:   1.790 /   283 build/make/core/package_internal.mk
+verbose: *kati*:   1.757 / 17382 build/make/core/link_type.mk
+verbose: *kati*:   1.078 /   297 build/make/core/aapt2.mk
+```
+
+This shows that soong_cc_prebuilt.mk was included 20144 times, for a total time
+spent of 13.137 secounds. While Android-aosp_arm.mk was only included once, and
+took 18.389 seconds. In this case, Android-aosp_arm.mk is the only file that
+includes soong_cc_prebuilt.mk, so we can safely assume that 13 of the 18 seconds
+in Android-aosp_arm.mk was actually spent within soong_cc_prebuilt.mk (or
+something that it included, like base_rules.mk).
+
+By default this only includes the top 10 entries, but you can ask for the stats
+for any makefile to be printed with `$(KATI_profile_makefile)`:
+
+```
+$(KATI_profile_makefile build/make/core/product.mk)
+```
+
+With these primitives, it's possible to get the timing information for small
+chunks, or even single lines, of a makefile. Just move the lines you want to
+measure into a new makefile, and replace their use with an `include` of the
+new makefile. It's possible to analyze where the time is being spent by doing
+a binary search using this method, but you do need to be careful not to split
+conditionals across two files (the ifeq/else/endif must all be in the same file).
+
 ### Ninja
 
 #### Understanding why something rebuilt
@@ -164,15 +205,14 @@
 
 ### Common
 
-#### mm
+### <= Android 10 (Q): mm
 
 Soong always loads the entire module graph, so as modules convert from Make to
 Soong, `mm` is becoming closer to `mma`. This produces more correct builds, but
 does slow down builds, as we need to verify/produce/load a larger build graph.
 
-We're exploring a few options to speed up build startup, one being [an
-experimental set of ninja patches][ninja parse optimization],
-though that's not the current path we're working towards.
+As of Android Q, loading large build graphs is fast, and in Android R, `mm` is
+now an alias of `mma`.
 
 ### Android 8.1 (Oreo MR1)
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 57ca9bc..a0008d3 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -30,10 +30,18 @@
 )
 
 func init() {
-	android.RegisterModuleType("genrule_defaults", defaultsFactory)
+	registerGenruleBuildComponents(android.InitRegistrationContext)
+}
 
-	android.RegisterModuleType("gensrcs", GenSrcsFactory)
-	android.RegisterModuleType("genrule", GenRuleFactory)
+func registerGenruleBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
+
+	ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
+	ctx.RegisterModuleType("genrule", GenRuleFactory)
+
+	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
+	})
 }
 
 var (
@@ -166,7 +174,7 @@
 	return g.outputDeps
 }
 
-func (g *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func toolDepsMutator(ctx android.BottomUpMutatorContext) {
 	if g, ok := ctx.Module().(*Module); ok {
 		for _, tool := range g.properties.Tools {
 			tag := hostToolDependencyTag{label: tool}
@@ -542,6 +550,7 @@
 
 func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext)                 {}
 func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool            { return false }
+func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool         { return false }
 func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool        { return false }
 func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
 func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index ea49e08..7eb43ac 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -55,10 +55,10 @@
 
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.RegisterModuleType("genrule", GenRuleFactory)
-	ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
-	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
 	ctx.RegisterModuleType("tool", toolFactory)
+
+	registerGenruleBuildComponents(ctx)
+
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.Register(config)
 
diff --git a/java/aar.go b/java/aar.go
index ae064e5..d707e36 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -15,11 +15,12 @@
 package java
 
 import (
-	"android/soong/android"
 	"fmt"
 	"path/filepath"
 	"strings"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -176,7 +177,10 @@
 	linkDeps = append(linkDeps, assetFiles...)
 
 	// SDK version flags
-	minSdkVersion := sdkVersionOrDefault(ctx, sdkContext.minSdkVersion())
+	minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
+	if err != nil {
+		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	}
 
 	linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
 	linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
@@ -523,22 +527,22 @@
 	exportedStaticPackages android.Paths
 }
 
-func (a *AARImport) sdkVersion() string {
-	return String(a.properties.Sdk_version)
+func (a *AARImport) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(a.properties.Sdk_version))
 }
 
 func (a *AARImport) systemModules() string {
 	return ""
 }
 
-func (a *AARImport) minSdkVersion() string {
+func (a *AARImport) minSdkVersion() sdkSpec {
 	if a.properties.Min_sdk_version != nil {
-		return *a.properties.Min_sdk_version
+		return sdkSpecFrom(*a.properties.Min_sdk_version)
 	}
 	return a.sdkVersion()
 }
 
-func (a *AARImport) targetSdkVersion() string {
+func (a *AARImport) targetSdkVersion() sdkSpec {
 	return a.sdkVersion()
 }
 
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 021883e..e3646f5 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -59,7 +59,7 @@
 	if isLibrary {
 		args = append(args, "--library")
 	} else {
-		minSdkVersion, err := sdkVersionToNumber(ctx, sdkContext.minSdkVersion())
+		minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersion(ctx)
 		if err != nil {
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
@@ -92,17 +92,28 @@
 	}
 
 	var deps android.Paths
-	targetSdkVersion := sdkVersionOrDefault(ctx, sdkContext.targetSdkVersion())
-	if targetSdkVersion == ctx.Config().PlatformSdkCodename() &&
-		ctx.Config().UnbundledBuild() &&
-		!ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
-		ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
-		apiFingerprint := ApiFingerprintPath(ctx)
-		targetSdkVersion += fmt.Sprintf(".$$(cat %s)", apiFingerprint.String())
-		deps = append(deps, apiFingerprint)
+	targetSdkVersion, err := sdkContext.targetSdkVersion().effectiveVersionString(ctx)
+	if err != nil {
+		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
+	}
+	if UseApiFingerprint(ctx, targetSdkVersion) {
+		targetSdkVersion += fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+		deps = append(deps, ApiFingerprintPath(ctx))
+	}
+
+	minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
+	if err != nil {
+		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	}
+	if UseApiFingerprint(ctx, minSdkVersion) {
+		minSdkVersion += fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+		deps = append(deps, ApiFingerprintPath(ctx))
 	}
 
 	fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
+	if err != nil {
+		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        manifestFixerRule,
 		Description: "fix manifest",
@@ -110,7 +121,7 @@
 		Implicits:   deps,
 		Output:      fixedManifest,
 		Args: map[string]string{
-			"minSdkVersion":    sdkVersionOrDefault(ctx, sdkContext.minSdkVersion()),
+			"minSdkVersion":    minSdkVersion,
 			"targetSdkVersion": targetSdkVersion,
 			"args":             strings.Join(args, " "),
 		},
diff --git a/java/androidmk.go b/java/androidmk.go
index 04bf15c..6d4d40b 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -93,7 +93,7 @@
 					if len(library.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
 					}
-					entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion())
+					entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion().raw)
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
 					entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
 
@@ -174,7 +174,7 @@
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion())
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion().raw)
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
@@ -223,7 +223,7 @@
 				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
 				entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion())
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion().raw)
 			},
 		},
 	}}
@@ -663,11 +663,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
 				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", a.Privileged())
-				if a.certificate != nil {
-					entries.SetPath("LOCAL_CERTIFICATE", a.certificate.Pem)
-				} else {
-					entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED")
-				}
+				entries.SetString("LOCAL_CERTIFICATE", a.certificate.AndroidMkString())
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
 				if len(a.dexpreopter.builtInstalled) > 0 {
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
@@ -695,3 +691,16 @@
 	}
 	entries.AddStrings("LOCAL_COMPATIBILITY_SUPPORT_FILES", testFiles...)
 }
+
+func (r *RuntimeResourceOverlay) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(r.outputFile),
+		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath())
+			},
+		},
+	}}
+}
diff --git a/java/app.go b/java/app.go
index a6b3408..a27996c 100755
--- a/java/app.go
+++ b/java/app.go
@@ -47,6 +47,7 @@
 	ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
 	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
 	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
+	ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
 }
 
 // AndroidManifest.xml merging
@@ -153,16 +154,31 @@
 	return a.outputFile
 }
 
+func (a *AndroidApp) Certificate() Certificate {
+	return a.certificate
+}
+
 var _ AndroidLibraryDependency = (*AndroidApp)(nil)
 
 type Certificate struct {
-	Pem, Key android.Path
+	Pem, Key  android.Path
+	presigned bool
+}
+
+var presignedCertificate = Certificate{presigned: true}
+
+func (c Certificate) AndroidMkString() string {
+	if c.presigned {
+		return "PRESIGNED"
+	} else {
+		return c.Pem.String()
+	}
 }
 
 func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.Module.deps(ctx)
 
-	if String(a.appProperties.Stl) == "c++_shared" && a.sdkVersion() == "" {
+	if String(a.appProperties.Stl) == "c++_shared" && !a.sdkVersion().specified() {
 		ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
 	}
 
@@ -211,7 +227,7 @@
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
-	minSdkVersion, err := sdkVersionToNumber(ctx, a.minSdkVersion())
+	minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.minSdkVersion(), err)
 	}
@@ -404,12 +420,15 @@
 		if certPropValue != "" {
 			defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
 			mainCert = Certificate{
-				defaultDir.Join(ctx, certPropValue+".x509.pem"),
-				defaultDir.Join(ctx, certPropValue+".pk8"),
+				Pem: defaultDir.Join(ctx, certPropValue+".x509.pem"),
+				Key: defaultDir.Join(ctx, certPropValue+".pk8"),
 			}
 		} else {
 			pem, key := ctx.Config().DefaultAppCertificate(ctx)
-			mainCert = Certificate{pem, key}
+			mainCert = Certificate{
+				Pem: pem,
+				Key: key,
+			}
 		}
 		certificates = append([]Certificate{mainCert}, certificates...)
 	}
@@ -644,22 +663,40 @@
 	}
 	a.generateAndroidBuildActions(ctx)
 
-	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
+	testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
 		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config)
-	if a.overridableAppProperties.Package_name != nil {
-		fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", "AndroidTest.xml")
-		rule := android.NewRuleBuilder()
-		rule.Command().BuiltTool(ctx, "test_config_fixer").
-			FlagWithInput("--manifest ", a.manifestPath).
-			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name).
-			Input(a.testConfig).
-			Output(fixedConfig)
-		rule.Build(pctx, ctx, "fix_test_config", "fix test config")
-		a.testConfig = fixedConfig
-	}
+	a.testConfig = a.FixTestConfig(ctx, testConfig)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
 
+func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path {
+	if testConfig == nil {
+		return nil
+	}
+
+	fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", "AndroidTest.xml")
+	rule := android.NewRuleBuilder()
+	command := rule.Command().BuiltTool(ctx, "test_config_fixer").Input(testConfig).Output(fixedConfig)
+	fixNeeded := false
+
+	if ctx.ModuleName() != a.installApkName {
+		fixNeeded = true
+		command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
+	}
+
+	if a.overridableAppProperties.Package_name != nil {
+		fixNeeded = true
+		command.FlagWithInput("--manifest ", a.manifestPath).
+			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
+	}
+
+	if fixNeeded {
+		rule.Build(pctx, ctx, "fix_test_config", "fix test config")
+		return fixedConfig
+	}
+	return testConfig
+}
+
 func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.AndroidApp.DepsMutator(ctx)
 }
@@ -779,8 +816,8 @@
 func (c *AndroidAppCertificate) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	cert := String(c.properties.Certificate)
 	c.Certificate = Certificate{
-		android.PathForModuleSrc(ctx, cert+".x509.pem"),
-		android.PathForModuleSrc(ctx, cert+".pk8"),
+		Pem: android.PathForModuleSrc(ctx, cert+".x509.pem"),
+		Key: android.PathForModuleSrc(ctx, cert+".pk8"),
 	}
 }
 
@@ -837,7 +874,7 @@
 	archVariants interface{}
 
 	outputFile  android.Path
-	certificate *Certificate
+	certificate Certificate
 
 	dexpreopter
 
@@ -926,6 +963,16 @@
 
 func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
 	ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+	// Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
+	// with them may invalidate pre-existing signature data.
+	if ctx.InstallInTestcases() && Bool(a.properties.Presigned) {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Output: outputPath,
+			Input:  inputPath,
+		})
+		return
+	}
 	rule := android.NewRuleBuilder()
 	rule.Command().
 		Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
@@ -1003,6 +1050,8 @@
 	var installDir android.InstallPath
 	if Bool(a.properties.Privileged) {
 		installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
+	} else if ctx.InstallInTestcases() {
+		installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
 	} else {
 		installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
 	}
@@ -1033,7 +1082,7 @@
 		if len(certificates) != 1 {
 			ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
 		}
-		a.certificate = &certificates[0]
+		a.certificate = certificates[0]
 		signed := android.PathForModuleOut(ctx, "signed", ctx.ModuleName()+".apk")
 		SignAppPackage(ctx, signed, dexOutput, certificates)
 		a.outputFile = signed
@@ -1041,6 +1090,7 @@
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", ctx.ModuleName()+".apk")
 		TransformZipAlign(ctx, alignedApk, dexOutput)
 		a.outputFile = alignedApk
+		a.certificate = presignedCertificate
 	}
 
 	// TODO: Optionally compress the output apk.
@@ -1067,6 +1117,10 @@
 	return nil
 }
 
+func (a *AndroidAppImport) Certificate() Certificate {
+	return a.certificate
+}
+
 var dpiVariantGroupType reflect.Type
 var archVariantGroupType reflect.Type
 
@@ -1159,6 +1213,10 @@
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
 
+func (a *AndroidTestImport) InstallInTestcases() bool {
+	return true
+}
+
 // android_test_import imports a prebuilt test apk with additional processing specified in the
 // module. DPI or arch variant configurations can be made as with android_app_import.
 func AndroidTestImportFactory() android.Module {
@@ -1178,6 +1236,96 @@
 	return module
 }
 
+type RuntimeResourceOverlay struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	aapt
+
+	properties RuntimeResourceOverlayProperties
+
+	outputFile android.Path
+	installDir android.InstallPath
+}
+
+type RuntimeResourceOverlayProperties struct {
+	// the name of a certificate in the default certificate directory or an android_app_certificate
+	// module name in the form ":module".
+	Certificate *string
+
+	// optional theme name. If specified, the overlay package will be applied
+	// only when the ro.boot.vendor.overlay.theme system property is set to the same value.
+	Theme *string
+
+	// if not blank, set to the version of the sdk to compile against.
+	// Defaults to compiling against the current platform.
+	Sdk_version *string
+
+	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+	// Defaults to sdk_version if not set.
+	Min_sdk_version *string
+}
+
+func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
+	sdkDep := decodeSdkDep(ctx, sdkContext(r))
+	if sdkDep.hasFrameworkLibs() {
+		r.aapt.deps(ctx, sdkDep)
+	}
+
+	cert := android.SrcIsModule(String(r.properties.Certificate))
+	if cert != "" {
+		ctx.AddDependency(ctx.Module(), certificateTag, cert)
+	}
+}
+
+func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Compile and link resources
+	r.aapt.hasNoCode = true
+	// Do not remove resources without default values nor dedupe resource configurations with the same value
+	r.aapt.buildActions(ctx, r, "--no-resource-deduping", "--no-resource-removal")
+
+	// Sign the built package
+	_, certificates := collectAppDeps(ctx, false)
+	certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
+	signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
+	SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates)
+
+	r.outputFile = signed
+	r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
+	ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
+}
+
+func (r *RuntimeResourceOverlay) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(r.properties.Sdk_version))
+}
+
+func (r *RuntimeResourceOverlay) systemModules() string {
+	return ""
+}
+
+func (r *RuntimeResourceOverlay) minSdkVersion() sdkSpec {
+	if r.properties.Min_sdk_version != nil {
+		return sdkSpecFrom(*r.properties.Min_sdk_version)
+	}
+	return r.sdkVersion()
+}
+
+func (r *RuntimeResourceOverlay) targetSdkVersion() sdkSpec {
+	return r.sdkVersion()
+}
+
+// runtime_resource_overlay generates a resource-only apk file that can overlay application and
+// system resources at run time.
+func RuntimeResourceOverlayFactory() android.Module {
+	module := &RuntimeResourceOverlay{}
+	module.AddProperties(
+		&module.properties,
+		&module.aaptProperties)
+
+	InitJavaModule(module, android.DeviceSupported)
+
+	return module
+}
+
 type UsesLibraryProperties struct {
 	// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file.
 	Uses_libs []string
diff --git a/java/app_test.go b/java/app_test.go
index 9bdef4e..2682682 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1305,6 +1305,87 @@
 	}
 }
 
+func TestAndroidTest_FixTestConfig(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			package_name: "com.android.foo",
+			sdk_version: "current",
+		}
+
+		android_test {
+			name: "foo_test",
+			srcs: ["b.java"],
+			instrumentation_for: "foo",
+		}
+
+		android_test {
+			name: "bar_test",
+			srcs: ["b.java"],
+			package_name: "com.android.bar.test",
+			instrumentation_for: "foo",
+		}
+
+		override_android_test {
+			name: "baz_test",
+			base: "foo_test",
+			package_name: "com.android.baz.test",
+		}
+		`)
+
+	testCases := []struct {
+		moduleName    string
+		variantName   string
+		expectedFlags []string
+	}{
+		{
+			moduleName:  "foo_test",
+			variantName: "android_common",
+		},
+		{
+			moduleName:  "bar_test",
+			variantName: "android_common",
+			expectedFlags: []string{
+				"--manifest " + buildDir + "/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
+				"--package-name com.android.bar.test",
+			},
+		},
+		{
+			moduleName:  "foo_test",
+			variantName: "android_common_baz_test",
+			expectedFlags: []string{
+				"--manifest " + buildDir +
+					"/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
+				"--package-name com.android.baz.test",
+				"--test-file-name baz_test.apk",
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		variant := ctx.ModuleForTests(test.moduleName, test.variantName)
+		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")
+
+		if len(test.expectedFlags) > 0 {
+			if params.Rule == nil {
+				t.Errorf("test_config_fixer was expected to run, but didn't")
+			} else {
+				for _, flag := range test.expectedFlags {
+					if !strings.Contains(params.RuleParams.Command, flag) {
+						t.Errorf("Flag %q was not found in command: %q", flag, params.RuleParams.Command)
+					}
+				}
+			}
+		} else {
+			if params.Rule != nil {
+				t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command)
+			}
+		}
+
+	}
+}
+
 func TestAndroidAppImport(t *testing.T) {
 	ctx, _ := testJava(t, `
 		android_app_import {
@@ -1628,6 +1709,40 @@
 	}
 }
 
+func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_test_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "cert/new_cert",
+			data: [
+				"testdata/data",
+			],
+		}
+
+		android_test_import {
+			name: "foo_presigned",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			data: [
+				"testdata/data",
+			],
+		}
+		`)
+
+	variant := ctx.ModuleForTests("foo", "android_common")
+	jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+	if !strings.HasPrefix(jniRule, "if (zipinfo") {
+		t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
+	}
+
+	variant = ctx.ModuleForTests("foo_presigned", "android_common")
+	jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
+	if jniRule != android.Cp.String() {
+		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+	}
+}
+
 func TestStl(t *testing.T) {
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
@@ -2092,3 +2207,54 @@
 		}
 	}
 }
+
+func TestRuntimeResourceOverlay(t *testing.T) {
+	ctx, config := testJava(t, `
+		runtime_resource_overlay {
+			name: "foo",
+			certificate: "platform",
+			product_specific: true,
+			aaptflags: ["--keep-raw-values"],
+		}
+
+		runtime_resource_overlay {
+			name: "foo_themed",
+			certificate: "platform",
+			product_specific: true,
+			theme: "faza",
+		}
+		`)
+
+	m := ctx.ModuleForTests("foo", "android_common")
+
+	// Check AAPT2 link flags.
+	aapt2Flags := m.Output("package-res.apk").Args["flags"]
+	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
+	absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
+	if len(absentFlags) > 0 {
+		t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
+	}
+
+	// Check cert signing flag.
+	signedApk := m.Output("signed/foo.apk")
+	signingFlag := signedApk.Args["certificates"]
+	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+	if expected != signingFlag {
+		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+	}
+
+	// Check device location.
+	path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+	expectedPath := []string{"/tmp/target/product/test_device/product/overlay"}
+	if !reflect.DeepEqual(path, expectedPath) {
+		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+	}
+
+	// A themed module has a different device location
+	m = ctx.ModuleForTests("foo_themed", "android_common")
+	path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+	expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
+	if !reflect.DeepEqual(path, expectedPath) {
+		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+	}
+}
diff --git a/java/builder.go b/java/builder.go
index 417a7fa..f9b5367 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -38,7 +38,7 @@
 	// this, all java rules write into separate directories and then are combined into a .jar file
 	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
 	// .srcjar files are unzipped into a temporary directory when compiled with javac.
-	javac = pctx.AndroidRemoteStaticRule("javac", android.SUPPORTS_GOMA,
+	javac = pctx.AndroidRemoteStaticRule("javac", android.RemoteRuleSupports{Goma: true, RBE: true, RBEFlag: android.RBE_JAVAC},
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
@@ -64,6 +64,8 @@
 
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+	_ = pctx.VariableFunc("kytheCuEncoding",
+		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
 	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
 	// Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about
 	// "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
@@ -76,6 +78,7 @@
 				`KYTHE_ROOT_DIRECTORY=. KYTHE_OUTPUT_FILE=$out ` +
 				`KYTHE_CORPUS=${kytheCorpus} ` +
 				`KYTHE_VNAMES=${kytheVnames} ` +
+				`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
 				`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
 				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
 				`-jar ${config.JavaKytheExtractorJar} ` +
diff --git a/java/config/config.go b/java/config/config.go
index 9738454..7f446e5 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -45,7 +45,9 @@
 		"core-icu4j",
 		"core-oj",
 		"core-libart",
+		// TODO: Could this be all updatable bootclasspath jars?
 		"updatable-media",
+		"framework-sdkextensions",
 		"ike",
 	}
 )
@@ -146,6 +148,20 @@
 		return ""
 	})
 
+	pctx.VariableFunc("R8Wrapper", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("R8_WRAPPER"); override != "" {
+			return override + " "
+		}
+		return ""
+	})
+
+	pctx.VariableFunc("D8Wrapper", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("D8_WRAPPER"); override != "" {
+			return override + " "
+		}
+		return ""
+	})
+
 	pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
 
 	pctx.HostBinToolVariable("ManifestCheckCmd", "manifest_check")
diff --git a/java/dex.go b/java/dex.go
index cd6d90d..6afdb6d 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -22,10 +22,10 @@
 	"android/soong/android"
 )
 
-var d8 = pctx.AndroidStaticRule("d8",
+var d8 = pctx.AndroidRemoteStaticRule("d8", android.RemoteRuleSupports{RBE: true, RBEFlag: android.RBE_D8},
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+			`${config.D8Wrapper}${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
 			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
@@ -36,11 +36,11 @@
 	},
 	"outDir", "d8Flags", "zipFlags")
 
-var r8 = pctx.AndroidStaticRule("r8",
+var r8 = pctx.AndroidRemoteStaticRule("r8", android.RemoteRuleSupports{RBE: true, RBEFlag: android.RBE_R8},
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && ` +
-			`${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
+			`${config.R8Wrapper}${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
 			`--force-proguard-compatibility ` +
 			`--no-data-resources ` +
 			`-printmapping $outDict ` +
@@ -73,12 +73,12 @@
 			"--verbose")
 	}
 
-	minSdkVersion, err := sdkVersionToNumberAsString(ctx, j.minSdkVersion())
+	minSdkVersion, err := j.minSdkVersion().effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
 
-	flags = append(flags, "--min-api "+minSdkVersion)
+	flags = append(flags, "--min-api "+minSdkVersion.asNumberString())
 	return flags
 }
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 66840b5..87f6d5e 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -88,6 +88,9 @@
 	images     map[android.ArchType]android.OutputPath  // first image file
 	imagesDeps map[android.ArchType]android.OutputPaths // all files
 
+	// Only for extensions, paths to the primary boot images (grouped by target).
+	primaryImages map[android.ArchType]android.OutputPath
+
 	// File path to a zip archive with all image files (or nil, if not needed).
 	zip android.WritablePath
 }
@@ -190,7 +193,18 @@
 	if skipDexpreoptBootJars(ctx) {
 		return nil
 	}
-	return artBootImageConfig(ctx).imagesDeps
+
+	// Include dexpreopt files for the primary boot image.
+	files := artBootImageConfig(ctx).imagesDeps
+
+	// For JIT-zygote config, also include dexpreopt files for the primary JIT-zygote image.
+	if dexpreoptGlobalConfig(ctx).UseApexImage {
+		for arch, paths := range artJZBootImageConfig(ctx).imagesDeps {
+			files[arch] = append(files[arch], paths...)
+		}
+	}
+
+	return files
 }
 
 // dexpreoptBoot singleton rules
@@ -344,7 +358,7 @@
 	}
 
 	if image.extension {
-		artImage := artBootImageConfig(ctx).images[arch]
+		artImage := image.primaryImages[arch]
 		cmd.
 			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
 			Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 31bec93..637a32f 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -246,10 +246,12 @@
 
 		// specific to the framework config
 		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
+		frameworkCfg.primaryImages = artCfg.images
 		frameworkCfg.imageLocations = append(artCfg.imageLocations, frameworkCfg.imageLocations...)
 
 		// specific to the jitzygote-framework config
 		frameworkJZCfg.dexPathsDeps = append(artJZCfg.dexPathsDeps, frameworkJZCfg.dexPathsDeps...)
+		frameworkJZCfg.primaryImages = artJZCfg.images
 		frameworkJZCfg.imageLocations = append(artJZCfg.imageLocations, frameworkJZCfg.imageLocations...)
 
 		return configs
diff --git a/java/droiddoc.go b/java/droiddoc.go
index f62f5f9..abdceba 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -424,19 +424,19 @@
 
 var _ android.OutputFileProducer = (*Javadoc)(nil)
 
-func (j *Javadoc) sdkVersion() string {
-	return String(j.properties.Sdk_version)
+func (j *Javadoc) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(j.properties.Sdk_version))
 }
 
 func (j *Javadoc) systemModules() string {
 	return proptools.String(j.properties.System_modules)
 }
 
-func (j *Javadoc) minSdkVersion() string {
+func (j *Javadoc) minSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
-func (j *Javadoc) targetSdkVersion() string {
+func (j *Javadoc) targetSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
@@ -1456,6 +1456,8 @@
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
 	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+	// Metalava uses lots of memory, restrict the number of metalava jobs that can run in parallel.
+	rule.HighMem()
 	cmd := rule.Command().BuiltTool(ctx, "metalava").
 		Flag(config.JavacVmFlags).
 		FlagWithArg("-encoding ", "UTF-8").
diff --git a/java/java.go b/java/java.go
index 4c6a5a5..ed3dca9 100644
--- a/java/java.go
+++ b/java/java.go
@@ -87,7 +87,7 @@
 	if j.SocSpecific() || j.DeviceSpecific() ||
 		(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
 		if sc, ok := ctx.Module().(sdkContext); ok {
-			if sc.sdkVersion() == "" {
+			if !sc.sdkVersion().specified() {
 				ctx.PropertyErrorf("sdk_version",
 					"sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
 			}
@@ -98,12 +98,11 @@
 func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
 	if sc, ok := ctx.Module().(sdkContext); ok {
 		usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
-		if usePlatformAPI != (sc.sdkVersion() == "") {
-			if usePlatformAPI {
-				ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
-			} else {
-				ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
-			}
+		sdkVersionSpecified := sc.sdkVersion().specified()
+		if usePlatformAPI && sdkVersionSpecified {
+			ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
+		} else if !usePlatformAPI && !sdkVersionSpecified {
+			ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
 		}
 
 	}
@@ -451,8 +450,8 @@
 }
 
 type SdkLibraryDependency interface {
-	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths
-	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths
+	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
+	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
 }
 
 type xref interface {
@@ -553,24 +552,24 @@
 			ctx.Config().UnbundledBuild())
 }
 
-func (j *Module) sdkVersion() string {
-	return String(j.deviceProperties.Sdk_version)
+func (j *Module) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(j.deviceProperties.Sdk_version))
 }
 
 func (j *Module) systemModules() string {
 	return proptools.String(j.deviceProperties.System_modules)
 }
 
-func (j *Module) minSdkVersion() string {
+func (j *Module) minSdkVersion() sdkSpec {
 	if j.deviceProperties.Min_sdk_version != nil {
-		return *j.deviceProperties.Min_sdk_version
+		return sdkSpecFrom(*j.deviceProperties.Min_sdk_version)
 	}
 	return j.sdkVersion()
 }
 
-func (j *Module) targetSdkVersion() string {
+func (j *Module) targetSdkVersion() sdkSpec {
 	if j.deviceProperties.Target_sdk_version != nil {
-		return *j.deviceProperties.Target_sdk_version
+		return sdkSpecFrom(*j.deviceProperties.Target_sdk_version)
 	}
 	return j.sdkVersion()
 }
@@ -776,26 +775,25 @@
 		name == "stub-annotations" || name == "private-stub-annotations-jar" ||
 		name == "core-lambda-stubs" || name == "core-generated-annotation-stubs":
 		return javaCore, true
-	case ver == "core_current":
+	case ver.kind == sdkCore:
 		return javaCore, false
 	case name == "android_system_stubs_current":
 		return javaSystem, true
-	case strings.HasPrefix(ver, "system_"):
+	case ver.kind == sdkSystem:
 		return javaSystem, false
 	case name == "android_test_stubs_current":
 		return javaSystem, true
-	case strings.HasPrefix(ver, "test_"):
+	case ver.kind == sdkTest:
 		return javaPlatform, false
 	case name == "android_stubs_current":
 		return javaSdk, true
-	case ver == "current":
+	case ver.kind == sdkPublic:
 		return javaSdk, false
-	case ver == "" || ver == "none" || ver == "core_platform":
+	case ver.kind == sdkPrivate || ver.kind == sdkNone || ver.kind == sdkCorePlatform:
 		return javaPlatform, false
+	case !ver.valid():
+		panic(fmt.Errorf("sdk_version is invalid. got %q", ver.raw))
 	default:
-		if _, err := strconv.Atoi(ver); err != nil {
-			panic(fmt.Errorf("expected sdk_version to be a number, got %q", ver))
-		}
 		return javaSdk, false
 	}
 }
@@ -992,19 +990,7 @@
 }
 
 func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) javaVersion {
-	v := sdkContext.sdkVersion()
-	// For PDK builds, use the latest SDK version instead of "current"
-	if ctx.Config().IsPdkBuild() &&
-		(v == "" || v == "none" || v == "core_platform" || v == "current") {
-		sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
-		latestSdkVersion := 0
-		if len(sdkVersions) > 0 {
-			latestSdkVersion = sdkVersions[len(sdkVersions)-1]
-		}
-		v = strconv.Itoa(latestSdkVersion)
-	}
-
-	sdk, err := sdkVersionToNumber(ctx, v)
+	sdk, err := sdkContext.sdkVersion().effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("sdk_version", "%s", err)
 	}
@@ -1741,6 +1727,11 @@
 }
 
 func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool {
+	// Store uncompressed (and aligned) any dex files from jars in APEXes.
+	if am, ok := ctx.Module().(android.ApexModule); ok && !am.IsForPlatform() {
+		return true
+	}
+
 	// Store uncompressed (and do not strip) dex files from boot class path jars.
 	if inList(ctx.ModuleName(), ctx.Config().BootJars()) {
 		return true
@@ -2282,11 +2273,11 @@
 	exportedSdkLibs       []string
 }
 
-func (j *Import) sdkVersion() string {
-	return String(j.properties.Sdk_version)
+func (j *Import) sdkVersion() sdkSpec {
+	return sdkSpecFrom(String(j.properties.Sdk_version))
 }
 
-func (j *Import) minSdkVersion() string {
+func (j *Import) minSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index 30a8ca6..a2788cb 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1227,3 +1227,67 @@
 		checkPatchModuleFlag(t, ctx, "baz", expected)
 	})
 }
+
+func TestJavaSystemModules(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_system_modules {
+			name: "system-modules",
+			libs: ["system-module1", "system-module2"],
+		}
+		java_library {
+			name: "system-module1",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+		java_library {
+			name: "system-module2",
+			srcs: ["b.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+		`)
+
+	// check the existence of the module
+	systemModules := ctx.ModuleForTests("system-modules", "android_common")
+
+	cmd := systemModules.Rule("jarsTosystemModules")
+
+	// make sure the command compiles against the supplied modules.
+	for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
+		if !strings.Contains(cmd.Args["classpath"], module) {
+			t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
+				module)
+		}
+	}
+}
+
+func TestJavaSystemModulesImport(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_system_modules_import {
+			name: "system-modules",
+			libs: ["system-module1", "system-module2"],
+		}
+		java_import {
+			name: "system-module1",
+			jars: ["a.jar"],
+		}
+		java_import {
+			name: "system-module2",
+			jars: ["b.jar"],
+		}
+		`)
+
+	// check the existence of the module
+	systemModules := ctx.ModuleForTests("system-modules", "android_common")
+
+	cmd := systemModules.Rule("jarsTosystemModules")
+
+	// make sure the command compiles against the supplied modules.
+	for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
+		if !strings.Contains(cmd.Args["classpath"], module) {
+			t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
+				module)
+		}
+	}
+}
diff --git a/java/kotlin.go b/java/kotlin.go
index 5319a4f..cb7da20 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -26,7 +26,7 @@
 	"github.com/google/blueprint"
 )
 
-var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.SUPPORTS_GOMA,
+var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports{Goma: true},
 	blueprint.RuleParams{
 		Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` +
 			`mkdir -p "$classesDir" "$srcJarDir" "$emptyDir" && ` +
@@ -88,7 +88,7 @@
 	})
 }
 
-var kapt = pctx.AndroidRemoteStaticRule("kapt", android.SUPPORTS_GOMA,
+var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: true},
 	blueprint.RuleParams{
 		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && mkdir -p "$srcJarDir" "$kaptDir" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
diff --git a/java/sdk.go b/java/sdk.go
index 66eb284..f388358 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -15,14 +15,15 @@
 package java
 
 import (
-	"android/soong/android"
-	"android/soong/java/config"
 	"fmt"
 	"path/filepath"
 	"sort"
 	"strconv"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/java/config"
+
 	"github.com/google/blueprint/pathtools"
 )
 
@@ -37,83 +38,258 @@
 var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
 
 type sdkContext interface {
-	// sdkVersion returns the sdk_version property of the current module, or an empty string if it is not set.
-	sdkVersion() string
+	// sdkVersion returns sdkSpec that corresponds to the sdk_version property of the current module
+	sdkVersion() sdkSpec
 	// systemModules returns the system_modules property of the current module, or an empty string if it is not set.
 	systemModules() string
-	// minSdkVersion returns the min_sdk_version property of the current module, or sdkVersion() if it is not set.
-	minSdkVersion() string
-	// targetSdkVersion returns the target_sdk_version property of the current module, or sdkVersion() if it is not set.
-	targetSdkVersion() string
+	// minSdkVersion returns sdkSpec that corresponds to the min_sdk_version property of the current module,
+	// or from sdk_version if it is not set.
+	minSdkVersion() sdkSpec
+	// targetSdkVersion returns the sdkSpec that corresponds to the target_sdk_version property of the current module,
+	// or from sdk_version if it is not set.
+	targetSdkVersion() sdkSpec
 }
 
-func sdkVersionOrDefault(ctx android.BaseModuleContext, v string) string {
-	switch v {
-	case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
-		return ctx.Config().DefaultAppTargetSdk()
+func UseApiFingerprint(ctx android.BaseModuleContext, v string) bool {
+	if v == ctx.Config().PlatformSdkCodename() &&
+		ctx.Config().UnbundledBuild() &&
+		!ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
+		ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
+		return true
+	}
+	return false
+}
+
+// sdkKind represents a particular category of an SDK spec like public, system, test, etc.
+type sdkKind int
+
+const (
+	sdkInvalid sdkKind = iota
+	sdkNone
+	sdkCore
+	sdkCorePlatform
+	sdkPublic
+	sdkSystem
+	sdkTest
+	sdkPrivate
+)
+
+// String returns the string representation of this sdkKind
+func (k sdkKind) String() string {
+	switch k {
+	case sdkPrivate:
+		return "private"
+	case sdkNone:
+		return "none"
+	case sdkPublic:
+		return "public"
+	case sdkSystem:
+		return "system"
+	case sdkTest:
+		return "test"
+	case sdkCore:
+		return "core"
+	case sdkCorePlatform:
+		return "core_platform"
 	default:
-		return v
+		return "invalid"
 	}
 }
 
-// Returns a sdk version as a number.  For modules targeting an unreleased SDK (meaning it does not yet have a number)
-// it returns android.FutureApiLevel (10000).
-func sdkVersionToNumber(ctx android.EarlyModuleContext, v string) (int, error) {
-	switch v {
-	case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
-		return ctx.Config().DefaultAppTargetSdkInt(), nil
-	default:
-		n := android.GetNumericSdkVersion(v)
-		if i, err := strconv.Atoi(n); err != nil {
-			return -1, fmt.Errorf("invalid sdk version %q", n)
-		} else {
-			return i, nil
+// sdkVersion represents a specific version number of an SDK spec of a particular kind
+type sdkVersion int
+
+const (
+	// special version number for a not-yet-frozen SDK
+	sdkVersionCurrent sdkVersion = sdkVersion(android.FutureApiLevel)
+	// special version number to be used for SDK specs where version number doesn't
+	// make sense, e.g. "none", "", etc.
+	sdkVersionNone sdkVersion = sdkVersion(0)
+)
+
+// isCurrent checks if the sdkVersion refers to the not-yet-published version of an sdkKind
+func (v sdkVersion) isCurrent() bool {
+	return v == sdkVersionCurrent
+}
+
+// isNumbered checks if the sdkVersion refers to the published (a.k.a numbered) version of an sdkKind
+func (v sdkVersion) isNumbered() bool {
+	return !v.isCurrent() && v != sdkVersionNone
+}
+
+// String returns the string representation of this sdkVersion.
+func (v sdkVersion) String() string {
+	if v.isCurrent() {
+		return "current"
+	} else if v.isNumbered() {
+		return strconv.Itoa(int(v))
+	}
+	return "(no version)"
+}
+
+// asNumberString directly converts the numeric value of this sdk version as a string.
+// When isNumbered() is true, this method is the same as String(). However, for sdkVersionCurrent
+// and sdkVersionNone, this returns 10000 and 0 while String() returns "current" and "(no version"),
+// respectively.
+func (v sdkVersion) asNumberString() string {
+	return strconv.Itoa(int(v))
+}
+
+// sdkSpec represents the kind and the version of an SDK for a module to build against
+type sdkSpec struct {
+	kind    sdkKind
+	version sdkVersion
+	raw     string
+}
+
+// valid checks if this sdkSpec is well-formed. Note however that true doesn't mean that the
+// specified SDK actually exists.
+func (s sdkSpec) valid() bool {
+	return s.kind != sdkInvalid
+}
+
+// specified checks if this sdkSpec is well-formed and is not "".
+func (s sdkSpec) specified() bool {
+	return s.valid() && s.kind != sdkPrivate
+}
+
+// prebuiltSdkAvailableForUnbundledBuilt tells whether this sdkSpec can have a prebuilt SDK
+// that can be used for unbundled builds.
+func (s sdkSpec) prebuiltSdkAvailableForUnbundledBuild() bool {
+	// "", "none", and "core_platform" are not available for unbundled build
+	// as we don't/can't have prebuilt stub for the versions
+	return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
+}
+
+// forPdkBuild converts this sdkSpec into another sdkSpec that is for the PDK builds.
+func (s sdkSpec) forPdkBuild(ctx android.EarlyModuleContext) sdkSpec {
+	// For PDK builds, use the latest SDK version instead of "current" or ""
+	if s.kind == sdkPrivate || s.kind == sdkPublic {
+		kind := s.kind
+		if kind == sdkPrivate {
+			// We don't have prebuilt SDK for private APIs, so use the public SDK
+			// instead. This looks odd, but that's how it has been done.
+			// TODO(b/148271073): investigate the need for this.
+			kind = sdkPublic
 		}
+		version := sdkVersion(LatestSdkVersionInt(ctx))
+		return sdkSpec{kind, version, s.raw}
 	}
+	return s
 }
 
-func sdkVersionToNumberAsString(ctx android.EarlyModuleContext, v string) (string, error) {
-	n, err := sdkVersionToNumber(ctx, v)
-	if err != nil {
-		return "", err
+// usePrebuilt determines whether prebuilt SDK should be used for this sdkSpec with the given context.
+func (s sdkSpec) usePrebuilt(ctx android.EarlyModuleContext) bool {
+	if s.version.isCurrent() {
+		// "current" can be built from source and be from prebuilt SDK
+		return ctx.Config().UnbundledBuildUsePrebuiltSdks()
+	} else if s.version.isNumbered() {
+		// sanity check
+		if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest {
+			panic(fmt.Errorf("prebuilt SDK is not not available for sdkKind=%q", s.kind))
+			return false
+		}
+		// numbered SDKs are always from prebuilt
+		return true
 	}
-	return strconv.Itoa(n), nil
+	// "", "none", "core_platform" fall here
+	return false
+}
+
+// effectiveVersion converts an sdkSpec into the concrete sdkVersion that the module
+// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns android.FutureApiLevel(10000).
+func (s sdkSpec) effectiveVersion(ctx android.EarlyModuleContext) (sdkVersion, error) {
+	if !s.valid() {
+		return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
+	}
+	if ctx.Config().IsPdkBuild() {
+		s = s.forPdkBuild(ctx)
+	}
+	if s.version.isNumbered() {
+		return s.version, nil
+	}
+	return sdkVersion(ctx.Config().DefaultAppTargetSdkInt()), nil
+}
+
+// effectiveVersionString converts an sdkSpec into the concrete version string that the module
+// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns the codename (P, Q, R, etc.)
+func (s sdkSpec) effectiveVersionString(ctx android.EarlyModuleContext) (string, error) {
+	ver, err := s.effectiveVersion(ctx)
+	if err == nil && int(ver) == ctx.Config().DefaultAppTargetSdkInt() {
+		return ctx.Config().DefaultAppTargetSdk(), nil
+	}
+	return ver.String(), err
+}
+
+func sdkSpecFrom(str string) sdkSpec {
+	switch str {
+	// special cases first
+	case "":
+		return sdkSpec{sdkPrivate, sdkVersionNone, str}
+	case "none":
+		return sdkSpec{sdkNone, sdkVersionNone, str}
+	case "core_platform":
+		return sdkSpec{sdkCorePlatform, sdkVersionNone, str}
+	default:
+		// the syntax is [kind_]version
+		sep := strings.LastIndex(str, "_")
+
+		var kindString string
+		if sep == 0 {
+			return sdkSpec{sdkInvalid, sdkVersionNone, str}
+		} else if sep == -1 {
+			kindString = ""
+		} else {
+			kindString = str[0:sep]
+		}
+		versionString := str[sep+1 : len(str)]
+
+		var kind sdkKind
+		switch kindString {
+		case "":
+			kind = sdkPublic
+		case "core":
+			kind = sdkCore
+		case "system":
+			kind = sdkSystem
+		case "test":
+			kind = sdkTest
+		default:
+			return sdkSpec{sdkInvalid, sdkVersionNone, str}
+		}
+
+		var version sdkVersion
+		if versionString == "current" {
+			version = sdkVersionCurrent
+		} else if i, err := strconv.Atoi(versionString); err == nil {
+			version = sdkVersion(i)
+		} else {
+			return sdkSpec{sdkInvalid, sdkVersionNone, str}
+		}
+
+		return sdkSpec{kind, version, str}
+	}
 }
 
 func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
-	v := sdkContext.sdkVersion()
-
-	// For PDK builds, use the latest SDK version instead of "current"
-	if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
-		sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
-		latestSdkVersion := 0
-		if len(sdkVersions) > 0 {
-			latestSdkVersion = sdkVersions[len(sdkVersions)-1]
-		}
-		v = strconv.Itoa(latestSdkVersion)
-	}
-
-	numericSdkVersion, err := sdkVersionToNumber(ctx, v)
-	if err != nil {
-		ctx.PropertyErrorf("sdk_version", "%s", err)
+	sdkVersion := sdkContext.sdkVersion()
+	if !sdkVersion.valid() {
+		ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.raw)
 		return sdkDep{}
 	}
 
-	toPrebuilt := func(sdk string) sdkDep {
-		var api, v string
-		if strings.Contains(sdk, "_") {
-			t := strings.Split(sdk, "_")
-			api = t[0]
-			v = t[1]
-		} else {
-			api = "public"
-			v = sdk
-		}
-		dir := filepath.Join("prebuilts", "sdk", v, api)
+	if ctx.Config().IsPdkBuild() {
+		sdkVersion = sdkVersion.forPdkBuild(ctx)
+	}
+
+	if sdkVersion.usePrebuilt(ctx) {
+		dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), sdkVersion.kind.String())
 		jar := filepath.Join(dir, "android.jar")
 		// There's no aidl for other SDKs yet.
 		// TODO(77525052): Add aidl files for other SDKs too.
-		public_dir := filepath.Join("prebuilts", "sdk", v, "public")
+		public_dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), "public")
 		aidl := filepath.Join(public_dir, "framework.aidl")
 		jarPath := android.ExistentPathForSource(ctx, jar)
 		aidlPath := android.ExistentPathForSource(ctx, aidl)
@@ -122,17 +298,17 @@
 		if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
 			return sdkDep{
 				invalidVersion: true,
-				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", api, v)},
+				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.kind, sdkVersion.version.String())},
 			}
 		}
 
 		if !jarPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, jar)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, jar)
 			return sdkDep{}
 		}
 
 		if !aidlPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdk, aidl)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, aidl)
 			return sdkDep{}
 		}
 
@@ -156,31 +332,26 @@
 
 	// 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 strings.HasPrefix(v, "system_") && numericSdkVersion != android.FutureApiLevel {
+	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(strconv.Itoa(numericSdkVersion), allowed_versions) {
+		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",
-				v, allowed_versions)
+				sdkVersion.raw, allowed_versions)
 		}
 	}
 
-	if ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
-		v != "" && v != "none" && v != "core_platform" {
-		return toPrebuilt(v)
-	}
-
-	switch v {
-	case "":
+	switch sdkVersion.kind {
+	case sdkPrivate:
 		return sdkDep{
 			useDefaultLibs:     true,
 			frameworkResModule: "framework-res",
 		}
-	case "none":
+	case sdkNone:
 		systemModules := sdkContext.systemModules()
 		if systemModules == "" {
 			ctx.PropertyErrorf("sdk_version",
@@ -197,22 +368,22 @@
 			systemModules:  systemModules,
 			bootclasspath:  []string{systemModules},
 		}
-	case "core_platform":
+	case sdkCorePlatform:
 		return sdkDep{
 			useDefaultLibs:     true,
 			frameworkResModule: "framework-res",
 			noFrameworksLibs:   true,
 		}
-	case "current":
+	case sdkPublic:
 		return toModule("android_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
-	case "system_current":
+	case sdkSystem:
 		return toModule("android_system_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
-	case "test_current":
+	case sdkTest:
 		return toModule("android_test_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
-	case "core_current":
+	case sdkCore:
 		return toModule("core.current.stubs", "", nil)
 	default:
-		return toPrebuilt(v)
+		panic(fmt.Errorf("invalid sdk %q", sdkVersion.raw))
 	}
 }
 
@@ -245,6 +416,15 @@
 	ctx.Config().Once(sdkVersionsKey, func() interface{} { return sdkVersions })
 }
 
+func LatestSdkVersionInt(ctx android.EarlyModuleContext) int {
+	sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
+	latestSdkVersion := 0
+	if len(sdkVersions) > 0 {
+		latestSdkVersion = sdkVersions[len(sdkVersions)-1]
+	}
+	return latestSdkVersion
+}
+
 func sdkSingletonFactory() android.Singleton {
 	return sdkSingleton{}
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 9b30e2c..530e02c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -650,27 +650,27 @@
 	mctx.CreateModule(android.PrebuiltEtcFactory, &etcProps)
 }
 
-func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
-	var api, v string
-	if sdkVersion == "" || sdkVersion == "none" {
-		api = "system"
-		v = "current"
-	} else if strings.Contains(sdkVersion, "_") {
-		t := strings.Split(sdkVersion, "_")
-		api = t[0]
-		v = t[1]
+func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, s sdkSpec) android.Paths {
+	var ver sdkVersion
+	var kind sdkKind
+	if s.usePrebuilt(ctx) {
+		ver = s.version
+		kind = s.kind
 	} else {
-		api = "public"
-		v = sdkVersion
+		// We don't have prebuilt SDK for the specific sdkVersion.
+		// Instead of breaking the build, fallback to use "system_current"
+		ver = sdkVersionCurrent
+		kind = sdkSystem
 	}
-	dir := filepath.Join("prebuilts", "sdk", v, api)
+
+	dir := filepath.Join("prebuilts", "sdk", ver.String(), kind.String())
 	jar := filepath.Join(dir, module.BaseModuleName()+".jar")
 	jarPath := android.ExistentPathForSource(ctx, jar)
 	if !jarPath.Valid() {
 		if ctx.Config().AllowMissingDependencies() {
 			return android.Paths{android.PathForSource(ctx, jar)}
 		} else {
-			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", sdkVersion, jar)
+			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", s.raw, jar)
 		}
 		return nil
 	}
@@ -678,32 +678,34 @@
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the stubs.
 	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		return module.PrebuiltJars(ctx, sdkVersion)
 	} else {
-		if strings.HasPrefix(sdkVersion, "system_") {
+		switch sdkVersion.kind {
+		case sdkSystem:
 			return module.systemApiStubsPath
-		} else if sdkVersion == "" {
+		case sdkPrivate:
 			return module.Library.HeaderJars()
-		} else {
+		default:
 			return module.publicApiStubsPath
 		}
 	}
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the stubs.
 	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		return module.PrebuiltJars(ctx, sdkVersion)
 	} else {
-		if strings.HasPrefix(sdkVersion, "system_") {
+		switch sdkVersion.kind {
+		case sdkSystem:
 			return module.systemApiStubsImplPath
-		} else if sdkVersion == "" {
+		case sdkPrivate:
 			return module.Library.ImplementationJars()
-		} else {
+		default:
 			return module.publicApiStubsImplPath
 		}
 	}
@@ -838,16 +840,8 @@
 
 	Sdk_version *string
 
-	Installable *bool
-
 	// List of shared java libs that this module has dependencies to
 	Libs []string
-
-	// List of files to remove from the jar file(s)
-	Exclude_files []string
-
-	// List of directories to remove from the jar file(s)
-	Exclude_dirs []string
 }
 
 type sdkLibraryImport struct {
@@ -931,13 +925,13 @@
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *sdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *sdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the prebuilt stubs.
 	return module.stubsPath
 }
 
 // to satisfy SdkLibraryDependency interface
-func (module *sdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
+func (module *sdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the stubs.
 	return module.stubsPath
 }
diff --git a/java/system_modules.go b/java/system_modules.go
index ed2fc18..92297c4 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -35,6 +35,7 @@
 
 func RegisterSystemModulesBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_system_modules", SystemModulesFactory)
+	ctx.RegisterModuleType("java_system_modules_import", systemModulesImportFactory)
 }
 
 var (
@@ -92,6 +93,9 @@
 	return outDir, outputs.Paths()
 }
 
+// java_system_modules creates a system module from a set of java libraries that can
+// be referenced from the system_modules property. It must contain at a minimum the
+// java.base module which must include classes from java.lang amongst other java packages.
 func SystemModulesFactory() android.Module {
 	module := &SystemModules{}
 	module.AddProperties(&module.properties)
@@ -157,3 +161,30 @@
 		},
 	}
 }
+
+// A prebuilt version of java_system_modules. It does not import the
+// generated system module, it generates the system module from imported
+// java libraries in the same way that java_system_modules does. It just
+// acts as a prebuilt, i.e. can have the same base name as another module
+// type and the one to use is selected at runtime.
+func systemModulesImportFactory() android.Module {
+	module := &systemModulesImport{}
+	module.AddProperties(&module.properties)
+	android.InitPrebuiltModule(module, &module.properties.Libs)
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+type systemModulesImport struct {
+	SystemModules
+	prebuilt android.Prebuilt
+}
+
+func (system *systemModulesImport) Name() string {
+	return system.prebuilt.Name(system.ModuleBase.Name())
+}
+
+func (system *systemModulesImport) Prebuilt() *android.Prebuilt {
+	return &system.prebuilt
+}
diff --git a/python/python.go b/python/python.go
index c67c577..8b912be 100644
--- a/python/python.go
+++ b/python/python.go
@@ -340,6 +340,11 @@
 			// dependencies later.
 			ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
 
+			if ctx.Device() {
+				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
+					"liblog")
+			}
+
 			if ctx.Target().Os.Bionic() {
 				ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
 					"libc", "libdl", "libm")
diff --git a/rust/config/global.go b/rust/config/global.go
index fb9b14b..690d83e 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -54,7 +54,6 @@
 		"-Wl,--fatal-warnings",
 
 		"-Wl,--pack-dyn-relocs=android+relr",
-		"-Wl,--use-android-relr-tags",
 		"-Wl,--no-undefined",
 		"-Wl,--hash-style=gnu",
 	}
diff --git a/rust/library.go b/rust/library.go
index 43819ce..0cf2dd0 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -323,6 +323,7 @@
 	return deps
 }
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.baseModuleName())
 	flags = library.baseCompiler.compilerFlags(ctx, flags)
 	if library.shared() || library.static() {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
@@ -337,7 +338,7 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 
-	if library.dylib() || library.shared() {
+	if library.dylib() {
 		// We need prefer-dynamic for now to avoid linking in the static stdlib. See:
 		// https://github.com/rust-lang/rust/issues/19680
 		// https://github.com/rust-lang/rust/issues/34909
diff --git a/rust/rust.go b/rust/rust.go
index 14513fb..e2af6f0 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -85,6 +85,10 @@
 	return true
 }
 
+func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
+	return mod.InRamdisk()
+}
+
 func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
 	return mod.InRecovery()
 }
@@ -152,6 +156,10 @@
 	panic(fmt.Errorf("Toc() called on non-library module: %q", mod.BaseModuleName()))
 }
 
+func (mod *Module) OnlyInRamdisk() bool {
+	return false
+}
+
 func (mod *Module) OnlyInRecovery() bool {
 	return false
 }
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
index 7bb4b52..32d5b17 100644
--- a/scripts/test_config_fixer.py
+++ b/scripts/test_config_fixer.py
@@ -37,6 +37,8 @@
                       help=('AndroidManifest.xml that contains the original package name'))
   parser.add_argument('--package-name', default='', dest='package_name',
                       help=('overwrite package fields in the test config'))
+  parser.add_argument('--test-file-name', default='', dest='test_file_name',
+                      help=('overwrite test file name in the test config'))
   parser.add_argument('input', help='input test config file')
   parser.add_argument('output', help='output test config file')
   return parser.parse_args()
@@ -46,7 +48,6 @@
 
   manifest = parse_manifest(manifest_doc)
   original_package = manifest.getAttribute('package')
-  print('package: ' + original_package)
 
   test_config = parse_test_config(test_config_doc)
   tests = get_children_with_tag(test_config, 'test')
@@ -57,6 +58,18 @@
       if option.getAttribute('name') == "package" and option.getAttribute('value') == original_package:
         option.setAttribute('value', package_name)
 
+def overwrite_test_file_name(test_config_doc, test_file_name):
+
+  test_config = parse_test_config(test_config_doc)
+  tests = get_children_with_tag(test_config, 'target_preparer')
+
+  for test in tests:
+    if test.getAttribute('class') == "com.android.tradefed.targetprep.TestAppInstallSetup":
+      options = get_children_with_tag(test, 'option')
+      for option in options:
+        if option.getAttribute('name') == "test-file-name":
+          option.setAttribute('value', test_file_name)
+
 def main():
   """Program entry point."""
   try:
@@ -70,6 +83,9 @@
       manifest_doc = minidom.parse(args.manifest)
       overwrite_package_name(doc, manifest_doc, args.package_name)
 
+    if args.test_file_name:
+      overwrite_test_file_name(doc, args.test_file_name)
+
     with open(args.output, 'wb') as f:
       write_xml(f, doc)
 
diff --git a/scripts/test_config_fixer_test.py b/scripts/test_config_fixer_test.py
index b90582e..1272c6b 100644
--- a/scripts/test_config_fixer_test.py
+++ b/scripts/test_config_fixer_test.py
@@ -67,5 +67,32 @@
     self.assertEqual(expected, output.getvalue())
 
 
+class OverwriteTestFileNameTest(unittest.TestCase):
+  """ Unit tests for overwrite_test_file_name function """
+
+  test_config = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<configuration description="Runs some tests.">\n'
+      '    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">\n'
+      '        <option name="test-file-name" value="%s"/>\n'
+      '    </target_preparer>\n'
+      '    <test class="com.android.tradefed.testtype.AndroidJUnitTest">\n'
+      '        <option name="package" value="com.android.foo"/>\n'
+      '        <option name="runtime-hint" value="20s"/>\n'
+      '    </test>\n'
+      '</configuration>\n')
+
+  def test_all(self):
+    doc = minidom.parseString(self.test_config % ("foo.apk"))
+
+    test_config_fixer.overwrite_test_file_name(doc, "bar.apk")
+    output = StringIO.StringIO()
+    test_config_fixer.write_xml(output, doc)
+
+    # Only the matching package name in a test node should be updated.
+    expected = self.test_config % ("bar.apk")
+    self.assertEqual(expected, output.getvalue())
+
+
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/sdk/bp.go b/sdk/bp.go
index 19fb70d..ae06a09 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -51,13 +51,23 @@
 	return s.properties[name]
 }
 
-func (s *bpPropertySet) copy() bpPropertySet {
+func (s *bpPropertySet) deepCopy() *bpPropertySet {
 	propertiesCopy := make(map[string]interface{})
 	for p, v := range s.properties {
-		propertiesCopy[p] = v
+		var valueCopy interface{}
+		if ps, ok := v.(*bpPropertySet); ok {
+			valueCopy = ps.deepCopy()
+		} else if values, ok := v.([]string); ok {
+			valuesCopy := make([]string, len(values))
+			copy(valuesCopy, values)
+			valueCopy = valuesCopy
+		} else {
+			valueCopy = v
+		}
+		propertiesCopy[p] = valueCopy
 	}
 
-	return bpPropertySet{
+	return &bpPropertySet{
 		properties: propertiesCopy,
 		order:      append([]string(nil), s.order...),
 	}
@@ -95,15 +105,15 @@
 }
 
 type bpModule struct {
-	bpPropertySet
+	*bpPropertySet
 	moduleType string
 }
 
 var _ android.BpModule = (*bpModule)(nil)
 
-func (m *bpModule) copy() *bpModule {
+func (m *bpModule) deepCopy() *bpModule {
 	return &bpModule{
-		bpPropertySet: m.bpPropertySet.copy(),
+		bpPropertySet: m.bpPropertySet.deepCopy(),
 		moduleType:    m.moduleType,
 	}
 }
@@ -134,8 +144,9 @@
 
 func (f *bpFile) newModule(moduleType string) *bpModule {
 	module := &bpModule{
-		moduleType: moduleType,
+		moduleType:    moduleType,
+		bpPropertySet: &bpPropertySet{},
 	}
-	(&module.bpPropertySet).init()
+	module.bpPropertySet.init()
 	return module
 }
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 255ac08..9a75610 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -101,6 +101,11 @@
 			srcs: ["libfoo.so"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
 		}
 
 		cc_prebuilt_library_shared {
@@ -109,6 +114,11 @@
 			srcs: ["libfoo.so"],
 			system_shared_libs: [],
 			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex2",
+			],
 		}
 
 		cc_library_shared {
@@ -117,6 +127,10 @@
 			shared_libs: ["sdkmember"],
 			system_shared_libs: [],
 			stl: "none",
+			apex_available: [
+				"myapex",
+				"myapex2",
+			],
 		}
 
 		apex {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 218a16a..692c205 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -73,6 +73,10 @@
 			sdk_version: "none",
 			compile_dex: true,
 			host_supported: true,
+			apex_available: [
+				"myapex",
+				"myapex2",
+			],
 		}
 
 		apex {
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 44e5cbb..3b0752f 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -285,12 +285,15 @@
 
 // For dependencies from an in-development version of an SDK member to frozen versions of the same member
 // e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
-type sdkMemberVesionedDepTag struct {
+type sdkMemberVersionedDepTag struct {
 	dependencyTag
 	member  string
 	version string
 }
 
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
+
 // Step 1: create dependencies from an SDK module to its members.
 func memberMutator(mctx android.BottomUpMutatorContext) {
 	if s, ok := mctx.Module().(*sdk); ok {
@@ -337,7 +340,7 @@
 	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
 		if !m.ContainingSdk().Unversioned() {
 			memberName := m.MemberName()
-			tag := sdkMemberVesionedDepTag{member: memberName, version: m.ContainingSdk().Version}
+			tag := sdkMemberVersionedDepTag{member: memberName, version: m.ContainingSdk().Version}
 			mctx.AddReverseDependency(mctx.Module(), tag, memberName)
 		}
 	}
diff --git a/sdk/testing.go b/sdk/testing.go
index 8097889..c9cc30f 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -62,14 +62,12 @@
 	ctx := android.NewTestArchContext()
 
 	// from android package
-	ctx.PreArchMutators(android.RegisterPackageRenamer)
+	android.RegisterPackageBuildComponents(ctx)
 	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
-	ctx.RegisterModuleType("package", android.PackageFactory)
-
 	// from java package
 	java.RegisterJavaBuildComponents(ctx)
 	java.RegisterAppBuildComponents(ctx)
diff --git a/sdk/update.go b/sdk/update.go
index 5bc3b83..2731d50 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -196,7 +196,7 @@
 
 	for _, unversioned := range builder.prebuiltOrder {
 		// Copy the unversioned module so it can be modified to make it versioned.
-		versioned := unversioned.copy()
+		versioned := unversioned.deepCopy()
 		name := versioned.properties["name"].(string)
 		versioned.setProperty("name", builder.versionedSdkMemberName(name))
 		versioned.insertAfter("name", "sdk_member_name", name)
@@ -286,7 +286,7 @@
 	for _, bpModule := range bpFile.order {
 		contents.Printfln("")
 		contents.Printfln("%s {", bpModule.moduleType)
-		outputPropertySet(contents, &bpModule.bpPropertySet)
+		outputPropertySet(contents, bpModule.bpPropertySet)
 		contents.Printfln("}")
 	}
 }
diff --git a/ui/build/build.go b/ui/build/build.go
index 1c2d864..f3feac2 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -19,6 +19,8 @@
 	"os"
 	"path/filepath"
 	"text/template"
+
+	"android/soong/ui/metrics"
 )
 
 // Ensures the out directory exists, and has the proper files to prevent kati
@@ -46,8 +48,11 @@
 
 var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
 builddir = {{.OutDir}}
-pool local_pool
+{{if .UseRemoteBuild }}pool local_pool
  depth = {{.Parallel}}
+{{end -}}
+pool highmem_pool
+ depth = {{.HighmemParallel}}
 build _kati_always_build_: phony
 {{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
 subninja {{.KatiPackageNinjaFile}}
@@ -139,6 +144,9 @@
 	ctx.Verboseln("Environment:", config.Environment().Environ())
 	ctx.Verbosef("Total RAM: %dGB", config.TotalRAM()/1024/1024/1024)
 
+	ctx.BeginTrace(metrics.Total, "total")
+	defer ctx.EndTrace()
+
 	if config.SkipMake() {
 		ctx.Verboseln("Skipping Make/Kati as requested")
 		what = what & (BuildSoong | BuildNinja)
diff --git a/ui/build/config.go b/ui/build/config.go
index c084171..5b9d10a 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -722,6 +722,33 @@
 	return c.parallel
 }
 
+func (c *configImpl) HighmemParallel() int {
+	if i, ok := c.environ.GetInt("NINJA_HIGHMEM_NUM_JOBS"); ok {
+		return i
+	}
+
+	const minMemPerHighmemProcess = 8 * 1024 * 1024 * 1024
+	parallel := c.Parallel()
+	if c.UseRemoteBuild() {
+		// Ninja doesn't support nested pools, and when remote builds are enabled the total ninja parallelism
+		// is set very high (i.e. 500).  Using a large value here would cause the total number of running jobs
+		// to be the sum of the sizes of the local and highmem pools, which will cause extra CPU contention.
+		// Return 1/16th of the size of the local pool, rounding up.
+		return (parallel + 15) / 16
+	} else if c.totalRAM == 0 {
+		// Couldn't detect the total RAM, don't restrict highmem processes.
+		return parallel
+	} else if c.totalRAM <= 32*1024*1024*1024 {
+		// Less than 32GB of ram, restrict to 2 highmem processes
+		return 2
+	} else if p := int(c.totalRAM / minMemPerHighmemProcess); p < parallel {
+		// If less than 8GB total RAM per process, reduce the number of highmem processes
+		return p
+	}
+	// No restriction on highmem processes
+	return parallel
+}
+
 func (c *configImpl) TotalRAM() uint64 {
 	return c.totalRAM
 }
@@ -760,6 +787,48 @@
 	return false
 }
 
+func (c *configImpl) UseRBEJAVAC() bool {
+	if !c.UseRBE() {
+		return false
+	}
+
+	if v, ok := c.environ.Get("RBE_JAVAC"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *configImpl) UseRBER8() bool {
+	if !c.UseRBE() {
+		return false
+	}
+
+	if v, ok := c.environ.Get("RBE_R8"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *configImpl) UseRBED8() bool {
+	if !c.UseRBE() {
+		return false
+	}
+
+	if v, ok := c.environ.Get("RBE_D8"); ok {
+		v = strings.TrimSpace(v)
+		if v != "" && v != "false" {
+			return true
+		}
+	}
+	return false
+}
+
 func (c *configImpl) StartRBE() bool {
 	if !c.UseRBE() {
 		return false
@@ -782,10 +851,11 @@
 // gomacc) are run in parallel.  Note the parallelism of all other jobs is
 // still limited by Parallel()
 func (c *configImpl) RemoteParallel() int {
-	if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok {
-		if i, err := strconv.Atoi(v); err == nil {
-			return i
-		}
+	if !c.UseRemoteBuild() {
+		return 0
+	}
+	if i, ok := c.environ.GetInt("NINJA_REMOTE_NUM_JOBS"); ok {
+		return i
 	}
 	return 500
 }
diff --git a/ui/build/config_darwin.go b/ui/build/config_darwin.go
index 480d8d1..fe74e31 100644
--- a/ui/build/config_darwin.go
+++ b/ui/build/config_darwin.go
@@ -22,7 +22,7 @@
 func detectTotalRAM(ctx Context) uint64 {
 	s, err := syscall.Sysctl("hw.memsize")
 	if err != nil {
-		ctx.Printf("Failed to get system memory size: %s")
+		ctx.Printf("Failed to get system memory size: %v", err)
 		return 0
 	}
 
@@ -32,7 +32,7 @@
 	}
 
 	if len(s) != 8 {
-		ctx.Printf("Failed to get system memory size, returned %d bytes, 8", len(s))
+		ctx.Printf("Failed to get system memory size, returned %d bytes, expecting 8 bytes", len(s))
 		return 0
 	}
 
diff --git a/ui/build/config_linux.go b/ui/build/config_linux.go
index 9e1bdc7..162d372 100644
--- a/ui/build/config_linux.go
+++ b/ui/build/config_linux.go
@@ -20,9 +20,8 @@
 	var info syscall.Sysinfo_t
 	err := syscall.Sysinfo(&info)
 	if err != nil {
-		ctx.Printf("Failed to get system memory size: %s")
+		ctx.Printf("Failed to get system memory size: %v", err)
 		return 0
 	}
-	memBytes := uint64(info.Totalram) * uint64(info.Unit)
-	return memBytes
+	return uint64(info.Totalram) * uint64(info.Unit)
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index c3da38b..ce8f968 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -191,6 +191,8 @@
 		"CC_WRAPPER",
 		"CXX_WRAPPER",
 		"JAVAC_WRAPPER",
+		"R8_WRAPPER",
+		"D8_WRAPPER",
 
 		// ccache settings
 		"CCACHE_COMPILERCHECK",
diff --git a/ui/build/environment.go b/ui/build/environment.go
index d8ff7f2..9bca7c0 100644
--- a/ui/build/environment.go
+++ b/ui/build/environment.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"io"
 	"os"
+	"strconv"
 	"strings"
 )
 
@@ -44,6 +45,17 @@
 	return "", false
 }
 
+// Get returns the int value associated with the key, and whether it exists
+// and is a valid int.
+func (e *Environment) GetInt(key string) (int, bool) {
+	if v, ok := e.Get(key); ok {
+		if i, err := strconv.Atoi(v); err == nil {
+			return i, true
+		}
+	}
+	return 0, false
+}
+
 // Set sets the value associated with the key, overwriting the current value
 // if it exists.
 func (e *Environment) Set(key, value string) {
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 0b56b67..22ec1f1 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -133,6 +133,8 @@
 			"FLAG_invocation_id",
 			"FLAG_log_dir",
 			"FLAG_platform",
+			"FLAG_remote_accept_cache",
+			"FLAG_remote_update_cache",
 			"FLAG_server_address",
 
 			// ccache settings
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index ee6e9dc..bfe662d 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -87,7 +87,6 @@
 	"java":     Allowed,
 	"javap":    Allowed,
 	"lsof":     Allowed,
-	"m4":       Log,
 	"openssl":  Allowed,
 	"patch":    Allowed,
 	"pstree":   Allowed,
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 64bbbf3..8254e4a 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -30,6 +30,7 @@
 	RunSoong     = "soong"
 	PrimaryNinja = "ninja"
 	TestRun      = "test"
+	Total        = "total"
 )
 
 type Metrics struct {
@@ -56,6 +57,8 @@
 	case PrimaryNinja:
 		m.metrics.NinjaRuns = append(m.metrics.NinjaRuns, &perf)
 		break
+	case Total:
+		m.metrics.Total = &perf
 	default:
 		// ignored
 	}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 0fe5a0d..3986d0e 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -195,10 +195,12 @@
 	// The metrics for calling Soong.
 	SoongRuns []*PerfInfo `protobuf:"bytes,19,rep,name=soong_runs,json=soongRuns" json:"soong_runs,omitempty"`
 	// The metrics for calling Ninja.
-	NinjaRuns            []*PerfInfo `protobuf:"bytes,20,rep,name=ninja_runs,json=ninjaRuns" json:"ninja_runs,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
-	XXX_unrecognized     []byte      `json:"-"`
-	XXX_sizecache        int32       `json:"-"`
+	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:"-"`
 }
 
 func (m *MetricsBase) Reset()         { *m = MetricsBase{} }
@@ -371,6 +373,13 @@
 	return nil
 }
 
+func (m *MetricsBase) GetTotal() *PerfInfo {
+	if m != nil {
+		return m.Total
+	}
+	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"`
@@ -612,58 +621,58 @@
 func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
 
 var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 834 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x6f, 0x6b, 0xdb, 0x46,
-	0x18, 0xaf, 0x62, 0x25, 0xb6, 0x1e, 0xc5, 0xae, 0x7a, 0xc9, 0xa8, 0xba, 0x12, 0x66, 0xc4, 0x3a,
-	0xf2, 0x62, 0x75, 0x8b, 0x29, 0xa1, 0x98, 0x32, 0x48, 0x1c, 0x53, 0xba, 0x60, 0xbb, 0x28, 0x71,
-	0x57, 0xb6, 0x17, 0x87, 0x22, 0x9d, 0x1b, 0x75, 0x96, 0x4e, 0xdc, 0x9d, 0xca, 0xfc, 0x21, 0xf6,
-	0x4d, 0xf6, 0xb5, 0xf6, 0x3d, 0xc6, 0x3d, 0x27, 0x39, 0x0a, 0x78, 0x34, 0xf4, 0xdd, 0xe9, 0xf9,
-	0xfd, 0xb9, 0xdf, 0x73, 0xd2, 0x3d, 0x82, 0x6e, 0xc6, 0x94, 0x48, 0x63, 0x39, 0x28, 0x04, 0x57,
-	0x9c, 0x1c, 0x48, 0xce, 0xf3, 0x4f, 0xf4, 0xba, 0x4c, 0x57, 0x09, 0xad, 0xa0, 0xe0, 0x1f, 0x07,
-	0xdc, 0xa9, 0x59, 0x9f, 0x45, 0x92, 0x91, 0x97, 0x70, 0x68, 0x08, 0x49, 0xa4, 0x18, 0x55, 0x69,
-	0xc6, 0xa4, 0x8a, 0xb2, 0xc2, 0xb7, 0xfa, 0xd6, 0x71, 0x2b, 0x24, 0x88, 0x9d, 0x47, 0x8a, 0x5d,
-	0xd5, 0x08, 0x79, 0x02, 0x1d, 0xa3, 0x48, 0x13, 0x7f, 0xa7, 0x6f, 0x1d, 0x3b, 0x61, 0x1b, 0x9f,
-	0xdf, 0x25, 0x64, 0x04, 0x4f, 0x8a, 0x55, 0xa4, 0x96, 0x5c, 0x64, 0xf4, 0x0b, 0x13, 0x32, 0xe5,
-	0x39, 0x8d, 0x79, 0xc2, 0xf2, 0x28, 0x63, 0x7e, 0x0b, 0xb9, 0x8f, 0x6b, 0xc2, 0x07, 0x83, 0x8f,
-	0x2b, 0x98, 0x3c, 0x83, 0x9e, 0x8a, 0xc4, 0x27, 0xa6, 0x68, 0x21, 0x78, 0x52, 0xc6, 0xca, 0xb7,
-	0x51, 0xd0, 0x35, 0xd5, 0xf7, 0xa6, 0x48, 0x12, 0x38, 0xac, 0x68, 0x26, 0xc4, 0x97, 0x48, 0xa4,
-	0x51, 0xae, 0xfc, 0xdd, 0xbe, 0x75, 0xdc, 0x1b, 0x3e, 0x1f, 0x6c, 0xe9, 0x79, 0xd0, 0xe8, 0x77,
-	0x70, 0xa6, 0x91, 0x0f, 0x46, 0x34, 0x6a, 0x4d, 0x66, 0x6f, 0x43, 0x62, 0xfc, 0x9a, 0x00, 0x99,
-	0x83, 0x5b, 0xed, 0x12, 0x89, 0xf8, 0xc6, 0xdf, 0x43, 0xf3, 0x67, 0x5f, 0x35, 0x3f, 0x15, 0xf1,
-	0xcd, 0xa8, 0xbd, 0x98, 0x5d, 0xcc, 0xe6, 0xbf, 0xcd, 0x42, 0x30, 0x16, 0xba, 0x48, 0x06, 0x70,
-	0xd0, 0x30, 0xdc, 0xa4, 0x6e, 0x63, 0x8b, 0x8f, 0x6e, 0x89, 0x75, 0x80, 0x9f, 0xa1, 0x8a, 0x45,
-	0xe3, 0xa2, 0xdc, 0xd0, 0x3b, 0x48, 0xf7, 0x0c, 0x32, 0x2e, 0xca, 0x9a, 0x7d, 0x01, 0xce, 0x0d,
-	0x97, 0x55, 0x58, 0xe7, 0x9b, 0xc2, 0x76, 0xb4, 0x01, 0x46, 0x0d, 0xa1, 0x8b, 0x66, 0xc3, 0x3c,
-	0x31, 0x86, 0xf0, 0x4d, 0x86, 0xae, 0x36, 0x19, 0xe6, 0x09, 0x7a, 0x3e, 0x86, 0x36, 0x7a, 0x72,
-	0xe9, 0xbb, 0xd8, 0xc3, 0x9e, 0x7e, 0x9c, 0x4b, 0x12, 0x54, 0x9b, 0x71, 0x49, 0xd9, 0x5f, 0x4a,
-	0x44, 0xfe, 0x3e, 0xc2, 0xae, 0x81, 0x27, 0xba, 0xb4, 0xe1, 0xc4, 0x82, 0x4b, 0xa9, 0x2d, 0xba,
-	0xb7, 0x9c, 0xb1, 0xae, 0xcd, 0x25, 0xf9, 0x09, 0x1e, 0x36, 0x38, 0x18, 0xbb, 0x67, 0x3e, 0x9f,
-	0x0d, 0x0b, 0x83, 0x3c, 0x87, 0x83, 0x06, 0x6f, 0xd3, 0xe2, 0x43, 0x73, 0xb0, 0x1b, 0x6e, 0x23,
-	0x37, 0x2f, 0x15, 0x4d, 0x52, 0xe1, 0x7b, 0x26, 0x37, 0x2f, 0xd5, 0x79, 0x2a, 0xc8, 0x2f, 0xe0,
-	0x4a, 0xa6, 0xca, 0x82, 0x2a, 0xce, 0x57, 0xd2, 0x7f, 0xd4, 0x6f, 0x1d, 0xbb, 0xc3, 0xa3, 0xad,
-	0x47, 0xf4, 0x9e, 0x89, 0xe5, 0xbb, 0x7c, 0xc9, 0x43, 0x40, 0xc5, 0x95, 0x16, 0x90, 0x11, 0x38,
-	0x7f, 0x46, 0x2a, 0xa5, 0xa2, 0xcc, 0xa5, 0x4f, 0xee, 0xa3, 0xee, 0x68, 0x7e, 0x58, 0xe6, 0x92,
-	0xbc, 0x01, 0x30, 0x4c, 0x14, 0x1f, 0xdc, 0x47, 0xec, 0x20, 0x5a, 0xab, 0xf3, 0x34, 0xff, 0x1c,
-	0x19, 0xf5, 0xe1, 0xbd, 0xd4, 0x28, 0xd0, 0xea, 0xe0, 0x25, 0xec, 0xdf, 0xb9, 0x28, 0x1d, 0xb0,
-	0x17, 0x97, 0x93, 0xd0, 0x7b, 0x40, 0xba, 0xe0, 0xe8, 0xd5, 0xf9, 0xe4, 0x6c, 0xf1, 0xd6, 0xb3,
-	0x48, 0x1b, 0xf4, 0xe5, 0xf2, 0x76, 0x82, 0x37, 0x60, 0xe3, 0x51, 0xba, 0x50, 0x7f, 0x1a, 0xde,
-	0x03, 0x8d, 0x9e, 0x86, 0x53, 0xcf, 0x22, 0x0e, 0xec, 0x9e, 0x86, 0xd3, 0x93, 0x57, 0xde, 0x8e,
-	0xae, 0x7d, 0x7c, 0x7d, 0xe2, 0xb5, 0x08, 0xc0, 0xde, 0xc7, 0xd7, 0x27, 0xf4, 0xe4, 0x95, 0x67,
-	0x07, 0x7f, 0x5b, 0xd0, 0xa9, 0x73, 0x10, 0x02, 0x76, 0xc2, 0x64, 0x8c, 0xb3, 0xc9, 0x09, 0x71,
-	0xad, 0x6b, 0x38, 0x5d, 0xcc, 0x24, 0xc2, 0x35, 0x39, 0x02, 0x90, 0x2a, 0x12, 0x0a, 0xc7, 0x19,
-	0xce, 0x1d, 0x3b, 0x74, 0xb0, 0xa2, 0xa7, 0x18, 0x79, 0x0a, 0x8e, 0x60, 0xd1, 0xca, 0xa0, 0x36,
-	0xa2, 0x1d, 0x5d, 0x40, 0xf0, 0x08, 0x20, 0x63, 0x19, 0x17, 0x6b, 0x5a, 0x4a, 0x86, 0x53, 0xc5,
-	0x0e, 0x1d, 0x53, 0x59, 0x48, 0x16, 0xfc, 0x6b, 0x41, 0x6f, 0xca, 0x93, 0x72, 0xc5, 0xae, 0xd6,
-	0x05, 0xc3, 0x54, 0x7f, 0xc0, 0xbe, 0x39, 0x37, 0xb9, 0x96, 0x8a, 0x65, 0x98, 0xae, 0x37, 0x7c,
-	0xb1, 0xfd, 0xba, 0xdc, 0x91, 0x9a, 0x61, 0x74, 0x89, 0xb2, 0xc6, 0xc5, 0xb9, 0xbe, 0xad, 0x92,
-	0x1f, 0xc0, 0xcd, 0x50, 0x43, 0xd5, 0xba, 0xa8, 0xbb, 0x84, 0x6c, 0x63, 0x43, 0x7e, 0x84, 0x5e,
-	0x5e, 0x66, 0x94, 0x2f, 0xa9, 0x29, 0x4a, 0xec, 0xb7, 0x1b, 0xee, 0xe7, 0x65, 0x36, 0x5f, 0x9a,
-	0xfd, 0x64, 0xf0, 0x02, 0xdc, 0xc6, 0x5e, 0x77, 0xdf, 0x85, 0x03, 0xbb, 0x97, 0xf3, 0xf9, 0x4c,
-	0xbf, 0xb4, 0x0e, 0xd8, 0xd3, 0xd3, 0x8b, 0x89, 0xb7, 0x13, 0xac, 0xe0, 0xfb, 0xb1, 0x48, 0x55,
-	0x1a, 0x47, 0xab, 0x85, 0x64, 0xe2, 0x57, 0x5e, 0x8a, 0x9c, 0xad, 0xab, 0xdb, 0xbe, 0x39, 0x74,
-	0xab, 0x71, 0xe8, 0x23, 0x68, 0x57, 0x5d, 0x62, 0x4a, 0x77, 0xd8, 0xff, 0xda, 0xc0, 0x08, 0x6b,
-	0x41, 0x70, 0x0d, 0x4f, 0xb7, 0xec, 0x26, 0xeb, 0xed, 0xc6, 0x60, 0xc7, 0xe5, 0x67, 0xe9, 0x5b,
-	0xf8, 0xb1, 0x6e, 0x3f, 0xd9, 0xff, 0x4f, 0x1b, 0xa2, 0xf8, 0xec, 0xbb, 0xdf, 0xab, 0xff, 0x61,
-	0xa5, 0xa0, 0xf8, 0x93, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x20, 0x07, 0xbc, 0xf0, 0x34, 0x07,
-	0x00, 0x00,
+	// 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,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 1ea24bf..194aa6b 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -89,6 +89,9 @@
 
   // The metrics for calling Ninja.
   repeated PerfInfo ninja_runs = 20;
+
+  // The metrics for the whole build
+  optional PerfInfo total = 21;
 }
 
 message PerfInfo {