Merge "AIDEGen: Remove the duplicate *.srcjar from srcs"
diff --git a/Android.bp b/Android.bp
index 89a1711..1dfac87 100644
--- a/Android.bp
+++ b/Android.bp
@@ -67,6 +67,7 @@
         "android/proto.go",
         "android/register.go",
         "android/rule_builder.go",
+        "android/sdk.go",
         "android/sh_binary.go",
         "android/singleton.go",
         "android/testing.go",
@@ -171,6 +172,7 @@
         "cc/vndk_prebuilt.go",
         "cc/xom.go",
 
+        "cc/cflag_artifacts.go",
         "cc/cmakelists.go",
         "cc/compdb.go",
         "cc/compiler.go",
@@ -303,7 +305,6 @@
         "java/jdeps_test.go",
         "java/kotlin_test.go",
         "java/plugin_test.go",
-        "java/robolectric_test.go",
         "java/sdk_test.go",
     ],
     pluginFor: ["soong_build"],
@@ -332,6 +333,8 @@
         "soong-cc-config",
     ],
     srcs: [
+        "rust/config/arm_device.go",
+        "rust/config/arm64_device.go",
         "rust/config/global.go",
         "rust/config/toolchain.go",
         "rust/config/whitelist.go",
@@ -475,6 +478,26 @@
     pluginFor: ["soong_build"],
 }
 
+bootstrap_go_package {
+    name: "soong-sdk",
+    pkgPath: "android/soong/sdk",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-apex",
+        "soong-cc",
+        "soong-java",
+    ],
+    srcs: [
+        "sdk/sdk.go",
+    ],
+    testSrcs: [
+        "sdk/sdk_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
+
 //
 // Defaults to enable various configurations of host bionic
 //
diff --git a/OWNERS b/OWNERS
index 9f0fcf2..797229f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,4 +4,4 @@
 per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
 per-file tidy.go = srhines@google.com, chh@google.com
 per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
-per-file rust/config/whitelist.go = ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, srhines@google.com
+per-file rust/config/whitelist.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, srhines@google.com
diff --git a/android/androidmk.go b/android/androidmk.go
index 124523f..9071347 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -80,12 +80,14 @@
 	footer bytes.Buffer
 
 	ExtraEntries []AndroidMkExtraEntriesFunc
+	ExtraFooters []AndroidMkExtraFootersFunc
 
 	EntryMap   map[string][]string
 	entryOrder []string
 }
 
 type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
+type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries)
 
 func (a *AndroidMkEntries) SetString(name, value string) {
 	if _, ok := a.EntryMap[name]; !ok {
@@ -94,6 +96,13 @@
 	a.EntryMap[name] = []string{value}
 }
 
+func (a *AndroidMkEntries) SetPath(name string, path Path) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.entryOrder = append(a.entryOrder, name)
+	}
+	a.EntryMap[name] = []string{path.String()}
+}
+
 func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
 	if flag {
 		if _, ok := a.EntryMap[name]; !ok {
@@ -103,6 +112,17 @@
 	}
 }
 
+func (a *AndroidMkEntries) SetBool(name string, flag bool) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.entryOrder = append(a.entryOrder, name)
+	}
+	if flag {
+		a.EntryMap[name] = []string{"true"}
+	} else {
+		a.EntryMap[name] = []string{"false"}
+	}
+}
+
 func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
 	if len(value) == 0 {
 		return
@@ -254,9 +274,21 @@
 
 	// Write to footer.
 	fmt.Fprintln(&a.footer, "include "+a.Include)
+	blueprintDir := filepath.Dir(bpPath)
+	for _, footerFunc := range a.ExtraFooters {
+		footerFunc(&a.footer, name, prefix, blueprintDir, a)
+	}
 }
 
 func (a *AndroidMkEntries) write(w io.Writer) {
+	if a.Disabled {
+		return
+	}
+
+	if !a.OutputFile.Valid() {
+		return
+	}
+
 	w.Write(a.header.Bytes())
 	for _, name := range a.entryOrder {
 		fmt.Fprintln(w, name+" := "+strings.Join(a.EntryMap[name], " "))
@@ -264,6 +296,10 @@
 	w.Write(a.footer.Bytes())
 }
 
+func (a *AndroidMkEntries) FooterLinesForTests() []string {
+	return strings.Split(string(a.footer.Bytes()), "\n")
+}
+
 func AndroidMkSingleton() Singleton {
 	return &androidMkSingleton{}
 }
diff --git a/android/apex.go b/android/apex.go
index 99b13ab..c548095 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -19,7 +19,6 @@
 	"sync"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
 )
 
 // ApexModule is the interface that a module type is expected to implement if
@@ -76,13 +75,17 @@
 	// CreateApexVariations.
 	setApexName(apexName string)
 
-	// Return the no_apex property
-	NoApex() bool
+	// Tests if this module is available for the specified APEX or ":platform"
+	AvailableFor(what string) bool
 }
 
 type ApexProperties struct {
-	// Whether this module should not be part of any APEX. Default is false.
-	No_apex *bool
+	// Availability of this module in APEXes. Only the listed APEXes can include this module.
+	// "//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"].
+	// TODO(b/128708192) change the default to ["//apex_available:platform"]
+	Apex_available []string
 
 	// Name of the apex variant that this module is mutated into
 	ApexName string `blueprint:"mutated"`
@@ -132,19 +135,46 @@
 	return false
 }
 
-func (m *ApexModuleBase) NoApex() bool {
-	return proptools.Bool(m.ApexProperties.No_apex)
+const (
+	availableToPlatform = "//apex_available:platform"
+	availableToAnyApex  = "//apex_available:anyapex"
+)
+
+func (m *ApexModuleBase) AvailableFor(what string) bool {
+	if len(m.ApexProperties.Apex_available) == 0 {
+		// apex_available defaults to ["//apex_available:platform", "//apex_available:anyapex"],
+		// which means 'available to everybody'.
+		return true
+	}
+	return InList(what, m.ApexProperties.Apex_available) ||
+		(what != availableToPlatform && InList(availableToAnyApex, m.ApexProperties.Apex_available))
+}
+
+func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
+	for _, n := range m.ApexProperties.Apex_available {
+		if n == availableToPlatform || n == availableToAnyApex {
+			continue
+		}
+		if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
+			mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n)
+		}
+	}
 }
 
 func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module {
 	if len(m.apexVariations) > 0 {
+		m.checkApexAvailableProperty(mctx)
 		sort.Strings(m.apexVariations)
-		variations := []string{""} // Original variation for platform
+		variations := []string{}
+		availableForPlatform := m.AvailableFor(availableToPlatform)
+		if availableForPlatform {
+			variations = append(variations, "") // Original variation for platform
+		}
 		variations = append(variations, m.apexVariations...)
 
 		modules := mctx.CreateVariations(variations...)
 		for i, m := range modules {
-			if i == 0 {
+			if availableForPlatform && i == 0 {
 				continue
 			}
 			m.(ApexModule).setApexName(variations[i])
diff --git a/android/arch.go b/android/arch.go
index 907c58b..348b064 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -527,7 +527,6 @@
 	CpuVariant   string
 	Abi          []string
 	ArchFeatures []string
-	Native       bool
 }
 
 func (a Arch) String() string {
@@ -855,149 +854,11 @@
 	}
 }
 
-func filterArchStructFields(fields []reflect.StructField) (filteredFields []reflect.StructField, filtered bool) {
-	for _, field := range fields {
-		if !proptools.HasTag(field, "android", "arch_variant") {
-			filtered = true
-			continue
-		}
-
-		// The arch_variant field isn't necessary past this point
-		// Instead of wasting space, just remove it. Go also has a
-		// 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 = ""
-		}
-
-		// Recurse into structs
-		switch field.Type.Kind() {
-		case reflect.Struct:
-			var subFiltered bool
-			field.Type, subFiltered = filterArchStruct(field.Type)
-			filtered = filtered || subFiltered
-			if field.Type == nil {
-				continue
-			}
-		case reflect.Ptr:
-			if field.Type.Elem().Kind() == reflect.Struct {
-				nestedType, subFiltered := filterArchStruct(field.Type.Elem())
-				filtered = filtered || subFiltered
-				if nestedType == nil {
-					continue
-				}
-				field.Type = reflect.PtrTo(nestedType)
-			}
-		case reflect.Interface:
-			panic("Interfaces are not supported in arch_variant properties")
-		}
-
-		filteredFields = append(filteredFields, field)
-	}
-
-	return filteredFields, filtered
-}
-
-// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a reflect.Type
-// that only contains the fields in the original type that have an `android:"arch_variant"` struct tag, and a bool
-// that is true if the new struct type has fewer fields than the original type.  If there are no fields in the
-// original type with the struct tag it returns nil and true.
-func filterArchStruct(prop reflect.Type) (filteredProp reflect.Type, filtered bool) {
-	var fields []reflect.StructField
-
-	ptr := prop.Kind() == reflect.Ptr
-	if ptr {
-		prop = prop.Elem()
-	}
-
-	for i := 0; i < prop.NumField(); i++ {
-		fields = append(fields, prop.Field(i))
-	}
-
-	filteredFields, filtered := filterArchStructFields(fields)
-
-	if len(filteredFields) == 0 {
-		return nil, true
-	}
-
-	if !filtered {
-		if ptr {
-			return reflect.PtrTo(prop), false
-		}
-		return prop, false
-	}
-
-	ret := reflect.StructOf(filteredFields)
-	if ptr {
-		ret = reflect.PtrTo(ret)
-	}
-
-	return ret, true
-}
-
-// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a list of
-// reflect.Type that only contains the fields in the original type that have an `android:"arch_variant"` struct tag,
-// and a bool that is true if the new struct type has fewer fields than the original type.  If there are no fields in
-// the original type with the struct tag it returns nil and true.  Each returned struct type will have a maximum of
-// 10 top level fields in it to attempt to avoid hitting the reflect.StructOf name length limit, although the limit
-// can still be reached with a single struct field with many fields in it.
-func filterArchStructSharded(prop reflect.Type) (filteredProp []reflect.Type, filtered bool) {
-	var fields []reflect.StructField
-
-	ptr := prop.Kind() == reflect.Ptr
-	if ptr {
-		prop = prop.Elem()
-	}
-
-	for i := 0; i < prop.NumField(); i++ {
-		fields = append(fields, prop.Field(i))
-	}
-
-	fields, filtered = filterArchStructFields(fields)
-	if !filtered {
-		if ptr {
-			return []reflect.Type{reflect.PtrTo(prop)}, false
-		}
-		return []reflect.Type{prop}, false
-	}
-
-	if len(fields) == 0 {
-		return nil, true
-	}
-
-	shards := shardFields(fields, 10)
-
-	for _, shard := range shards {
-		s := reflect.StructOf(shard)
-		if ptr {
-			s = reflect.PtrTo(s)
-		}
-		filteredProp = append(filteredProp, s)
-	}
-
-	return filteredProp, true
-}
-
-func shardFields(fields []reflect.StructField, shardSize int) [][]reflect.StructField {
-	ret := make([][]reflect.StructField, 0, (len(fields)+shardSize-1)/shardSize)
-	for len(fields) > shardSize {
-		ret = append(ret, fields[0:shardSize])
-		fields = fields[shardSize:]
-	}
-	if len(fields) > 0 {
-		ret = append(ret, fields)
-	}
-	return ret
-}
-
 // 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, _ := filterArchStructSharded(props)
+	propShards, _ := proptools.FilterPropertyStructSharded(props, filterArchStruct)
 	if len(propShards) == 0 {
 		return nil
 	}
@@ -1096,6 +957,23 @@
 	return ret
 }
 
+func filterArchStruct(field reflect.StructField, prefix string) (bool, reflect.StructField) {
+	if proptools.HasTag(field, "android", "arch_variant") {
+		// The arch_variant field isn't necessary past this point
+		// Instead of wasting space, just remove it. Go also has a
+		// 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 = ""
+		}
+		return true, field
+	}
+	return false, field
+}
+
 var archPropTypeMap OncePer
 
 func InitArchModule(m Module) {
@@ -1482,11 +1360,6 @@
 			addTarget(Android, *variables.DeviceSecondaryArch,
 				variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
 				variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
-
-			deviceArches := targets[Android]
-			if deviceArches[0].Arch.ArchType.Multilib == deviceArches[1].Arch.ArchType.Multilib {
-				deviceArches[1].Arch.Native = false
-			}
 		}
 
 		if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
@@ -1634,7 +1507,7 @@
 		if err != nil {
 			return nil, err
 		}
-		arch.Native = false
+
 		ret = append(ret, Target{
 			Os:   Android,
 			Arch: arch,
@@ -1663,7 +1536,6 @@
 		ArchVariant: stringPtr(archVariant),
 		CpuVariant:  stringPtr(cpuVariant),
 		Abi:         abi,
-		Native:      true,
 	}
 
 	if a.ArchVariant == a.ArchType.Name || a.ArchVariant == "generic" {
diff --git a/android/arch_test.go b/android/arch_test.go
index 0589e6c..11edb4f 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -17,6 +17,8 @@
 import (
 	"reflect"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 type Named struct {
@@ -219,7 +221,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			out, filtered := filterArchStruct(reflect.TypeOf(test.in))
+			out, filtered := proptools.FilterPropertyStruct(reflect.TypeOf(test.in), filterArchStruct)
 			if filtered != test.filtered {
 				t.Errorf("expected filtered %v, got %v", test.filtered, filtered)
 			}
diff --git a/android/config.go b/android/config.go
index cb1bdf5..26c4e6e 100644
--- a/android/config.go
+++ b/android/config.go
@@ -93,6 +93,10 @@
 	BuildOsVariant       string
 	BuildOsCommonVariant string
 
+	// multilibConflicts for an ArchType is true if there is earlier configured device architecture with the same
+	// multilib value.
+	multilibConflicts map[ArchType]bool
+
 	deviceConfig *deviceConfig
 
 	srcDir   string // the path of the root source directory
@@ -240,10 +244,10 @@
 	config := testConfig.config
 
 	config.Targets[Android] = []Target{
-		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
-		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
-		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64"},
-		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm"},
+		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
+		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64"},
+		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm"},
 	}
 
 	return testConfig
@@ -255,7 +259,7 @@
 
 	config.Targets = map[OsType][]Target{
 		Fuchsia: []Target{
-			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Native: true}, NativeBridgeDisabled, "", ""},
+			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: ""}, NativeBridgeDisabled, "", ""},
 		},
 		BuildOs: []Target{
 			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
@@ -272,8 +276,8 @@
 
 	config.Targets = map[OsType][]Target{
 		Android: []Target{
-			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
-			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
+			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
 		},
 		BuildOs: []Target{
 			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
@@ -305,8 +309,9 @@
 
 		env: originalEnv,
 
-		srcDir:   srcDir,
-		buildDir: buildDir,
+		srcDir:            srcDir,
+		buildDir:          buildDir,
+		multilibConflicts: make(map[ArchType]bool),
 	}
 
 	config.deviceConfig = &deviceConfig{
@@ -360,6 +365,14 @@
 		targets[Android] = androidTargets
 	}
 
+	multilib := make(map[string]bool)
+	for _, target := range targets[Android] {
+		if seen := multilib[target.Arch.ArchType.Multilib]; seen {
+			config.multilibConflicts[target.Arch.ArchType] = true
+		}
+		multilib[target.Arch.ArchType.Multilib] = true
+	}
+
 	config.Targets = targets
 	config.BuildOsVariant = targets[BuildOs][0].String()
 	config.BuildOsCommonVariant = getCommonTargets(targets[BuildOs])[0].String()
@@ -800,7 +813,7 @@
 func (c *config) EnforceRROForModule(name string) bool {
 	enforceList := c.productVariables.EnforceRROTargets
 	if enforceList != nil {
-		if len(enforceList) == 1 && (enforceList)[0] == "*" {
+		if InList("*", enforceList) {
 			return true
 		}
 		return InList(name, enforceList)
@@ -852,6 +865,10 @@
 	return Bool(c.productVariables.VndkSnapshotBuildArtifacts)
 }
 
+func (c *config) HasMultilibConflict(arch ArchType) bool {
+	return c.multilibConflicts[arch]
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -1009,19 +1026,6 @@
 	return "", false
 }
 
-// SecondArchIsTranslated returns true if the primary device arch is X86 or X86_64 and the device also has an arch
-// that is Arm or Arm64.
-func (c *config) SecondArchIsTranslated() bool {
-	deviceTargets := c.Targets[Android]
-	if len(deviceTargets) < 2 {
-		return false
-	}
-
-	arch := deviceTargets[0].Arch
-
-	return (arch.ArchType == X86 || arch.ArchType == X86_64) && hasArmAndroidArch(deviceTargets)
-}
-
 func (c *config) IntegerOverflowDisabledForPath(path string) bool {
 	if c.productVariables.IntegerOverflowExcludePaths == nil {
 		return false
diff --git a/android/hooks.go b/android/hooks.go
index 5810996..604cb9c 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -30,7 +30,7 @@
 	BaseModuleContext
 	AppendProperties(...interface{})
 	PrependProperties(...interface{})
-	CreateModule(blueprint.ModuleFactory, ...interface{})
+	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 // Arch hooks are run after the module has been split into architecture variants, and can be used
@@ -75,7 +75,7 @@
 
 type InstallHookContext interface {
 	ModuleContext
-	Path() OutputPath
+	Path() InstallPath
 	Symlink() bool
 }
 
@@ -89,11 +89,11 @@
 
 type installHookContext struct {
 	ModuleContext
-	path    OutputPath
+	path    InstallPath
 	symlink bool
 }
 
-func (x *installHookContext) Path() OutputPath {
+func (x *installHookContext) Path() InstallPath {
 	return x.path
 }
 
@@ -101,7 +101,7 @@
 	return x.symlink
 }
 
-func (x *hooks) runInstallHooks(ctx ModuleContext, path OutputPath, symlink bool) {
+func (x *hooks) runInstallHooks(ctx ModuleContext, path InstallPath, symlink bool) {
 	if len(x.install) > 0 {
 		mctx := &installHookContext{
 			ModuleContext: ctx,
diff --git a/android/module.go b/android/module.go
index 8076a99..5d1a609 100644
--- a/android/module.go
+++ b/android/module.go
@@ -147,15 +147,17 @@
 	ExpandSource(srcFile, prop string) Path
 	ExpandOptionalSource(srcFile *string, prop string) OptionalPath
 
-	InstallExecutable(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
-	InstallFile(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
-	InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath
-	InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath
+	InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+	InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+	InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath
+	InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath
 	CheckbuildFile(srcPath Path)
 
 	InstallInData() bool
+	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
 	InstallBypassMake() bool
 
 	RequiredModuleNames() []string
@@ -192,8 +194,10 @@
 	Enabled() bool
 	Target() Target
 	InstallInData() bool
+	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
 	InstallBypassMake() bool
 	SkipInstall()
 	ExportedToMake() bool
@@ -510,8 +514,19 @@
 
 	m.AddProperties(
 		&base.nameProperties,
-		&base.commonProperties,
-		&base.variableProperties)
+		&base.commonProperties)
+
+	// Allow tests to override the default product variables
+	if base.variableProperties == nil {
+		base.variableProperties = zeroProductVariables
+	}
+
+	// Filter the product variables properties to the ones that exist on this module
+	base.variableProperties = createVariableProperties(m.GetProperties(), base.variableProperties)
+	if base.variableProperties != nil {
+		m.AddProperties(base.variableProperties)
+	}
+
 	base.generalProperties = m.GetProperties()
 	base.customizableProperties = m.GetProperties()
 
@@ -593,7 +608,7 @@
 
 	nameProperties          nameProperties
 	commonProperties        commonProperties
-	variableProperties      variableProperties
+	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
 	generalProperties       []interface{}
 	archProperties          [][]interface{}
@@ -832,6 +847,10 @@
 	return false
 }
 
+func (m *ModuleBase) InstallInTestcases() bool {
+	return false
+}
+
 func (m *ModuleBase) InstallInSanitizerDir() bool {
 	return false
 }
@@ -840,6 +859,10 @@
 	return Bool(m.commonProperties.Recovery)
 }
 
+func (m *ModuleBase) InstallInRoot() bool {
+	return false
+}
+
 func (m *ModuleBase) InstallBypassMake() bool {
 	return false
 }
@@ -1177,6 +1200,12 @@
 func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
 
+	if m.config.UseGoma() && params.Pool == nil {
+		// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
+		// local parallelism value
+		params.Pool = localPool
+	}
+
 	rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...)
 
 	if m.config.captureBuild {
@@ -1504,6 +1533,10 @@
 	return m.module.InstallInData()
 }
 
+func (m *moduleContext) InstallInTestcases() bool {
+	return m.module.InstallInTestcases()
+}
+
 func (m *moduleContext) InstallInSanitizerDir() bool {
 	return m.module.InstallInSanitizerDir()
 }
@@ -1512,11 +1545,15 @@
 	return m.module.InstallInRecovery()
 }
 
+func (m *moduleContext) InstallInRoot() bool {
+	return m.module.InstallInRoot()
+}
+
 func (m *moduleContext) InstallBypassMake() bool {
 	return m.module.InstallBypassMake()
 }
 
-func (m *moduleContext) skipInstall(fullInstallPath OutputPath) bool {
+func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
 	}
@@ -1541,18 +1578,18 @@
 	return false
 }
 
-func (m *moduleContext) InstallFile(installPath OutputPath, name string, srcPath Path,
-	deps ...Path) OutputPath {
+func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
+	deps ...Path) InstallPath {
 	return m.installFile(installPath, name, srcPath, Cp, deps)
 }
 
-func (m *moduleContext) InstallExecutable(installPath OutputPath, name string, srcPath Path,
-	deps ...Path) OutputPath {
+func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
+	deps ...Path) InstallPath {
 	return m.installFile(installPath, name, srcPath, CpExecutable, deps)
 }
 
-func (m *moduleContext) installFile(installPath OutputPath, name string, srcPath Path,
-	rule blueprint.Rule, deps []Path) OutputPath {
+func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path,
+	rule blueprint.Rule, deps []Path) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, false)
@@ -1587,7 +1624,7 @@
 	return fullInstallPath
 }
 
-func (m *moduleContext) InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath {
+func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
 
@@ -1616,7 +1653,7 @@
 
 // installPath/name -> absPath where absPath might be a path that is available only at runtime
 // (e.g. /apex/...)
-func (m *moduleContext) InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath {
+func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
 
diff --git a/android/mutator.go b/android/mutator.go
index e76f847..88ac521 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"reflect"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -121,7 +123,7 @@
 
 	Rename(name string)
 
-	CreateModule(blueprint.ModuleFactory, ...interface{})
+	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 type topDownMutatorContext struct {
@@ -243,9 +245,25 @@
 	t.Module().base().commonProperties.DebugName = name
 }
 
-func (t *topDownMutatorContext) CreateModule(factory blueprint.ModuleFactory, props ...interface{}) {
-	inherited := []interface{}{&t.Module().base().commonProperties, &t.Module().base().variableProperties}
-	t.bp.CreateModule(factory, append(inherited, props...)...)
+func (t *topDownMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
+	inherited := []interface{}{&t.Module().base().commonProperties}
+	module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module)
+
+	if t.Module().base().variableProperties != nil && module.base().variableProperties != nil {
+		src := t.Module().base().variableProperties
+		dst := []interface{}{
+			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(),
+		}
+		err := proptools.AppendMatchingProperties(dst, src, nil)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	return module
 }
 
 func (b *bottomUpMutatorContext) MutatorName() string {
diff --git a/android/notices.go b/android/notices.go
index 7b61d65..bf273b5 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -60,7 +60,7 @@
 	})
 }
 
-func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string,
+func BuildNoticeOutput(ctx ModuleContext, installPath InstallPath, installFilename string,
 	noticePaths []Path) NoticeOutputs {
 	// Merge all NOTICE files into one.
 	// TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 00b99ff..548450e 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -104,7 +104,8 @@
 }
 
 // RuleFunc wraps blueprint.PackageContext.RuleFunc, converting the interface{} config
-// argument to a Context that supports Config().
+// argument to a Context that supports Config(), and provides a default Pool if none is
+// specified.
 func (p PackageContext) RuleFunc(name string,
 	f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule {
 
@@ -114,6 +115,11 @@
 		if len(ctx.errors) > 0 {
 			return params, ctx.errors[0]
 		}
+		if ctx.Config().UseGoma() && params.Pool == nil {
+			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
+			// local parallelism value
+			params.Pool = localPool
+		}
 		return params, nil
 	}, argNames...)
 }
@@ -234,10 +240,16 @@
 	})
 }
 
-// AndroidStaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified
+// AndroidStaticRule is an alias for StaticRule.
 func (p PackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
-	return p.AndroidRuleFunc(name, func(PackageRuleContext) blueprint.RuleParams {
+	return p.StaticRule(name, params, argNames...)
+}
+
+// StaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified.
+func (p PackageContext) StaticRule(name string, params blueprint.RuleParams,
+	argNames ...string) blueprint.Rule {
+	return p.RuleFunc(name, func(PackageRuleContext) blueprint.RuleParams {
 		return params
 	}, argNames...)
 }
@@ -245,18 +257,6 @@
 // AndroidGomaStaticRule wraps blueprint.StaticRule but uses goma's parallelism if goma is enabled
 func (p PackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams,
 	argNames ...string) blueprint.Rule {
-	return p.StaticRule(name, params, argNames...)
-}
-
-func (p PackageContext) AndroidRuleFunc(name string,
-	f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule {
-	return p.RuleFunc(name, func(ctx PackageRuleContext) blueprint.RuleParams {
-		params := f(ctx)
-		if ctx.Config().UseGoma() && params.Pool == nil {
-			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
-			// local parallelism value
-			params.Pool = localPool
-		}
-		return params
-	}, argNames...)
+	// bypass android.PackageContext.StaticRule so that Pool does not get set to local_pool.
+	return p.PackageContext.StaticRule(name, params, argNames...)
 }
diff --git a/android/paths.go b/android/paths.go
index 0d64a61..8dbb086 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -44,8 +44,10 @@
 	BaseModuleContext
 
 	InstallInData() bool
+	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
 	InstallBypassMake() bool
 }
 
@@ -791,7 +793,7 @@
 	return OptionalPathForPath(PathForSource(ctx, relPath))
 }
 
-// OutputPath is a Path representing a file path rooted from the build directory
+// OutputPath is a Path representing an intermediates file path rooted from the build directory
 type OutputPath struct {
 	basePath
 }
@@ -819,17 +821,6 @@
 	return OutputPath{basePath{path, ctx.Config(), ""}}
 }
 
-// pathForInstallInMakeDir is used by PathForModuleInstall when the module returns true
-// for InstallBypassMake to produce an OutputPath that installs to $OUT_DIR instead of
-// $OUT_DIR/soong.
-func pathForInstallInMakeDir(ctx PathContext, pathComponents ...string) OutputPath {
-	path, err := validatePath(pathComponents...)
-	if err != nil {
-		reportPathError(ctx, err)
-	}
-	return OutputPath{basePath{"../" + path, ctx.Config(), ""}}
-}
-
 // PathsForOutput returns Paths rooted from buildDir
 func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
 	ret := make(WritablePaths, len(paths))
@@ -845,10 +836,6 @@
 	return filepath.Join(p.config.buildDir, p.path)
 }
 
-func (p OutputPath) RelPathString() string {
-	return p.path
-}
-
 // Join creates a new OutputPath with paths... joined with the current path. The
 // provided paths... may not use '..' to escape from the current path.
 func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
@@ -1117,9 +1104,44 @@
 	return ModuleResPath{PathForModuleOut(ctx, "res", p)}
 }
 
+// InstallPath is a Path representing a installed file path rooted from the build directory
+type InstallPath struct {
+	basePath
+
+	baseDir string // "../" for Make paths to convert "out/soong" to "out", "" for Soong paths
+}
+
+func (p InstallPath) writablePath() {}
+
+func (p InstallPath) String() string {
+	return filepath.Join(p.config.buildDir, p.baseDir, p.path)
+}
+
+// Join creates a new InstallPath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
+	path, err := validatePath(paths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return p.withRel(path)
+}
+
+func (p InstallPath) withRel(rel string) InstallPath {
+	p.basePath = p.basePath.withRel(rel)
+	return p
+}
+
+// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's,
+// i.e. out/ instead of out/soong/.
+func (p InstallPath) ToMakePath() InstallPath {
+	p.baseDir = "../"
+	return p
+}
+
 // PathForModuleInstall returns a Path representing the install path for the
 // module appended with paths...
-func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
+func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
 	var outPaths []string
 	if ctx.Device() {
 		partition := modulePartition(ctx)
@@ -1139,13 +1161,30 @@
 		outPaths = append([]string{"debug"}, outPaths...)
 	}
 	outPaths = append(outPaths, pathComponents...)
-	if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
-		return pathForInstallInMakeDir(ctx, outPaths...)
+
+	path, err := validatePath(outPaths...)
+	if err != nil {
+		reportPathError(ctx, err)
 	}
-	return PathForOutput(ctx, outPaths...)
+
+	ret := InstallPath{basePath{path, ctx.Config(), ""}, ""}
+	if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
+		ret = ret.ToMakePath()
+	}
+
+	return ret
 }
 
-func InstallPathToOnDevicePath(ctx PathContext, path OutputPath) string {
+func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
+	paths = append([]string{"ndk"}, paths...)
+	path, err := validatePath(paths...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return InstallPath{basePath{path, ctx.Config(), ""}, ""}
+}
+
+func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
 	rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
 
 	return "/" + rel
@@ -1155,9 +1194,15 @@
 	var partition string
 	if ctx.InstallInData() {
 		partition = "data"
+	} else if ctx.InstallInTestcases() {
+		partition = "testcases"
 	} else if ctx.InstallInRecovery() {
-		// the layout of recovery partion is the same as that of system partition
-		partition = "recovery/root/system"
+		if ctx.InstallInRoot() {
+			partition = "recovery/root"
+		} else {
+			// the layout of recovery partion is the same as that of system partition
+			partition = "recovery/root/system"
+		}
 	} else if ctx.SocSpecific() {
 		partition = ctx.DeviceConfig().VendorPath()
 	} else if ctx.DeviceSpecific() {
@@ -1166,6 +1211,8 @@
 		partition = ctx.DeviceConfig().ProductPath()
 	} else if ctx.SystemExtSpecific() {
 		partition = ctx.DeviceConfig().SystemExtPath()
+	} else if ctx.InstallInRoot() {
+		partition = "root"
 	} else {
 		partition = "system"
 	}
diff --git a/android/paths_test.go b/android/paths_test.go
index f2996bf..2e67272 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -201,8 +201,10 @@
 	baseModuleContext
 
 	inData         bool
+	inTestcases    bool
 	inSanitizerDir bool
 	inRecovery     bool
+	inRoot         bool
 }
 
 func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
@@ -219,6 +221,10 @@
 	return m.inData
 }
 
+func (m moduleInstallPathContextImpl) InstallInTestcases() bool {
+	return m.inTestcases
+}
+
 func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool {
 	return m.inSanitizerDir
 }
@@ -227,6 +233,10 @@
 	return m.inRecovery
 }
 
+func (m moduleInstallPathContextImpl) InstallInRoot() bool {
+	return m.inRoot
+}
+
 func (m moduleInstallPathContextImpl) InstallBypassMake() bool {
 	return false
 }
@@ -308,6 +318,40 @@
 			in:  []string{"bin", "my_test"},
 			out: "target/product/test_device/system_ext/bin/my_test",
 		},
+		{
+			name: "root binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					target: deviceTarget,
+				},
+				inRoot: true,
+			},
+			in:  []string{"my_test"},
+			out: "target/product/test_device/root/my_test",
+		},
+		{
+			name: "recovery binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					target: deviceTarget,
+				},
+				inRecovery: true,
+			},
+			in:  []string{"bin/my_test"},
+			out: "target/product/test_device/recovery/root/system/bin/my_test",
+		},
+		{
+			name: "recovery root binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					target: deviceTarget,
+				},
+				inRecovery: true,
+				inRoot:     true,
+			},
+			in:  []string{"my_test"},
+			out: "target/product/test_device/recovery/root/my_test",
+		},
 
 		{
 			name: "system native test binary",
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index d29ed16..6c4813b 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -65,7 +65,7 @@
 	installDirBase string
 	// The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware.
 	socInstallDirBase      string
-	installDirPath         OutputPath
+	installDirPath         InstallPath
 	additionalDependencies *Paths
 }
 
@@ -91,7 +91,7 @@
 	return PathForModuleSrc(ctx, String(p.properties.Src))
 }
 
-func (p *PrebuiltEtc) InstallDirPath() OutputPath {
+func (p *PrebuiltEtc) InstallDirPath() InstallPath {
 	return p.installDirPath
 }
 
@@ -158,7 +158,7 @@
 		ExtraEntries: []AndroidMkExtraEntriesFunc{
 			func(entries *AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
 				entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
 				if p.additionalDependencies != nil {
diff --git a/android/prebuilt_etc_test.go b/android/prebuilt_etc_test.go
index 0a2c7a4..f675ea3 100644
--- a/android/prebuilt_etc_test.go
+++ b/android/prebuilt_etc_test.go
@@ -182,9 +182,9 @@
 	`)
 
 	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-	expected := "target/product/test_device/system/usr/share/bar"
-	if p.installDirPath.RelPathString() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+	expected := buildDir + "/target/product/test_device/system/usr/share/bar"
+	if p.installDirPath.String() != expected {
+		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
 	}
 }
 
@@ -199,9 +199,9 @@
 
 	buildOS := BuildOs.String()
 	p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
-	expected := filepath.Join("host", config.PrebuiltOS(), "usr", "share", "bar")
-	if p.installDirPath.RelPathString() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+	expected := filepath.Join(buildDir, "host", config.PrebuiltOS(), "usr", "share", "bar")
+	if p.installDirPath.String() != expected {
+		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
 	}
 }
 
@@ -214,14 +214,14 @@
 	`)
 
 	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-	expected := "target/product/test_device/system/fonts"
-	if p.installDirPath.RelPathString() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+	expected := buildDir + "/target/product/test_device/system/fonts"
+	if p.installDirPath.String() != expected {
+		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
 	}
 }
 
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
-	targetPath := "target/product/test_device"
+	targetPath := buildDir + "/target/product/test_device"
 	tests := []struct {
 		description  string
 		config       string
@@ -249,7 +249,7 @@
 		t.Run(tt.description, func(t *testing.T) {
 			ctx, _ := testPrebuiltEtc(t, tt.config)
 			p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
-			if p.installDirPath.RelPathString() != tt.expectedPath {
+			if p.installDirPath.String() != tt.expectedPath {
 				t.Errorf("expected %q, got %q", tt.expectedPath, p.installDirPath)
 			}
 		})
diff --git a/android/sdk.go b/android/sdk.go
new file mode 100644
index 0000000..52c392f
--- /dev/null
+++ b/android/sdk.go
@@ -0,0 +1,146 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+// SdkAware is the interface that must be supported by any module to become a member of SDK or to be
+// built with SDK
+type SdkAware interface {
+	Module
+	sdkBase() *SdkBase
+	MakeMemberOf(sdk SdkRef)
+	IsInAnySdk() bool
+	ContainingSdk() SdkRef
+	MemberName() string
+	BuildWithSdks(sdks SdkRefs)
+	RequiredSdks() SdkRefs
+}
+
+// SdkRef refers to a version of an SDK
+type SdkRef struct {
+	Name    string
+	Version string
+}
+
+const (
+	// currentVersion refers to the in-development version of an SDK
+	currentVersion = "current"
+)
+
+// IsCurrentVersion determines if the SdkRef is referencing to an in-development version of an SDK
+func (s SdkRef) IsCurrentVersion() bool {
+	return s.Version == currentVersion
+}
+
+// IsCurrentVersionOf determines if the SdkRef is referencing to an in-development version of the
+// specified SDK
+func (s SdkRef) IsCurrentVersionOf(name string) bool {
+	return s.Name == name && s.IsCurrentVersion()
+}
+
+// ParseSdkRef parses a `name#version` style string into a corresponding SdkRef struct
+func ParseSdkRef(ctx BaseModuleContext, str string, property string) SdkRef {
+	tokens := strings.Split(str, "#")
+	if len(tokens) < 1 || len(tokens) > 2 {
+		ctx.PropertyErrorf(property, "%q does not follow name#version syntax", str)
+		return SdkRef{Name: "invalid sdk name", Version: "invalid sdk version"}
+	}
+
+	name := tokens[0]
+
+	version := currentVersion // If version is omitted, defaults to "current"
+	if len(tokens) == 2 {
+		version = tokens[1]
+	}
+
+	return SdkRef{Name: name, Version: version}
+}
+
+type SdkRefs []SdkRef
+
+func (refs SdkRefs) Contains(s SdkRef) bool {
+	for _, r := range refs {
+		if r == s {
+			return true
+		}
+	}
+	return false
+}
+
+type sdkProperties struct {
+	// The SDK that this module is a member of. nil if it is not a member of any SDK
+	ContainingSdk *SdkRef `blueprint:"mutated"`
+
+	// The list of SDK names and versions that are used to build this module
+	RequiredSdks SdkRefs `blueprint:"mutated"`
+
+	// Name of the module that this sdk member is representing
+	Sdk_member_name *string
+}
+
+// SdkBase is a struct that is expected to be included in module types to implement the SdkAware
+// interface. InitSdkAwareModule should be called to initialize this struct.
+type SdkBase struct {
+	properties sdkProperties
+}
+
+func (s *SdkBase) sdkBase() *SdkBase {
+	return s
+}
+
+// MakeMemberof sets this module to be a member of a specific SDK
+func (s *SdkBase) MakeMemberOf(sdk SdkRef) {
+	s.properties.ContainingSdk = &sdk
+}
+
+// IsInAnySdk returns true if this module is a member of any SDK
+func (s *SdkBase) IsInAnySdk() bool {
+	return s.properties.ContainingSdk != nil
+}
+
+// ContainingSdk returns the SDK that this module is a member of
+func (s *SdkBase) ContainingSdk() SdkRef {
+	if s.properties.ContainingSdk != nil {
+		return *s.properties.ContainingSdk
+	}
+	return SdkRef{Name: "", Version: currentVersion}
+}
+
+// Membername returns the name of the module that this SDK member is overriding
+func (s *SdkBase) MemberName() string {
+	return proptools.String(s.properties.Sdk_member_name)
+}
+
+// BuildWithSdks is used to mark that this module has to be built with the given SDK(s).
+func (s *SdkBase) BuildWithSdks(sdks SdkRefs) {
+	s.properties.RequiredSdks = sdks
+}
+
+// RequiredSdks returns the SDK(s) that this module has to be built with
+func (s *SdkBase) RequiredSdks() SdkRefs {
+	return s.properties.RequiredSdks
+}
+
+// InitSdkAwareModule initializes the SdkBase struct. This must be called by all modules including
+// SdkBase.
+func InitSdkAwareModule(m SdkAware) {
+	base := m.sdkBase()
+	m.AddProperties(&base.properties)
+}
diff --git a/android/sh_binary.go b/android/sh_binary.go
index ba0c8be..6db9892 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -48,6 +48,9 @@
 
 	// Whether this module is directly installable to one of the partitions. Default: true.
 	Installable *bool
+
+	// install symlinks to the binary
+	Symlinks []string `android:"arch_variant"`
 }
 
 type TestProperties struct {
@@ -103,6 +106,10 @@
 	return s.properties.Installable == nil || Bool(s.properties.Installable)
 }
 
+func (s *ShBinary) Symlinks() []string {
+	return s.properties.Symlinks
+}
+
 func (s *ShBinary) GenerateAndroidBuildActions(ctx ModuleContext) {
 	s.sourceFilePath = PathForModuleSrc(ctx, String(s.properties.Src))
 	filename := String(s.properties.Filename)
@@ -145,6 +152,9 @@
 	entries.SetString("LOCAL_MODULE_RELATIVE_PATH", String(s.properties.Sub_dir))
 	entries.SetString("LOCAL_MODULE_SUFFIX", "")
 	entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
+	if len(s.properties.Symlinks) > 0 {
+		entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " "))
+	}
 }
 
 func (s *ShTest) GenerateAndroidBuildActions(ctx ModuleContext) {
diff --git a/android/singleton.go b/android/singleton.go
index a59d54a..7f9c216 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -127,6 +127,11 @@
 }
 
 func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
+	if s.Config().UseGoma() && params.Pool == nil {
+		// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
+		// local parallelism value
+		params.Pool = localPool
+	}
 	rule := s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...)
 	if s.Config().captureBuild {
 		s.ruleParams[rule] = params
diff --git a/android/util.go b/android/util.go
index 0102442..71ded5e 100644
--- a/android/util.go
+++ b/android/util.go
@@ -319,3 +319,36 @@
 
 	return root, suffix, ext
 }
+
+// ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths.
+func ShardPaths(paths Paths, shardSize int) []Paths {
+	if len(paths) == 0 {
+		return nil
+	}
+	ret := make([]Paths, 0, (len(paths)+shardSize-1)/shardSize)
+	for len(paths) > shardSize {
+		ret = append(ret, paths[0:shardSize])
+		paths = paths[shardSize:]
+	}
+	if len(paths) > 0 {
+		ret = append(ret, paths)
+	}
+	return ret
+}
+
+// ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize
+// elements.
+func ShardStrings(s []string, shardSize int) [][]string {
+	if len(s) == 0 {
+		return nil
+	}
+	ret := make([][]string, 0, (len(s)+shardSize-1)/shardSize)
+	for len(s) > shardSize {
+		ret = append(ret, s[0:shardSize])
+		s = s[shardSize:]
+	}
+	if len(s) > 0 {
+		ret = append(ret, s)
+	}
+	return ret
+}
diff --git a/android/util_test.go b/android/util_test.go
index 1df1c5a..90fefee 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -469,3 +469,102 @@
 		}
 	})
 }
+
+func Test_Shard(t *testing.T) {
+	type args struct {
+		strings   []string
+		shardSize int
+	}
+	tests := []struct {
+		name string
+		args args
+		want [][]string
+	}{
+		{
+			name: "empty",
+			args: args{
+				strings:   nil,
+				shardSize: 1,
+			},
+			want: [][]string(nil),
+		},
+		{
+			name: "single shard",
+			args: args{
+				strings:   []string{"a", "b"},
+				shardSize: 2,
+			},
+			want: [][]string{{"a", "b"}},
+		},
+		{
+			name: "single short shard",
+			args: args{
+				strings:   []string{"a", "b"},
+				shardSize: 3,
+			},
+			want: [][]string{{"a", "b"}},
+		},
+		{
+			name: "shard per input",
+			args: args{
+				strings:   []string{"a", "b", "c"},
+				shardSize: 1,
+			},
+			want: [][]string{{"a"}, {"b"}, {"c"}},
+		},
+		{
+			name: "balanced shards",
+			args: args{
+				strings:   []string{"a", "b", "c", "d"},
+				shardSize: 2,
+			},
+			want: [][]string{{"a", "b"}, {"c", "d"}},
+		},
+		{
+			name: "unbalanced shards",
+			args: args{
+				strings:   []string{"a", "b", "c"},
+				shardSize: 2,
+			},
+			want: [][]string{{"a", "b"}, {"c"}},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Run("strings", func(t *testing.T) {
+				if got := ShardStrings(tt.args.strings, tt.args.shardSize); !reflect.DeepEqual(got, tt.want) {
+					t.Errorf("ShardStrings(%v, %v) = %v, want %v",
+						tt.args.strings, tt.args.shardSize, got, tt.want)
+				}
+			})
+
+			t.Run("paths", func(t *testing.T) {
+				stringsToPaths := func(strings []string) Paths {
+					if strings == nil {
+						return nil
+					}
+					paths := make(Paths, len(strings))
+					for i, s := range strings {
+						paths[i] = PathForTesting(s)
+					}
+					return paths
+				}
+
+				paths := stringsToPaths(tt.args.strings)
+
+				var want []Paths
+				if sWant := tt.want; sWant != nil {
+					want = make([]Paths, len(sWant))
+					for i, w := range sWant {
+						want[i] = stringsToPaths(w)
+					}
+				}
+
+				if got := ShardPaths(paths, tt.args.shardSize); !reflect.DeepEqual(got, want) {
+					t.Errorf("ShardPaths(%v, %v) = %v, want %v",
+						paths, tt.args.shardSize, got, want)
+				}
+			})
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 3f2b9e9..41943b0 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -122,7 +122,7 @@
 	} `android:"arch_variant"`
 }
 
-var zeroProductVariables variableProperties
+var zeroProductVariables interface{} = variableProperties{}
 
 type productVariables struct {
 	// Suffix to add to generated Makefiles
@@ -366,8 +366,13 @@
 
 	// TODO: depend on config variable, create variants, propagate variants up tree
 	a := module.base()
-	variableValues := reflect.ValueOf(&a.variableProperties.Product_variables).Elem()
-	zeroValues := reflect.ValueOf(zeroProductVariables.Product_variables)
+
+	if a.variableProperties == nil {
+		return
+	}
+
+	variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
+	zeroValues := reflect.ValueOf(zeroProductVariables).FieldByName("Product_variables")
 
 	for i := 0; i < variableValues.NumField(); i++ {
 		variableValue := variableValues.Field(i)
@@ -496,3 +501,106 @@
 
 	return nil
 }
+
+var variablePropTypeMap OncePer
+
+// sliceToTypeArray takes a slice of property structs and returns a reflection created array containing the
+// reflect.Types of each property struct.  The result can be used as a key in a map.
+func sliceToTypeArray(s []interface{}) interface{} {
+	// Create an array using reflection whose length is the length of the input slice
+	ret := reflect.New(reflect.ArrayOf(len(s), reflect.TypeOf(reflect.TypeOf(0)))).Elem()
+	for i, e := range s {
+		ret.Index(i).Set(reflect.ValueOf(reflect.TypeOf(e)))
+	}
+	return ret.Interface()
+}
+
+// createVariableProperties takes the list of property structs for a module and returns a property struct that
+// contains the product variable properties that exist in the property structs, or nil if there are none.  It
+// caches the result.
+func createVariableProperties(moduleTypeProps []interface{}, productVariables interface{}) interface{} {
+	// Convert the moduleTypeProps to an array of reflect.Types that can be used as a key in the OncePer.
+	key := sliceToTypeArray(moduleTypeProps)
+
+	// Use the variablePropTypeMap OncePer to cache the result for each set of property struct types.
+	typ, _ := variablePropTypeMap.Once(NewCustomOnceKey(key), func() interface{} {
+		// Compute the filtered property struct type.
+		return createVariablePropertiesType(moduleTypeProps, productVariables)
+	}).(reflect.Type)
+
+	if typ == nil {
+		return nil
+	}
+
+	// Create a new pointer to a filtered property struct.
+	return reflect.New(typ).Interface()
+}
+
+// createVariablePropertiesType creates a new type that contains only the product variable properties that exist in
+// a list of property structs.
+func createVariablePropertiesType(moduleTypeProps []interface{}, productVariables interface{}) reflect.Type {
+	typ, _ := proptools.FilterPropertyStruct(reflect.TypeOf(productVariables),
+		func(field reflect.StructField, prefix string) (bool, reflect.StructField) {
+			// Filter function, returns true if the field should be in the resulting struct
+			if prefix == "" {
+				// Keep the top level Product_variables field
+				return true, field
+			}
+			_, rest := splitPrefix(prefix)
+			if rest == "" {
+				// Keep the 2nd level field (i.e. Product_variables.Eng)
+				return true, field
+			}
+
+			// Strip off the first 2 levels of the prefix
+			_, prefix = splitPrefix(rest)
+
+			for _, p := range moduleTypeProps {
+				if fieldExistsByNameRecursive(reflect.TypeOf(p).Elem(), prefix, field.Name) {
+					// Keep any fields that exist in one of the property structs
+					return true, field
+				}
+			}
+
+			return false, field
+		})
+	return typ
+}
+
+func splitPrefix(prefix string) (first, rest string) {
+	index := strings.IndexByte(prefix, '.')
+	if index == -1 {
+		return prefix, ""
+	}
+	return prefix[:index], prefix[index+1:]
+}
+
+func fieldExistsByNameRecursive(t reflect.Type, prefix, name string) bool {
+	if t.Kind() != reflect.Struct {
+		panic(fmt.Errorf("fieldExistsByNameRecursive can only be called on a reflect.Struct"))
+	}
+
+	if prefix != "" {
+		split := strings.SplitN(prefix, ".", 2)
+		firstPrefix := split[0]
+		rest := ""
+		if len(split) > 1 {
+			rest = split[1]
+		}
+		f, exists := t.FieldByName(firstPrefix)
+		if !exists {
+			return false
+		}
+		ft := f.Type
+		if ft.Kind() == reflect.Ptr {
+			ft = ft.Elem()
+		}
+		if ft.Kind() != reflect.Struct {
+			panic(fmt.Errorf("field %q in %q is not a struct", firstPrefix, t))
+		}
+		return fieldExistsByNameRecursive(ft, rest, name)
+	} else {
+		_, exists := t.FieldByName(name)
+		return exists
+	}
+}
diff --git a/android/variable_test.go b/android/variable_test.go
index ce9ba54..c1910fe 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -16,7 +16,10 @@
 
 import (
 	"reflect"
+	"strconv"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 type printfIntoPropertyTestCase struct {
@@ -122,3 +125,111 @@
 		}
 	}
 }
+
+type testProductVariableModule struct {
+	ModuleBase
+}
+
+func (m *testProductVariableModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+var testProductVariableProperties = struct {
+	Product_variables struct {
+		Eng struct {
+			Srcs   []string
+			Cflags []string
+		}
+	}
+}{}
+
+func testProductVariableModuleFactoryFactory(props interface{}) func() Module {
+	return func() Module {
+		m := &testProductVariableModule{}
+		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
+		// for this test module.
+		m.variableProperties = testProductVariableProperties
+		InitAndroidModule(m)
+		return m
+	}
+}
+
+func TestProductVariables(t *testing.T) {
+	ctx := NewTestContext()
+	// A module type that has a srcs property but not a cflags property.
+	ctx.RegisterModuleType("module1", ModuleFactoryAdaptor(testProductVariableModuleFactoryFactory(struct {
+		Srcs []string
+	}{})))
+	// A module type that has a cflags property but not a srcs property.
+	ctx.RegisterModuleType("module2", ModuleFactoryAdaptor(testProductVariableModuleFactoryFactory(struct {
+		Cflags []string
+	}{})))
+	// A module type that does not have any properties that match product_variables.
+	ctx.RegisterModuleType("module3", ModuleFactoryAdaptor(testProductVariableModuleFactoryFactory(struct {
+		Foo []string
+	}{})))
+	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+		ctx.BottomUp("variable", variableMutator).Parallel()
+	})
+
+	// Test that a module can use one product variable even if it doesn't have all the properties
+	// supported by that product variable.
+	bp := `
+		module1 {
+			name: "foo",
+			product_variables: {
+				eng: {
+					srcs: ["foo.c"],
+				},
+			},
+		}
+		module2 {
+			name: "bar",
+			product_variables: {
+				eng: {
+					cflags: ["-DBAR"],
+				},
+			},
+		}
+
+		module3 {
+			name: "baz",
+		}
+	`
+
+	mockFS := map[string][]byte{
+		"Android.bp": []byte(bp),
+	}
+
+	ctx.MockFileSystem(mockFS)
+
+	ctx.Register()
+
+	config := TestConfig(buildDir, nil)
+	config.TestProductVariables.Eng = proptools.BoolPtr(true)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	FailIfErrored(t, errs)
+}
+
+func BenchmarkSliceToTypeArray(b *testing.B) {
+	for _, n := range []int{1, 2, 4, 8, 100} {
+		var propStructs []interface{}
+		for i := 0; i < n; i++ {
+			propStructs = append(propStructs, &struct {
+				A *string
+				B string
+			}{})
+
+		}
+		b.Run(strconv.Itoa(n), func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				_ = sliceToTypeArray(propStructs)
+			}
+		})
+	}
+}
diff --git a/apex/apex.go b/apex/apex.go
index c3f6a74..dce9bea 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -47,13 +47,15 @@
 		Description: "fs_config ${out}",
 	}, "ro_paths", "exec_paths")
 
-	injectApexDependency = pctx.StaticRule("injectApexDependency", blueprint.RuleParams{
+	apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
 		Command: `rm -f $out && ${jsonmodify} $in ` +
 			`-a provideNativeLibs ${provideNativeLibs} ` +
-			`-a requireNativeLibs ${requireNativeLibs} -o $out`,
+			`-a requireNativeLibs ${requireNativeLibs} ` +
+			`${opt} ` +
+			`-o $out`,
 		CommandDeps: []string{"${jsonmodify}"},
-		Description: "Inject dependency into ${out}",
-	}, "provideNativeLibs", "requireNativeLibs")
+		Description: "prepare ${out}",
+	}, "provideNativeLibs", "requireNativeLibs", "opt")
 
 	// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
 	// against the binary policy using sefcontext_compiler -p <policy>.
@@ -147,17 +149,6 @@
 	androidAppTag  = dependencyTag{name: "androidApp"}
 )
 
-var (
-	whitelistNoApex = map[string][]string{
-		"apex_test_build_features":       []string{"libbinder"},
-		"com.android.neuralnetworks":     []string{"libbinder"},
-		"com.android.media":              []string{"libbinder"},
-		"com.android.media.swcodec":      []string{"libbinder"},
-		"test_com.android.media.swcodec": []string{"libbinder"},
-		"com.android.vndk":               []string{"libbinder"},
-	}
-)
-
 func init() {
 	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/java")
@@ -185,7 +176,7 @@
 	pctx.HostBinToolVariable("zipalign", "zipalign")
 	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
 
-	android.RegisterModuleType("apex", apexBundleFactory)
+	android.RegisterModuleType("apex", BundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
 	android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
 	android.RegisterModuleType("apex_defaults", defaultsFactory)
@@ -195,14 +186,22 @@
 		ctx.TopDown("apex_vndk_gather", apexVndkGatherMutator).Parallel()
 		ctx.BottomUp("apex_vndk_add_deps", apexVndkAddDepsMutator).Parallel()
 	})
-	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("apex_deps", apexDepsMutator)
-		ctx.BottomUp("apex", apexMutator).Parallel()
-		ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
-		ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
+	android.PostDepsMutators(RegisterPostDepsMutators)
+
+	android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
+		apexFileContextsInfos := apexFileContextsInfos(ctx.Config())
+		sort.Strings(*apexFileContextsInfos)
+		ctx.Strict("APEX_FILE_CONTEXTS_INFOS", strings.Join(*apexFileContextsInfos, " "))
 	})
 }
 
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.TopDown("apex_deps", apexDepsMutator)
+	ctx.BottomUp("apex", apexMutator).Parallel()
+	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+	ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
+}
+
 var (
 	vndkApexListKey   = android.NewOnceKey("vndkApexList")
 	vndkApexListMutex sync.Mutex
@@ -220,12 +219,14 @@
 		if ab.IsNativeBridgeSupported() {
 			mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType())
 		}
-		vndkVersion := proptools.StringDefault(ab.vndkProperties.Vndk_version, mctx.DeviceConfig().PlatformVndkVersion())
+
+		vndkVersion := proptools.String(ab.vndkProperties.Vndk_version)
+
 		vndkApexListMutex.Lock()
 		defer vndkApexListMutex.Unlock()
 		vndkApexList := vndkApexList(mctx.Config())
 		if other, ok := vndkApexList[vndkVersion]; ok {
-			mctx.PropertyErrorf("vndk_version", "%v is already defined in %q", vndkVersion, other.Name())
+			mctx.PropertyErrorf("vndk_version", "%v is already defined in %q", vndkVersion, other.BaseModuleName())
 		}
 		vndkApexList[vndkVersion] = ab
 	}
@@ -278,20 +279,49 @@
 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 {
 		// apex bundle itself is mutated so that it and its modules have same
 		// apex variant.
 		apexBundleName := mctx.ModuleName()
 		mctx.CreateVariations(apexBundleName)
+
+		// collects APEX list
+		if mctx.Device() && a.installable() {
+			addApexFileContextsInfos(mctx, a)
+		}
 	}
 }
 
+var (
+	apexFileContextsInfosKey   = android.NewOnceKey("apexFileContextsInfosKey")
+	apexFileContextsInfosMutex sync.Mutex
+)
+
+func apexFileContextsInfos(config android.Config) *[]string {
+	return config.Once(apexFileContextsInfosKey, func() interface{} {
+		return &[]string{}
+	}).(*[]string)
+}
+
+func addApexFileContextsInfos(ctx android.BaseModuleContext, a *apexBundle) {
+	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
+	fileContextsName := proptools.StringDefault(a.properties.File_contexts, ctx.ModuleName())
+
+	apexFileContextsInfosMutex.Lock()
+	defer apexFileContextsInfosMutex.Unlock()
+	apexFileContextsInfos := apexFileContextsInfos(ctx.Config())
+	*apexFileContextsInfos = append(*apexFileContextsInfos, apexName+":"+fileContextsName)
+}
+
 func apexFlattenedMutator(mctx android.BottomUpMutatorContext) {
-	if _, ok := mctx.Module().(*apexBundle); ok {
+	if ab, ok := mctx.Module().(*apexBundle); ok {
 		if !mctx.Config().FlattenApex() || mctx.Config().UnbundledBuild() {
 			modules := mctx.CreateLocalVariations("", "flattened")
 			modules[0].(*apexBundle).SetFlattened(false)
 			modules[1].(*apexBundle).SetFlattened(true)
+		} else {
+			ab.SetFlattened(true)
+			ab.SetFlattenedConfigValue()
 		}
 	}
 }
@@ -407,9 +437,19 @@
 	// List of APKs to package inside APEX
 	Apps []string
 
-	// To distinguish between flattened and non-flattened variants.
-	// if set true, then this variant is flattened variant.
+	// To distinguish between flattened and non-flattened apex.
+	// if set true, then output files are flattened.
 	Flattened bool `blueprint:"mutated"`
+
+	// if true, it means that TARGET_FLATTEN_APEX is true and
+	// TARGET_BUILD_APPS is false
+	FlattenedConfigValue bool `blueprint:"mutated"`
+
+	// List of SDKs that are used to build this APEX. A reference to an SDK should be either
+	// `name#version` or `name` which is an alias for `name#current`. If left empty, `platform#current`
+	// is implied. This value affects all modules included in this APEX. In other words, they are
+	// also built with the SDKs specified here.
+	Uses_sdks []string
 }
 
 type apexTargetBundleProperties struct {
@@ -518,7 +558,14 @@
 	case nativeTest:
 		return "NATIVE_TESTS"
 	case app:
-		return "APPS"
+		// b/142537672 Why isn't this APP? We want to have full control over
+		// the paths and file names of the apk file under the flattend APEX.
+		// If this is set to APP, then the paths and file names are modified
+		// by the Make build system. For example, it is installed to
+		// /system/apex/<apexname>/app/<Appname>/<apexname>.<Appname>/ instead of
+		// /system/apex/<apexname>/app/<Appname> because the build system automatically
+		// appends module name (which is <apexname>.<Appname> to the path.
+		return "ETC"
 	default:
 		panic(fmt.Errorf("unknown class %d", class))
 	}
@@ -536,6 +583,7 @@
 type apexBundle struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.SdkBase
 
 	properties       apexBundleProperties
 	targetProperties apexTargetBundleProperties
@@ -545,8 +593,8 @@
 
 	bundleModuleFile android.WritablePath
 	outputFiles      map[apexPackaging]android.WritablePath
-	flattenedOutput  android.OutputPath
-	installDir       android.OutputPath
+	flattenedOutput  android.InstallPath
+	installDir       android.InstallPath
 
 	prebuiltFileToDelete string
 
@@ -567,9 +615,6 @@
 
 	// intermediate path for apex_manifest.json
 	manifestOut android.WritablePath
-
-	// A config value of (TARGET_FLATTEN_APEX && !TARGET_BUILD_APPS)
-	flattenedConfigValue bool
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -738,6 +783,16 @@
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
+
+	// TODO(jiyong): ensure that all apexes are with non-empty uses_sdks
+	if len(a.properties.Uses_sdks) > 0 {
+		sdkRefs := []android.SdkRef{}
+		for _, str := range a.properties.Uses_sdks {
+			parsed := android.ParseSdkRef(ctx, str, "uses_sdks")
+			sdkRefs = append(sdkRefs, parsed)
+		}
+		a.BuildWithSdks(sdkRefs)
+	}
 }
 
 func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
@@ -820,6 +875,20 @@
 	a.properties.Flattened = flattened
 }
 
+func (a *apexBundle) SetFlattenedConfigValue() {
+	a.properties.FlattenedConfigValue = true
+}
+
+// isFlattenedVariant returns true when the current module is the flattened
+// variant of an apex that has both a flattened and an unflattened variant.
+// It returns false when the current module is flattened but there is no
+// unflattened variant, which occurs when ctx.Config().FlattenedApex() returns
+// true. It can be used to avoid collisions between the install paths of the
+// flattened and unflattened variants.
+func (a *apexBundle) isFlattenedVariant() bool {
+	return a.properties.Flattened && !a.properties.FlattenedConfigValue
+}
+
 func getCopyManifestForNativeLibrary(ccMod *cc.Module, config android.Config, handleSpecialLibs bool) (fileToCopy android.Path, dirInApex string) {
 	// Decide the APEX-local directory by the multilib of the library
 	// In the future, we may query this to the module.
@@ -830,9 +899,7 @@
 		dirInApex = "lib64"
 	}
 	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
-	if !ccMod.Arch().Native {
-		dirInApex = filepath.Join(dirInApex, ccMod.Arch().ArchType.String())
-	} else if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
+	if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
 	}
 	if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), config) {
@@ -855,9 +922,7 @@
 
 func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
 	dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
-	if !cc.Arch().Native {
-		dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
-	} else if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+	if cc.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
 	}
 	fileToCopy = cc.OutputFile().Path()
@@ -998,7 +1063,7 @@
 					return true
 				} else if sh, ok := child.(*android.ShBinary); ok {
 					fileToCopy, dirInApex := getCopyManifestForShBinary(sh)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, shBinary, sh, nil})
+					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, shBinary, sh, sh.Symlinks()})
 				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
 					fileToCopy, dirInApex := getCopyManifestForPyBinary(py)
 					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, pyBinary, py, nil})
@@ -1133,18 +1198,12 @@
 					}
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
 					ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName)
-				} else if am.NoApex() && !android.InList(depName, whitelistNoApex[ctx.ModuleName()]) {
-					ctx.ModuleErrorf("tries to include no_apex module %s", depName)
 				}
 			}
 		}
 		return false
 	})
 
-	a.flattenedConfigValue = ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
-	if a.flattenedConfigValue {
-		a.properties.Flattened = true
-	}
 	if a.private_key_file == nil {
 		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
 		return
@@ -1170,12 +1229,14 @@
 		return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
 	})
 
-	// check no_apex modules
-	whitelist := whitelistNoApex[ctx.ModuleName()]
-	for i := range filesInfo {
-		if am, ok := filesInfo[i].module.(android.ApexModule); ok {
-			if am.NoApex() && !android.InList(filesInfo[i].moduleName, whitelist) {
-				ctx.ModuleErrorf("tries to include no_apex module %s", filesInfo[i].moduleName)
+	// check apex_available requirements
+	if !ctx.Host() {
+		for _, fi := range filesInfo {
+			if am, ok := fi.module.(android.ApexModule); ok {
+				if !am.AvailableFor(ctx.ModuleName()) {
+					ctx.ModuleErrorf("requires %q that is not available for the APEX", fi.module.Name())
+					return
+				}
 			}
 		}
 	}
@@ -1189,18 +1250,28 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
+	// prepare apex_manifest.json
 	a.manifestOut = android.PathForModuleOut(ctx, "apex_manifest.json")
-	// put dependency({provide|require}NativeLibs) in apex_manifest.json
 	manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
+
+	// put dependency({provide|require}NativeLibs) in apex_manifest.json
 	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
 	requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
+
+	// apex name can be overridden
+	optCommands := []string{}
+	if a.properties.Apex_name != nil {
+		optCommands = append(optCommands, "-v name "+*a.properties.Apex_name)
+	}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   injectApexDependency,
+		Rule:   apexManifestRule,
 		Input:  manifestSrc,
 		Output: a.manifestOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
+			"opt":               strings.Join(optCommands, " "),
 		},
 	})
 
@@ -1210,7 +1281,8 @@
 	// instead of `android.PathForOutput`) to return the correct path to the flattened
 	// APEX (as its contents is installed by Make, not Soong).
 	factx := flattenedApexContext{ctx}
-	a.flattenedOutput = android.PathForModuleInstall(&factx, "apex", factx.ModuleName())
+	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
+	a.flattenedOutput = android.PathForModuleInstall(&factx, "apex", apexName)
 
 	if a.apexTypes.zip() {
 		a.buildUnflattenedApex(ctx, zipApex)
@@ -1414,6 +1486,12 @@
 			optFlags = append(optFlags, "--no_hashtree")
 		}
 
+		if a.properties.Apex_name != nil {
+			// If apex_name is set, apexer can skip checking if key name matches with apex name.
+			// Note that apex_manifest is also mended.
+			optFlags = append(optFlags, "--do_not_check_keyname")
+		}
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        apexRule,
 			Implicits:   implicitInputs,
@@ -1483,7 +1561,7 @@
 	})
 
 	// Install to $OUT/soong/{target,host}/.../apex
-	if a.installable() && (!ctx.Config().FlattenApex() || apexType.zip()) && (!a.properties.Flattened || a.flattenedConfigValue) {
+	if a.installable() && (!ctx.Config().FlattenApex() || apexType.zip()) && !a.isFlattenedVariant() {
 		ctx.InstallFile(a.installDir, ctx.ModuleName()+suffix, a.outputFiles[apexType])
 	}
 }
@@ -1504,8 +1582,9 @@
 		a.filesInfo = append(a.filesInfo, apexFile{copiedPubkey, ctx.ModuleName() + ".apex_pubkey", ".", etc, nil, nil})
 
 		if ctx.Config().FlattenApex() {
+			apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
 			for _, fi := range a.filesInfo {
-				dir := filepath.Join("apex", ctx.ModuleName(), fi.installDir)
+				dir := filepath.Join("apex", apexName, fi.installDir)
 				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
 				for _, sym := range fi.symlinks {
 					ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
@@ -1536,7 +1615,7 @@
 		}}
 }
 
-func (a *apexBundle) androidMkForFiles(w io.Writer, name, moduleDir string, apexType apexPackaging) []string {
+func (a *apexBundle) androidMkForFiles(w io.Writer, apexName, moduleDir string, apexType apexPackaging) []string {
 	moduleNames := []string{}
 
 	for _, fi := range a.filesInfo {
@@ -1548,7 +1627,7 @@
 		}
 
 		var suffix string
-		if a.properties.Flattened && !a.flattenedConfigValue {
+		if a.isFlattenedVariant() {
 			suffix = ".flattened"
 		}
 
@@ -1560,13 +1639,12 @@
 		fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 		fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName+suffix)
 		// /apex/<apex_name>/{lib|framework|...}
-		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex",
-			proptools.StringDefault(a.properties.Apex_name, name), fi.installDir)
+		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
 		if a.properties.Flattened && apexType.image() {
 			// /system/apex/<name>/{lib|framework|...}
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)",
-				a.installDir.RelPathString(), name, fi.installDir))
-			if a.flattenedConfigValue {
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join(a.installDir.ToMakePath().String(),
+				apexName, fi.installDir))
+			if !a.isFlattenedVariant() {
 				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
 			}
 			if len(fi.symlinks) > 0 {
@@ -1645,10 +1723,11 @@
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
 			moduleNames := []string{}
 			if a.installable() {
-				moduleNames = a.androidMkForFiles(w, name, moduleDir, apexType)
+				apexName := proptools.StringDefault(a.properties.Apex_name, name)
+				moduleNames = a.androidMkForFiles(w, apexName, moduleDir, apexType)
 			}
 
-			if a.properties.Flattened && !a.flattenedConfigValue {
+			if a.isFlattenedVariant() {
 				name = name + ".flattened"
 			}
 
@@ -1663,7 +1742,7 @@
 				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
 				fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): .KATI_IMPLICIT_OUTPUTS :=", a.flattenedOutput.String())
 
-			} else if !a.properties.Flattened || a.flattenedConfigValue {
+			} else if !a.isFlattenedVariant() {
 				// zip-apex is the less common type so have the name refer to the image-apex
 				// only and use {name}.zip if you want the zip-apex
 				if apexType == zipApex && a.apexTypes == both {
@@ -1674,7 +1753,7 @@
 				fmt.Fprintln(w, "LOCAL_MODULE :=", name)
 				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
 				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFiles[apexType].String())
-				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
+				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
 				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
 				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
 				if len(moduleNames) > 0 {
@@ -1685,7 +1764,7 @@
 				}
 				if a.prebuiltFileToDelete != "" {
 					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", "rm -rf "+
-						filepath.Join("$(OUT_DIR)", a.installDir.RelPathString(), a.prebuiltFileToDelete))
+						filepath.Join(a.installDir.ToMakePath().String(), a.prebuiltFileToDelete))
 				}
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 
@@ -1707,6 +1786,7 @@
 	})
 	android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitSdkAwareModule(module)
 	return module
 }
 
@@ -1722,7 +1802,7 @@
 	return bundle
 }
 
-func apexBundleFactory() android.Module {
+func BundleFactory() android.Module {
 	return newApexBundle()
 }
 
@@ -1739,6 +1819,15 @@
 		}{
 			proptools.StringPtr("both"),
 		})
+
+		vndkVersion := proptools.StringDefault(bundle.vndkProperties.Vndk_version, "current")
+		if vndkVersion == "current" {
+			vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
+			bundle.vndkProperties.Vndk_version = proptools.StringPtr(vndkVersion)
+		}
+
+		// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
+		bundle.properties.Apex_name = proptools.StringPtr("com.android.vndk.v" + vndkVersion)
 	})
 	return bundle
 }
@@ -1778,7 +1867,7 @@
 	properties PrebuiltProperties
 
 	inputApex       android.Path
-	installDir      android.OutputPath
+	installDir      android.InstallPath
 	installFilename string
 	outputApex      android.WritablePath
 }
@@ -1925,7 +2014,7 @@
 		Include:    "$(BUILD_PREBUILT)",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
+				entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
 				entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", p.properties.Overrides...)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index d16917d..ae0ea7d 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -95,10 +95,10 @@
 	config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
 
 	ctx := android.NewTestArchContext()
-	ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apexBundleFactory))
+	ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(BundleFactory))
 	ctx.RegisterModuleType("apex_test", android.ModuleFactoryAdaptor(testApexBundleFactory))
 	ctx.RegisterModuleType("apex_vndk", android.ModuleFactoryAdaptor(vndkApexBundleFactory))
-	ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apexKeyFactory))
+	ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(ApexKeyFactory))
 	ctx.RegisterModuleType("apex_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
 	ctx.RegisterModuleType("prebuilt_apex", android.ModuleFactoryAdaptor(PrebuiltFactory))
 
@@ -768,9 +768,9 @@
 	// Ensure that runtime_libs dep in included
 	ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
 
-	injectRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("injectApexDependency")
-	ensureListEmpty(t, names(injectRule.Args["provideNativeLibs"]))
-	ensureListContains(t, names(injectRule.Args["requireNativeLibs"]), "libfoo.so")
+	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+	ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
+	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so")
 
 }
 
@@ -818,11 +818,11 @@
 	// Ensure that LLNDK dep is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so")
 
-	injectRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("injectApexDependency")
-	ensureListEmpty(t, names(injectRule.Args["provideNativeLibs"]))
+	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+	ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
 
 	// Ensure that LLNDK dep is required
-	ensureListContains(t, names(injectRule.Args["requireNativeLibs"]), "libbar.so")
+	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
 
 }
 
@@ -1379,6 +1379,7 @@
 			vndk: {
 				enabled: true,
 			},
+			target_arch: "arm64",
 			srcs: ["libvndk27.so"],
 		}
 	`, withFiles(map[string][]byte{
@@ -1438,6 +1439,37 @@
 	}))
 }
 
+func TestVndkApexNameRule(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex",
+			key: "myapex.key",
+			file_contexts: "myapex",
+		}
+		apex_vndk {
+			name: "myapex_v28",
+			key: "myapex.key",
+			file_contexts: "myapex",
+			vndk_version: "28",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}`)
+
+	assertApexName := func(expected, moduleName string) {
+		bundle := ctx.ModuleForTests(moduleName, "android_common_"+moduleName).Module().(*apexBundle)
+		actual := proptools.String(bundle.properties.Apex_name)
+		if !reflect.DeepEqual(actual, expected) {
+			t.Errorf("Got '%v', expected '%v'", actual, expected)
+		}
+	}
+
+	assertApexName("com.android.vndk.vVER", "myapex")
+	assertApexName("com.android.vndk.v28", "myapex_v28")
+}
+
 func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex_vndk {
@@ -1466,10 +1498,10 @@
 		}
 	`, withTargets(map[android.OsType][]android.Target{
 		android.Android: []android.Target{
-			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm64", NativeBridgeRelativePath: "x86_64"},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm", NativeBridgeRelativePath: "x86"},
+			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+			{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm64", NativeBridgeRelativePath: "x86_64"},
+			{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm", NativeBridgeRelativePath: "x86"},
 		},
 	}))
 
@@ -1579,34 +1611,56 @@
 		}
 	`)
 
-	var injectRule android.TestingBuildParams
+	var apexManifestRule android.TestingBuildParams
 	var provideNativeLibs, requireNativeLibs []string
 
-	injectRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("injectApexDependency")
-	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
-	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	apexManifestRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListEmpty(t, provideNativeLibs)
 	ensureListEmpty(t, requireNativeLibs)
 
-	injectRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("injectApexDependency")
-	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
-	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	apexManifestRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListEmpty(t, provideNativeLibs)
 	ensureListContains(t, requireNativeLibs, "libfoo.so")
 
-	injectRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("injectApexDependency")
-	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
-	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	apexManifestRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListContains(t, provideNativeLibs, "libfoo.so")
 	ensureListEmpty(t, requireNativeLibs)
 
-	injectRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("injectApexDependency")
-	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
-	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("apexManifestRule")
+	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListContains(t, provideNativeLibs, "libfoo.so")
 	ensureListEmpty(t, requireNativeLibs)
 }
 
+func TestApexName(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apex_name: "com.android.myapex",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	apexManifestRule := module.Rule("apexManifestRule")
+	ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex")
+	apexRule := module.Rule("apexRule")
+	ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
+}
+
 func TestNonTestApex(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -1839,8 +1893,8 @@
 	`)
 
 	apex := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
-	expected := "target/product/test_device/product/apex"
-	actual := apex.installDir.RelPathString()
+	expected := buildDir + "/target/product/test_device/product/apex"
+	actual := apex.installDir.String()
 	if actual != expected {
 		t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
 	}
@@ -2126,118 +2180,6 @@
 	`)
 }
 
-func TestApexUsesFailsIfUseNoApex(t *testing.T) {
-	testApexError(t, `tries to include no_apex module mylib2`, `
-		apex {
-			name: "commonapex",
-			key: "myapex.key",
-			native_shared_libs: ["mylib"],
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2"],
-			system_shared_libs: [],
-			stl: "none",
-		}
-
-		cc_library {
-			name: "mylib2",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			no_apex: true,
-		}
-	`)
-
-	testApexError(t, `tries to include no_apex module mylib2`, `
-		apex {
-			name: "commonapex",
-			key: "myapex.key",
-			native_shared_libs: ["mylib"],
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			static_libs: ["mylib2"],
-			system_shared_libs: [],
-			stl: "none",
-		}
-
-		cc_library {
-			name: "mylib2",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			no_apex: true,
-		}
-	`)
-
-	ctx, _ := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["mylib"],
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2"],
-			system_shared_libs: [],
-			stl: "none",
-		}
-
-		cc_library {
-			name: "mylib2",
-			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib3"],
-			system_shared_libs: [],
-			stl: "none",
-			stubs: {
-				versions: ["1", "2", "3"],
-			},
-		}
-
-		cc_library {
-			name: "mylib3",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			no_apex: true,
-		}
-	`)
-
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
-	apexRule := module.Rule("apexRule")
-	copyCmds := apexRule.Args["copy_commands"]
-
-	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
-	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
-	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib3.so")
-
-}
-
 func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
 	testApexError(t, `module "myapex" .* depends on disabled module "libfoo"`, `
 		apex {
@@ -2315,6 +2257,158 @@
 
 }
 
+func TestApexAvailable(t *testing.T) {
+	// libfoo is not available to myapex, but only to otherapex
+	testApexError(t, "requires \"libfoo\" that is not available for the APEX", `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["otherapex"],
+	}`)
+
+	// libbar is an indirect dep
+	testApexError(t, "requires \"libbar\" that is not available for the APEX", `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		shared_libs: ["libbar"],
+		system_shared_libs: [],
+		apex_available: ["myapex", "otherapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["otherapex"],
+	}`)
+
+	testApexError(t, "\"otherapex\" is not a valid module name", `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["otherapex"],
+	}`)
+
+	ctx, _ := testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo", "libbar"],
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["//apex_available:anyapex"],
+	}`)
+
+	// check that libfoo and libbar are created only for myapex, but not for the platform
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_core_shared")
+
+	ctx, _ = testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["//apex_available:platform"],
+	}`)
+
+	// check that libfoo is created only for the platform
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared_myapex")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared")
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/key.go b/apex/key.go
index 08cd45e..ffde315 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -27,7 +27,7 @@
 var String = proptools.String
 
 func init() {
-	android.RegisterModuleType("apex_key", apexKeyFactory)
+	android.RegisterModuleType("apex_key", ApexKeyFactory)
 	android.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
 }
 
@@ -53,7 +53,7 @@
 	Installable *bool
 }
 
-func apexKeyFactory() android.Module {
+func ApexKeyFactory() android.Module {
 	module := &apexKey{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.HostAndDeviceDefault, android.MultilibCommon)
diff --git a/build_kzip.bash b/build_kzip.bash
index 5364e7f..1e0d48f 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -1,25 +1,27 @@
-# /bin/bash -uv
+#! /bin/bash -uv
 #
 # Build kzip files (source files for the indexing pipeline) for the given configuration,
 # merge them and place the resulting all.kzip into $DIST_DIR.
 # It is assumed that the current directory is the top of the source tree.
-# The following enviromnet variables affect the result:
-#   TARGET_PRODUCT        target device name, e.g., `aosp_blueline`
-#   TARGET_BUILD_VARIANT  variant, e.g., `userdebug`
-#   OUT_DIR               where the build is happening (./out if not specified)
+# 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
-#   XREF_CORPUS           source code repository URI, e.g.,
-#                        `android.googlesource.com/platform/superproject`
+#   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'
 
-# The extraction might fail for some source files, so run with -k
+: ${BUILD_NUMBER:=$(uuidgen)}
+
+# The extraction might fail for some source files, so run with -k and then check that
+# sufficiently many files were generated.
 build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java
-
-# We build with -k, so check that we have generated at least 100K files
-# (the actual number is 180K+)
-declare -r kzip_count=$(find $OUT_DIR -name '*.kzip' | wc -l)
+declare -r out="${OUT_DIR:-out}"
+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; }
 
 # Pack
 # TODO(asmundak): this should be done by soong.
-declare -r allkzip=all.kzip
-"${OUT_DIR:-out}/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find $OUT_DIR -name '*.kzip')
+declare -r allkzip="$BUILD_NUMBER.kzip"
+"$out/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip')
+
diff --git a/cc/androidmk.go b/cc/androidmk.go
index aab4edd..f1d329f 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -350,11 +350,10 @@
 	}
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		path := installer.path.RelPathString()
-		dir, file := filepath.Split(path)
+		path, file := filepath.Split(installer.path.ToMakePath().String())
 		stem, suffix, _ := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
-		fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 	})
 }
@@ -395,12 +394,11 @@
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
 
-		path := c.path.RelPathString()
-		dir, file := filepath.Split(path)
+		path, file := filepath.Split(c.path.ToMakePath().String())
 		stem, suffix, ext := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
-		fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 	})
 }
diff --git a/cc/binary.go b/cc/binary.go
index 0d69405..9f18d6c 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -453,7 +453,7 @@
 	// Bionic binaries (e.g. linker) is installed to the bootstrap subdirectory.
 	// The original path becomes a symlink to the corresponding file in the
 	// runtime APEX.
-	translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled || !ctx.Arch().Native
+	translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
 	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRecovery() {
 		if ctx.Device() && isBionic(ctx.baseModuleName()) {
 			binary.installSymlinkToRuntimeApex(ctx, file)
diff --git a/cc/builder.go b/cc/builder.go
index b353814..0760dd4 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -195,7 +195,7 @@
 
 	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
 
-	sAbiDiff = pctx.AndroidRuleFunc("sAbiDiff",
+	sAbiDiff = pctx.RuleFunc("sAbiDiff",
 		func(ctx android.PackageRuleContext) blueprint.RuleParams {
 			// TODO(b/78139997): Add -check-all-apis back
 			commandStr := "($sAbiDiffer ${allowFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
@@ -224,12 +224,13 @@
 
 	_ = pctx.SourcePathVariable("cxxExtractor",
 		"prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/cxx_extractor")
+	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
 	kytheExtract = pctx.StaticRule("kythe",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out $cxxExtractor $cFlags $in ",
-			CommandDeps: []string{"$cxxExtractor"},
+			Command:     "rm -f $out && KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out KYTHE_VNAMES=$kytheVnames $cxxExtractor $cFlags $in ",
+			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
 		},
 		"cFlags")
 )
diff --git a/cc/cc.go b/cc/cc.go
index b255a14..9031afe 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -401,6 +401,14 @@
 	return staticDepTag
 }
 
+func CrtBeginDepTag() dependencyTag {
+	return crtBeginDepTag
+}
+
+func CrtEndDepTag() dependencyTag {
+	return crtEndDepTag
+}
+
 // Module contains the properties and members used by all C/C++ module types, and implements
 // the blueprint.Module interface.  It delegates to compiler, linker, and installer interfaces
 // to construct the output file.  Behavior can be customized with a Customizer interface
@@ -408,6 +416,7 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.ApexModuleBase
+	android.SdkBase
 
 	Properties       BaseProperties
 	VendorProperties VendorProperties
@@ -547,6 +556,7 @@
 	android.InitDefaultableModule(c)
 
 	android.InitApexModule(c)
+	android.InitSdkAwareModule(c)
 
 	return c
 }
@@ -2200,6 +2210,8 @@
 		&BaseLinkerProperties{},
 		&ObjectLinkerProperties{},
 		&LibraryProperties{},
+		&StaticProperties{},
+		&SharedProperties{},
 		&FlagExporterProperties{},
 		&BinaryLinkerProperties{},
 		&TestProperties{},
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 6275822..689aacd 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -15,8 +15,6 @@
 package cc
 
 import (
-	"android/soong/android"
-
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -25,6 +23,8 @@
 	"sort"
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 var buildDir string
@@ -53,6 +53,7 @@
 }
 
 func testCcWithConfig(t *testing.T, bp string, config android.Config) *android.TestContext {
+	t.Helper()
 	return testCcWithConfigForOs(t, bp, config, android.Android)
 }
 
@@ -2321,3 +2322,67 @@
 		}
 	}
 }
+
+func TestDefaults(t *testing.T) {
+	ctx := testCc(t, `
+		cc_defaults {
+			name: "defaults",
+			srcs: ["foo.c"],
+			static: {
+				srcs: ["bar.c"],
+			},
+			shared: {
+				srcs: ["baz.c"],
+			},
+		}
+
+		cc_library_static {
+			name: "libstatic",
+			defaults: ["defaults"],
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			defaults: ["defaults"],
+		}
+
+		cc_library {
+			name: "libboth",
+			defaults: ["defaults"],
+		}
+
+		cc_binary {
+			name: "binary",
+			defaults: ["defaults"],
+		}`)
+
+	pathsToBase := func(paths android.Paths) []string {
+		var ret []string
+		for _, p := range paths {
+			ret = append(ret, p.Base())
+		}
+		return ret
+	}
+
+	shared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_core_shared").Rule("ld")
+	if g, w := pathsToBase(shared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libshared ld rule wanted %q, got %q", w, g)
+	}
+	bothShared := ctx.ModuleForTests("libboth", "android_arm64_armv8-a_core_shared").Rule("ld")
+	if g, w := pathsToBase(bothShared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libboth ld rule wanted %q, got %q", w, g)
+	}
+	binary := ctx.ModuleForTests("binary", "android_arm64_armv8-a_core").Rule("ld")
+	if g, w := pathsToBase(binary.Inputs), []string{"foo.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("binary ld rule wanted %q, got %q", w, g)
+	}
+
+	static := ctx.ModuleForTests("libstatic", "android_arm64_armv8-a_core_static").Rule("ar")
+	if g, w := pathsToBase(static.Inputs), []string{"foo.o", "bar.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libstatic ar rule wanted %q, got %q", w, g)
+	}
+	bothStatic := ctx.ModuleForTests("libboth", "android_arm64_armv8-a_core_static").Rule("ar")
+	if g, w := pathsToBase(bothStatic.Inputs), []string{"foo.o", "bar.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("libboth ar rule wanted %q, got %q", w, g)
+	}
+}
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
new file mode 100644
index 0000000..9ed3876
--- /dev/null
+++ b/cc/cflag_artifacts.go
@@ -0,0 +1,188 @@
+package cc
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("cflag_artifacts_text", cflagArtifactsTextFactory)
+}
+
+var (
+	TrackedCFlags = []string{
+		"-Wall",
+		"-Werror",
+		"-Wextra",
+		"-Wthread-safety",
+		"-O3",
+	}
+
+	TrackedCFlagsDir = []string{
+		"device/google/",
+		"vendor/google/",
+	}
+)
+
+const FileBP = 50
+
+// Stores output files.
+type cflagArtifactsText struct {
+	interOutputs map[string]android.WritablePaths
+	outputs      android.WritablePaths
+}
+
+// allowedDir verifies if the directory/project is part of the TrackedCFlagsDir
+// filter.
+func allowedDir(subdir string) bool {
+	subdir += "/"
+	for _, prefix := range TrackedCFlagsDir {
+		if strings.HasPrefix(subdir, prefix) {
+			return true
+		}
+	}
+	return false
+}
+
+func (s *cflagArtifactsText) genFlagFilename(flag string) string {
+	return fmt.Sprintf("module_cflags%s.txt", flag)
+}
+
+// incrementFile is used to generate an output path object with the passed in flag
+// and part number.
+// e.g. FLAG + part # -> out/soong/cflags/module_cflags-FLAG.txt.0
+func (s *cflagArtifactsText) incrementFile(ctx android.SingletonContext,
+	flag string, part int) (string, android.OutputPath) {
+
+	filename := fmt.Sprintf("%s.%d", s.genFlagFilename(flag), part)
+	filepath := android.PathForOutput(ctx, "cflags", filename)
+	s.interOutputs[flag] = append(s.interOutputs[flag], filepath)
+	return filename, filepath
+}
+
+// GenCFlagArtifactParts is used to generate the build rules which produce the
+// intermediary files for each desired C Flag artifact
+// e.g. module_cflags-FLAG.txt.0, module_cflags-FLAG.txt.1, ...
+func (s *cflagArtifactsText) GenCFlagArtifactParts(ctx android.SingletonContext,
+	flag string, using bool, modules []string, part int) int {
+
+	cleanedName := strings.Replace(flag, "=", "_", -1)
+	filename, filepath := s.incrementFile(ctx, cleanedName, part)
+	rule := android.NewRuleBuilder()
+	rule.Command().Textf("rm -f %s", filepath.String())
+
+	if using {
+		rule.Command().
+			Textf("echo '# Modules using %s'", flag).
+			FlagWithOutput(">> ", filepath)
+	} else {
+		rule.Command().
+			Textf("echo '# Modules not using %s'", flag).
+			FlagWithOutput(">> ", filepath)
+	}
+
+	length := len(modules)
+
+	if length == 0 {
+		rule.Build(pctx, ctx, filename, "gen "+filename)
+		part++
+	}
+
+	// Following loop splits the module list for each tracked C Flag into
+	// chunks of length FileBP (file breakpoint) and generates a partial artifact
+	// (intermediary file) build rule for each split.
+	moduleShards := android.ShardStrings(modules, FileBP)
+	for index, shard := range moduleShards {
+		rule.Command().
+			Textf("for m in %s; do echo $m",
+				strings.Join(proptools.ShellEscapeList(shard), " ")).
+			FlagWithOutput(">> ", filepath).
+			Text("; done")
+		rule.Build(pctx, ctx, filename, "gen "+filename)
+
+		if index+1 != len(moduleShards) {
+			filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
+			rule = android.NewRuleBuilder()
+			rule.Command().Textf("rm -f %s", filepath.String())
+		}
+	}
+
+	return part + len(moduleShards)
+}
+
+// GenCFlagArtifacts is used to generate build rules which combine the
+// intermediary files of a specific tracked flag into a single C Flag artifact
+// for each tracked flag.
+// e.g. module_cflags-FLAG.txt.0 + module_cflags-FLAG.txt.1 = module_cflags-FLAG.txt
+func (s *cflagArtifactsText) GenCFlagArtifacts(ctx android.SingletonContext) {
+	// Scans through s.interOutputs and creates a build rule for each tracked C
+	// Flag that concatenates the associated intermediary file into a single
+	// artifact.
+	for _, flag := range TrackedCFlags {
+		// Generate build rule to combine related intermediary files into a
+		// C Flag artifact
+		rule := android.NewRuleBuilder()
+		filename := s.genFlagFilename(flag)
+		outputpath := android.PathForOutput(ctx, "cflags", filename)
+		rule.Command().
+			Text("cat").
+			Inputs(s.interOutputs[flag].Paths()).
+			FlagWithOutput("> ", outputpath)
+		rule.Build(pctx, ctx, filename, "gen "+filename)
+		s.outputs = append(s.outputs, outputpath)
+	}
+}
+
+func (s *cflagArtifactsText) GenerateBuildActions(ctx android.SingletonContext) {
+	modulesWithCFlag := make(map[string][]string)
+
+	// Scan through all modules, selecting the ones that are part of the filter,
+	// and then storing into a map which tracks whether or not tracked C flag is
+	// used or not.
+	ctx.VisitAllModules(func(module android.Module) {
+		if ccModule, ok := module.(*Module); ok {
+			if allowedDir(ctx.ModuleDir(ccModule)) {
+				cflags := ccModule.flags.CFlags
+				cppflags := ccModule.flags.CppFlags
+				module := fmt.Sprintf("%s:%s (%s)",
+					ctx.BlueprintFile(ccModule),
+					ctx.ModuleName(ccModule),
+					ctx.ModuleSubDir(ccModule))
+				for _, flag := range TrackedCFlags {
+					if inList(flag, cflags) || inList(flag, cppflags) {
+						modulesWithCFlag[flag] = append(modulesWithCFlag[flag], module)
+					} else {
+						modulesWithCFlag["!"+flag] = append(modulesWithCFlag["!"+flag], module)
+					}
+				}
+			}
+		}
+	})
+
+	// Traversing map and setting up rules to produce intermediary files which
+	// contain parts of each expected C Flag artifact.
+	for _, flag := range TrackedCFlags {
+		sort.Strings(modulesWithCFlag[flag])
+		part := s.GenCFlagArtifactParts(ctx, flag, true, modulesWithCFlag[flag], 0)
+		sort.Strings(modulesWithCFlag["!"+flag])
+		s.GenCFlagArtifactParts(ctx, flag, false, modulesWithCFlag["!"+flag], part)
+	}
+
+	// Combine intermediary files into a single C Flag artifact.
+	s.GenCFlagArtifacts(ctx)
+}
+
+func cflagArtifactsTextFactory() android.Singleton {
+	return &cflagArtifactsText{
+		interOutputs: make(map[string]android.WritablePaths),
+	}
+}
+
+func (s *cflagArtifactsText) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict("SOONG_MODULES_CFLAG_ARTIFACTS", strings.Join(s.outputs.Strings(), " "))
+}
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 3636ae9..71bea42 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -115,10 +115,6 @@
 		// TODO: can we remove this now?
 		"-Wno-reserved-id-macro",
 
-		// Disable overly aggressive warning for format strings.
-		// Bug: 20148343
-		"-Wno-format-pedantic",
-
 		// Workaround for ccache with clang.
 		// See http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html.
 		"-Wno-unused-command-line-argument",
@@ -167,10 +163,6 @@
 		// new warnings are fixed.
 		"-Wno-tautological-constant-compare",
 		"-Wno-tautological-type-limit-compare",
-
-		// Disable c++98-specific warning since Android is not concerned with C++98
-		// compatibility.
-		"-Wno-c++98-compat-extra-semi",
 	}, " "))
 
 	// Extra cflags for projects under external/ directory to disable warnings that are infeasible
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 325be63..c19fdc5 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint/proptools"
 
@@ -33,6 +34,7 @@
 
 func init() {
 	android.RegisterModuleType("cc_fuzz", FuzzFactory)
+	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
 }
 
 // cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
@@ -155,3 +157,95 @@
 
 	return module
 }
+
+// Responsible for generating GNU Make rules that package fuzz targets into
+// their architecture & target/host specific zip file.
+type fuzzPackager struct {
+	packages android.Paths
+}
+
+func fuzzPackagingFactory() android.Singleton {
+	return &fuzzPackager{}
+}
+
+type fileToZip struct {
+	SourceFilePath        android.Path
+	DestinationPathPrefix string
+}
+
+func (s *fuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+	// Map between each architecture + host/device combination, and the files that
+	// need to be packaged (in the tuple of {source file, destination folder in
+	// archive}).
+	archDirs := make(map[android.OutputPath][]fileToZip)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		// Discard non-fuzz targets.
+		ccModule, ok := module.(*Module)
+		if !ok {
+			return
+		}
+		fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
+		if !ok {
+			return
+		}
+
+		// Discard vendor-NDK-linked modules, they're duplicates of fuzz targets
+		// we're going to package anyway.
+		if ccModule.useVndk() || !ccModule.Enabled() {
+			return
+		}
+
+		hostOrTargetString := "target"
+		if ccModule.Host() {
+			hostOrTargetString = "host"
+		}
+
+		archString := ccModule.Arch().ArchType.String()
+		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
+
+		// The executable.
+		archDirs[archDir] = append(archDirs[archDir],
+			fileToZip{ccModule.outputFile.Path(), ccModule.Name()})
+
+		// The corpora.
+		for _, corpusEntry := range fuzzModule.corpus {
+			archDirs[archDir] = append(archDirs[archDir],
+				fileToZip{corpusEntry, ccModule.Name() + "/corpus/" + corpusEntry.Base()})
+		}
+
+		// The dictionary.
+		if fuzzModule.dictionary != nil {
+			archDirs[archDir] = append(archDirs[archDir],
+				fileToZip{fuzzModule.dictionary, ccModule.Name()})
+		}
+	})
+
+	for archDir, filesToZip := range archDirs {
+		arch := archDir.Base()
+		hostOrTarget := filepath.Base(filepath.Dir(archDir.String()))
+		builder := android.NewRuleBuilder()
+		outputFile := android.PathForOutput(ctx, "fuzz-"+hostOrTarget+"-"+arch+".zip")
+		s.packages = append(s.packages, outputFile)
+
+		command := builder.Command().BuiltTool(ctx, "soong_zip").
+			Flag("-j").
+			FlagWithOutput("-o ", outputFile)
+
+		for _, fileToZip := range filesToZip {
+			command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix).
+				FlagWithInput("-f ", fileToZip.SourceFilePath)
+		}
+
+		builder.Build(pctx, ctx, "create-fuzz-package-"+arch+"-"+hostOrTarget,
+			"Create fuzz target packages for "+arch+"-"+hostOrTarget)
+	}
+}
+
+func (s *fuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+	// TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
+	// ready to handle phony targets created in Soong. In the meantime, this
+	// exports the phony 'fuzz' target and dependencies on packages to
+	// core/main.mk so that we can use dist-for-goals.
+	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(s.packages.Strings(), " "))
+}
diff --git a/cc/gen.go b/cc/gen.go
index 42b0cbe..17ab45f 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -113,7 +113,14 @@
 
 	aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
 	baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
-	shortName := strings.TrimPrefix(baseName, "I")
+	shortName := baseName
+	// TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if
+	//   an interface name has a leading I. Those same heuristics have been
+	//   moved here.
+	if len(baseName) >= 2 && baseName[0] == 'I' &&
+		strings.ToUpper(baseName)[1] == baseName[1] {
+		shortName = strings.TrimPrefix(baseName, "I")
+	}
 
 	outDir := android.PathForModuleGen(ctx, "aidl")
 	headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
diff --git a/cc/installer.go b/cc/installer.go
index a52ccf1..9fdc88a 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -52,7 +52,7 @@
 	relative string
 	location installLocation
 
-	path android.OutputPath
+	path android.InstallPath
 }
 
 var _ installer = (*baseInstaller)(nil)
@@ -61,16 +61,15 @@
 	return []interface{}{&installer.Properties}
 }
 
-func (installer *baseInstaller) installDir(ctx ModuleContext) android.OutputPath {
+func (installer *baseInstaller) installDir(ctx ModuleContext) android.InstallPath {
 	dir := installer.dir
 	if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
 		dir = installer.dir64
 	}
-	if !ctx.Host() && !ctx.Arch().Native {
-		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
-	}
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
 		dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
+	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
+		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 	if installer.location == InstallInData && ctx.useVndk() {
 		dir = filepath.Join(dir, "vendor")
diff --git a/cc/library.go b/cc/library.go
index c402ea0..0fb3c78 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -31,24 +31,7 @@
 	"android/soong/genrule"
 )
 
-type StaticSharedLibraryProperties struct {
-	Srcs   []string `android:"path,arch_variant"`
-	Cflags []string `android:"arch_variant"`
-
-	Enabled            *bool    `android:"arch_variant"`
-	Whole_static_libs  []string `android:"arch_variant"`
-	Static_libs        []string `android:"arch_variant"`
-	Shared_libs        []string `android:"arch_variant"`
-	System_shared_libs []string `android:"arch_variant"`
-
-	Export_shared_lib_headers []string `android:"arch_variant"`
-	Export_static_lib_headers []string `android:"arch_variant"`
-}
-
 type LibraryProperties struct {
-	Static StaticSharedLibraryProperties `android:"arch_variant"`
-	Shared StaticSharedLibraryProperties `android:"arch_variant"`
-
 	// local file name to pass to the linker as -unexported_symbols_list
 	Unexported_symbols_list *string `android:"path,arch_variant"`
 	// local file name to pass to the linker as -force_symbols_not_weak_list
@@ -128,6 +111,28 @@
 	Inject_bssl_hash *bool `android:"arch_variant"`
 }
 
+type StaticProperties struct {
+	Static StaticOrSharedProperties `android:"arch_variant"`
+}
+
+type SharedProperties struct {
+	Shared StaticOrSharedProperties `android:"arch_variant"`
+}
+
+type StaticOrSharedProperties struct {
+	Srcs   []string `android:"path,arch_variant"`
+	Cflags []string `android:"arch_variant"`
+
+	Enabled            *bool    `android:"arch_variant"`
+	Whole_static_libs  []string `android:"arch_variant"`
+	Static_libs        []string `android:"arch_variant"`
+	Shared_libs        []string `android:"arch_variant"`
+	System_shared_libs []string `android:"arch_variant"`
+
+	Export_shared_lib_headers []string `android:"arch_variant"`
+	Export_static_lib_headers []string `android:"arch_variant"`
+}
+
 type LibraryMutatedProperties struct {
 	// Build a static variant
 	BuildStatic bool `blueprint:"mutated"`
@@ -295,6 +300,8 @@
 // functionality: static vs. shared linkage, reusing object files for shared libraries
 type libraryDecorator struct {
 	Properties        LibraryProperties
+	StaticProperties  StaticProperties
+	SharedProperties  SharedProperties
 	MutatedProperties LibraryMutatedProperties
 
 	// For reusing static library objects for shared library
@@ -355,11 +362,20 @@
 func (library *libraryDecorator) linkerProps() []interface{} {
 	var props []interface{}
 	props = append(props, library.baseLinker.linkerProps()...)
-	return append(props,
+	props = append(props,
 		&library.Properties,
 		&library.MutatedProperties,
 		&library.flagExporter.Properties,
 		&library.stripper.StripProperties)
+
+	if library.MutatedProperties.BuildShared {
+		props = append(props, &library.SharedProperties)
+	}
+	if library.MutatedProperties.BuildStatic {
+		props = append(props, &library.StaticProperties)
+	}
+
+	return props
 }
 
 func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -373,9 +389,9 @@
 	}
 
 	if library.static() {
-		flags.CFlags = append(flags.CFlags, library.Properties.Static.Cflags...)
+		flags.CFlags = append(flags.CFlags, library.StaticProperties.Static.Cflags...)
 	} else if library.shared() {
-		flags.CFlags = append(flags.CFlags, library.Properties.Shared.Cflags...)
+		flags.CFlags = append(flags.CFlags, library.SharedProperties.Shared.Cflags...)
 	}
 
 	if library.shared() {
@@ -498,10 +514,10 @@
 		if len(library.baseCompiler.Properties.Srcs) > 0 {
 			ctx.PropertyErrorf("srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.Properties.Static.Srcs) > 0 {
+		if len(library.StaticProperties.Static.Srcs) > 0 {
 			ctx.PropertyErrorf("static.srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.Properties.Shared.Srcs) > 0 {
+		if len(library.SharedProperties.Shared.Srcs) > 0 {
 			ctx.PropertyErrorf("shared.srcs", "cc_library_headers must not have any srcs")
 		}
 		return Objects{}
@@ -516,8 +532,8 @@
 			SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
 		}
 		flags.SAbiFlags = SourceAbiFlags
-		total_length := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) + len(library.Properties.Shared.Srcs) +
-			len(library.Properties.Static.Srcs)
+		total_length := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) +
+			len(library.SharedProperties.Shared.Srcs) + len(library.StaticProperties.Static.Srcs)
 		if total_length > 0 {
 			flags.SAbiDump = true
 		}
@@ -527,11 +543,11 @@
 	buildFlags := flagsToBuilderFlags(flags)
 
 	if library.static() {
-		srcs := android.PathsForModuleSrc(ctx, library.Properties.Static.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary,
 			srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	} else if library.shared() {
-		srcs := android.PathsForModuleSrc(ctx, library.Properties.Shared.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary,
 			srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	}
@@ -625,12 +641,12 @@
 
 func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	if library.static() {
-		if library.Properties.Static.System_shared_libs != nil {
-			library.baseLinker.Properties.System_shared_libs = library.Properties.Static.System_shared_libs
+		if library.StaticProperties.Static.System_shared_libs != nil {
+			library.baseLinker.Properties.System_shared_libs = library.StaticProperties.Static.System_shared_libs
 		}
 	} else if library.shared() {
-		if library.Properties.Shared.System_shared_libs != nil {
-			library.baseLinker.Properties.System_shared_libs = library.Properties.Shared.System_shared_libs
+		if library.SharedProperties.Shared.System_shared_libs != nil {
+			library.baseLinker.Properties.System_shared_libs = library.SharedProperties.Shared.System_shared_libs
 		}
 	}
 
@@ -638,12 +654,12 @@
 
 	if library.static() {
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs,
-			library.Properties.Static.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Static.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Static.Shared_libs...)
+			library.StaticProperties.Static.Whole_static_libs...)
+		deps.StaticLibs = append(deps.StaticLibs, library.StaticProperties.Static.Static_libs...)
+		deps.SharedLibs = append(deps.SharedLibs, library.StaticProperties.Static.Shared_libs...)
 
-		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.Properties.Static.Export_shared_lib_headers...)
-		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.Properties.Static.Export_static_lib_headers...)
+		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
+		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
 	} else if library.shared() {
 		if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
 			if !ctx.useSdk() {
@@ -662,12 +678,12 @@
 				deps.CrtEnd = "ndk_crtend_so." + version
 			}
 		}
-		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.Properties.Shared.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.Properties.Shared.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.Properties.Shared.Shared_libs...)
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
+		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
+		deps.SharedLibs = append(deps.SharedLibs, library.SharedProperties.Shared.Shared_libs...)
 
-		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.Properties.Shared.Export_shared_lib_headers...)
-		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.Properties.Shared.Export_static_lib_headers...)
+		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.SharedProperties.Shared.Export_shared_lib_headers...)
+		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.SharedProperties.Shared.Export_static_lib_headers...)
 	}
 	if ctx.useVndk() {
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
@@ -775,9 +791,7 @@
 
 	// Optimize out relinking against shared libraries whose interface hasn't changed by
 	// depending on a table of contents file instead of the library itself.
-	tocPath := outputFile.RelPathString()
-	tocPath = pathtools.ReplaceExtension(tocPath, flags.Toolchain.ShlibSuffix()[1:]+".toc")
-	tocFile := android.PathForOutput(ctx, tocPath)
+	tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
 	library.tocFile = android.OptionalPathForPath(tocFile)
 	TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
 
@@ -978,11 +992,13 @@
 }
 
 func (library *libraryDecorator) buildStatic() bool {
-	return library.MutatedProperties.BuildStatic && BoolDefault(library.Properties.Static.Enabled, true)
+	return library.MutatedProperties.BuildStatic &&
+		BoolDefault(library.StaticProperties.Static.Enabled, true)
 }
 
 func (library *libraryDecorator) buildShared() bool {
-	return library.MutatedProperties.BuildShared && BoolDefault(library.Properties.Shared.Enabled, true)
+	return library.MutatedProperties.BuildShared &&
+		BoolDefault(library.SharedProperties.Shared.Enabled, true)
 }
 
 func (library *libraryDecorator) getWholeStaticMissingDeps() []string {
@@ -1032,7 +1048,7 @@
 			// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
 			// The original path becomes a symlink to the corresponding file in the
 			// runtime APEX.
-			translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled || !ctx.Arch().Native
+			translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
 			if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && !translatedArch && !ctx.inRecovery() {
 				if ctx.Device() {
 					library.installSymlinkToRuntimeApex(ctx, file)
@@ -1166,16 +1182,16 @@
 
 		// Check libraries in addition to cflags, since libraries may be exporting different
 		// include directories.
-		if len(staticCompiler.Properties.Static.Cflags) == 0 &&
-			len(sharedCompiler.Properties.Shared.Cflags) == 0 &&
-			len(staticCompiler.Properties.Static.Whole_static_libs) == 0 &&
-			len(sharedCompiler.Properties.Shared.Whole_static_libs) == 0 &&
-			len(staticCompiler.Properties.Static.Static_libs) == 0 &&
-			len(sharedCompiler.Properties.Shared.Static_libs) == 0 &&
-			len(staticCompiler.Properties.Static.Shared_libs) == 0 &&
-			len(sharedCompiler.Properties.Shared.Shared_libs) == 0 &&
-			staticCompiler.Properties.Static.System_shared_libs == nil &&
-			sharedCompiler.Properties.Shared.System_shared_libs == nil {
+		if len(staticCompiler.StaticProperties.Static.Cflags) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Cflags) == 0 &&
+			len(staticCompiler.StaticProperties.Static.Whole_static_libs) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Whole_static_libs) == 0 &&
+			len(staticCompiler.StaticProperties.Static.Static_libs) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Static_libs) == 0 &&
+			len(staticCompiler.StaticProperties.Static.Shared_libs) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Shared_libs) == 0 &&
+			staticCompiler.StaticProperties.Static.System_shared_libs == nil &&
+			sharedCompiler.SharedProperties.Shared.System_shared_libs == nil {
 
 			mctx.AddInterVariantDependency(reuseObjTag, shared, static)
 			sharedCompiler.baseCompiler.Properties.OriginalSrcs =
@@ -1333,6 +1349,7 @@
 		rule := android.NewRuleBuilder()
 		rule.Command().
 			BuiltTool(ctx, "bssl_inject_hash").
+			Flag("-sha256").
 			FlagWithInput("-in-object ", outputFile).
 			FlagWithOutput("-o ", hashedOutputfile)
 		rule.Build(pctx, ctx, "injectCryptoHash", "inject crypto hash")
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 4065128..b8423be 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -48,7 +48,7 @@
 }
 
 // Returns the NDK base include path for use with sdk_version current. Usable with -I.
-func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
+func getCurrentIncludePath(ctx android.ModuleContext) android.InstallPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
 }
 
@@ -94,7 +94,7 @@
 }
 
 func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
-	to string) android.OutputPath {
+	to string) android.InstallPath {
 	// Output path is the sysroot base + "usr/include" + to directory + directory component
 	// of the file without the leading from directory stripped.
 	//
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e39bae5..f6de4ef 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -66,12 +66,12 @@
 	pctx.Import("android/soong/android")
 }
 
-func getNdkInstallBase(ctx android.PathContext) android.OutputPath {
-	return android.PathForOutput(ctx, "ndk")
+func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
+	return android.PathForNdkInstall(ctx)
 }
 
 // Returns the main install directory for the NDK sysroot. Usable with --sysroot.
-func getNdkSysrootBase(ctx android.PathContext) android.OutputPath {
+func getNdkSysrootBase(ctx android.PathContext) android.InstallPath {
 	return getNdkInstallBase(ctx).Join(ctx, "sysroot")
 }
 
diff --git a/cc/object.go b/cc/object.go
index f619c79..1f1ac8e 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -52,8 +52,9 @@
 // input to a cc_genrule module.
 func ObjectFactory() android.Module {
 	module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
+	module.sanitize = &sanitize{}
 	module.linker = &objectLinker{
-		baseLinker: NewBaseLinker(nil),
+		baseLinker: NewBaseLinker(module.sanitize),
 	}
 	module.compiler = NewBaseCompiler()
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 8c72b69..4e6cdd7 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -152,6 +152,7 @@
 
 	// Prebuilt libraries can be included in APEXes
 	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 
 	return module, library
 }
@@ -176,6 +177,7 @@
 	module.AddProperties(&prebuilt.properties)
 
 	android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
+	android.InitSdkAwareModule(module)
 	return module, library
 }
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 415518c..c0a7c63 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -674,7 +674,7 @@
 
 func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
 	t, ok := tag.(dependencyTag)
-	return ok && t.library || t == reuseObjTag
+	return ok && t.library || t == reuseObjTag || t == objDepTag
 }
 
 // Propagate sanitizer requirements down from binaries
diff --git a/cc/stl.go b/cc/stl.go
index 458129c..aa34240 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -221,13 +221,13 @@
 
 		if !ctx.toolchain().Bionic() {
 			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
-			flags.extraLibFlags = append(flags.extraLibFlags, "-nodefaultlibs")
-			if ctx.staticBinary() {
-				flags.extraLibFlags = append(flags.extraLibFlags, hostStaticGccLibs[ctx.Os()]...)
-			} else {
-				flags.extraLibFlags = append(flags.extraLibFlags, hostDynamicGccLibs[ctx.Os()]...)
-			}
+			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 			if ctx.Windows() {
+				if stl.Properties.SelectedStl == "libc++_static" {
+					// These are transitively needed by libc++_static.
+					flags.extraLibFlags = append(flags.extraLibFlags,
+						"-lmsvcrt", "-lucrt")
+				}
 				// Use SjLj exceptions for 32-bit.  libgcc_eh implements SjLj
 				// exception model for 32-bit.
 				if ctx.Arch().ArchType == android.X86 {
@@ -260,12 +260,7 @@
 		// None or error.
 		if !ctx.toolchain().Bionic() {
 			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
-			flags.extraLibFlags = append(flags.extraLibFlags, "-nodefaultlibs")
-			if ctx.staticBinary() {
-				flags.extraLibFlags = append(flags.extraLibFlags, hostStaticGccLibs[ctx.Os()]...)
-			} else {
-				flags.extraLibFlags = append(flags.extraLibFlags, hostDynamicGccLibs[ctx.Os()]...)
-			}
+			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 		}
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
@@ -273,22 +268,3 @@
 
 	return flags
 }
-
-var hostDynamicGccLibs, hostStaticGccLibs map[android.OsType][]string
-
-func init() {
-	hostDynamicGccLibs = map[android.OsType][]string{
-		android.Fuchsia: []string{"-lc", "-lunwind"},
-		android.Linux:   []string{"-lgcc_s", "-lgcc", "-lc", "-lgcc_s", "-lgcc"},
-		android.Darwin:  []string{"-lc", "-lSystem"},
-		android.Windows: []string{"-Wl,--start-group", "-lmingw32", "-lgcc", "-lgcc_eh",
-			"-lmoldname", "-lmingwex", "-lmsvcrt", "-lucrt", "-lpthread",
-			"-ladvapi32", "-lshell32", "-luser32", "-lkernel32", "-lpsapi",
-			"-Wl,--end-group"},
-	}
-	hostStaticGccLibs = map[android.OsType][]string{
-		android.Linux:   []string{"-Wl,--start-group", "-lgcc", "-lgcc_eh", "-lc", "-Wl,--end-group"},
-		android.Darwin:  []string{"NO_STATIC_HOST_BINARIES_ON_DARWIN"},
-		android.Windows: []string{"NO_STATIC_HOST_BINARIES_ON_WINDOWS"},
-	}
-}
diff --git a/cc/test.go b/cc/test.go
index ba0b7e4..0e66e28 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -80,6 +80,11 @@
 	// Add MinApiLevelModuleController to auto generated test config. If the device property of
 	// "ro.build.version.sdk" < Test_min_sdk_version, then skip this module.
 	Test_min_sdk_version *int64
+
+	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+	// explicitly.
+	Auto_gen_config *bool
 }
 
 func init() {
@@ -362,7 +367,7 @@
 	}
 
 	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
-		test.Properties.Test_config_template, test.Properties.Test_suites, configs)
+		test.Properties.Test_config_template, test.Properties.Test_suites, configs, test.Properties.Auto_gen_config)
 
 	test.binaryDecorator.baseInstaller.dir = "nativetest"
 	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
@@ -453,6 +458,11 @@
 	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
 	// with root permission.
 	Require_root *bool
+
+	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+	// explicitly.
+	Auto_gen_config *bool
 }
 
 type benchmarkDecorator struct {
@@ -490,7 +500,7 @@
 		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
 	}
 	benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
-		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs)
+		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs, benchmark.Properties.Auto_gen_config)
 
 	benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
 	benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
diff --git a/cc/testing.go b/cc/testing.go
index a0b1634..11a5e3b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -239,6 +239,7 @@
 	os android.OsType) *android.TestContext {
 
 	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("cc_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
 	ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(BinaryFactory))
 	ctx.RegisterModuleType("cc_binary_host", android.ModuleFactoryAdaptor(binaryHostFactory))
 	ctx.RegisterModuleType("cc_fuzz", android.ModuleFactoryAdaptor(FuzzFactory))
@@ -264,6 +265,7 @@
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
 	})
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.RegisterSingletonType("vndk-snapshot", android.SingletonFactoryAdaptor(VndkSnapshotSingleton))
 
 	// add some modules that are required by the compiler and/or linker
@@ -274,6 +276,7 @@
 		"foo.c":       nil,
 		"foo.lds":     nil,
 		"bar.c":       nil,
+		"baz.c":       nil,
 		"baz.o":       nil,
 		"a.proto":     nil,
 		"b.aidl":      nil,
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 8126e4a..2cebb6d 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -129,6 +129,18 @@
 
 func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
+
+	arches := ctx.DeviceConfig().Arches()
+	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
+		ctx.Module().SkipInstall()
+		return nil
+	}
+
+	if ctx.DeviceConfig().BinderBitness() != p.binderBit() {
+		ctx.Module().SkipInstall()
+		return nil
+	}
+
 	if len(p.properties.Srcs) > 0 && p.shared() {
 		p.libraryDecorator.exportIncludes(ctx)
 		p.libraryDecorator.reexportSystemDirs(p.properties.Export_system_include_dirs...)
@@ -136,6 +148,8 @@
 		// current VNDK prebuilts are only shared libs.
 		return p.singleSourcePath(ctx)
 	}
+
+	ctx.Module().SkipInstall()
 	return nil
 }
 
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 1171a65..2800ade 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -158,7 +158,7 @@
 func main() {
 	stdio := terminal.StdioImpl{}
 
-	output := terminal.NewStatusOutput(stdio.Stdout(), "",
+	output := terminal.NewStatusOutput(stdio.Stdout(), "", false,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
 
 	log := logger.New(output)
@@ -391,7 +391,7 @@
 		Thread:  mpctx.Tracer.NewThread(product),
 		Status:  &status.Status{},
 	}}
-	ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "",
+	ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "", false,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
 
 	config := build.NewConfig(ctx, flag.Args()...)
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index ec4f90e..974c644 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -41,6 +41,12 @@
 	// description for the flag (to display when running help)
 	description string
 
+	// Forces the status output into dumb terminal mode.
+	forceDumbOutput bool
+
+	// Sets a prefix string to use for filenames of log files.
+	logsPrefix string
+
 	// Creates the build configuration based on the args and build context.
 	config func(ctx build.Context, args ...string) build.Config
 
@@ -64,17 +70,21 @@
 		stdio: stdio,
 		run:   make,
 	}, {
-		flag:        "--dumpvar-mode",
-		description: "print the value of the legacy make variable VAR to stdout",
-		config:      dumpVarConfig,
-		stdio:       customStdio,
-		run:         dumpVar,
+		flag:            "--dumpvar-mode",
+		description:     "print the value of the legacy make variable VAR to stdout",
+		forceDumbOutput: true,
+		logsPrefix:      "dumpvars-",
+		config:          dumpVarConfig,
+		stdio:           customStdio,
+		run:             dumpVar,
 	}, {
-		flag:        "--dumpvars-mode",
-		description: "dump the values of one or more legacy make variables, in shell syntax",
-		config:      dumpVarConfig,
-		stdio:       customStdio,
-		run:         dumpVars,
+		flag:            "--dumpvars-mode",
+		description:     "dump the values of one or more legacy make variables, in shell syntax",
+		forceDumbOutput: true,
+		logsPrefix:      "dumpvars-",
+		config:          dumpVarConfig,
+		stdio:           customStdio,
+		run:             dumpVars,
 	}, {
 		flag:        "--build-mode",
 		description: "build modules based on the specified build action",
@@ -113,7 +123,7 @@
 		os.Exit(1)
 	}
 
-	output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"),
+	output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.forceDumbOutput,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
 
 	log := logger.New(output)
@@ -157,14 +167,14 @@
 	}
 
 	os.MkdirAll(logsDir, 0777)
-	log.SetOutput(filepath.Join(logsDir, "soong.log"))
-	trace.SetOutput(filepath.Join(logsDir, "build.trace"))
-	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
-	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
-	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, "build_error")))
+	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
+	trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
+	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
+	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
+	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"build_error")))
 	stat.AddOutput(status.NewCriticalPath(log))
 
-	defer met.Dump(filepath.Join(logsDir, "soong_metrics"))
+	defer met.Dump(filepath.Join(logsDir, c.logsPrefix+"soong_metrics"))
 
 	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
 		if !strings.HasSuffix(start, "N") {
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index c378f09..511b840 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -248,7 +248,7 @@
 	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
 	odexInstallPath := toOdexPath(module.DexLocation)
 	if odexOnSystemOther(module, global) {
-		odexInstallPath = strings.Replace(odexInstallPath, SystemPartition, SystemOtherPartition, 1)
+		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
 	}
 
 	vdexPath := odexPath.ReplaceExtension(ctx, "vdex")
@@ -581,7 +581,8 @@
 	}
 
 	for _, f := range global.PatternsOnSystemOther {
-		if makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
+		// See comment of SYSTEM_OTHER_ODEX_FILTER for details on the matching.
+		if makefileMatch("/"+f, dexLocation) || makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
 			return true
 		}
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 7f1fe42..bf692fe 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -16,17 +16,30 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 	"reflect"
 	"strings"
 	"testing"
 )
 
-func testModuleConfig(ctx android.PathContext) ModuleConfig {
+func testSystemModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+	return testModuleConfig(ctx, name, "system")
+}
+
+func testSystemProductModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+	return testModuleConfig(ctx, name, "system/product")
+}
+
+func testProductModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+	return testModuleConfig(ctx, name, "product")
+}
+
+func testModuleConfig(ctx android.PathContext, name, partition string) ModuleConfig {
 	return ModuleConfig{
-		Name:                            "test",
-		DexLocation:                     "/system/app/test/test.apk",
-		BuildPath:                       android.PathForOutput(ctx, "test/test.apk"),
-		DexPath:                         android.PathForOutput(ctx, "test/dex/test.jar"),
+		Name:                            name,
+		DexLocation:                     fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
+		BuildPath:                       android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
+		DexPath:                         android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
 		UncompressedDex:                 false,
 		HasApkLibraries:                 false,
 		PreoptFlags:                     nil,
@@ -46,14 +59,14 @@
 		ForceCreateAppImage:             false,
 		PresignedPrebuilt:               false,
 		NoStripping:                     false,
-		StripInputPath:                  android.PathForOutput(ctx, "unstripped/test.apk"),
-		StripOutputPath:                 android.PathForOutput(ctx, "stripped/test.apk"),
+		StripInputPath:                  android.PathForOutput(ctx, fmt.Sprintf("unstripped/%s.apk", name)),
+		StripOutputPath:                 android.PathForOutput(ctx, fmt.Sprintf("stripped/%s.apk", name)),
 	}
 }
 
 func TestDexPreopt(t *testing.T) {
 	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+	global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 	rule, err := GenerateDexpreoptRule(ctx, global, module)
 	if err != nil {
@@ -73,7 +86,7 @@
 func TestDexPreoptStrip(t *testing.T) {
 	// Test that we panic if we strip in a configuration where stripping is not allowed.
 	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+	global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 	global.NeverAllowStripping = true
 	module.NoStripping = false
@@ -86,29 +99,64 @@
 
 func TestDexPreoptSystemOther(t *testing.T) {
 	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+	global := GlobalConfigForTests(ctx)
+	systemModule := testSystemModuleConfig(ctx, "Stest")
+	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
+	productModule := testProductModuleConfig(ctx, "Ptest")
 
 	global.HasSystemOther = true
-	global.PatternsOnSystemOther = []string{"app/%"}
 
-	rule, err := GenerateDexpreoptRule(ctx, global, module)
-	if err != nil {
-		t.Fatal(err)
+	type moduleTest struct {
+		module            ModuleConfig
+		expectedPartition string
+	}
+	tests := []struct {
+		patterns    []string
+		moduleTests []moduleTest
+	}{
+		{
+			patterns: []string{"app/%"},
+			moduleTests: []moduleTest{
+				{module: systemModule, expectedPartition: "system_other/system"},
+				{module: systemProductModule, expectedPartition: "system/product"},
+				{module: productModule, expectedPartition: "product"},
+			},
+		},
+		{
+			patterns: []string{"app/%", "product/app/%"},
+			moduleTests: []moduleTest{
+				{module: systemModule, expectedPartition: "system_other/system"},
+				{module: systemProductModule, expectedPartition: "system_other/system/product"},
+				{module: productModule, expectedPartition: "system_other/product"},
+			},
+		},
 	}
 
-	wantInstalls := android.RuleBuilderInstalls{
-		{android.PathForOutput(ctx, "test/oat/arm/package.odex"), "/system_other/app/test/oat/arm/test.odex"},
-		{android.PathForOutput(ctx, "test/oat/arm/package.vdex"), "/system_other/app/test/oat/arm/test.vdex"},
+	for _, test := range tests {
+		global.PatternsOnSystemOther = test.patterns
+		for _, mt := range test.moduleTests {
+			rule, err := GenerateDexpreoptRule(ctx, global, mt.module)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			name := mt.module.Name
+			wantInstalls := android.RuleBuilderInstalls{
+				{android.PathForOutput(ctx, name+"/oat/arm/package.odex"), fmt.Sprintf("/%s/app/test/oat/arm/%s.odex", mt.expectedPartition, name)},
+				{android.PathForOutput(ctx, name+"/oat/arm/package.vdex"), fmt.Sprintf("/%s/app/test/oat/arm/%s.vdex", mt.expectedPartition, name)},
+			}
+
+			if rule.Installs().String() != wantInstalls.String() {
+				t.Errorf("\npatterns: %v\nwant installs:\n   %v\ngot:\n   %v", test.patterns, wantInstalls, rule.Installs())
+			}
+		}
 	}
 
-	if rule.Installs().String() != wantInstalls.String() {
-		t.Errorf("\nwant installs:\n   %v\ngot:\n   %v", wantInstalls, rule.Installs())
-	}
 }
 
 func TestDexPreoptProfile(t *testing.T) {
 	ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-	global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+	global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
 
@@ -156,7 +204,7 @@
 		t.Run(test.name, func(t *testing.T) {
 
 			ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
-			global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+			global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
 
 			test.setup(&global, &module)
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b8b0e01..c21df4c 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -37,11 +38,21 @@
 
 var (
 	pctx = android.NewPackageContext("android/soong/genrule")
+
+	gensrcsMerge = pctx.AndroidStaticRule("gensrcsMerge", blueprint.RuleParams{
+		Command:        "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}",
+		CommandDeps:    []string{"${soongZip}", "${zipSync}"},
+		Rspfile:        "${tmpZip}.rsp",
+		RspfileContent: "${zipArgs}",
+	}, "tmpZip", "genDir", "zipArgs")
 )
 
 func init() {
 	pctx.Import("android/soong/android")
 	pctx.HostBinToolVariable("sboxCmd", "sbox")
+
+	pctx.HostBinToolVariable("soongZip", "soong_zip")
+	pctx.HostBinToolVariable("zipSync", "zipsync")
 }
 
 type SourceFileGenerator interface {
@@ -112,9 +123,9 @@
 
 	taskGenerator taskFunc
 
-	deps       android.Paths
-	rule       blueprint.Rule
-	rawCommand string
+	deps        android.Paths
+	rule        blueprint.Rule
+	rawCommands []string
 
 	exportedIncludeDirs android.Paths
 
@@ -122,15 +133,20 @@
 	outputDeps  android.Paths
 
 	subName string
+	subDir  string
 }
 
-type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask
+type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
 
 type generateTask struct {
 	in          android.Paths
 	out         android.WritablePaths
+	copyTo      android.WritablePaths
+	genDir      android.WritablePath
 	sandboxOuts []string
 	cmd         string
+	shard       int
+	shards      int
 }
 
 func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -169,10 +185,10 @@
 	if len(g.properties.Export_include_dirs) > 0 {
 		for _, dir := range g.properties.Export_include_dirs {
 			g.exportedIncludeDirs = append(g.exportedIncludeDirs,
-				android.PathForModuleGen(ctx, ctx.ModuleDir(), dir))
+				android.PathForModuleGen(ctx, g.subDir, ctx.ModuleDir(), dir))
 		}
 	} else {
-		g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, ""))
+		g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir))
 	}
 
 	locationLabels := map[string][]string{}
@@ -277,120 +293,170 @@
 		}
 	}
 
-	task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
+	var copyFrom android.Paths
+	var outputFiles android.WritablePaths
+	var zipArgs strings.Builder
 
-	for _, out := range task.out {
-		addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
-	}
-
-	referencedDepfile := false
-
-	rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
-		// report the error directly without returning an error to android.Expand to catch multiple errors in a
-		// single run
-		reportError := func(fmt string, args ...interface{}) (string, bool, error) {
-			ctx.PropertyErrorf("cmd", fmt, args...)
-			return "SOONG_ERROR", false, nil
+	for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) {
+		for _, out := range task.out {
+			addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
 		}
 
-		switch name {
-		case "location":
-			if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
-				return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
+		referencedDepfile := false
+
+		rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
+			// report the error directly without returning an error to android.Expand to catch multiple errors in a
+			// single run
+			reportError := func(fmt string, args ...interface{}) (string, bool, error) {
+				ctx.PropertyErrorf("cmd", fmt, args...)
+				return "SOONG_ERROR", false, nil
 			}
-			paths := locationLabels[firstLabel]
-			if len(paths) == 0 {
-				return reportError("default label %q has no files", firstLabel)
-			} else if len(paths) > 1 {
-				return reportError("default label %q has multiple files, use $(locations %s) to reference it",
-					firstLabel, firstLabel)
-			}
-			return locationLabels[firstLabel][0], false, nil
-		case "in":
-			return "${in}", true, nil
-		case "out":
-			return "__SBOX_OUT_FILES__", false, nil
-		case "depfile":
-			referencedDepfile = true
-			if !Bool(g.properties.Depfile) {
-				return reportError("$(depfile) used without depfile property")
-			}
-			return "__SBOX_DEPFILE__", false, nil
-		case "genDir":
-			return "__SBOX_OUT_DIR__", false, nil
-		default:
-			if strings.HasPrefix(name, "location ") {
-				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
-				if paths, ok := locationLabels[label]; ok {
-					if len(paths) == 0 {
-						return reportError("label %q has no files", label)
-					} else if len(paths) > 1 {
-						return reportError("label %q has multiple files, use $(locations %s) to reference it",
-							label, label)
-					}
-					return paths[0], false, nil
-				} else {
-					return reportError("unknown location label %q", label)
+
+			switch name {
+			case "location":
+				if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
+					return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
 				}
-			} else if strings.HasPrefix(name, "locations ") {
-				label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
-				if paths, ok := locationLabels[label]; ok {
-					if len(paths) == 0 {
-						return reportError("label %q has no files", label)
-					}
-					return strings.Join(paths, " "), false, nil
-				} else {
-					return reportError("unknown locations label %q", label)
+				paths := locationLabels[firstLabel]
+				if len(paths) == 0 {
+					return reportError("default label %q has no files", firstLabel)
+				} else if len(paths) > 1 {
+					return reportError("default label %q has multiple files, use $(locations %s) to reference it",
+						firstLabel, firstLabel)
 				}
-			} else {
-				return reportError("unknown variable '$(%s)'", name)
+				return locationLabels[firstLabel][0], false, nil
+			case "in":
+				return "${in}", true, nil
+			case "out":
+				return "__SBOX_OUT_FILES__", false, nil
+			case "depfile":
+				referencedDepfile = true
+				if !Bool(g.properties.Depfile) {
+					return reportError("$(depfile) used without depfile property")
+				}
+				return "__SBOX_DEPFILE__", false, nil
+			case "genDir":
+				return "__SBOX_OUT_DIR__", false, nil
+			default:
+				if strings.HasPrefix(name, "location ") {
+					label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
+					if paths, ok := locationLabels[label]; ok {
+						if len(paths) == 0 {
+							return reportError("label %q has no files", label)
+						} else if len(paths) > 1 {
+							return reportError("label %q has multiple files, use $(locations %s) to reference it",
+								label, label)
+						}
+						return paths[0], false, nil
+					} else {
+						return reportError("unknown location label %q", label)
+					}
+				} else if strings.HasPrefix(name, "locations ") {
+					label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
+					if paths, ok := locationLabels[label]; ok {
+						if len(paths) == 0 {
+							return reportError("label %q has no files", label)
+						}
+						return strings.Join(paths, " "), false, nil
+					} else {
+						return reportError("unknown locations label %q", label)
+					}
+				} else {
+					return reportError("unknown variable '$(%s)'", name)
+				}
 			}
+		})
+
+		if err != nil {
+			ctx.PropertyErrorf("cmd", "%s", err.Error())
+			return
 		}
-	})
 
-	if err != nil {
-		ctx.PropertyErrorf("cmd", "%s", err.Error())
-		return
+		if Bool(g.properties.Depfile) && !referencedDepfile {
+			ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
+			return
+		}
+
+		// tell the sbox command which directory to use as its sandbox root
+		buildDir := android.PathForOutput(ctx).String()
+		sandboxPath := shared.TempDirForOutDir(buildDir)
+
+		// recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written,
+		// to be replaced later by ninja_strings.go
+		depfilePlaceholder := ""
+		if Bool(g.properties.Depfile) {
+			depfilePlaceholder = "$depfileArgs"
+		}
+
+		// Escape the command for the shell
+		rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
+		g.rawCommands = append(g.rawCommands, rawCommand)
+		sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s -c %s %s $allouts",
+			task.genDir, sandboxPath, task.genDir, rawCommand, depfilePlaceholder)
+
+		ruleParams := blueprint.RuleParams{
+			Command:     sandboxCommand,
+			CommandDeps: []string{"$sboxCmd"},
+		}
+		args := []string{"allouts"}
+		if Bool(g.properties.Depfile) {
+			ruleParams.Deps = blueprint.DepsGCC
+			args = append(args, "depfileArgs")
+		}
+		name := "generator"
+		if task.shards > 1 {
+			name += strconv.Itoa(task.shard)
+		}
+		rule := ctx.Rule(pctx, name, ruleParams, args...)
+
+		g.generateSourceFile(ctx, task, rule)
+
+		if len(task.copyTo) > 0 {
+			outputFiles = append(outputFiles, task.copyTo...)
+			copyFrom = append(copyFrom, task.out.Paths()...)
+			zipArgs.WriteString(" -C " + task.genDir.String())
+			zipArgs.WriteString(android.JoinWithPrefix(task.out.Strings(), " -f "))
+		} else {
+			outputFiles = append(outputFiles, task.out...)
+		}
 	}
 
-	if Bool(g.properties.Depfile) && !referencedDepfile {
-		ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
+	if len(copyFrom) > 0 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:      gensrcsMerge,
+			Implicits: copyFrom,
+			Outputs:   outputFiles,
+			Args: map[string]string{
+				"zipArgs": zipArgs.String(),
+				"tmpZip":  android.PathForModuleGen(ctx, g.subDir+".zip").String(),
+				"genDir":  android.PathForModuleGen(ctx, g.subDir).String(),
+			},
+		})
 	}
 
-	// tell the sbox command which directory to use as its sandbox root
-	buildDir := android.PathForOutput(ctx).String()
-	sandboxPath := shared.TempDirForOutDir(buildDir)
+	g.outputFiles = outputFiles.Paths()
 
-	// recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written,
-	// to be replaced later by ninja_strings.go
-	depfilePlaceholder := ""
-	if Bool(g.properties.Depfile) {
-		depfilePlaceholder = "$depfileArgs"
+	// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
+	// the genrules on AOSP. That will make things simpler to look at the graph in the common
+	// case. For larger sets of outputs, inject a phony target in between to limit ninja file
+	// growth.
+	if len(g.outputFiles) <= 6 {
+		g.outputDeps = g.outputFiles
+	} else {
+		phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   blueprint.Phony,
+			Output: phonyFile,
+			Inputs: g.outputFiles,
+		})
+
+		g.outputDeps = android.Paths{phonyFile}
 	}
 
-	genDir := android.PathForModuleGen(ctx)
-	// Escape the command for the shell
-	rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
-	g.rawCommand = rawCommand
-	sandboxCommand := fmt.Sprintf("$sboxCmd --sandbox-path %s --output-root %s -c %s %s $allouts",
-		sandboxPath, genDir, rawCommand, depfilePlaceholder)
-
-	ruleParams := blueprint.RuleParams{
-		Command:     sandboxCommand,
-		CommandDeps: []string{"$sboxCmd"},
-	}
-	args := []string{"allouts"}
-	if Bool(g.properties.Depfile) {
-		ruleParams.Deps = blueprint.DepsGCC
-		args = append(args, "depfileArgs")
-	}
-	g.rule = ctx.Rule(pctx, "generator", ruleParams, args...)
-
-	g.generateSourceFile(ctx, task)
-
 }
 
-func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask) {
+func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask, rule blueprint.Rule) {
 	desc := "generate"
 	if len(task.out) == 0 {
 		ctx.ModuleErrorf("must have at least one output file")
@@ -405,9 +471,13 @@
 		depFile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
 	}
 
+	if task.shards > 1 {
+		desc += " " + strconv.Itoa(task.shard)
+	}
+
 	params := android.BuildParams{
-		Rule:            g.rule,
-		Description:     "generate",
+		Rule:            rule,
+		Description:     desc,
 		Output:          task.out[0],
 		ImplicitOutputs: task.out[1:],
 		Inputs:          task.in,
@@ -422,28 +492,6 @@
 	}
 
 	ctx.Build(pctx, params)
-
-	for _, outputFile := range task.out {
-		g.outputFiles = append(g.outputFiles, outputFile)
-	}
-
-	// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
-	// the genrules on AOSP. That will make things simpler to look at the graph in the common
-	// case. For larger sets of outputs, inject a phony target in between to limit ninja file
-	// growth.
-	if len(task.out) <= 6 {
-		g.outputDeps = g.outputFiles
-	} else {
-		phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
-
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   blueprint.Phony,
-			Output: phonyFile,
-			Inputs: g.outputFiles,
-		})
-
-		g.outputDeps = android.Paths{phonyFile}
-	}
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
@@ -465,7 +513,7 @@
 		SubName:    g.subName,
 		Extra: []android.AndroidMkExtraFunc{
 			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=", strings.Join(g.outputFiles.Strings(), " "))
+				fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=", strings.Join(g.outputDeps.Strings(), " "))
 			},
 		},
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
@@ -502,47 +550,80 @@
 func NewGenSrcs() *Module {
 	properties := &genSrcsProperties{}
 
-	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask {
-		commands := []string{}
-		outFiles := android.WritablePaths{}
-		genDir := android.PathForModuleGen(ctx)
-		sandboxOuts := []string{}
-		for _, in := range srcFiles {
-			outFile := android.GenPathWithExt(ctx, "", in, String(properties.Output_extension))
-			outFiles = append(outFiles, outFile)
+	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
+		genDir := android.PathForModuleGen(ctx, "gensrcs")
+		shardSize := defaultShardSize
+		if s := properties.Shard_size; s != nil {
+			shardSize = int(*s)
+		}
 
-			sandboxOutfile := pathToSandboxOut(outFile, genDir)
-			sandboxOuts = append(sandboxOuts, sandboxOutfile)
+		shards := android.ShardPaths(srcFiles, shardSize)
+		var generateTasks []generateTask
 
-			command, err := android.Expand(rawCommand, func(name string) (string, error) {
-				switch name {
-				case "in":
-					return in.String(), nil
-				case "out":
-					return sandboxOutfile, nil
-				default:
-					return "$(" + name + ")", nil
-				}
-			})
-			if err != nil {
-				ctx.PropertyErrorf("cmd", err.Error())
+		for i, shard := range shards {
+			var commands []string
+			var outFiles android.WritablePaths
+			var copyTo android.WritablePaths
+			var shardDir android.WritablePath
+			var sandboxOuts []string
+
+			if len(shards) > 1 {
+				shardDir = android.PathForModuleGen(ctx, strconv.Itoa(i))
+			} else {
+				shardDir = genDir
 			}
 
-			// escape the command in case for example it contains '#', an odd number of '"', etc
-			command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command))
-			commands = append(commands, command)
-		}
-		fullCommand := strings.Join(commands, " && ")
+			for _, in := range shard {
+				outFile := android.GenPathWithExt(ctx, "gensrcs", in, String(properties.Output_extension))
+				sandboxOutfile := pathToSandboxOut(outFile, genDir)
 
-		return generateTask{
-			in:          srcFiles,
-			out:         outFiles,
-			sandboxOuts: sandboxOuts,
-			cmd:         fullCommand,
+				if len(shards) > 1 {
+					shardFile := android.GenPathWithExt(ctx, strconv.Itoa(i), in, String(properties.Output_extension))
+					copyTo = append(copyTo, outFile)
+					outFile = shardFile
+				}
+
+				outFiles = append(outFiles, outFile)
+				sandboxOuts = append(sandboxOuts, sandboxOutfile)
+
+				command, err := android.Expand(rawCommand, func(name string) (string, error) {
+					switch name {
+					case "in":
+						return in.String(), nil
+					case "out":
+						return sandboxOutfile, nil
+					default:
+						return "$(" + name + ")", nil
+					}
+				})
+				if err != nil {
+					ctx.PropertyErrorf("cmd", err.Error())
+				}
+
+				// escape the command in case for example it contains '#', an odd number of '"', etc
+				command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command))
+				commands = append(commands, command)
+			}
+			fullCommand := strings.Join(commands, " && ")
+
+			generateTasks = append(generateTasks, generateTask{
+				in:          shard,
+				out:         outFiles,
+				copyTo:      copyTo,
+				genDir:      shardDir,
+				sandboxOuts: sandboxOuts,
+				cmd:         fullCommand,
+				shard:       i,
+				shards:      len(shards),
+			})
 		}
+
+		return generateTasks
 	}
 
-	return generatorFactory(taskGenerator, properties)
+	g := generatorFactory(taskGenerator, properties)
+	g.subDir = "gensrcs"
+	return g
 }
 
 func GenSrcsFactory() android.Module {
@@ -554,12 +635,17 @@
 type genSrcsProperties struct {
 	// extension that will be substituted for each output file
 	Output_extension *string
+
+	// maximum number of files that will be passed on a single command line.
+	Shard_size *int64
 }
 
+const defaultShardSize = 100
+
 func NewGenRule() *Module {
 	properties := &genRuleProperties{}
 
-	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask {
+	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
 		outs := make(android.WritablePaths, len(properties.Out))
 		sandboxOuts := make([]string, len(properties.Out))
 		genDir := android.PathForModuleGen(ctx)
@@ -567,12 +653,13 @@
 			outs[i] = android.PathForModuleGen(ctx, out)
 			sandboxOuts[i] = pathToSandboxOut(outs[i], genDir)
 		}
-		return generateTask{
+		return []generateTask{{
 			in:          srcFiles,
 			out:         outs,
+			genDir:      android.PathForModuleGen(ctx),
 			sandboxOuts: sandboxOuts,
 			cmd:         rawCommand,
-		}
+		}}
 	}
 
 	return generatorFactory(taskGenerator, properties)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 0b6952f..5ac0e8c 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -57,6 +57,7 @@
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
 	ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(GenRuleFactory))
+	ctx.RegisterModuleType("gensrcs", android.ModuleFactoryAdaptor(GenSrcsFactory))
 	ctx.RegisterModuleType("genrule_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
 	ctx.RegisterModuleType("tool", android.ModuleFactoryAdaptor(toolFactory))
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
@@ -109,6 +110,9 @@
 		"tool_file2": nil,
 		"in1":        nil,
 		"in2":        nil,
+		"in1.txt":    nil,
+		"in2.txt":    nil,
+		"in3.txt":    nil,
 	}
 
 	for k, v := range fs {
@@ -491,11 +495,102 @@
 			}
 
 			gen := ctx.ModuleForTests("gen", "").Module().(*Module)
-			if g, w := gen.rawCommand, "'"+test.expect+"'"; w != g {
+			if g, w := gen.rawCommands[0], "'"+test.expect+"'"; w != g {
 				t.Errorf("want %q, got %q", w, g)
 			}
 		})
 	}
+}
+
+func TestGenSrcs(t *testing.T) {
+	testcases := []struct {
+		name string
+		prop string
+
+		allowMissingDependencies bool
+
+		err   string
+		cmds  []string
+		deps  []string
+		files []string
+	}{
+		{
+			name: "gensrcs",
+			prop: `
+				tools: ["tool"],
+				srcs: ["in1.txt", "in2.txt"],
+				cmd: "$(location) $(in) > $(out)",
+			`,
+			cmds: []string{
+				"'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
+			},
+			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
+			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
+		},
+		{
+			name: "shards",
+			prop: `
+				tools: ["tool"],
+				srcs: ["in1.txt", "in2.txt", "in3.txt"],
+				cmd: "$(location) $(in) > $(out)",
+				shard_size: 2,
+			`,
+			cmds: []string{
+				"'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
+				"'bash -c '\\''out/tool in3.txt > __SBOX_OUT_DIR__/in3.h'\\'''",
+			},
+			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
+			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			config := android.TestArchConfig(buildDir, nil)
+			bp := "gensrcs {\n"
+			bp += `name: "gen",` + "\n"
+			bp += `output_extension: "h",` + "\n"
+			bp += test.prop
+			bp += "}\n"
+
+			ctx := testContext(config, bp, nil)
+
+			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+			if errs == nil {
+				_, errs = ctx.PrepareBuildActions(config)
+			}
+			if errs == nil && test.err != "" {
+				t.Fatalf("want error %q, got no error", test.err)
+			} else if errs != nil && test.err == "" {
+				android.FailIfErrored(t, errs)
+			} else if test.err != "" {
+				if len(errs) != 1 {
+					t.Errorf("want 1 error, got %d errors:", len(errs))
+					for _, err := range errs {
+						t.Errorf("   %s", err.Error())
+					}
+					t.FailNow()
+				}
+				if !strings.Contains(errs[0].Error(), test.err) {
+					t.Fatalf("want %q, got %q", test.err, errs[0].Error())
+				}
+				return
+			}
+
+			gen := ctx.ModuleForTests("gen", "").Module().(*Module)
+			if g, w := gen.rawCommands, test.cmds; !reflect.DeepEqual(w, g) {
+				t.Errorf("want %q, got %q", w, g)
+			}
+
+			if g, w := gen.outputDeps.Strings(), test.deps; !reflect.DeepEqual(w, g) {
+				t.Errorf("want deps %q, got %q", w, g)
+			}
+
+			if g, w := gen.outputFiles.Strings(), test.files; !reflect.DeepEqual(w, g) {
+				t.Errorf("want files %q, got %q", w, g)
+			}
+		})
+	}
 
 }
 
@@ -529,8 +624,8 @@
 	gen := ctx.ModuleForTests("gen", "").Module().(*Module)
 
 	expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
-	if gen.rawCommand != expectedCmd {
-		t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommand)
+	if gen.rawCommands[0] != expectedCmd {
+		t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommands[0])
 	}
 
 	expectedSrcs := []string{"in1"}
diff --git a/go.mod b/go.mod
index cc328e0..1483a31 100644
--- a/go.mod
+++ b/go.mod
@@ -7,3 +7,5 @@
 replace github.com/golang/protobuf v0.0.0 => ../../external/golang-protobuf
 
 replace github.com/google/blueprint v0.0.0 => ../blueprint
+
+go 1.13
diff --git a/java/aapt2.go b/java/aapt2.go
index f0eb99c..cfe0dea 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -63,7 +63,7 @@
 func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths,
 	flags []string) android.WritablePaths {
 
-	shards := shardPaths(paths, AAPT2_SHARD_SIZE)
+	shards := android.ShardPaths(paths, AAPT2_SHARD_SIZE)
 
 	ret := make(android.WritablePaths, 0, len(paths))
 
diff --git a/java/aar.go b/java/aar.go
index 6a883d3..6426ac3 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -520,6 +520,10 @@
 	return proptools.StringDefault(a.properties.Sdk_version, defaultSdkVersion(a))
 }
 
+func (a *AARImport) systemModules() string {
+	return ""
+}
+
 func (a *AARImport) minSdkVersion() string {
 	if a.properties.Min_sdk_version != nil {
 		return *a.properties.Min_sdk_version
diff --git a/java/androidmk.go b/java/androidmk.go
index 0e8e422..5067e2f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -22,7 +22,9 @@
 	"android/soong/android"
 )
 
-func (library *Library) AndroidMkHostDex(w io.Writer, name string, data android.AndroidMkData) {
+// TODO(jungjw): We'll probably want AndroidMkEntriesProvider.AndroidMkEntries to return multiple
+// entries so that this can be more error-proof.
+func (library *Library) AndroidMkHostDex(w io.Writer, name string, entries *android.AndroidMkEntries) {
 	if Bool(library.deviceProperties.Hostdex) && !library.Host() {
 		fmt.Fprintln(w, "include $(CLEAR_VARS)")
 		fmt.Fprintln(w, "LOCAL_MODULE := "+name+"-hostdex")
@@ -38,14 +40,14 @@
 		}
 		fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
 		fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String())
-		if len(data.Required) > 0 {
-			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(data.Required, " "))
+		if len(entries.Required) > 0 {
+			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(entries.Required, " "))
 		}
-		if len(data.Host_required) > 0 {
-			fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES :=", strings.Join(data.Host_required, " "))
+		if len(entries.Host_required) > 0 {
+			fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES :=", strings.Join(entries.Host_required, " "))
 		}
-		if len(data.Target_required) > 0 {
-			fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=", strings.Join(data.Target_required, " "))
+		if len(entries.Target_required) > 0 {
+			fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=", strings.Join(entries.Target_required, " "))
 		}
 		if r := library.deviceProperties.Target.Hostdex.Required; len(r) > 0 {
 			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(r, " "))
@@ -54,75 +56,64 @@
 	}
 }
 
-func (library *Library) AndroidMk() android.AndroidMkData {
+func (library *Library) AndroidMkEntries() android.AndroidMkEntries {
 	if !library.IsForPlatform() {
-		return android.AndroidMkData{
+		return android.AndroidMkEntries{
 			Disabled: true,
 		}
 	}
-	return android.AndroidMkData{
+	return android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(library.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if len(library.logtagsSrcs) > 0 {
 					var logtags []string
 					for _, l := range library.logtagsSrcs {
 						logtags = append(logtags, l.Rel())
 					}
-					fmt.Fprintln(w, "LOCAL_LOGTAGS_FILES :=", strings.Join(logtags, " "))
+					entries.AddStrings("LOCAL_LOGTAGS_FILES", logtags...)
 				}
 
 				if library.installFile == nil {
-					fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+					entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
 				}
 				if library.dexJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile)
 				}
 				if len(library.dexpreopter.builtInstalled) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", library.dexpreopter.builtInstalled)
+					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
 				}
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.sdkVersion())
-				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
+				entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion())
+				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
+				entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
 
 				if library.jacocoReportClassesFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", library.jacocoReportClassesFile.String())
+					entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
 				}
 
-				if len(library.exportedSdkLibs) != 0 {
-					fmt.Fprintln(w, "LOCAL_EXPORT_SDK_LIBRARIES :=", strings.Join(library.exportedSdkLibs, " "))
-				}
+				entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.exportedSdkLibs...)
 
 				if len(library.additionalCheckedModules) != 0 {
-					fmt.Fprintln(w, "LOCAL_ADDITIONAL_CHECKED_MODULE +=", strings.Join(library.additionalCheckedModules.Strings(), " "))
+					entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
 				}
 
 				if library.proguardDictionary != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", library.proguardDictionary.String())
+					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.proguardDictionary)
 				}
 			},
 		},
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			android.WriteAndroidMkData(w, data)
-			library.AndroidMkHostDex(w, name, data)
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+				library.AndroidMkHostDex(w, name, entries)
+			},
 		},
 	}
 }
 
 // Called for modules that are a component of a test suite.
-func testSuiteComponent(w io.Writer, test_suites []string) {
-	fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
-	if len(test_suites) > 0 {
-		fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
-			strings.Join(test_suites, " "))
-	} else {
-		fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
-	}
-}
-
-func testSuiteComponentEntries(entries *android.AndroidMkEntries, test_suites []string) {
+func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string) {
 	entries.SetString("LOCAL_MODULE_TAGS", "tests")
 	if len(test_suites) > 0 {
 		entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test_suites...)
@@ -131,179 +122,175 @@
 	}
 }
 
-func (j *Test) AndroidMk() android.AndroidMkData {
-	data := j.Library.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, j.testProperties.Test_suites)
+func (j *Test) AndroidMkEntries() android.AndroidMkEntries {
+	entries := j.Library.AndroidMkEntries()
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, j.testProperties.Test_suites)
 		if j.testConfig != nil {
-			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", j.testConfig.String())
+			entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig)
 		}
+		androidMkWriteTestData(j.data, entries)
 	})
 
-	androidMkWriteTestData(j.data, &data)
-
-	return data
+	return entries
 }
 
-func (j *TestHelperLibrary) AndroidMk() android.AndroidMkData {
-	data := j.Library.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, j.testHelperLibraryProperties.Test_suites)
+func (j *TestHelperLibrary) AndroidMkEntries() android.AndroidMkEntries {
+	entries := j.Library.AndroidMkEntries()
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites)
 	})
 
-	return data
+	return entries
 }
 
-func (prebuilt *Import) AndroidMk() android.AndroidMkData {
-	if !prebuilt.IsForPlatform() {
-		return android.AndroidMkData{
+func (prebuilt *Import) AndroidMkEntries() android.AndroidMkEntries {
+	if !prebuilt.IsForPlatform() || !prebuilt.ContainingSdk().IsCurrentVersion() {
+		return android.AndroidMkEntries{
 			Disabled: true,
 		}
 	}
-	return android.AndroidMkData{
+	return android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := ", !Bool(prebuilt.properties.Installable))
-				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.combinedClasspathFile.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.combinedClasspathFile.String())
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				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())
 			},
 		},
 	}
 }
 
-func (prebuilt *DexImport) AndroidMk() android.AndroidMkData {
+func (prebuilt *DexImport) AndroidMkEntries() android.AndroidMkEntries {
 	if !prebuilt.IsForPlatform() {
-		return android.AndroidMkData{
+		return android.AndroidMkEntries{
 			Disabled: true,
 		}
 	}
-	return android.AndroidMkData{
+	return android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if prebuilt.dexJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", prebuilt.dexJarFile.String())
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
 					// TODO(b/125517186): export the dex jar as a classes jar to match some mis-uses in Make until
 					// boot_jars_package_check.mk can check dex jars.
-					fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.dexJarFile.String())
-					fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.dexJarFile.String())
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.dexJarFile)
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.dexJarFile)
 				}
 				if len(prebuilt.dexpreopter.builtInstalled) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", prebuilt.dexpreopter.builtInstalled)
+					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled)
 				}
 			},
 		},
 	}
 }
 
-func (prebuilt *AARImport) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (prebuilt *AARImport) AndroidMkEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.classpathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
-				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.classpathFile.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.classpathFile.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", prebuilt.exportPackage.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", prebuilt.proguardFlags.String())
-				fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", prebuilt.extraAaptPackagesFile.String())
-				fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", prebuilt.manifest.String())
-				fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile)
+				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile)
+				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", prebuilt.exportPackage)
+				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())
 			},
 		},
 	}
 }
 
-func (binary *Binary) AndroidMk() android.AndroidMkData {
+func (binary *Binary) AndroidMkEntries() android.AndroidMkEntries {
 
 	if !binary.isWrapperVariant {
-		return android.AndroidMkData{
+		return android.AndroidMkEntries{
 			Class:      "JAVA_LIBRARIES",
 			OutputFile: android.OptionalPathForPath(binary.outputFile),
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-			Extra: []android.AndroidMkExtraFunc{
-				func(w io.Writer, outputFile android.Path) {
-					fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", binary.headerJarFile.String())
-					fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", binary.implementationAndResourcesJar.String())
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(entries *android.AndroidMkEntries) {
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile)
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar)
 					if binary.dexJarFile != nil {
-						fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", binary.dexJarFile.String())
+						entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile)
 					}
 					if len(binary.dexpreopter.builtInstalled) > 0 {
-						fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", binary.dexpreopter.builtInstalled)
+						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
 					}
 				},
 			},
-			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-				android.WriteAndroidMkData(w, data)
-
-				fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
+			ExtraFooters: []android.AndroidMkExtraFootersFunc{
+				func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+					fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
+				},
 			},
 		}
 	} else {
-		return android.AndroidMkData{
+		return android.AndroidMkEntries{
 			Class:      "EXECUTABLES",
 			OutputFile: android.OptionalPathForPath(binary.wrapperFile),
-			Extra: []android.AndroidMkExtraFunc{
-				func(w io.Writer, outputFile android.Path) {
-					fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(entries *android.AndroidMkEntries) {
+					entries.SetBool("LOCAL_STRIP_MODULE", false)
 				},
 			},
-			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-				android.WriteAndroidMkData(w, data)
-
-				// Ensure that the wrapper script timestamp is always updated when the jar is updated
-				fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
-				fmt.Fprintln(w, "jar_installed_module :=")
+			ExtraFooters: []android.AndroidMkExtraFootersFunc{
+				func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+					// Ensure that the wrapper script timestamp is always updated when the jar is updated
+					fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
+					fmt.Fprintln(w, "jar_installed_module :=")
+				},
 			},
 		}
 	}
 }
 
-func (app *AndroidApp) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (app *AndroidApp) AndroidMkEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
 		Class:      "APPS",
 		OutputFile: android.OptionalPathForPath(app.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
-				// TODO(jungjw): This, outputting two LOCAL_MODULE lines, works, but is not ideal. Find a better solution.
-				if app.Name() != app.installApkName {
-					fmt.Fprintln(w, "# Overridden by PRODUCT_PACKAGE_NAME_OVERRIDES")
-					fmt.Fprintln(w, "LOCAL_MODULE :=", app.installApkName)
-				}
-				fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", app.exportPackage.String())
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				// App module names can be overridden.
+				entries.SetString("LOCAL_MODULE", app.installApkName)
+				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", app.exportPackage)
 				if app.dexJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", app.dexJarFile.String())
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile)
 				}
 				if app.implementationAndResourcesJar != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", app.implementationAndResourcesJar.String())
+					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", app.implementationAndResourcesJar)
 				}
 				if app.headerJarFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", app.headerJarFile.String())
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", app.headerJarFile)
 				}
 				if app.bundleFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUNDLE :=", app.bundleFile.String())
+					entries.SetPath("LOCAL_SOONG_BUNDLE", app.bundleFile)
 				}
 				if app.jacocoReportClassesFile != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", app.jacocoReportClassesFile.String())
+					entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", app.jacocoReportClassesFile)
 				}
 				if app.proguardDictionary != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", app.proguardDictionary.String())
+					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", app.proguardDictionary)
 				}
 
 				if app.Name() == "framework-res" {
-					fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)")
+					entries.SetString("LOCAL_MODULE_PATH", "$(TARGET_OUT_JAVA_LIBRARIES)")
 					// Make base_rules.mk not put framework-res in a subdirectory called
 					// framework_res.
-					fmt.Fprintln(w, "LOCAL_NO_STANDARD_LIBRARIES := true")
+					entries.SetBoolIfTrue("LOCAL_NO_STANDARD_LIBRARIES", true)
 				}
 
 				filterRRO := func(filter overlayType) android.Paths {
@@ -319,38 +306,37 @@
 				}
 				deviceRRODirs := filterRRO(device)
 				if len(deviceRRODirs) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_DEVICE_RRO_DIRS :=", strings.Join(deviceRRODirs.Strings(), " "))
+					entries.AddStrings("LOCAL_SOONG_DEVICE_RRO_DIRS", deviceRRODirs.Strings()...)
 				}
 				productRRODirs := filterRRO(product)
 				if len(productRRODirs) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_PRODUCT_RRO_DIRS :=", strings.Join(productRRODirs.Strings(), " "))
+					entries.AddStrings("LOCAL_SOONG_PRODUCT_RRO_DIRS", productRRODirs.Strings()...)
 				}
 
-				if Bool(app.appProperties.Export_package_resources) {
-					fmt.Fprintln(w, "LOCAL_EXPORT_PACKAGE_RESOURCES := true")
-				}
+				entries.SetBoolIfTrue("LOCAL_EXPORT_PACKAGE_RESOURCES", Bool(app.appProperties.Export_package_resources))
 
-				fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", app.manifestPath.String())
+				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", app.manifestPath)
 
-				if Bool(app.appProperties.Privileged) {
-					fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
-				}
+				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", Bool(app.appProperties.Privileged))
 
-				fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.Pem.String())
-				if overriddenPkgs := app.getOverriddenPackages(); len(overriddenPkgs) > 0 {
-					fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(overriddenPkgs, " "))
-				}
+				entries.SetPath("LOCAL_CERTIFICATE", app.certificate.Pem)
+				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", app.getOverriddenPackages()...)
 
 				for _, jniLib := range app.installJniLibs {
-					fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), "+=", jniLib.name)
+					entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
 				}
 				if len(app.dexpreopter.builtInstalled) > 0 {
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", app.dexpreopter.builtInstalled)
+					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", app.dexpreopter.builtInstalled)
 				}
 				for _, split := range app.aapt.splits {
-					install := "$(LOCAL_MODULE_PATH)/" + strings.TrimSuffix(app.installApkName, ".apk") + split.suffix + ".apk"
-					fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED +=", split.path.String()+":"+install)
+					install := app.onDeviceDir + "/" +
+						strings.TrimSuffix(app.installApkName, ".apk") + "_" + split.suffix + ".apk"
+					entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", split.path.String()+":"+install)
 				}
+			},
+		},
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
 				if app.noticeOutputs.Merged.Valid() {
 					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
 						app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE")
@@ -379,85 +365,116 @@
 	return overridden
 }
 
-func (a *AndroidTest) AndroidMk() android.AndroidMkData {
-	data := a.AndroidApp.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, a.testProperties.Test_suites)
+func (a *AndroidTest) AndroidMkEntries() android.AndroidMkEntries {
+	entries := a.AndroidApp.AndroidMkEntries()
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, a.testProperties.Test_suites)
 		if a.testConfig != nil {
-			fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", a.testConfig.String())
+			entries.SetPath("LOCAL_FULL_TEST_CONFIG", a.testConfig)
 		}
-	})
-	androidMkWriteTestData(a.data, &data)
-
-	return data
-}
-
-func (a *AndroidTestHelperApp) AndroidMk() android.AndroidMkData {
-	data := a.AndroidApp.AndroidMk()
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
-		testSuiteComponent(w, a.appTestHelperAppProperties.Test_suites)
+		androidMkWriteTestData(a.data, entries)
 	})
 
-	return data
+	return entries
 }
 
-func (a *AndroidLibrary) AndroidMk() android.AndroidMkData {
-	data := a.Library.AndroidMk()
+func (a *AndroidTestHelperApp) AndroidMkEntries() android.AndroidMkEntries {
+	entries := a.AndroidApp.AndroidMkEntries()
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites)
+	})
 
-	data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+	return entries
+}
+
+func (a *AndroidLibrary) AndroidMkEntries() android.AndroidMkEntries {
+	entries := a.Library.AndroidMkEntries()
+
+	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 		if a.aarFile != nil {
-			fmt.Fprintln(w, "LOCAL_SOONG_AAR :=", a.aarFile.String())
+			entries.SetPath("LOCAL_SOONG_AAR", a.aarFile)
 		}
 
 		if a.Name() == "framework-res" {
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)")
+			entries.SetString("LOCAL_MODULE_PATH", "$(TARGET_OUT_JAVA_LIBRARIES)")
 			// Make base_rules.mk not put framework-res in a subdirectory called
 			// framework_res.
-			fmt.Fprintln(w, "LOCAL_NO_STANDARD_LIBRARIES := true")
+			entries.SetBoolIfTrue("LOCAL_NO_STANDARD_LIBRARIES", true)
 		}
 
-		fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", a.exportPackage.String())
-		fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", a.extraAaptPackagesFile.String())
-		fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", a.mergedManifestFile.String())
-		fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=",
-			strings.Join(a.exportedProguardFlagFiles.Strings(), " "))
-		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+		entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.exportPackage)
+		entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", a.extraAaptPackagesFile)
+		entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile)
+		entries.AddStrings("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.exportedProguardFlagFiles.Strings()...)
+		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
 	})
 
-	return data
+	return entries
 }
 
-func (jd *Javadoc) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (jd *Javadoc) AndroidMkEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(jd.stubsSrcJar),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if BoolDefault(jd.properties.Installable, true) {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_DOC_ZIP := ", jd.docZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", jd.docZip)
 				}
 				if jd.stubsSrcJar != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", jd.stubsSrcJar.String())
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", jd.stubsSrcJar)
 				}
 			},
 		},
 	}
 }
 
-func (ddoc *Droiddoc) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (ddoc *Droiddoc) AndroidMkEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(ddoc.stubsSrcJar),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if BoolDefault(ddoc.Javadoc.properties.Installable, true) && ddoc.Javadoc.docZip != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_DOC_ZIP := ", ddoc.Javadoc.docZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", ddoc.Javadoc.docZip)
 				}
 				if ddoc.Javadoc.stubsSrcJar != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", ddoc.Javadoc.stubsSrcJar.String())
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", ddoc.Javadoc.stubsSrcJar)
 				}
+				apiFilePrefix := "INTERNAL_PLATFORM_"
+				if String(ddoc.properties.Api_tag_name) != "" {
+					apiFilePrefix += String(ddoc.properties.Api_tag_name) + "_"
+				}
+				if ddoc.apiFile != nil {
+					entries.SetPath(apiFilePrefix+"API_FILE", ddoc.apiFile)
+				}
+				if ddoc.dexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"DEX_API_FILE", ddoc.dexApiFile)
+				}
+				if ddoc.privateApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_API_FILE", ddoc.privateApiFile)
+				}
+				if ddoc.privateDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_DEX_API_FILE", ddoc.privateDexApiFile)
+				}
+				if ddoc.removedApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_API_FILE", ddoc.removedApiFile)
+				}
+				if ddoc.removedDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_DEX_API_FILE", ddoc.removedDexApiFile)
+				}
+				if ddoc.exactApiFile != nil {
+					entries.SetPath(apiFilePrefix+"EXACT_API_FILE", ddoc.exactApiFile)
+				}
+				if ddoc.proguardFile != nil {
+					entries.SetPath(apiFilePrefix+"PROGUARD_FILE", ddoc.proguardFile)
+				}
+			},
+		},
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
 				if ddoc.checkCurrentApiTimestamp != nil {
 					fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-current-api")
 					fmt.Fprintln(w, ddoc.Name()+"-check-current-api:",
@@ -493,58 +510,59 @@
 						fmt.Fprintln(w, "droidcore: checkapi")
 					}
 				}
-				apiFilePrefix := "INTERNAL_PLATFORM_"
-				if String(ddoc.properties.Api_tag_name) != "" {
-					apiFilePrefix += String(ddoc.properties.Api_tag_name) + "_"
-				}
-				if ddoc.apiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", ddoc.apiFile.String())
-				}
-				if ddoc.dexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"DEX_API_FILE := ", ddoc.dexApiFile.String())
-				}
-				if ddoc.privateApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", ddoc.privateApiFile.String())
-				}
-				if ddoc.privateDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", ddoc.privateDexApiFile.String())
-				}
-				if ddoc.removedApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", ddoc.removedApiFile.String())
-				}
-				if ddoc.removedDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", ddoc.removedDexApiFile.String())
-				}
-				if ddoc.exactApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", ddoc.exactApiFile.String())
-				}
-				if ddoc.proguardFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PROGUARD_FILE := ", ddoc.proguardFile.String())
-				}
 			},
 		},
 	}
 }
 
-func (dstubs *Droidstubs) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
+func (dstubs *Droidstubs) AndroidMkEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(dstubs.stubsSrcJar),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
-		Extra: []android.AndroidMkExtraFunc{
-			func(w io.Writer, outputFile android.Path) {
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
 				if dstubs.Javadoc.stubsSrcJar != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", dstubs.Javadoc.stubsSrcJar.String())
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.stubsSrcJar)
 				}
 				if dstubs.apiVersionsXml != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_API_VERSIONS_XML := ", dstubs.apiVersionsXml.String())
+					entries.SetPath("LOCAL_DROIDDOC_API_VERSIONS_XML", dstubs.apiVersionsXml)
 				}
 				if dstubs.annotationsZip != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_ANNOTATIONS_ZIP := ", dstubs.annotationsZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_ANNOTATIONS_ZIP", dstubs.annotationsZip)
 				}
 				if dstubs.jdiffDocZip != nil {
-					fmt.Fprintln(w, "LOCAL_DROIDDOC_JDIFF_DOC_ZIP := ", dstubs.jdiffDocZip.String())
+					entries.SetPath("LOCAL_DROIDDOC_JDIFF_DOC_ZIP", dstubs.jdiffDocZip)
 				}
+				apiFilePrefix := "INTERNAL_PLATFORM_"
+				if String(dstubs.properties.Api_tag_name) != "" {
+					apiFilePrefix += String(dstubs.properties.Api_tag_name) + "_"
+				}
+				if dstubs.apiFile != nil {
+					entries.SetPath(apiFilePrefix+"API_FILE", dstubs.apiFile)
+				}
+				if dstubs.dexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"DEX_API_FILE", dstubs.dexApiFile)
+				}
+				if dstubs.privateApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_API_FILE", dstubs.privateApiFile)
+				}
+				if dstubs.privateDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"PRIVATE_DEX_API_FILE", dstubs.privateDexApiFile)
+				}
+				if dstubs.removedApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_API_FILE", dstubs.removedApiFile)
+				}
+				if dstubs.removedDexApiFile != nil {
+					entries.SetPath(apiFilePrefix+"REMOVED_DEX_API_FILE", dstubs.removedDexApiFile)
+				}
+				if dstubs.exactApiFile != nil {
+					entries.SetPath(apiFilePrefix+"EXACT_API_FILE", dstubs.exactApiFile)
+				}
+			},
+		},
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
 				if dstubs.checkCurrentApiTimestamp != nil {
 					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-current-api")
 					fmt.Fprintln(w, dstubs.Name()+"-check-current-api:",
@@ -580,6 +598,18 @@
 						fmt.Fprintln(w, "droidcore: checkapi")
 					}
 				}
+				if dstubs.apiLintTimestamp != nil {
+					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-api-lint")
+					fmt.Fprintln(w, dstubs.Name()+"-api-lint:",
+						dstubs.apiLintTimestamp.String())
+
+					fmt.Fprintln(w, ".PHONY: checkapi")
+					fmt.Fprintln(w, "checkapi:",
+						dstubs.apiLintTimestamp.String())
+
+					fmt.Fprintln(w, ".PHONY: droidcore")
+					fmt.Fprintln(w, "droidcore: checkapi")
+				}
 				if dstubs.checkNullabilityWarningsTimestamp != nil {
 					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-nullability-warnings")
 					fmt.Fprintln(w, dstubs.Name()+"-check-nullability-warnings:",
@@ -588,31 +618,6 @@
 					fmt.Fprintln(w, ".PHONY:", "droidcore")
 					fmt.Fprintln(w, "droidcore: ", dstubs.Name()+"-check-nullability-warnings")
 				}
-				apiFilePrefix := "INTERNAL_PLATFORM_"
-				if String(dstubs.properties.Api_tag_name) != "" {
-					apiFilePrefix += String(dstubs.properties.Api_tag_name) + "_"
-				}
-				if dstubs.apiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", dstubs.apiFile.String())
-				}
-				if dstubs.dexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"DEX_API_FILE := ", dstubs.dexApiFile.String())
-				}
-				if dstubs.privateApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", dstubs.privateApiFile.String())
-				}
-				if dstubs.privateDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", dstubs.privateDexApiFile.String())
-				}
-				if dstubs.removedApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", dstubs.removedApiFile.String())
-				}
-				if dstubs.removedDexApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", dstubs.removedDexApiFile.String())
-				}
-				if dstubs.exactApiFile != nil {
-					fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", dstubs.exactApiFile.String())
-				}
 			},
 		},
 	}
@@ -627,7 +632,7 @@
 			func(entries *android.AndroidMkEntries) {
 				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", Bool(a.properties.Privileged))
 				if a.certificate != nil {
-					entries.SetString("LOCAL_CERTIFICATE", a.certificate.Pem.String())
+					entries.SetPath("LOCAL_CERTIFICATE", a.certificate.Pem)
 				} else {
 					entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED")
 				}
@@ -644,25 +649,13 @@
 func (a *AndroidTestImport) AndroidMkEntries() android.AndroidMkEntries {
 	entries := a.AndroidAppImport.AndroidMkEntries()
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		testSuiteComponentEntries(entries, a.testProperties.Test_suites)
-		androidMkEntriesWriteTestData(a.data, entries)
+		testSuiteComponent(entries, a.testProperties.Test_suites)
+		androidMkWriteTestData(a.data, entries)
 	})
 	return entries
 }
 
-func androidMkWriteTestData(data android.Paths, ret *android.AndroidMkData) {
-	var testFiles []string
-	for _, d := range data {
-		testFiles = append(testFiles, d.String()+":"+d.Rel())
-	}
-	if len(testFiles) > 0 {
-		ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUPPORT_FILES := "+strings.Join(testFiles, " "))
-		})
-	}
-}
-
-func androidMkEntriesWriteTestData(data android.Paths, entries *android.AndroidMkEntries) {
+func androidMkWriteTestData(data android.Paths, entries *android.AndroidMkEntries) {
 	var testFiles []string
 	for _, d := range data {
 		testFiles = append(testFiles, d.String()+":"+d.Rel())
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index fbf2baa..438b66a 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -15,107 +15,12 @@
 package java
 
 import (
-	"bytes"
-	"io"
-	"io/ioutil"
-	"strings"
+	"reflect"
 	"testing"
 
 	"android/soong/android"
 )
 
-type testAndroidMk struct {
-	*testing.T
-	body []byte
-}
-
-type testAndroidMkModule struct {
-	*testing.T
-	props map[string]string
-}
-
-func newTestAndroidMk(t *testing.T, r io.Reader) *testAndroidMk {
-	t.Helper()
-	buf, err := ioutil.ReadAll(r)
-	if err != nil {
-		t.Fatal("failed to open read Android.mk.", err)
-	}
-	return &testAndroidMk{
-		T:    t,
-		body: buf,
-	}
-}
-
-func parseAndroidMkProps(lines []string) map[string]string {
-	props := make(map[string]string)
-	for _, line := range lines {
-		line = strings.TrimLeft(line, " ")
-		if line == "" || strings.HasPrefix(line, "#") {
-			continue
-		}
-		tokens := strings.Split(line, " ")
-		if tokens[1] == "+=" {
-			props[tokens[0]] += " " + strings.Join(tokens[2:], " ")
-		} else {
-			props[tokens[0]] = strings.Join(tokens[2:], " ")
-		}
-	}
-	return props
-}
-
-func (t *testAndroidMk) moduleFor(moduleName string) *testAndroidMkModule {
-	t.Helper()
-	lines := strings.Split(string(t.body), "\n")
-	index := android.IndexList("LOCAL_MODULE := "+moduleName, lines)
-	if index == -1 {
-		t.Fatalf("%q is not found.", moduleName)
-	}
-	lines = lines[index:]
-	includeIndex := android.IndexListPred(func(line string) bool {
-		return strings.HasPrefix(line, "include")
-	}, lines)
-	if includeIndex == -1 {
-		t.Fatalf("%q is not properly defined. (\"include\" not found).", moduleName)
-	}
-	props := parseAndroidMkProps(lines[:includeIndex])
-	return &testAndroidMkModule{
-		T:     t.T,
-		props: props,
-	}
-}
-
-func (t *testAndroidMkModule) hasRequired(dep string) {
-	t.Helper()
-	required, ok := t.props["LOCAL_REQUIRED_MODULES"]
-	if !ok {
-		t.Error("LOCAL_REQUIRED_MODULES is not found.")
-		return
-	}
-	if !android.InList(dep, strings.Split(required, " ")) {
-		t.Errorf("%q is expected in LOCAL_REQUIRED_MODULES, but not found in %q.", dep, required)
-	}
-}
-
-func (t *testAndroidMkModule) hasNoRequired(dep string) {
-	t.Helper()
-	required, ok := t.props["LOCAL_REQUIRED_MODULES"]
-	if !ok {
-		return
-	}
-	if android.InList(dep, strings.Split(required, " ")) {
-		t.Errorf("%q is not expected in LOCAL_REQUIRED_MODULES, but found.", dep)
-	}
-}
-
-func getAndroidMk(t *testing.T, ctx *android.TestContext, config android.Config, name string) *testAndroidMk {
-	t.Helper()
-	lib, _ := ctx.ModuleForTests(name, "android_common").Module().(*Library)
-	data := android.AndroidMkDataForTest(t, config, "", lib)
-	w := &bytes.Buffer{}
-	data.Custom(w, name, "", "", data)
-	return newTestAndroidMk(t, w)
-}
-
 func TestRequired(t *testing.T) {
 	ctx, config := testJava(t, `
 		java_library {
@@ -125,8 +30,14 @@
 		}
 	`)
 
-	mk := getAndroidMk(t, ctx, config, "foo")
-	mk.moduleFor("foo").hasRequired("libfoo")
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+
+	expected := []string{"libfoo"}
+	actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected required modules - expected: %q, actual: %q", expected, actual)
+	}
 }
 
 func TestHostdex(t *testing.T) {
@@ -138,9 +49,19 @@
 		}
 	`)
 
-	mk := getAndroidMk(t, ctx, config, "foo")
-	mk.moduleFor("foo")
-	mk.moduleFor("foo-hostdex")
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+
+	expected := []string{"foo"}
+	actual := entries.EntryMap["LOCAL_MODULE"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected module name - expected: %q, actual: %q", expected, actual)
+	}
+
+	footerLines := entries.FooterLinesForTests()
+	if !android.InList("LOCAL_MODULE := foo-hostdex", footerLines) {
+		t.Errorf("foo-hostdex is not found in the footers: %q", footerLines)
+	}
 }
 
 func TestHostdexRequired(t *testing.T) {
@@ -153,9 +74,19 @@
 		}
 	`)
 
-	mk := getAndroidMk(t, ctx, config, "foo")
-	mk.moduleFor("foo").hasRequired("libfoo")
-	mk.moduleFor("foo-hostdex").hasRequired("libfoo")
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+
+	expected := []string{"libfoo"}
+	actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Unexpected required modules - expected: %q, actual: %q", expected, actual)
+	}
+
+	footerLines := entries.FooterLinesForTests()
+	if !android.InList("LOCAL_REQUIRED_MODULES := libfoo", footerLines) {
+		t.Errorf("Wrong or missing required line for foo-hostdex in the footers: %q", footerLines)
+	}
 }
 
 func TestHostdexSpecificRequired(t *testing.T) {
@@ -172,7 +103,15 @@
 		}
 	`)
 
-	mk := getAndroidMk(t, ctx, config, "foo")
-	mk.moduleFor("foo").hasNoRequired("libfoo")
-	mk.moduleFor("foo-hostdex").hasRequired("libfoo")
+	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)
+
+	if r, ok := entries.EntryMap["LOCAL_REQUIRED_MODULES"]; ok {
+		t.Errorf("Unexpected required modules: %q", r)
+	}
+
+	footerLines := entries.FooterLinesForTests()
+	if !android.InList("LOCAL_REQUIRED_MODULES += libfoo", footerLines) {
+		t.Errorf("Wrong or missing required line for foo-hostdex in the footers: %q", footerLines)
+	}
 }
diff --git a/java/app.go b/java/app.go
index d00c4c0..e033661 100644
--- a/java/app.go
+++ b/java/app.go
@@ -126,6 +126,10 @@
 	// the install APK name is normally the same as the module name, but can be overridden with PRODUCT_PACKAGE_NAME_OVERRIDES.
 	installApkName string
 
+	installDir android.InstallPath
+
+	onDeviceDir string
+
 	additionalAaptFlags []string
 
 	noticeOutputs android.NoticeOutputs
@@ -319,7 +323,6 @@
 	} else {
 		installDir = filepath.Join("app", a.installApkName)
 	}
-
 	a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 	a.dexpreopter.isInstallable = Bool(a.properties.Installable)
 	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
@@ -352,7 +355,7 @@
 	return jniJarFile
 }
 
-func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) {
+func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) {
 	// Collect NOTICE files from all dependencies.
 	seenModules := make(map[android.Module]bool)
 	noticePathSet := make(map[android.Path]bool)
@@ -392,7 +395,7 @@
 		return noticePaths[i].String() < noticePaths[j].String()
 	})
 
-	a.noticeOutputs = android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths)
+	a.noticeOutputs = android.BuildNoticeOutput(ctx, a.installDir, a.installApkName+".apk", noticePaths)
 }
 
 // Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
@@ -438,17 +441,19 @@
 	// Check if the install APK name needs to be overridden.
 	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
 
-	var installDir android.OutputPath
 	if ctx.ModuleName() == "framework-res" {
 		// framework-res.apk is installed as system/framework/framework-res.apk
-		installDir = android.PathForModuleInstall(ctx, "framework")
+		a.installDir = android.PathForModuleInstall(ctx, "framework")
 	} else if Bool(a.appProperties.Privileged) {
-		installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
+		a.installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
+	} else if ctx.InstallInTestcases() {
+		a.installDir = android.PathForModuleInstall(ctx, a.installApkName)
 	} else {
-		installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
+		a.installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
 	}
+	a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir)
 
-	a.noticeBuildActions(ctx, installDir)
+	a.noticeBuildActions(ctx)
 	if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
 		a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
 	}
@@ -494,9 +499,9 @@
 	a.bundleFile = bundleFile
 
 	// Install the app package.
-	ctx.InstallFile(installDir, a.installApkName+".apk", a.outputFile)
+	ctx.InstallFile(a.installDir, a.installApkName+".apk", a.outputFile)
 	for _, split := range a.aapt.splits {
-		ctx.InstallFile(installDir, a.installApkName+"_"+split.suffix+".apk", split.path)
+		ctx.InstallFile(a.installDir, a.installApkName+"_"+split.suffix+".apk", split.path)
 	}
 }
 
@@ -598,6 +603,10 @@
 	data       android.Paths
 }
 
+func (a *AndroidTest) InstallInTestcases() bool {
+	return true
+}
+
 func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Check if the instrumentation target package is overridden before generating build actions.
 	if a.appTestProperties.Instrumentation_for != nil {
@@ -608,7 +617,8 @@
 	}
 	a.generateAndroidBuildActions(ctx)
 
-	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites)
+	a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
+		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
 
@@ -656,6 +666,11 @@
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
 	Test_suites []string `android:"arch_variant"`
+
+	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+	// explicitly.
+	Auto_gen_config *bool
 }
 
 type AndroidTestHelperApp struct {
@@ -758,7 +773,7 @@
 
 	usesLibrary usesLibrary
 
-	installPath android.OutputPath
+	installPath android.InstallPath
 }
 
 type AndroidAppImportProperties struct {
diff --git a/java/builder.go b/java/builder.go
index 9e068fa..0a5c79b 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -64,6 +64,7 @@
 
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+	_ = 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 ...
 	// to field java.nio.Buffer.address"
@@ -74,6 +75,7 @@
 				`( [ ! -s $srcJarDir/list -a ! -s $out.rsp ] || ` +
 				`KYTHE_ROOT_DIRECTORY=. KYTHE_OUTPUT_FILE=$out ` +
 				`KYTHE_CORPUS=${kytheCorpus} ` +
+				`KYTHE_VNAMES=${kytheVnames} ` +
 				`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
 				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
 				`-jar ${config.JavaKytheExtractorJar} ` +
@@ -84,6 +86,7 @@
 			CommandDeps: []string{
 				"${config.JavaCmd}",
 				"${config.JavaKytheExtractorJar}",
+				"${kytheVnames}",
 				"${config.ZipSyncCmd}",
 			},
 			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
diff --git a/java/config/config.go b/java/config/config.go
index ce62b93..f418ee7 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -86,6 +86,14 @@
 		// This is set up and guaranteed by soong_ui
 		return ctx.Config().Getenv("ANDROID_JAVA_HOME")
 	})
+	pctx.VariableFunc("JlinkVersion", func(ctx android.PackageVarContext) string {
+		switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN") {
+		case "true":
+			return "11"
+		default:
+			return "9"
+		}
+	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
 	pctx.SourcePathVariableWithEnvOverride("JavacCmd",
diff --git a/java/config/makevars.go b/java/config/makevars.go
index c40f4fc..2fa6f89 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -38,6 +38,7 @@
 	ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
 	ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}")
 	ctx.Strict("ANDROID_JAVA9_HOME", "prebuilts/jdk/jdk9/${hostPrebuiltTag}")
+	ctx.Strict("ANDROID_JAVA11_HOME", "prebuilts/jdk/jdk11/${hostPrebuiltTag}")
 	ctx.Strict("ANDROID_JAVA_TOOLCHAIN", "${JavaToolchain}")
 	ctx.Strict("JAVA", "${JavaCmd} ${JavaVmFlags}")
 	ctx.Strict("JAVAC", "${JavacCmd} ${JavacVmFlags}")
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 6214dac..b48871e 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -22,7 +22,7 @@
 type dexpreopter struct {
 	dexpreoptProperties DexpreoptProperties
 
-	installPath         android.OutputPath
+	installPath         android.InstallPath
 	uncompressedDex     bool
 	isSDKLibrary        bool
 	isTest              bool
@@ -94,7 +94,7 @@
 	return false
 }
 
-func odexOnSystemOther(ctx android.ModuleContext, installPath android.OutputPath) bool {
+func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
 	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx))
 }
 
@@ -126,10 +126,6 @@
 			archs = archs[:1]
 		}
 	}
-	if ctx.Config().SecondArchIsTranslated() {
-		// Only preopt primary arch for translated arch since there is only an image there.
-		archs = archs[:1]
-	}
 
 	var images android.Paths
 	var imagesDeps []android.Paths
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 8c699b8..043f9da 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -87,11 +87,7 @@
 // supported through native bridge.
 func dexpreoptTargets(ctx android.PathContext) []android.Target {
 	var targets []android.Target
-	for i, target := range ctx.Config().Targets[android.Android] {
-		if ctx.Config().SecondArchIsTranslated() && i > 0 {
-			break
-		}
-
+	for _, target := range ctx.Config().Targets[android.Android] {
 		if target.NativeBridge == android.NativeBridgeDisabled {
 			targets = append(targets, target)
 		}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 1d5331e..8a283cd 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -67,9 +67,15 @@
 	// If set to false, don't allow this module(-docs.zip) to be exported. Defaults to true.
 	Installable *bool
 
-	// if not blank, set to the version of the sdk to compile against
+	// if not blank, set to the version of the sdk to compile against.
+	// Defaults to compiling against the current platform.
 	Sdk_version *string `android:"arch_variant"`
 
+	// When targeting 1.9 and above, override the modules to use with --system,
+	// otherwise provides defaults libraries to add to the bootclasspath.
+	// Defaults to "none"
+	System_modules *string
+
 	Aidl struct {
 		// Top level directories to pass to aidl tool
 		Include_dirs []string
@@ -238,6 +244,16 @@
 		// do not perform API check against Last_released, in the case that both two specified API
 		// files by Last_released are modules which don't exist.
 		Ignore_missing_latest_api *bool `blueprint:"mutated"`
+
+		Api_lint struct {
+			Enabled *bool
+
+			// If set, performs api_lint on any new APIs not found in the given signature file
+			New_since *string `android:"path"`
+
+			// If not blank, path to the baseline txt file for approved API lint violations.
+			Baseline_file *string `android:"path"`
+		}
 	}
 
 	// user can specify the version of previous released API file in order to do compatibility check.
@@ -391,6 +407,10 @@
 	return proptools.StringDefault(j.properties.Sdk_version, defaultSdkVersion(j))
 }
 
+func (j *Javadoc) systemModules() string {
+	return proptools.String(j.properties.System_modules)
+}
+
 func (j *Javadoc) minSdkVersion() string {
 	return j.sdkVersion()
 }
@@ -417,6 +437,10 @@
 				}
 				ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.modules...)
 			}
+		} else if sdkDep.systemModules != "" {
+			// Add the system modules to both the system modules and bootclasspath.
+			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.systemModules)
 		}
 	}
 
@@ -461,14 +485,14 @@
 	flags droiddocBuilderFlags) android.Paths {
 
 	outSrcFiles := make(android.Paths, 0, len(srcFiles))
+	var aidlSrcs android.Paths
 
 	aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
 
 	for _, srcFile := range srcFiles {
 		switch srcFile.Ext() {
 		case ".aidl":
-			javaFile := genAidl(ctx, srcFile, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
-			outSrcFiles = append(outSrcFiles, javaFile)
+			aidlSrcs = append(aidlSrcs, srcFile)
 		case ".logtags":
 			javaFile := genLogtags(ctx, srcFile)
 			outSrcFiles = append(outSrcFiles, javaFile)
@@ -477,6 +501,12 @@
 		}
 	}
 
+	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
+	if len(aidlSrcs) > 0 {
+		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+		outSrcFiles = append(outSrcFiles, srcJarFiles...)
+	}
+
 	return outSrcFiles
 }
 
@@ -498,6 +528,10 @@
 		case bootClasspathTag:
 			if dep, ok := module.(Dependency); ok {
 				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
+			} else if sm, ok := module.(*SystemModules); ok {
+				// A system modules dependency has been added to the bootclasspath
+				// so add its libs to the bootclasspath.
+				deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...)
 			} else {
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
@@ -1148,6 +1182,7 @@
 	checkCurrentApiTimestamp      android.WritablePath
 	updateCurrentApiTimestamp     android.WritablePath
 	checkLastReleasedApiTimestamp android.WritablePath
+	apiLintTimestamp              android.WritablePath
 
 	checkNullabilityWarningsTimestamp android.WritablePath
 
@@ -1484,6 +1519,55 @@
 
 	// Create rule for apicheck
 
+	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().IsPdkBuild() {
+		rule := android.NewRuleBuilder()
+		rule.Command().Text("( true")
+
+		srcJarDir := android.PathForModuleOut(ctx, "api_lint", "srcjars")
+		srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+		cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
+		if newSince.Valid() {
+			cmd.FlagWithInput("--api-lint ", newSince.Path())
+		} else {
+			cmd.Flag("--api-lint")
+		}
+
+		d.inclusionAnnotationsFlags(ctx, cmd)
+		d.mergeAnnoDirFlags(ctx, cmd)
+
+		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
+		updatedBaselineOutput := android.PathForModuleOut(ctx, "api_lint_baseline.txt")
+		d.apiLintTimestamp = android.PathForModuleOut(ctx, "api_lint.timestamp")
+
+		if baselineFile.Valid() {
+			cmd.FlagWithInput("--baseline ", baselineFile.Path())
+			cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput)
+		}
+
+		zipSyncCleanupCmd(rule, srcJarDir)
+
+		msg := fmt.Sprintf(`\n******************************\n`+
+			`Your API changes are triggering API Lint warnings or errors.\n\n`+
+			`To make these errors go away, you have two choices:\n`+
+			`   1. You can suppress the errors with @SuppressLint(\"<id>\").\n\n`+
+			`   2. You can update the baseline by executing the following command:\n`+
+			`         cp \"$PWD/%s\" \"$PWD/%s\"\n\n`+
+			`******************************\n`, updatedBaselineOutput, baselineFile.Path())
+		rule.Command().
+			Text("touch").Output(d.apiLintTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build(pctx, ctx, "metalavaApiLint", "metalava API lint")
+
+	}
+
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
 		!ctx.Config().IsPdkBuild() {
 
diff --git a/java/gen.go b/java/gen.go
index a69e9a2..d50a665 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -15,28 +15,22 @@
 package java
 
 import (
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
 )
 
 func init() {
-	pctx.HostBinToolVariable("syspropCmd", "sysprop_java")
 	pctx.SourcePathVariable("logtagsCmd", "build/make/tools/java-event-log-tags.py")
 	pctx.SourcePathVariable("mergeLogtagsCmd", "build/make/tools/merge-event-log-tags.py")
 	pctx.SourcePathVariable("logtagsLib", "build/make/tools/event_log_tags.py")
 }
 
 var (
-	aidl = pctx.AndroidStaticRule("aidl",
-		blueprint.RuleParams{
-			Command:     "${config.AidlCmd} -d$depFile $aidlFlags $in $out",
-			CommandDeps: []string{"${config.AidlCmd}"},
-		},
-		"depFile", "aidlFlags")
-
 	logtags = pctx.AndroidStaticRule("logtags",
 		blueprint.RuleParams{
 			Command:     "$logtagsCmd -o $out $in",
@@ -48,36 +42,66 @@
 			Command:     "$mergeLogtagsCmd -o $out $in",
 			CommandDeps: []string{"$mergeLogtagsCmd", "$logtagsLib"},
 		})
-
-	sysprop = pctx.AndroidStaticRule("sysprop",
-		blueprint.RuleParams{
-			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
-				`$syspropCmd --scope $scope --java-output-dir $out.tmp $in && ` +
-				`${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
-			CommandDeps: []string{
-				"$syspropCmd",
-				"${config.SoongZipCmd}",
-			},
-		}, "scope")
 )
 
-func genAidl(ctx android.ModuleContext, aidlFile android.Path, aidlFlags string, deps android.Paths) android.Path {
-	javaFile := android.GenPathWithExt(ctx, "aidl", aidlFile, "java")
-	depFile := javaFile.String() + ".d"
+func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags string, deps android.Paths) android.Paths {
+	// Shard aidl files into groups of 50 to avoid having to recompile all of them if one changes and to avoid
+	// hitting command line length limits.
+	shards := android.ShardPaths(aidlFiles, 50)
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aidl,
-		Description: "aidl " + aidlFile.Rel(),
-		Output:      javaFile,
-		Input:       aidlFile,
-		Implicits:   deps,
-		Args: map[string]string{
-			"depFile":   depFile,
-			"aidlFlags": aidlFlags,
-		},
-	})
+	srcJarFiles := make(android.Paths, 0, len(shards))
 
-	return javaFile
+	for i, shard := range shards {
+		srcJarFile := android.PathForModuleGen(ctx, "aidl", "aidl"+strconv.Itoa(i)+".srcjar")
+		srcJarFiles = append(srcJarFiles, srcJarFile)
+
+		outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
+
+		rule := android.NewRuleBuilder()
+
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+		rule.Command().Text("mkdir -p").Flag(outDir.String())
+		rule.Command().Text("FLAGS=' " + aidlFlags + "'")
+
+		for _, aidlFile := range shard {
+			depFile := srcJarFile.InSameDir(ctx, aidlFile.String()+".d")
+			javaFile := outDir.Join(ctx, pathtools.ReplaceExtension(aidlFile.String(), "java"))
+			rule.Command().
+				Tool(ctx.Config().HostToolPath(ctx, "aidl")).
+				FlagWithDepFile("-d", depFile).
+				Flag("$FLAGS").
+				Input(aidlFile).
+				Output(javaFile).
+				Implicits(deps)
+			rule.Temporary(javaFile)
+		}
+
+		rule.Command().
+			Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+			// TODO(b/124333557): this can't use -srcjar for now, aidl on parcelables generates java files
+			//  without a package statement, which causes -srcjar to put them in the top level of the zip file.
+			//  Once aidl skips parcelables we can use -srcjar.
+			//Flag("-srcjar").
+			Flag("-write_if_changed").
+			FlagWithOutput("-o ", srcJarFile).
+			FlagWithArg("-C ", outDir.String()).
+			FlagWithArg("-D ", outDir.String())
+
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+
+		rule.Restat()
+
+		ruleName := "aidl"
+		ruleDesc := "aidl"
+		if len(shards) > 1 {
+			ruleName += "_" + strconv.Itoa(i)
+			ruleDesc += " " + strconv.Itoa(i)
+		}
+
+		rule.Build(pctx, ctx, ruleName, ruleDesc)
+	}
+
+	return srcJarFiles
 }
 
 func genLogtags(ctx android.ModuleContext, logtagsFile android.Path) android.Path {
@@ -93,22 +117,6 @@
 	return javaFile
 }
 
-func genSysprop(ctx android.ModuleContext, syspropFile android.Path, scope string) android.Path {
-	srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        sysprop,
-		Description: "sysprop_java " + syspropFile.Rel(),
-		Output:      srcJarFile,
-		Input:       syspropFile,
-		Args: map[string]string{
-			"scope": scope,
-		},
-	})
-
-	return srcJarFile
-}
-
 func genAidlIncludeFlags(srcFiles android.Paths) string {
 	var baseDirs []string
 	for _, srcFile := range srcFiles {
@@ -126,49 +134,38 @@
 	flags javaBuilderFlags) android.Paths {
 
 	outSrcFiles := make(android.Paths, 0, len(srcFiles))
+	var protoSrcs android.Paths
+	var aidlSrcs android.Paths
 
 	aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
 
 	for _, srcFile := range srcFiles {
 		switch srcFile.Ext() {
 		case ".aidl":
-			javaFile := genAidl(ctx, srcFile, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
-			outSrcFiles = append(outSrcFiles, javaFile)
+			aidlSrcs = append(aidlSrcs, srcFile)
 		case ".logtags":
 			j.logtagsSrcs = append(j.logtagsSrcs, srcFile)
 			javaFile := genLogtags(ctx, srcFile)
 			outSrcFiles = append(outSrcFiles, javaFile)
 		case ".proto":
-			srcJarFile := genProto(ctx, srcFile, flags.proto)
-			outSrcFiles = append(outSrcFiles, srcJarFile)
-		case ".sysprop":
-			// internal scope contains all properties
-			// public scope only contains public properties
-			// use public if the owner is different from client
-			scope := "internal"
-			if j.properties.Sysprop.Platform != nil {
-				isProduct := ctx.ProductSpecific()
-				isVendor := ctx.SocSpecific()
-				isOwnerPlatform := Bool(j.properties.Sysprop.Platform)
-
-				if isProduct {
-					// product can't own any sysprop_library now, so product must use public scope
-					scope = "public"
-				} else if isVendor && !isOwnerPlatform {
-					// vendor and odm can't use system's internal property.
-					scope = "public"
-				}
-
-				// We don't care about clients under system.
-				// They can't use sysprop_library owned by other partitions.
-			}
-			srcJarFile := genSysprop(ctx, srcFile, scope)
-			outSrcFiles = append(outSrcFiles, srcJarFile)
+			protoSrcs = append(protoSrcs, srcFile)
 		default:
 			outSrcFiles = append(outSrcFiles, srcFile)
 		}
 	}
 
+	// Process all proto files together to support sharding them into one or more rules that produce srcjars.
+	if len(protoSrcs) > 0 {
+		srcJarFiles := genProto(ctx, protoSrcs, flags.proto)
+		outSrcFiles = append(outSrcFiles, srcJarFiles...)
+	}
+
+	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
+	if len(aidlSrcs) > 0 {
+		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+		outSrcFiles = append(outSrcFiles, srcJarFiles...)
+	}
+
 	return outSrcFiles
 }
 
diff --git a/java/java.go b/java/java.go
index 9c7f845..52f3c11 100644
--- a/java/java.go
+++ b/java/java.go
@@ -184,10 +184,6 @@
 		Output_params []string
 	}
 
-	Sysprop struct {
-		Platform *bool
-	} `blueprint:"mutated"`
-
 	Instrument bool `blueprint:"mutated"`
 
 	// List of files to include in the META-INF/services folder of the resulting jar.
@@ -274,7 +270,8 @@
 		Proguard_flags_files []string `android:"path"`
 	}
 
-	// When targeting 1.9, override the modules to use with --system
+	// When targeting 1.9 and above, override the modules to use with --system,
+	// otherwise provides defaults libraries to add to the bootclasspath.
 	System_modules *string
 
 	UncompressDex bool `blueprint:"mutated"`
@@ -290,6 +287,7 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.ApexModuleBase
+	android.SdkBase
 
 	properties       CompilerProperties
 	protoProperties  android.ProtoProperties
@@ -460,7 +458,10 @@
 type sdkDep struct {
 	useModule, useFiles, useDefaultLibs, invalidVersion bool
 
-	modules       []string
+	modules []string
+
+	// The default system modules to use. Will be an empty string if no system
+	// modules are to be used.
 	systemModules string
 
 	frameworkResModule string
@@ -499,6 +500,10 @@
 	return proptools.StringDefault(j.deviceProperties.Sdk_version, defaultSdkVersion(j))
 }
 
+func (j *Module) systemModules() string {
+	return proptools.String(j.deviceProperties.System_modules)
+}
+
 func (j *Module) minSdkVersion() string {
 	if j.deviceProperties.Min_sdk_version != nil {
 		return *j.deviceProperties.Min_sdk_version
@@ -531,11 +536,10 @@
 					ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
 				}
 			}
-		} else if j.deviceProperties.System_modules == nil {
-			ctx.PropertyErrorf("sdk_version",
-				`system_modules is required to be set when sdk_version is "none", did you mean "core_platform"`)
-		} else if *j.deviceProperties.System_modules != "none" {
-			ctx.AddVariationDependencies(nil, systemModulesTag, *j.deviceProperties.System_modules)
+		} else if sdkDep.systemModules != "" {
+			// Add the system modules to both the system modules and bootclasspath.
+			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.systemModules)
 		}
 		if ctx.ModuleName() == "android_stubs_current" ||
 			ctx.ModuleName() == "android_system_stubs_current" ||
@@ -581,18 +585,6 @@
 	return false
 }
 
-func shardPaths(paths android.Paths, shardSize int) []android.Paths {
-	ret := make([]android.Paths, 0, (len(paths)+shardSize-1)/shardSize)
-	for len(paths) > shardSize {
-		ret = append(ret, paths[0:shardSize])
-		paths = paths[shardSize:]
-	}
-	if len(paths) > 0 {
-		ret = append(ret, paths)
-	}
-	return ret
-}
-
 func (j *Module) hasSrcExt(ext string) bool {
 	return hasSrcExt(j.properties.Srcs, ext)
 }
@@ -848,6 +840,12 @@
 			}
 		default:
 			switch tag {
+			case bootClasspathTag:
+				// If a system modules dependency has been added to the bootclasspath
+				// then add its libs to the bootclasspath.
+				sm := module.(*SystemModules)
+				deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...)
+
 			case systemModulesTag:
 				if deps.systemModules != nil {
 					panic("Found two system module dependencies")
@@ -1154,7 +1152,7 @@
 			shardSize := int(*(j.properties.Javac_shard_size))
 			var shardSrcs []android.Paths
 			if len(uniqueSrcFiles) > 0 {
-				shardSrcs = shardPaths(uniqueSrcFiles, shardSize)
+				shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize)
 				for idx, shardSrc := range shardSrcs {
 					classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
 						nil, flags, extraJarDeps)
@@ -1636,6 +1634,7 @@
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 	return module
 }
 
@@ -1682,6 +1681,11 @@
 	// list of files or filegroup modules that provide data that should be installed alongside
 	// the test
 	Data []string `android:"path"`
+
+	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+	// explicitly.
+	Auto_gen_config *bool
 }
 
 type testHelperLibraryProperties struct {
@@ -1706,7 +1710,8 @@
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, j.testProperties.Test_suites)
+	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
+		j.testProperties.Test_suites, j.testProperties.Auto_gen_config)
 	j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
 
 	j.Library.GenerateAndroidBuildActions(ctx)
@@ -1798,7 +1803,7 @@
 	isWrapperVariant bool
 
 	wrapperFile android.Path
-	binaryFile  android.OutputPath
+	binaryFile  android.InstallPath
 }
 
 func (j *Binary) HostToolPath() android.OptionalPath {
@@ -1916,6 +1921,7 @@
 	android.DefaultableModuleBase
 	android.ApexModuleBase
 	prebuilt android.Prebuilt
+	android.SdkBase
 
 	properties ImportProperties
 
@@ -2072,6 +2078,7 @@
 	android.InitPrebuiltModule(module, &module.properties.Jars)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 	return module
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index a932319..f0cb6f8 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -18,6 +18,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 	"strconv"
 	"strings"
 	"testing"
@@ -811,19 +812,22 @@
 		}
 		`)
 
-	inputs := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc").Inputs
+	barDoc := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc")
 	var javaSrcs []string
-	for _, i := range inputs {
+	for _, i := range barDoc.Inputs {
 		javaSrcs = append(javaSrcs, i.Base())
 	}
-	if len(javaSrcs) != 3 || javaSrcs[0] != "a.java" || javaSrcs[1] != "IFoo.java" || javaSrcs[2] != "IBar.java" {
-		t.Errorf("inputs of bar-doc must be []string{\"a.java\", \"IFoo.java\", \"IBar.java\", but was %#v.", javaSrcs)
+	if len(javaSrcs) != 1 || javaSrcs[0] != "a.java" {
+		t.Errorf("inputs of bar-doc must be []string{\"a.java\"}, but was %#v.", javaSrcs)
 	}
 
-	aidlRule := ctx.ModuleForTests("bar-doc", "android_common").Output(inputs[2].String())
-	aidlFlags := aidlRule.Args["aidlFlags"]
-	if !strings.Contains(aidlFlags, "-Ibar-doc") {
-		t.Errorf("aidl flags for IBar.aidl should contain \"-Ibar-doc\", but was %q", aidlFlags)
+	aidl := ctx.ModuleForTests("bar-doc", "android_common").Rule("aidl")
+	if g, w := barDoc.Implicits.Strings(), aidl.Output.String(); !inList(w, g) {
+		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
+	}
+
+	if g, w := aidl.Implicits.Strings(), []string{"bar-doc/IBar.aidl", "bar-doc/IFoo.aidl"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("aidl inputs must be %q, but was %q", w, g)
 	}
 }
 
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 3d46077..23ba2b0 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -30,7 +30,7 @@
 	android.ModuleBase
 
 	properties     platformCompatConfigProperties
-	installDirPath android.OutputPath
+	installDirPath android.InstallPath
 	configFile     android.OutputPath
 }
 
@@ -78,7 +78,7 @@
 		Include:    "$(BUILD_PREBUILT)",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
 			},
 		},
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c370811..0d5e31f 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -82,7 +82,7 @@
 	props.Sdk_version = proptools.StringPtr("current")
 	props.Installable = proptools.BoolPtr(false)
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(ImportFactory), &props)
+	mctx.CreateModule(ImportFactory, &props)
 }
 
 func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
@@ -93,7 +93,7 @@
 	}{}
 	filegroupProps.Name = proptools.StringPtr(fgName)
 	filegroupProps.Srcs = []string{path}
-	mctx.CreateModule(android.ModuleFactoryAdaptor(android.FileGroupFactory), &filegroupProps)
+	mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
 }
 
 func getPrebuiltFiles(mctx android.TopDownMutatorContext, name string) []string {
diff --git a/java/proto.go b/java/proto.go
index f5c233c..e013bb4 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -15,36 +15,61 @@
 package java
 
 import (
+	"path/filepath"
+	"strconv"
+
 	"android/soong/android"
 )
 
-func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags) android.Path {
-	srcJarFile := android.GenPathWithExt(ctx, "proto", protoFile, "srcjar")
+func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths {
+	// Shard proto files into groups of 100 to avoid having to recompile all of them if one changes and to avoid
+	// hitting command line length limits.
+	shards := android.ShardPaths(protoFiles, 100)
 
-	outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
-	depFile := srcJarFile.ReplaceExtension(ctx, "srcjar.d")
+	srcJarFiles := make(android.Paths, 0, len(shards))
 
-	rule := android.NewRuleBuilder()
+	for i, shard := range shards {
+		srcJarFile := android.PathForModuleGen(ctx, "proto", "proto"+strconv.Itoa(i)+".srcjar")
+		srcJarFiles = append(srcJarFiles, srcJarFile)
 
-	rule.Command().Text("rm -rf").Flag(outDir.String())
-	rule.Command().Text("mkdir -p").Flag(outDir.String())
+		outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
 
-	android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+		rule := android.NewRuleBuilder()
 
-	// Proto generated java files have an unknown package name in the path, so package the entire output directory
-	// into a srcjar.
-	rule.Command().
-		BuiltTool(ctx, "soong_zip").
-		Flag("-jar").
-		FlagWithOutput("-o ", srcJarFile).
-		FlagWithArg("-C ", outDir.String()).
-		FlagWithArg("-D ", outDir.String())
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+		rule.Command().Text("mkdir -p").Flag(outDir.String())
 
-	rule.Command().Text("rm -rf").Flag(outDir.String())
+		for _, protoFile := range shard {
+			depFile := srcJarFile.InSameDir(ctx, protoFile.String()+".d")
+			rule.Command().Text("mkdir -p").Flag(filepath.Dir(depFile.String()))
+			android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+		}
 
-	rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
+		// Proto generated java files have an unknown package name in the path, so package the entire output directory
+		// into a srcjar.
+		rule.Command().
+			BuiltTool(ctx, "soong_zip").
+			Flag("-jar").
+			Flag("-write_if_changed").
+			FlagWithOutput("-o ", srcJarFile).
+			FlagWithArg("-C ", outDir.String()).
+			FlagWithArg("-D ", outDir.String())
 
-	return srcJarFile
+		rule.Command().Text("rm -rf").Flag(outDir.String())
+
+		rule.Restat()
+
+		ruleName := "protoc"
+		ruleDesc := "protoc"
+		if len(shards) > 1 {
+			ruleName += "_" + strconv.Itoa(i)
+			ruleDesc += " " + strconv.Itoa(i)
+		}
+
+		rule.Build(pctx, ctx, ruleName, ruleDesc)
+	}
+
+	return srcJarFiles
 }
 
 func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
diff --git a/java/robolectric.go b/java/robolectric.go
index 9669e1b..b7646eb 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -123,25 +123,6 @@
 	}
 }
 
-func shardTests(paths []string, shards int) [][]string {
-	if shards > len(paths) {
-		shards = len(paths)
-	}
-	if shards == 0 {
-		return nil
-	}
-	ret := make([][]string, 0, shards)
-	shardSize := (len(paths) + shards - 1) / shards
-	for len(paths) > shardSize {
-		ret = append(ret, paths[0:shardSize])
-		paths = paths[shardSize:]
-	}
-	if len(paths) > 0 {
-		ret = append(ret, paths)
-	}
-	return ret
-}
-
 func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, instrumentedApp *AndroidApp) {
 	manifest := instrumentedApp.mergedManifestFile
 	resourceApk := instrumentedApp.outputFile
@@ -177,32 +158,34 @@
 	TransformResourcesToJar(ctx, outputFile, srcJarArgs, srcJarDeps)
 }
 
-func (r *robolectricTest) AndroidMk() android.AndroidMkData {
-	data := r.Library.AndroidMk()
+func (r *robolectricTest) AndroidMkEntries() android.AndroidMkEntries {
+	entries := r.Library.AndroidMkEntries()
 
-	data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-		android.WriteAndroidMkData(w, data)
+	entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
+		func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+			if s := r.robolectricProperties.Test_options.Shards; s != nil && *s > 1 {
+				numShards := int(*s)
+				shardSize := (len(r.tests) + numShards - 1) / numShards
+				shards := android.ShardStrings(r.tests, shardSize)
+				for i, shard := range shards {
+					r.writeTestRunner(w, name, "Run"+name+strconv.Itoa(i), shard)
+				}
 
-		if s := r.robolectricProperties.Test_options.Shards; s != nil && *s > 1 {
-			shards := shardTests(r.tests, int(*s))
-			for i, shard := range shards {
-				r.writeTestRunner(w, name, "Run"+name+strconv.Itoa(i), shard)
+				// TODO: add rules to dist the outputs of the individual tests, or combine them together?
+				fmt.Fprintln(w, "")
+				fmt.Fprintln(w, ".PHONY:", "Run"+name)
+				fmt.Fprintln(w, "Run"+name, ": \\")
+				for i := range shards {
+					fmt.Fprintln(w, "   ", "Run"+name+strconv.Itoa(i), "\\")
+				}
+				fmt.Fprintln(w, "")
+			} else {
+				r.writeTestRunner(w, name, "Run"+name, r.tests)
 			}
-
-			// TODO: add rules to dist the outputs of the individual tests, or combine them together?
-			fmt.Fprintln(w, "")
-			fmt.Fprintln(w, ".PHONY:", "Run"+name)
-			fmt.Fprintln(w, "Run"+name, ": \\")
-			for i := range shards {
-				fmt.Fprintln(w, "   ", "Run"+name+strconv.Itoa(i), "\\")
-			}
-			fmt.Fprintln(w, "")
-		} else {
-			r.writeTestRunner(w, name, "Run"+name, r.tests)
-		}
+		},
 	}
 
-	return data
+	return entries
 }
 
 func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) {
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
deleted file mode 100644
index e89c6e7..0000000
--- a/java/robolectric_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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 java
-
-import (
-	"reflect"
-	"testing"
-)
-
-func Test_shardTests(t *testing.T) {
-	type args struct {
-		paths  []string
-		shards int
-	}
-	tests := []struct {
-		name string
-		args args
-		want [][]string
-	}{
-		{
-			name: "empty",
-			args: args{
-				paths:  nil,
-				shards: 1,
-			},
-			want: [][]string(nil),
-		},
-		{
-			name: "too many shards",
-			args: args{
-				paths:  []string{"a", "b"},
-				shards: 3,
-			},
-			want: [][]string{{"a"}, {"b"}},
-		},
-		{
-			name: "single shard",
-			args: args{
-				paths:  []string{"a", "b"},
-				shards: 1,
-			},
-			want: [][]string{{"a", "b"}},
-		},
-		{
-			name: "shard per input",
-			args: args{
-				paths:  []string{"a", "b", "c"},
-				shards: 3,
-			},
-			want: [][]string{{"a"}, {"b"}, {"c"}},
-		},
-		{
-			name: "balanced shards",
-			args: args{
-				paths:  []string{"a", "b", "c", "d"},
-				shards: 2,
-			},
-			want: [][]string{{"a", "b"}, {"c", "d"}},
-		},
-		{
-			name: "unbalanced shards",
-			args: args{
-				paths:  []string{"a", "b", "c"},
-				shards: 2,
-			},
-			want: [][]string{{"a", "b"}, {"c"}},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := shardTests(tt.args.paths, tt.args.shards); !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("shardTests() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
diff --git a/java/sdk.go b/java/sdk.go
index 3451774..c6a9a73 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -39,6 +39,8 @@
 type sdkContext interface {
 	// sdkVersion returns the sdk_version property of the current module, or an empty string if it is not set.
 	sdkVersion() string
+	// 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.
@@ -185,8 +187,18 @@
 			frameworkResModule: "framework-res",
 		}
 	case "none":
+		systemModules := sdkContext.systemModules()
+		if systemModules == "" {
+			ctx.PropertyErrorf("sdk_version",
+				`system_modules is required to be set to a non-empty value when sdk_version is "none", did you mean sdk_version: "core_platform"?`)
+		} else if systemModules == "none" {
+			// Normalize no system modules to an empty string.
+			systemModules = ""
+		}
+
 		return sdkDep{
 			noStandardLibs: true,
+			systemModules:  systemModules,
 		}
 	case "core_platform":
 		return sdkDep{
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 30fd1c4..476e549 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -197,65 +197,65 @@
 	})
 }
 
-func (module *SdkLibrary) AndroidMk() android.AndroidMkData {
-	data := module.Library.AndroidMk()
-	data.Required = append(data.Required, module.xmlFileName())
+func (module *SdkLibrary) AndroidMkEntries() android.AndroidMkEntries {
+	entries := module.Library.AndroidMkEntries()
+	entries.Required = append(entries.Required, module.xmlFileName())
 
-	data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-		android.WriteAndroidMkData(w, data)
-
-		module.Library.AndroidMkHostDex(w, name, data)
-		if !Bool(module.sdkLibraryProperties.No_dist) {
-			// Create a phony module that installs the impl library, for the case when this lib is
-			// in PRODUCT_PACKAGES.
-			owner := module.ModuleBase.Owner()
-			if owner == "" {
-				if Bool(module.sdkLibraryProperties.Core_lib) {
-					owner = "core"
-				} else {
-					owner = "android"
+	entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
+		func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+			module.Library.AndroidMkHostDex(w, name, entries)
+			if !Bool(module.sdkLibraryProperties.No_dist) {
+				// Create a phony module that installs the impl library, for the case when this lib is
+				// in PRODUCT_PACKAGES.
+				owner := module.ModuleBase.Owner()
+				if owner == "" {
+					if Bool(module.sdkLibraryProperties.Core_lib) {
+						owner = "core"
+					} else {
+						owner = "android"
+					}
+				}
+				// Create dist rules to install the stubs libs to the dist dir
+				if len(module.publicApiStubsPath) == 1 {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.publicApiStubsImplPath.Strings()[0]+
+						":"+path.Join("apistubs", owner, "public",
+						module.BaseModuleName()+".jar")+")")
+				}
+				if len(module.systemApiStubsPath) == 1 {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.systemApiStubsImplPath.Strings()[0]+
+						":"+path.Join("apistubs", owner, "system",
+						module.BaseModuleName()+".jar")+")")
+				}
+				if len(module.testApiStubsPath) == 1 {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.testApiStubsImplPath.Strings()[0]+
+						":"+path.Join("apistubs", owner, "test",
+						module.BaseModuleName()+".jar")+")")
+				}
+				if module.publicApiFilePath != nil {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.publicApiFilePath.String()+
+						":"+path.Join("apistubs", owner, "public", "api",
+						module.BaseModuleName()+".txt")+")")
+				}
+				if module.systemApiFilePath != nil {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.systemApiFilePath.String()+
+						":"+path.Join("apistubs", owner, "system", "api",
+						module.BaseModuleName()+".txt")+")")
+				}
+				if module.testApiFilePath != nil {
+					fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+						module.testApiFilePath.String()+
+						":"+path.Join("apistubs", owner, "test", "api",
+						module.BaseModuleName()+".txt")+")")
 				}
 			}
-			// Create dist rules to install the stubs libs to the dist dir
-			if len(module.publicApiStubsPath) == 1 {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.publicApiStubsImplPath.Strings()[0]+
-					":"+path.Join("apistubs", owner, "public",
-					module.BaseModuleName()+".jar")+")")
-			}
-			if len(module.systemApiStubsPath) == 1 {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.systemApiStubsImplPath.Strings()[0]+
-					":"+path.Join("apistubs", owner, "system",
-					module.BaseModuleName()+".jar")+")")
-			}
-			if len(module.testApiStubsPath) == 1 {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.testApiStubsImplPath.Strings()[0]+
-					":"+path.Join("apistubs", owner, "test",
-					module.BaseModuleName()+".jar")+")")
-			}
-			if module.publicApiFilePath != nil {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.publicApiFilePath.String()+
-					":"+path.Join("apistubs", owner, "public", "api",
-					module.BaseModuleName()+".txt")+")")
-			}
-			if module.systemApiFilePath != nil {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.systemApiFilePath.String()+
-					":"+path.Join("apistubs", owner, "system", "api",
-					module.BaseModuleName()+".txt")+")")
-			}
-			if module.testApiFilePath != nil {
-				fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
-					module.testApiFilePath.String()+
-					":"+path.Join("apistubs", owner, "test", "api",
-					module.BaseModuleName()+".txt")+")")
-			}
-		}
+		},
 	}
-	return data
+	return entries
 }
 
 // Module name of the stubs library
@@ -422,7 +422,7 @@
 		props.Product_specific = proptools.BoolPtr(true)
 	}
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory), &props)
+	mctx.CreateModule(LibraryFactory, &props)
 }
 
 // Creates a droiddoc module that creates stubs source files from the given full source
@@ -522,7 +522,7 @@
 		module.latestRemovedApiFilegroupName(apiScope))
 	props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(DroidstubsFactory), &props)
+	mctx.CreateModule(DroidstubsFactory, &props)
 }
 
 // Creates the xml file that publicizes the runtime library
@@ -560,7 +560,7 @@
 	genruleProps.Name = proptools.StringPtr(module.xmlFileName() + "-gen")
 	genruleProps.Cmd = proptools.StringPtr("echo '" + xmlContent + "' > $(out)")
 	genruleProps.Out = []string{module.xmlFileName()}
-	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProps)
+	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
 
 	// creates a prebuilt_etc module to actually place the xml file under
 	// <partition>/etc/permissions
@@ -582,7 +582,7 @@
 	} else if module.ProductSpecific() {
 		etcProps.Product_specific = proptools.BoolPtr(true)
 	}
-	mctx.CreateModule(android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory), &etcProps)
+	mctx.CreateModule(android.PrebuiltEtcFactory, &etcProps)
 }
 
 func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
@@ -815,7 +815,7 @@
 		props.Product_specific = proptools.BoolPtr(true)
 	}
 
-	mctx.CreateModule(android.ModuleFactoryAdaptor(ImportFactory), &props, &module.properties)
+	mctx.CreateModule(ImportFactory, &props, &module.properties)
 
 	javaSdkLibraries := javaSdkLibraries(mctx.Config())
 	javaSdkLibrariesLock.Lock()
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 6be17eb..5001b47 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -124,7 +124,7 @@
 			name:          "nostdlib system_modules",
 			properties:    `sdk_version: "none", system_modules: "core-platform-api-stubs-system-modules"`,
 			system:        "core-platform-api-stubs-system-modules",
-			bootclasspath: []string{`""`},
+			bootclasspath: []string{"core-platform-api-stubs-system-modules-lib"},
 			classpath:     []string{},
 		},
 		{
@@ -250,7 +250,10 @@
 			}
 
 			checkClasspath := func(t *testing.T, ctx *android.TestContext) {
-				javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+				foo := ctx.ModuleForTests("foo", variant)
+				javac := foo.Rule("javac")
+
+				aidl := foo.MaybeRule("aidl")
 
 				got := javac.Args["bootClasspath"]
 				if got != bc {
@@ -263,6 +266,9 @@
 				}
 
 				var deps []string
+				if aidl.Rule != nil {
+					deps = append(deps, aidl.Output.String())
+				}
 				if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
 					deps = append(deps, bootclasspath...)
 				}
@@ -290,12 +296,8 @@
 				if testcase.host != android.Host {
 					aidl := ctx.ModuleForTests("foo", variant).Rule("aidl")
 
-					aidlFlags := aidl.Args["aidlFlags"]
-					// Trim trailing "-I." to avoid having to specify it in every test
-					aidlFlags = strings.TrimSpace(strings.TrimSuffix(aidlFlags, "-I."))
-
-					if g, w := aidlFlags, testcase.aidl; g != w {
-						t.Errorf("want aidl flags %q, got %q", w, g)
+					if g, w := aidl.RuleParams.Command, testcase.aidl+" -I."; !strings.Contains(g, w) {
+						t.Errorf("want aidl command to contain %q, got %q", w, g)
 					}
 				}
 			})
diff --git a/java/system_modules.go b/java/system_modules.go
index c616249..b56a401 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -36,13 +36,15 @@
 var (
 	jarsTosystemModules = pctx.AndroidStaticRule("jarsTosystemModules", blueprint.RuleParams{
 		Command: `rm -rf ${outDir} ${workDir} && mkdir -p ${workDir}/jmod && ` +
-			`${moduleInfoJavaPath} ${moduleName} $in > ${workDir}/module-info.java && ` +
+			`${moduleInfoJavaPath} java.base $in > ${workDir}/module-info.java && ` +
 			`${config.JavacCmd} --system=none --patch-module=java.base=${classpath} ${workDir}/module-info.java && ` +
 			`${config.SoongZipCmd} -jar -o ${workDir}/classes.jar -C ${workDir} -f ${workDir}/module-info.class && ` +
 			`${config.MergeZipsCmd} -j ${workDir}/module.jar ${workDir}/classes.jar $in && ` +
-			`${config.JmodCmd} create --module-version 9 --target-platform android ` +
-			`  --class-path ${workDir}/module.jar ${workDir}/jmod/${moduleName}.jmod && ` +
-			`${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules ${moduleName} --output ${outDir} ` +
+			// Note: The version of the java.base module created must match the version
+			// of the jlink tool which consumes it.
+			`${config.JmodCmd} create --module-version ${config.JlinkVersion} --target-platform android ` +
+			`  --class-path ${workDir}/module.jar ${workDir}/jmod/java.base.jmod && ` +
+			`${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules java.base --output ${outDir} ` +
 			// Note: The system-modules jlink plugin is disabled because (a) it is not
 			// useful on Android, and (b) it causes errors with later versions of jlink
 			// when the jdk.internal.module is absent from java.base (as it is here).
@@ -58,10 +60,10 @@
 			"${config.JrtFsJar}",
 		},
 	},
-		"moduleName", "classpath", "outDir", "workDir")
+		"classpath", "outDir", "workDir")
 )
 
-func TransformJarsToSystemModules(ctx android.ModuleContext, moduleName string, jars android.Paths) (android.Path, android.Paths) {
+func TransformJarsToSystemModules(ctx android.ModuleContext, jars android.Paths) (android.Path, android.Paths) {
 	outDir := android.PathForModuleOut(ctx, "system")
 	workDir := android.PathForModuleOut(ctx, "modules")
 	outputFile := android.PathForModuleOut(ctx, "system/lib/modules")
@@ -77,10 +79,9 @@
 		Outputs:     outputs,
 		Inputs:      jars,
 		Args: map[string]string{
-			"moduleName": moduleName,
-			"classpath":  strings.Join(jars.Strings(), ":"),
-			"workDir":    workDir.String(),
-			"outDir":     outDir.String(),
+			"classpath": strings.Join(jars.Strings(), ":"),
+			"workDir":   workDir.String(),
+			"outDir":    outDir.String(),
 		},
 	})
 
@@ -101,6 +102,9 @@
 
 	properties SystemModulesProperties
 
+	// The aggregated header jars from all jars specified in the libs property.
+	// Used when system module is added as a dependency to bootclasspath.
+	headerJars android.Paths
 	outputDir  android.Path
 	outputDeps android.Paths
 }
@@ -118,7 +122,9 @@
 		jars = append(jars, dep.HeaderJars()...)
 	})
 
-	system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, "java.base", jars)
+	system.headerJars = jars
+
+	system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, jars)
 }
 
 func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
diff --git a/java/testing.go b/java/testing.go
index a37c0a9..acbefb9 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -103,7 +103,6 @@
 	`
 
 	systemModules := []string{
-		"core-system-modules",
 		"core-current-stubs-system-modules",
 		"core-platform-api-stubs-system-modules",
 		"android_stubs_current_system_modules",
@@ -114,7 +113,13 @@
 	for _, extra := range systemModules {
 		bp += fmt.Sprintf(`
 			java_system_modules {
-				name: "%s",
+				name: "%[1]s",
+				libs: ["%[1]s-lib"],
+			}
+			java_library {
+				name: "%[1]s-lib",
+				sdk_version: "none",
+				system_modules: "none",
 			}
 		`, extra)
 	}
diff --git a/makedeps/deps.go b/makedeps/deps.go
index e64e6f7..db49532 100644
--- a/makedeps/deps.go
+++ b/makedeps/deps.go
@@ -57,10 +57,12 @@
 				return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Target.Dump())
 			}
 			outputs := x.Target.Words()
-			if len(outputs) == 0 {
-				return nil, fmt.Errorf("%smissing output: %v", pos(node), x)
+			if len(outputs) > 0 {
+				ret.Output = outputs[0].Value(nil)
+			} else {
+				// TODO(b/141372861): put this back
+				//return nil, fmt.Errorf("%smissing output: %v", pos(node), x)
 			}
-			ret.Output = outputs[0].Value(nil)
 
 			if !x.Prerequisites.Const() {
 				return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Prerequisites.Dump())
diff --git a/makedeps/deps_test.go b/makedeps/deps_test.go
index a32df65..ac2f699 100644
--- a/makedeps/deps_test.go
+++ b/makedeps/deps_test.go
@@ -147,6 +147,20 @@
 				},
 			},
 		},
+		{
+			// TODO(b/141372861): remove this
+			// AIDL produces a dep file with no output file for a parcelable (b/
+			name: "AIDL parcelable",
+			input: ` : \
+  frameworks/base/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
+`,
+			output: Deps{
+				Output: "",
+				Inputs: []string{
+					"frameworks/base/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl",
+				},
+			},
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/python/androidmk.go b/python/androidmk.go
index 1e51e7b..aae7ced 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -89,12 +89,11 @@
 
 	ret.Required = append(ret.Required, "libc++")
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		path := installer.path.RelPathString()
-		dir, file := filepath.Split(path)
+		path, file := filepath.Split(installer.path.ToMakePath().String())
 		stem := strings.TrimSuffix(file, filepath.Ext(file))
 
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file))
-		fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 		fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(installer.androidMkSharedLibs, " "))
 	})
diff --git a/python/binary.go b/python/binary.go
index 140f07a..695fa12 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -47,6 +47,11 @@
 	// false it will act much like the normal `python` executable, but with the sources and
 	// libraries automatically included in the PYTHONPATH.
 	Autorun *bool `android:"arch_variant"`
+
+	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+	// explicitly.
+	Auto_gen_config *bool
 }
 
 type binaryDecorator struct {
diff --git a/python/installer.go b/python/installer.go
index 62f36f4..396f036 100644
--- a/python/installer.go
+++ b/python/installer.go
@@ -33,7 +33,7 @@
 	dir64    string
 	relative string
 
-	path android.OutputPath
+	path android.InstallPath
 
 	androidMkSharedLibs []string
 }
@@ -47,12 +47,12 @@
 
 var _ installer = (*pythonInstaller)(nil)
 
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.OutputPath {
+func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
 	dir := installer.dir
 	if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
 		dir = installer.dir64
 	}
-	if !ctx.Host() && !ctx.Arch().Native {
+	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 	return android.PathForModuleInstall(ctx, dir, installer.relative)
diff --git a/python/test.go b/python/test.go
index 55b0ab5..f684fd5 100644
--- a/python/test.go
+++ b/python/test.go
@@ -50,7 +50,8 @@
 
 func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
 	test.testConfig = tradefed.AutoGenPythonBinaryHostTestConfig(ctx, test.testProperties.Test_config,
-		test.testProperties.Test_config_template, test.binaryDecorator.binaryProperties.Test_suites)
+		test.testProperties.Test_config_template, test.binaryDecorator.binaryProperties.Test_suites,
+		test.binaryDecorator.binaryProperties.Auto_gen_config)
 
 	test.binaryDecorator.pythonInstaller.dir = "nativetest"
 	test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 107959f..a6208db 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -116,11 +116,10 @@
 		ret.OutputFile = android.OptionalPathForPath(compiler.path)
 	}
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-		path := compiler.path.RelPathString()
-		dir, file := filepath.Split(path)
+		path, file := filepath.Split(compiler.path.ToMakePath().String())
 		stem, suffix, _ := android.SplitFileExt(file)
 		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
-		fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
 		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 	})
 }
diff --git a/rust/binary.go b/rust/binary.go
index 279c6f5..52f840e 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -71,6 +71,15 @@
 
 func (binary *binaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = binary.baseCompiler.compilerFlags(ctx, flags)
+
+	if ctx.toolchain().Bionic() {
+		// no-undefined-version breaks dylib compilation since __rust_*alloc* functions aren't defined, but we can apply this to binaries.
+		flags.LinkFlags = append(flags.LinkFlags,
+			"-Wl,--gc-sections",
+			"-Wl,-z,nocopyreloc",
+			"-Wl,--no-undefined-version")
+	}
+
 	if binary.preferDynamic() {
 		flags.RustFlags = append(flags.RustFlags, "-C prefer-dynamic")
 	}
@@ -86,6 +95,12 @@
 		}
 	}
 
+	if ctx.toolchain().Bionic() {
+		deps = binary.baseCompiler.bionicDeps(ctx, deps)
+		deps.CrtBegin = "crtbegin_dynamic"
+		deps.CrtEnd = "crtend_android"
+	}
+
 	return deps
 }
 
diff --git a/rust/builder.go b/rust/builder.go
index 64e387b..104313f 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -28,14 +28,13 @@
 		blueprint.RuleParams{
 			Command: "$rustcCmd " +
 				"-C linker=${config.RustLinker} " +
-				"-C link-args=\"${config.RustLinkerArgs} ${linkFlags}\" " +
-				"-o $out $in ${libFlags} $rustcFlags " +
-				"&& $rustcCmd --emit=dep-info -o $out.d $in ${libFlags} $rustcFlags",
+				"-C link-args=\"${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}\" " +
+				"--emit link -o $out --emit dep-info=$out.d $in ${libFlags} $rustcFlags",
 			CommandDeps: []string{"$rustcCmd"},
 			Depfile:     "$out.d",
 			Deps:        blueprint.DepsGCC, // Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
 		},
-		"rustcFlags", "linkFlags", "libFlags")
+		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd")
 )
 
 func init() {
@@ -43,28 +42,19 @@
 }
 
 func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
-	targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
-
-	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "bin", includeDirs, targetTriple)
+	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, deps.CrtBegin, deps.CrtEnd, flags, outputFile, "bin", includeDirs)
 }
 
 func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
-	targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
-
-	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "rlib", includeDirs, targetTriple)
+	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, deps.CrtBegin, deps.CrtEnd, flags, outputFile, "rlib", includeDirs)
 }
 
 func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
-	targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
-
-	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "dylib", includeDirs, targetTriple)
+	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, deps.CrtBegin, deps.CrtEnd, flags, outputFile, "dylib", includeDirs)
 }
 
 func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
-	// Proc macros are compiler plugins, and thus should target the host compiler
-	targetTriple := ""
-
-	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "proc-macro", includeDirs, targetTriple)
+	transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, deps.CrtBegin, deps.CrtEnd, flags, outputFile, "proc-macro", includeDirs)
 }
 
 func rustLibsToPaths(libs RustLibraries) android.Paths {
@@ -76,23 +66,28 @@
 }
 
 func transformSrctoCrate(ctx android.ModuleContext, main android.Path,
-	rlibs, dylibs, proc_macros RustLibraries, static_libs, shared_libs android.Paths, flags Flags, outputFile android.WritablePath, crate_type string, includeDirs []string, targetTriple string) {
+	rlibs, dylibs, proc_macros RustLibraries, static_libs, shared_libs android.Paths, crtBegin, crtEnd android.OptionalPath, flags Flags, outputFile android.WritablePath, crate_type string, includeDirs []string) {
 
 	var inputs android.Paths
 	var deps android.Paths
-	var libFlags, rustcFlags []string
+	var libFlags, rustcFlags, linkFlags []string
 	crate_name := ctx.(ModuleContext).CrateName()
+	targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
 
 	inputs = append(inputs, main)
 
 	// Collect rustc flags
-	rustcFlags = append(rustcFlags, flags.GlobalFlags...)
+	rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
 	rustcFlags = append(rustcFlags, flags.RustFlags...)
 	rustcFlags = append(rustcFlags, "--crate-type="+crate_type)
 	rustcFlags = append(rustcFlags, "--crate-name="+crate_name)
 	if targetTriple != "" {
 		rustcFlags = append(rustcFlags, "--target="+targetTriple)
+		linkFlags = append(linkFlags, "-target "+targetTriple)
 	}
+	// Collect linker flags
+	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
+	linkFlags = append(linkFlags, flags.LinkFlags...)
 
 	// Collect library/crate flags
 	for _, lib := range rlibs {
@@ -115,6 +110,9 @@
 	deps = append(deps, rustLibsToPaths(proc_macros)...)
 	deps = append(deps, static_libs...)
 	deps = append(deps, shared_libs...)
+	if crtBegin.Valid() {
+		deps = append(deps, crtBegin.Path(), crtEnd.Path())
+	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rustc,
@@ -124,8 +122,10 @@
 		Implicits:   deps,
 		Args: map[string]string{
 			"rustcFlags": strings.Join(rustcFlags, " "),
-			"linkFlags":  strings.Join(flags.LinkFlags, " "),
+			"linkFlags":  strings.Join(linkFlags, " "),
 			"libFlags":   strings.Join(libFlags, " "),
+			"crtBegin":   crtBegin.String(),
+			"crtEnd":     crtEnd.String(),
 		},
 	})
 
diff --git a/rust/compiler.go b/rust/compiler.go
index 87cf08b..3f02835 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -20,19 +20,29 @@
 
 	"android/soong/android"
 	"android/soong/rust/config"
+	"github.com/google/blueprint/proptools"
 )
 
+func getEdition(compiler *baseCompiler) string {
+	return proptools.StringDefault(compiler.Properties.Edition, config.DefaultEdition)
+}
+
+func getDenyWarnings(compiler *baseCompiler) bool {
+	return BoolDefault(compiler.Properties.Deny_warnings, config.DefaultDenyWarnings)
+}
+
 func NewBaseCompiler(dir, dir64 string) *baseCompiler {
 	return &baseCompiler{
-		Properties: BaseCompilerProperties{
-			Edition: &config.DefaultEdition,
-		},
-		dir:   dir,
-		dir64: dir64,
+		Properties: BaseCompilerProperties{},
+		dir:        dir,
+		dir64:      dir64,
 	}
 }
 
 type BaseCompilerProperties struct {
+	// whether to pass "-D warnings" to rustc. Defaults to true.
+	Deny_warnings *bool
+
 	// flags to pass to rustc
 	Flags []string `android:"path,arch_variant"`
 
@@ -90,7 +100,7 @@
 	dir64    string
 	subDir   string
 	relative string
-	path     android.OutputPath
+	path     android.InstallPath
 }
 
 var _ compiler = (*baseCompiler)(nil)
@@ -109,11 +119,16 @@
 
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 
+	if getDenyWarnings(compiler) {
+		flags.RustFlags = append(flags.RustFlags, "-D warnings")
+	}
 	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
 	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
-	flags.RustFlags = append(flags.RustFlags, "--edition="+*compiler.Properties.Edition)
+	flags.RustFlags = append(flags.RustFlags, "--edition="+getEdition(compiler))
 	flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
-	flags.GlobalFlags = append(flags.GlobalFlags, ctx.toolchain().ToolchainRustFlags())
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
+	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags())
 
 	if ctx.Host() && !ctx.Windows() {
 		rpath_prefix := `\$$ORIGIN/`
@@ -148,16 +163,28 @@
 	return deps
 }
 
+func (compiler *baseCompiler) bionicDeps(ctx DepsContext, deps Deps) Deps {
+	deps.SharedLibs = append(deps.SharedLibs, "liblog")
+	deps.SharedLibs = append(deps.SharedLibs, "libc")
+	deps.SharedLibs = append(deps.SharedLibs, "libm")
+	deps.SharedLibs = append(deps.SharedLibs, "libdl")
+
+	//TODO(b/141331117) libstd requires libgcc on Android
+	deps.StaticLibs = append(deps.StaticLibs, "libgcc")
+
+	return deps
+}
+
 func (compiler *baseCompiler) crateName() string {
 	return compiler.Properties.Crate_name
 }
 
-func (compiler *baseCompiler) installDir(ctx ModuleContext) android.OutputPath {
+func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath {
 	dir := compiler.dir
 	if ctx.toolchain().Is64Bit() && compiler.dir64 != "" {
 		dir = compiler.dir64
 	}
-	if (!ctx.Host() && !ctx.Arch().Native) || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+	if !ctx.Host() || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 	return android.PathForModuleInstall(ctx, dir, compiler.subDir,
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 5369096..bbf9f8d 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -64,7 +64,6 @@
 		rust_proc_macro {
 			name: "foo-bar-proc-macro",
 			srcs: ["foo.rs", "src/bar.rs"],
-			host_supported: true,
 		}`)
 
 	// Test prebuilts
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
new file mode 100644
index 0000000..0264052
--- /dev/null
+++ b/rust/config/arm64_device.go
@@ -0,0 +1,92 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	Arm64RustFlags            = []string{}
+	Arm64ArchFeatureRustFlags = map[string][]string{}
+	Arm64LinkFlags            = []string{
+		"-Wl,--icf=safe",
+		"-Wl,-z,max-page-size=4096",
+
+		"-Wl,-execute-only",
+	}
+
+	Arm64ArchVariantRustFlags = map[string][]string{
+		"armv8-a":  []string{},
+		"armv8-2a": []string{},
+	}
+)
+
+func init() {
+	registerToolchainFactory(android.Android, android.Arm64, Arm64ToolchainFactory)
+
+	pctx.StaticVariable("Arm64ToolchainRustFlags", strings.Join(Arm64RustFlags, " "))
+	pctx.StaticVariable("Arm64ToolchainLinkFlags", strings.Join(Arm64LinkFlags, " "))
+
+	for variant, rustFlags := range Arm64ArchVariantRustFlags {
+		pctx.StaticVariable("Arm64"+variant+"VariantRustFlags",
+			strings.Join(rustFlags, " "))
+	}
+
+}
+
+type toolchainArm64 struct {
+	toolchain64Bit
+	toolchainRustFlags string
+}
+
+func (t *toolchainArm64) RustTriple() string {
+	return "aarch64-linux-android"
+}
+
+func (t *toolchainArm64) ToolchainLinkFlags() string {
+	return "${config.DeviceGlobalLinkFlags} ${config.Arm64ToolchainLinkFlags}"
+}
+
+func (t *toolchainArm64) ToolchainRustFlags() string {
+	return t.toolchainRustFlags
+}
+
+func (t *toolchainArm64) RustFlags() string {
+	return "${config.Arm64ToolchainRustFlags}"
+}
+
+func (t *toolchainArm64) Supported() bool {
+	return true
+}
+
+func Arm64ToolchainFactory(arch android.Arch) Toolchain {
+	toolchainRustFlags := []string{
+		"${config.Arm64ToolchainRustFlags}",
+		"${config.Arm64" + arch.ArchVariant + "VariantRustFlags}",
+	}
+
+	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
+
+	for _, feature := range arch.ArchFeatures {
+		toolchainRustFlags = append(toolchainRustFlags, Arm64ArchFeatureRustFlags[feature]...)
+	}
+
+	return &toolchainArm64{
+		toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+	}
+}
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
new file mode 100644
index 0000000..aedb42b
--- /dev/null
+++ b/rust/config/arm_device.go
@@ -0,0 +1,92 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	ArmRustFlags            = []string{}
+	ArmArchFeatureRustFlags = map[string][]string{}
+	ArmLinkFlags            = []string{
+		"-Wl,--icf=safe",
+		"-Wl,-m,armelf",
+	}
+
+	ArmArchVariantRustFlags = map[string][]string{
+		"armv7-a":      []string{},
+		"armv7-a-neon": []string{},
+		"armv8-a":      []string{},
+		"armv8-2a":     []string{},
+	}
+)
+
+func init() {
+	registerToolchainFactory(android.Android, android.Arm, ArmToolchainFactory)
+
+	pctx.StaticVariable("ArmToolchainRustFlags", strings.Join(ArmRustFlags, " "))
+	pctx.StaticVariable("ArmToolchainLinkFlags", strings.Join(ArmLinkFlags, " "))
+
+	for variant, rustFlags := range ArmArchVariantRustFlags {
+		pctx.StaticVariable("Arm"+variant+"VariantRustFlags",
+			strings.Join(rustFlags, " "))
+	}
+
+}
+
+type toolchainArm struct {
+	toolchain64Bit
+	toolchainRustFlags string
+}
+
+func (t *toolchainArm) RustTriple() string {
+	return "arm-linux-androideabi"
+}
+
+func (t *toolchainArm) ToolchainLinkFlags() string {
+	return "${config.DeviceGlobalLinkFlags} ${config.ArmToolchainLinkFlags}"
+}
+
+func (t *toolchainArm) ToolchainRustFlags() string {
+	return t.toolchainRustFlags
+}
+
+func (t *toolchainArm) RustFlags() string {
+	return "${config.ArmToolchainRustFlags}"
+}
+
+func (t *toolchainArm) Supported() bool {
+	return true
+}
+
+func ArmToolchainFactory(arch android.Arch) Toolchain {
+	toolchainRustFlags := []string{
+		"${config.ArmToolchainRustFlags}",
+		"${config.Arm" + arch.ArchVariant + "VariantRustFlags}",
+	}
+
+	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
+
+	for _, feature := range arch.ArchFeatures {
+		toolchainRustFlags = append(toolchainRustFlags, ArmArchFeatureRustFlags[feature]...)
+	}
+
+	return &toolchainArm{
+		toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+	}
+}
diff --git a/rust/config/global.go b/rust/config/global.go
index 903a424..7846d21 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -15,6 +15,8 @@
 package config
 
 import (
+	"strings"
+
 	"android/soong/android"
 	_ "android/soong/cc/config"
 )
@@ -30,6 +32,30 @@
 		"libterm",
 		"libtest",
 	}
+
+	DefaultDenyWarnings = true
+
+	GlobalRustFlags = []string{
+		"--remap-path-prefix $$(pwd)=",
+	}
+
+	deviceGlobalRustFlags = []string{}
+
+	deviceGlobalLinkFlags = []string{
+		"-Bdynamic",
+		"-nostdlib",
+		"-Wl,-z,noexecstack",
+		"-Wl,-z,relro",
+		"-Wl,-z,now",
+		"-Wl,--build-id=md5",
+		"-Wl,--warn-shared-textrel",
+		"-Wl,--fatal-warnings",
+
+		"-Wl,--pack-dyn-relocs=android+relr",
+		"-Wl,--use-android-relr-tags",
+		"-Wl,--no-undefined",
+		"-Wl,--hash-style=gnu",
+	}
 )
 
 func init() {
@@ -56,4 +82,7 @@
 	pctx.ImportAs("ccConfig", "android/soong/cc/config")
 	pctx.StaticVariable("RustLinker", "${ccConfig.ClangBin}/clang++")
 	pctx.StaticVariable("RustLinkerArgs", "-B ${ccConfig.ClangBin} -fuse-ld=lld")
+
+	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
+
 }
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
index a36d61b..328bca3 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -32,6 +32,8 @@
 
 	Is64Bit() bool
 	Supported() bool
+
+	Bionic() bool
 }
 
 type toolchainBase struct {
@@ -53,6 +55,10 @@
 	panic("toolchainBase cannot determine datapath width.")
 }
 
+func (toolchainBase) Bionic() bool {
+	return true
+}
+
 type toolchain64Bit struct {
 	toolchainBase
 }
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index cb6bf1a..5376e5b 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -61,6 +61,10 @@
 	return true
 }
 
+func (toolchainLinuxX8664) Bionic() bool {
+	return false
+}
+
 func (t *toolchainLinuxX8664) Name() string {
 	return "x86_64"
 }
@@ -85,6 +89,10 @@
 	return true
 }
 
+func (toolchainLinuxX86) Bionic() bool {
+	return false
+}
+
 func (t *toolchainLinuxX86) Name() string {
 	return "x86"
 }
diff --git a/rust/library.go b/rust/library.go
index 5cf8ac7..c831727 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -191,6 +191,16 @@
 		&library.MutatedProperties)
 }
 
+func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps = library.baseCompiler.compilerDeps(ctx, deps)
+
+	if ctx.toolchain().Bionic() && library.dylib() {
+		deps = library.baseCompiler.bionicDeps(ctx, deps)
+	}
+
+	return deps
+}
+
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	var outputFile android.WritablePath
 
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index d4e631b..fa69fbb 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -63,3 +63,8 @@
 
 	return srcPath
 }
+
+func (prebuilt *prebuiltLibraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
+	return deps
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 4acb06f..1a247d9 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -45,7 +45,7 @@
 var _ compiler = (*procMacroDecorator)(nil)
 
 func ProcMacroFactory() android.Module {
-	module, _ := NewProcMacro(android.HostAndDeviceSupported)
+	module, _ := NewProcMacro(android.HostSupportedNoCross)
 	return module.Init()
 }
 
diff --git a/rust/rust.go b/rust/rust.go
index 2120916..61b51e5 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -43,11 +43,12 @@
 }
 
 type Flags struct {
-	GlobalFlags   []string      // Flags that apply globally
-	RustFlags     []string      // Flags that apply to rust
-	LinkFlags     []string      // Flags that apply to linker
-	RustFlagsDeps android.Paths // Files depended on by compiler flags
-	Toolchain     config.Toolchain
+	GlobalRustFlags []string      // Flags that apply globally to rust
+	GlobalLinkFlags []string      // Flags that apply globally to linker
+	RustFlags       []string      // Flags that apply to rust
+	LinkFlags       []string      // Flags that apply to linker
+	RustFlagsDeps   android.Paths // Files depended on by compiler flags
+	Toolchain       config.Toolchain
 }
 
 type BaseProperties struct {
@@ -92,6 +93,9 @@
 	linkDirs   []string
 	depFlags   []string
 	//ReexportedDeps android.Paths
+
+	CrtBegin android.OptionalPath
+	CrtEnd   android.OptionalPath
 }
 
 type RustLibraries []RustLibrary
@@ -321,15 +325,6 @@
 		if rustDep, ok := dep.(*Module); ok {
 			//Handle Rust Modules
 
-			if rustDep.Target().Os != ctx.Os() {
-				ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
-				return
-			}
-			if rustDep.Target().Arch.ArchType != ctx.Arch().ArchType {
-				ctx.ModuleErrorf("Arch mismatch between %q and %q", ctx.ModuleName(), depName)
-				return
-			}
-
 			linkFile := rustDep.outputFile
 			if !linkFile.Valid() {
 				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
@@ -361,9 +356,6 @@
 			if lib, ok := rustDep.compiler.(*libraryDecorator); ok {
 				depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedDirs()...)
 				depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
-			} else if procMacro, ok := rustDep.compiler.(*libraryDecorator); ok {
-				depPaths.linkDirs = append(depPaths.linkDirs, procMacro.exportedDirs()...)
-				depPaths.depFlags = append(depPaths.depFlags, procMacro.exportedDepFlags()...)
 			}
 
 			// Append this dependencies output to this mod's linkDirs so they can be exported to dependencies
@@ -410,6 +402,10 @@
 				directSharedLibDeps = append(directSharedLibDeps, ccDep)
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
 				exportDep = true
+			case cc.CrtBeginDepTag():
+				depPaths.CrtBegin = linkFile
+			case cc.CrtEndDepTag():
+				depPaths.CrtEnd = linkFile
 			}
 
 			// Make sure these dependencies are propagated
@@ -491,7 +487,16 @@
 	}
 	actx.AddVariationDependencies(append(ccDepVariations, blueprint.Variation{Mutator: "link", Variation: "shared"}), cc.SharedDepTag(), deps.SharedLibs...)
 	actx.AddVariationDependencies(append(ccDepVariations, blueprint.Variation{Mutator: "link", Variation: "static"}), cc.StaticDepTag(), deps.StaticLibs...)
-	actx.AddDependency(mod, procMacroDepTag, deps.ProcMacros...)
+
+	if deps.CrtBegin != "" {
+		actx.AddVariationDependencies(ccDepVariations, cc.CrtBeginDepTag(), deps.CrtBegin)
+	}
+	if deps.CrtEnd != "" {
+		actx.AddVariationDependencies(ccDepVariations, cc.CrtEndDepTag(), deps.CrtEnd)
+	}
+
+	// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
+	actx.AddFarVariationDependencies([]blueprint.Variation{{Mutator: "arch", Variation: ctx.Config().BuildOsVariant}}, procMacroDepTag, deps.ProcMacros...)
 }
 
 func (mod *Module) Name() string {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index f7c96dd..0c8d355 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -18,6 +18,7 @@
 	"io/ioutil"
 	"os"
 	"runtime"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -150,7 +151,6 @@
 		rust_proc_macro {
 			name: "libpm",
 			srcs: ["foo.rs"],
-			host_supported: true,
 		}
 		rust_binary_host {
 			name: "fizz-buzz",
@@ -176,3 +176,28 @@
 	}
 
 }
+
+// Test to make sure proc_macros use host variants when building device modules.
+func TestProcMacroDeviceDeps(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host_rlib {
+			name: "libbar",
+			srcs: ["foo.rs"],
+		}
+		rust_proc_macro {
+			name: "libpm",
+			rlibs: ["libbar"],
+			srcs: ["foo.rs"],
+		}
+		rust_binary {
+			name: "fizz-buzz",
+			proc_macros: ["libpm"],
+			srcs: ["foo.rs"],
+		}
+	`)
+	rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc")
+
+	if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") {
+		t.Errorf("Proc_macro is not using host variant of dependent modules.")
+	}
+}
diff --git a/rust/testing.go b/rust/testing.go
index a38697f..92347f1 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/cc"
 )
 
 func GatherRequiredDepsForTest() string {
@@ -70,12 +71,101 @@
 				srcs: [""],
 				host_supported: true,
 		}
+
+		//////////////////////////////
+		// Device module requirements
+
+		toolchain_library {
+			name: "libgcc",
+			no_libcrt: true,
+			nocrt: true,
+			src: "",
+			system_shared_libs: [],
+		}
+		cc_library {
+			name: "libc",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+		}
+		cc_library {
+			name: "libm",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+		}
+		cc_library {
+			name: "libdl",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+		}
+		cc_object {
+			name: "crtbegin_dynamic",
+		}
+
+		cc_object {
+			name: "crtend_android",
+		}
+		cc_library {
+			name: "liblog",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+		}
+
+		//////////////////////////////
+		// cc module requirements
+
+		toolchain_library {
+			name: "libatomic",
+			src: "",
+		}
+		toolchain_library {
+			name: "libclang_rt.builtins-aarch64-android",
+			src: "",
+		}
+		toolchain_library {
+			name: "libgcc_stripped",
+			src: "",
+		}
+		cc_library {
+			name: "libc++_static",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+		cc_library {
+			name: "libc++demangle",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+			host_supported: false,
+		}
+		cc_library {
+			name: "libc++",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+		cc_library {
+			name: "libunwind_llvm",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
 		`
 	return bp
 }
 
 func CreateTestContext(bp string) *android.TestContext {
 	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
 	ctx.RegisterModuleType("rust_binary", android.ModuleFactoryAdaptor(RustBinaryFactory))
 	ctx.RegisterModuleType("rust_binary_host", android.ModuleFactoryAdaptor(RustBinaryHostFactory))
 	ctx.RegisterModuleType("rust_library", android.ModuleFactoryAdaptor(RustLibraryFactory))
@@ -86,9 +176,16 @@
 	ctx.RegisterModuleType("rust_library_dylib", android.ModuleFactoryAdaptor(RustLibraryDylibFactory))
 	ctx.RegisterModuleType("rust_proc_macro", android.ModuleFactoryAdaptor(ProcMacroFactory))
 	ctx.RegisterModuleType("rust_prebuilt_dylib", android.ModuleFactoryAdaptor(PrebuiltDylibFactory))
+	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+
+		ctx.BottomUp("image", cc.ImageMutator).Parallel()
+		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
+		ctx.BottomUp("version", cc.VersionMutator).Parallel()
+		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
 	})
+
 	bp = bp + GatherRequiredDepsForTest()
 
 	mockFS := map[string][]byte{
diff --git a/sdk/sdk.go b/sdk/sdk.go
new file mode 100644
index 0000000..fcb3fb7
--- /dev/null
+++ b/sdk/sdk.go
@@ -0,0 +1,172 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+	// This package doesn't depend on the apex package, but import it to make its mutators to be
+	// registered before mutators in this package. See RegisterPostDepsMutators for more details.
+	_ "android/soong/apex"
+)
+
+func init() {
+	android.RegisterModuleType("sdk", ModuleFactory)
+	android.PreDepsMutators(RegisterPreDepsMutators)
+	android.PostDepsMutators(RegisterPostDepsMutators)
+}
+
+type sdk struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties sdkProperties
+}
+
+type sdkProperties struct {
+	// The list of java_import modules that provide Java stubs for this SDK
+	Java_libs          []string
+	Native_shared_libs []string
+}
+
+// sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
+// which Mainline modules like APEX can choose to build with.
+func ModuleFactory() android.Module {
+	s := &sdk{}
+	s.AddProperties(&s.properties)
+	android.InitAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(s)
+	return s
+}
+
+func (s *sdk) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// TODO(jiyong): add build rules for creating stubs from members of this SDK
+}
+
+// RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
+// interface and the sdk module type. This function has been made public to be called by tests
+// outside of the sdk package
+func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("SdkMember", memberMutator).Parallel()
+	ctx.TopDown("SdkMember_deps", memberDepsMutator).Parallel()
+	ctx.BottomUp("SdkMemberInterVersion", memberInterVersionMutator).Parallel()
+}
+
+// RegisterPostDepshMutators registers post-deps mutators to support modules implementing SdkAware
+// interface and the sdk module type. This function has been made public to be called by tests
+// outside of the sdk package
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+	// These must run AFTER apexMutator. Note that the apex package is imported even though there is
+	// no direct dependency to the package here. sdkDepsMutator sets the SDK requirements from an
+	// APEX to its dependents. Since different versions of the same SDK can be used by different
+	// APEXes, the apex and its dependents (which includes the dependencies to the sdk members)
+	// should have been mutated for the apex before the SDK requirements are set.
+	ctx.TopDown("SdkDepsMutator", sdkDepsMutator).Parallel()
+	ctx.BottomUp("SdkDepsReplaceMutator", sdkDepsReplaceMutator).Parallel()
+}
+
+type dependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+// For dependencies from an SDK module to its members
+// e.g. mysdk -> libfoo and libbar
+var sdkMemberDepTag dependencyTag
+
+// 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 {
+	dependencyTag
+	member  string
+	version string
+}
+
+// Step 1: create dependencies from an SDK module to its members.
+func memberMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*sdk); ok {
+		mctx.AddVariationDependencies(nil, sdkMemberDepTag, m.properties.Java_libs...)
+
+		targets := mctx.MultiTargets()
+		for _, target := range targets {
+			mctx.AddFarVariationDependencies([]blueprint.Variation{
+				{Mutator: "arch", Variation: target.String()},
+				{Mutator: "image", Variation: "core"},
+				{Mutator: "link", Variation: "shared"},
+			}, sdkMemberDepTag, m.properties.Native_shared_libs...)
+		}
+	}
+}
+
+// Step 2: record that dependencies of SDK modules are members of the SDK modules
+func memberDepsMutator(mctx android.TopDownMutatorContext) {
+	if _, ok := mctx.Module().(*sdk); ok {
+		mySdkRef := android.ParseSdkRef(mctx, mctx.ModuleName(), "name")
+		mctx.VisitDirectDeps(func(child android.Module) {
+			if member, ok := child.(android.SdkAware); ok {
+				member.MakeMemberOf(mySdkRef)
+			}
+		})
+	}
+}
+
+// Step 3: create dependencies from the in-development version of an SDK member to frozen versions
+// of the same member. By having these dependencies, they are mutated for multiple Mainline modules
+// (apex and apk), each of which might want different sdks to be built with. For example, if both
+// apex A and B are referencing libfoo which is a member of sdk 'mysdk', the two APEXes can be
+// built with libfoo.mysdk.11 and libfoo.mysdk.12, respectively depending on which sdk they are
+// using.
+func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+		if !m.ContainingSdk().IsCurrentVersion() {
+			memberName := m.MemberName()
+			tag := sdkMemberVesionedDepTag{member: memberName, version: m.ContainingSdk().Version}
+			mctx.AddReverseDependency(mctx.Module(), tag, memberName)
+		}
+	}
+}
+
+// Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
+// descendants
+func sdkDepsMutator(mctx android.TopDownMutatorContext) {
+	if m, ok := mctx.Module().(android.SdkAware); ok {
+		// Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
+		// by reading its own properties like `uses_sdks`.
+		requiredSdks := m.RequiredSdks()
+		if len(requiredSdks) > 0 {
+			mctx.VisitDirectDeps(func(m android.Module) {
+				if dep, ok := m.(android.SdkAware); ok {
+					dep.BuildWithSdks(requiredSdks)
+				}
+			})
+		}
+	}
+}
+
+// Step 5: if libfoo.mysdk.11 is in the context where version 11 of mysdk is requested, the
+// versioned module is used instead of the un-versioned (in-development) module libfoo
+func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+		if sdk := m.ContainingSdk(); !sdk.IsCurrentVersion() {
+			if m.RequiredSdks().Contains(sdk) {
+				// Note that this replacement is done only for the modules that have the same
+				// variations as the current module. Since current module is already mutated for
+				// apex references in other APEXes are not affected by this replacement.
+				memberName := m.MemberName()
+				mctx.ReplaceDependencies(memberName)
+			}
+		}
+	}
+}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
new file mode 100644
index 0000000..9eca72f
--- /dev/null
+++ b/sdk/sdk_test.go
@@ -0,0 +1,318 @@
+// 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 sdk
+
+import (
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/apex"
+	"android/soong/cc"
+	"android/soong/java"
+)
+
+func testSdkContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	config := android.TestArchConfig(buildDir, nil)
+	ctx := android.NewTestArchContext()
+
+	// from android package
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
+	})
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
+		ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
+	})
+
+	// from java package
+	ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(java.AndroidAppCertificateFactory))
+	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(java.LibraryFactory))
+	ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(java.ImportFactory))
+
+	// from cc package
+	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+	ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(cc.LibrarySharedFactory))
+	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
+	ctx.RegisterModuleType("cc_prebuilt_library_shared", android.ModuleFactoryAdaptor(cc.PrebuiltSharedLibraryFactory))
+	ctx.RegisterModuleType("cc_prebuilt_library_static", android.ModuleFactoryAdaptor(cc.PrebuiltStaticLibraryFactory))
+	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
+	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("image", cc.ImageMutator).Parallel()
+		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
+		ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
+		ctx.BottomUp("test_per_src", cc.TestPerSrcMutator).Parallel()
+		ctx.BottomUp("version", cc.VersionMutator).Parallel()
+		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
+	})
+
+	// from apex package
+	ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apex.BundleFactory))
+	ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apex.ApexKeyFactory))
+	ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
+
+	// from this package
+	ctx.RegisterModuleType("sdk", android.ModuleFactoryAdaptor(ModuleFactory))
+	ctx.PreDepsMutators(RegisterPreDepsMutators)
+	ctx.PostDepsMutators(RegisterPostDepsMutators)
+
+	ctx.Register()
+
+	bp = bp + `
+		apex_key {
+			name: "myapex.key",
+			public_key: "myapex.avbpubkey",
+			private_key: "myapex.pem",
+		}
+
+		android_app_certificate {
+			name: "myapex.cert",
+			certificate: "myapex",
+		}
+	` + cc.GatherRequiredDepsForTest(android.Android)
+
+	ctx.MockFileSystem(map[string][]byte{
+		"Android.bp":                                 []byte(bp),
+		"build/make/target/product/security":         nil,
+		"apex_manifest.json":                         nil,
+		"system/sepolicy/apex/myapex-file_contexts":  nil,
+		"system/sepolicy/apex/myapex2-file_contexts": nil,
+		"myapex.avbpubkey":                           nil,
+		"myapex.pem":                                 nil,
+		"myapex.x509.pem":                            nil,
+		"myapex.pk8":                                 nil,
+		"Test.java":                                  nil,
+		"Test.cpp":                                   nil,
+		"libfoo.so":                                  nil,
+	})
+
+	return ctx, config
+}
+
+func testSdk(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	ctx, config := testSdkContext(t, bp)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+	return ctx, config
+}
+
+// ensure that 'result' contains 'expected'
+func ensureContains(t *testing.T, result string, expected string) {
+	t.Helper()
+	if !strings.Contains(result, expected) {
+		t.Errorf("%q is not found in %q", expected, result)
+	}
+}
+
+// ensures that 'result' does not contain 'notExpected'
+func ensureNotContains(t *testing.T, result string, notExpected string) {
+	t.Helper()
+	if strings.Contains(result, notExpected) {
+		t.Errorf("%q is found in %q", notExpected, result)
+	}
+}
+
+func ensureListContains(t *testing.T, result []string, expected string) {
+	t.Helper()
+	if !android.InList(expected, result) {
+		t.Errorf("%q is not found in %v", expected, result)
+	}
+}
+
+func ensureListNotContains(t *testing.T, result []string, notExpected string) {
+	t.Helper()
+	if android.InList(notExpected, result) {
+		t.Errorf("%q is found in %v", notExpected, result)
+	}
+}
+
+func pathsToStrings(paths android.Paths) []string {
+	ret := []string{}
+	for _, p := range paths {
+		ret = append(ret, p.String())
+	}
+	return ret
+}
+
+func TestBasicSdkWithJava(t *testing.T) {
+	ctx, _ := testSdk(t, `
+		sdk {
+			name: "mysdk#1",
+			java_libs: ["sdkmember_mysdk_1"],
+		}
+
+		sdk {
+			name: "mysdk#2",
+			java_libs: ["sdkmember_mysdk_2"],
+		}
+
+		java_import {
+			name: "sdkmember",
+			prefer: false,
+			host_supported: true,
+		}
+
+		java_import {
+			name: "sdkmember_mysdk_1",
+			sdk_member_name: "sdkmember",
+			host_supported: true,
+		}
+
+		java_import {
+			name: "sdkmember_mysdk_2",
+			sdk_member_name: "sdkmember",
+			host_supported: true,
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			libs: ["sdkmember"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+
+		apex {
+			name: "myapex",
+			java_libs: ["myjavalib"],
+			uses_sdks: ["mysdk#1"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+
+		apex {
+			name: "myapex2",
+			java_libs: ["myjavalib"],
+			uses_sdks: ["mysdk#2"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+	`)
+
+	sdkMemberV1 := ctx.ModuleForTests("sdkmember_mysdk_1", "android_common_myapex").Rule("combineJar").Output
+	sdkMemberV2 := ctx.ModuleForTests("sdkmember_mysdk_2", "android_common_myapex2").Rule("combineJar").Output
+
+	javalibForMyApex := ctx.ModuleForTests("myjavalib", "android_common_myapex")
+	javalibForMyApex2 := ctx.ModuleForTests("myjavalib", "android_common_myapex2")
+
+	// Depending on the uses_sdks value, different libs are linked
+	ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
+	ensureListContains(t, pathsToStrings(javalibForMyApex2.Rule("javac").Implicits), sdkMemberV2.String())
+}
+
+func TestBasicSdkWithCc(t *testing.T) {
+	ctx, _ := testSdk(t, `
+		sdk {
+			name: "mysdk#1",
+			native_shared_libs: ["sdkmember_mysdk_1"],
+		}
+
+		sdk {
+			name: "mysdk#2",
+			native_shared_libs: ["sdkmember_mysdk_2"],
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember",
+			srcs: ["libfoo.so"],
+			prefer: false,
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember_mysdk_1",
+			sdk_member_name: "sdkmember",
+			srcs: ["libfoo.so"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_prebuilt_library_shared {
+			name: "sdkmember_mysdk_2",
+			sdk_member_name: "sdkmember",
+			srcs: ["libfoo.so"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library_shared {
+			name: "mycpplib",
+			srcs: ["Test.cpp"],
+			shared_libs: ["sdkmember"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		apex {
+			name: "myapex",
+			native_shared_libs: ["mycpplib"],
+			uses_sdks: ["mysdk#1"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+
+		apex {
+			name: "myapex2",
+			native_shared_libs: ["mycpplib"],
+			uses_sdks: ["mysdk#2"],
+			key: "myapex.key",
+			certificate: ":myapex.cert",
+		}
+	`)
+
+	sdkMemberV1 := ctx.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_core_shared_myapex").Rule("toc").Output
+	sdkMemberV2 := ctx.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_core_shared_myapex2").Rule("toc").Output
+
+	cpplibForMyApex := ctx.ModuleForTests("mycpplib", "android_arm64_armv8-a_core_shared_myapex")
+	cpplibForMyApex2 := ctx.ModuleForTests("mycpplib", "android_arm64_armv8-a_core_shared_myapex2")
+
+	// Depending on the uses_sdks value, different libs are linked
+	ensureListContains(t, pathsToStrings(cpplibForMyApex.Rule("ld").Implicits), sdkMemberV1.String())
+	ensureListContains(t, pathsToStrings(cpplibForMyApex2.Rule("ld").Implicits), sdkMemberV2.String())
+}
+
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_sdk_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index c7669bd..a876341 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -32,6 +32,86 @@
 	name string
 }
 
+type syspropGenProperties struct {
+	Srcs  []string `android:"path"`
+	Scope string
+}
+
+type syspropJavaGenRule struct {
+	android.ModuleBase
+
+	properties syspropGenProperties
+
+	genSrcjars android.Paths
+}
+
+var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil)
+
+var (
+	syspropJava = pctx.AndroidStaticRule("syspropJava",
+		blueprint.RuleParams{
+			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
+				`$syspropJavaCmd --scope $scope --java-output-dir $out.tmp $in && ` +
+				`$soongZipCmd -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
+			CommandDeps: []string{
+				"$syspropJavaCmd",
+				"$soongZipCmd",
+			},
+		}, "scope")
+)
+
+func init() {
+	pctx.HostBinToolVariable("soongZipCmd", "soong_zip")
+	pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java")
+
+	android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
+	})
+}
+
+func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var checkApiFileTimeStamp android.WritablePath
+
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if m, ok := dep.(*syspropLibrary); ok {
+			checkApiFileTimeStamp = m.checkApiFileTimeStamp
+		}
+	})
+
+	for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
+		srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        syspropJava,
+			Description: "sysprop_java " + syspropFile.Rel(),
+			Output:      srcJarFile,
+			Input:       syspropFile,
+			Implicit:    checkApiFileTimeStamp,
+			Args: map[string]string{
+				"scope": g.properties.Scope,
+			},
+		})
+
+		g.genSrcjars = append(g.genSrcjars, srcJarFile)
+	}
+}
+
+func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return g.genSrcjars, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func syspropJavaGenFactory() android.Module {
+	g := &syspropJavaGenRule{}
+	g.AddProperties(&g.properties)
+	android.InitAndroidModule(g)
+	return g
+}
+
 type syspropLibrary struct {
 	android.ModuleBase
 
@@ -81,13 +161,29 @@
 	return "lib" + m.BaseModuleName()
 }
 
+func (m *syspropLibrary) javaGenModuleName() string {
+	return m.BaseModuleName() + "_java_gen"
+}
+
 func (m *syspropLibrary) BaseModuleName() string {
 	return m.ModuleBase.Name()
 }
 
 func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", m.BaseModuleName()+"-current.txt")
-	m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", m.BaseModuleName()+"-latest.txt")
+	baseModuleName := m.BaseModuleName()
+
+	for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) {
+		if syspropFile.Ext() != ".sysprop" {
+			ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
+		}
+	}
+
+	if ctx.Failed() {
+		return
+	}
+
+	m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-current.txt")
+	m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
 
 	// dump API rule
 	rule := android.NewRuleBuilder()
@@ -96,7 +192,7 @@
 		BuiltTool(ctx, "sysprop_api_dump").
 		Output(m.dumpedApiFile).
 		Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
-	rule.Build(pctx, ctx, m.BaseModuleName()+"_api_dump", m.BaseModuleName()+" api dump")
+	rule.Build(pctx, ctx, baseModuleName+"_api_dump", baseModuleName+" api dump")
 
 	// check API rule
 	rule = android.NewRuleBuilder()
@@ -105,8 +201,8 @@
 	msg := fmt.Sprintf(`\n******************************\n`+
 		`API of sysprop_library %s doesn't match with current.txt\n`+
 		`Please update current.txt by:\n`+
-		`rm -rf %q && cp -f %q %q\n`+
-		`******************************\n`, m.BaseModuleName(),
+		`m %s-dump-api && rm -rf %q && cp -f %q %q\n`+
+		`******************************\n`, baseModuleName, baseModuleName,
 		m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String())
 
 	rule.Command().
@@ -121,7 +217,7 @@
 	msg = fmt.Sprintf(`\n******************************\n`+
 		`API of sysprop_library %s doesn't match with latest version\n`+
 		`Please fix the breakage and rebuild.\n`+
-		`******************************\n`, m.BaseModuleName())
+		`******************************\n`, baseModuleName)
 
 	rule.Command().
 		Text("( ").
@@ -138,7 +234,7 @@
 		Text("touch").
 		Output(m.checkApiFileTimeStamp)
 
-	rule.Build(pctx, ctx, m.BaseModuleName()+"_check_api", m.BaseModuleName()+" check api")
+	rule.Build(pctx, ctx, baseModuleName+"_check_api", baseModuleName+" check api")
 }
 
 func (m *syspropLibrary) AndroidMk() android.AndroidMkData {
@@ -153,13 +249,13 @@
 			fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")
 			fmt.Fprintf(w, "$(LOCAL_BUILT_MODULE): %s\n", m.checkApiFileTimeStamp.String())
 			fmt.Fprintf(w, "\ttouch $@\n\n")
-			fmt.Fprintf(w, ".PHONY: %s-check-api\n\n", name)
+			fmt.Fprintf(w, ".PHONY: %s-check-api %s-dump-api\n\n", name, name)
+
+			// dump API rule
+			fmt.Fprintf(w, "%s-dump-api: %s\n\n", name, m.dumpedApiFile.String())
 
 			// check API rule
 			fmt.Fprintf(w, "%s-check-api: %s\n\n", name, m.checkApiFileTimeStamp.String())
-
-			// "make {sysprop_library}" should also build the C++ library
-			fmt.Fprintf(w, "%s: %s\n\n", name, m.CcModuleName())
 		}}
 }
 
@@ -263,14 +359,38 @@
 	ccProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
 	ccProps.Header_libs = []string{"libbase_headers"}
 	ccProps.Shared_libs = []string{"liblog"}
-
-	// add sysprop_library module to perform check API
-	ccProps.Required = []string{m.Name()}
-	ccProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
 	ccProps.Recovery_available = m.properties.Recovery_available
 	ccProps.Vendor_available = m.properties.Vendor_available
 
-	ctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProps)
+	ctx.CreateModule(cc.LibraryFactory, &ccProps)
+
+	// internal scope contains all properties
+	// public scope only contains public properties
+	// use public if the owner is different from client
+	scope := "internal"
+	isProduct := ctx.ProductSpecific()
+	isVendor := ctx.SocSpecific()
+	isOwnerPlatform := owner == "Platform"
+
+	if isProduct {
+		// product can't own any sysprop_library now, so product must use public scope
+		scope = "public"
+	} else if isVendor && !isOwnerPlatform {
+		// vendor and odm can't use system's internal property.
+		scope = "public"
+	}
+
+	javaGenProps := struct {
+		Srcs  []string
+		Scope string
+		Name  *string
+	}{
+		Srcs:  m.properties.Srcs,
+		Scope: scope,
+		Name:  proptools.StringPtr(m.javaGenModuleName()),
+	}
+
+	ctx.CreateModule(syspropJavaGenFactory, &javaGenProps)
 
 	javaProps := struct {
 		Name             *string
@@ -278,27 +398,26 @@
 		Soc_specific     *bool
 		Device_specific  *bool
 		Product_specific *bool
-		Sysprop          struct {
-			Platform *bool
-		}
-		Required    []string
-		Sdk_version *string
-		Installable *bool
-		Libs        []string
+		Required         []string
+		Sdk_version      *string
+		Installable      *bool
+		Libs             []string
 	}{}
 
 	javaProps.Name = proptools.StringPtr(m.BaseModuleName())
-	javaProps.Srcs = m.properties.Srcs
+	javaProps.Srcs = []string{":" + *javaGenProps.Name}
 	javaProps.Soc_specific = proptools.BoolPtr(socSpecific)
 	javaProps.Device_specific = proptools.BoolPtr(deviceSpecific)
 	javaProps.Product_specific = proptools.BoolPtr(productSpecific)
 	javaProps.Installable = m.properties.Installable
-
-	// add sysprop_library module to perform check API
-	javaProps.Required = []string{m.Name()}
 	javaProps.Sdk_version = proptools.StringPtr("core_current")
-	javaProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
 	javaProps.Libs = []string{stub}
 
-	ctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory), &javaProps)
+	ctx.CreateModule(java.LibraryFactory, &javaProps)
+}
+
+func syspropDepsMutator(ctx android.BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(*syspropLibrary); ok {
+		ctx.AddReverseDependency(m, nil, m.javaGenModuleName())
+	}
 }
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 8db6833..6b4337a 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -62,6 +62,9 @@
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
+	})
 
 	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
 	ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 1400345..3d30cfa 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -44,10 +44,11 @@
 	CommandDeps: []string{"$template"},
 }, "name", "template", "extraConfigs")
 
-func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string) (path android.Path, autogenPath android.WritablePath) {
-	if p := getTestConfig(ctx, prop); p != nil {
+func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool) (path android.Path, autogenPath android.WritablePath) {
+	p := getTestConfig(ctx, prop)
+	if !Bool(autoGenConfig) && p != nil {
 		return p, nil
-	} else if !android.InList("cts", testSuites) {
+	} else if !android.InList("cts", testSuites) && BoolDefault(autoGenConfig, true) {
 		outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config")
 		return nil, outputFile
 	} else {
@@ -124,8 +125,8 @@
 }
 
 func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string, config []Config) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -143,8 +144,8 @@
 }
 
 func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string, configs []Config) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+	testConfigTemplateProp *string, testSuites []string, configs []Config, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -157,8 +158,9 @@
 	return path
 }
 
-func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
+	testSuites []string, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -176,9 +178,9 @@
 }
 
 func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string) android.Path {
+	testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path {
 
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
@@ -200,8 +202,9 @@
 	},
 }, "name", "template")
 
-func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, manifest android.Path, testSuites []string) android.Path {
-	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string,
+	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig)
 	if autogenPath != nil {
 		template := "${InstrumentationTestConfigTemplate}"
 		moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp)
@@ -222,3 +225,6 @@
 	}
 	return path
 }
+
+var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 0a2b510..1dbeb26 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -107,6 +107,8 @@
 		productOut("obj/NOTICE_FILES"),
 		productOut("obj/PACKAGING"),
 		productOut("ramdisk"),
+		productOut("debug_ramdisk"),
+		productOut("test_harness_ramdisk"),
 		productOut("recovery"),
 		productOut("root"),
 		productOut("system"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 665d2f0..919b9ce 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -16,7 +16,6 @@
 
 import (
 	"io/ioutil"
-	"log"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -189,36 +188,40 @@
 	checkTopDir(ctx)
 
 	if srcDir := absPath(ctx, "."); strings.ContainsRune(srcDir, ' ') {
-		log.Println("You are building in a directory whose absolute path contains a space character:")
-		log.Println()
-		log.Printf("%q\n", srcDir)
-		log.Println()
-		log.Fatalln("Directory names containing spaces are not supported")
+		ctx.Println("You are building in a directory whose absolute path contains a space character:")
+		ctx.Println()
+		ctx.Printf("%q\n", srcDir)
+		ctx.Println()
+		ctx.Fatalln("Directory names containing spaces are not supported")
 	}
 
 	if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') {
-		log.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:")
-		log.Println()
-		log.Printf("%q\n", outDir)
-		log.Println()
-		log.Fatalln("Directory names containing spaces are not supported")
+		ctx.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:")
+		ctx.Println()
+		ctx.Printf("%q\n", outDir)
+		ctx.Println()
+		ctx.Fatalln("Directory names containing spaces are not supported")
 	}
 
 	if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') {
-		log.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:")
-		log.Println()
-		log.Printf("%q\n", distDir)
-		log.Println()
-		log.Fatalln("Directory names containing spaces are not supported")
+		ctx.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:")
+		ctx.Println()
+		ctx.Printf("%q\n", distDir)
+		ctx.Println()
+		ctx.Fatalln("Directory names containing spaces are not supported")
 	}
 
 	// Configure Java-related variables, including adding it to $PATH
 	java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag())
 	java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag())
+	java11Home := filepath.Join("prebuilts/jdk/jdk11", ret.HostPrebuiltTag())
 	javaHome := func() string {
 		if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
 			return override
 		}
+		if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 == "true" {
+			return java11Home
+		}
 		return java9Home
 	}()
 	absJavaHome := absPath(ctx, javaHome)
@@ -229,11 +232,13 @@
 	if path, ok := ret.environ.Get("PATH"); ok && path != "" {
 		newPath = append(newPath, path)
 	}
+
 	ret.environ.Unset("OVERRIDE_ANDROID_JAVA_HOME")
 	ret.environ.Set("JAVA_HOME", absJavaHome)
 	ret.environ.Set("ANDROID_JAVA_HOME", javaHome)
 	ret.environ.Set("ANDROID_JAVA8_HOME", java8Home)
 	ret.environ.Set("ANDROID_JAVA9_HOME", java9Home)
+	ret.environ.Set("ANDROID_JAVA11_HOME", java11Home)
 	ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator)))
 
 	outDir := ret.OutDir()
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 6b9eac1..4270bb1 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -17,6 +17,8 @@
 import (
 	"bytes"
 	"fmt"
+	"io/ioutil"
+	"os"
 	"strings"
 
 	"android/soong/ui/metrics"
@@ -40,6 +42,7 @@
 	soongUiVars := map[string]func() string{
 		"OUT_DIR":  func() string { return config.OutDir() },
 		"DIST_DIR": func() string { return config.DistDir() },
+		"TMPDIR":   func() string { return absPath(ctx, config.TempDir()) },
 	}
 
 	makeVars := make([]string, 0, len(vars))
@@ -51,7 +54,17 @@
 
 	var ret map[string]string
 	if len(makeVars) > 0 {
-		var err error
+		tmpDir, err := ioutil.TempDir("", "dumpvars")
+		if err != nil {
+			return nil, err
+		}
+		defer os.RemoveAll(tmpDir)
+
+		// It's not safe to use the same TMPDIR as the build, as that can be removed.
+		config.Environment().Set("TMPDIR", tmpDir)
+
+		SetupLitePath(ctx, config)
+
 		ret, err = dumpMakeVars(ctx, config, goals, makeVars, false)
 		if err != nil {
 			return ret, err
@@ -208,6 +221,8 @@
 
 		"DEFAULT_WARNING_BUILD_MODULE_TYPES",
 		"DEFAULT_ERROR_BUILD_MODULE_TYPES",
+		"BUILD_BROKEN_PREBUILT_ELF_FILES",
+		"BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW",
 		"BUILD_BROKEN_USES_BUILD_AUX_EXECUTABLE",
 		"BUILD_BROKEN_USES_BUILD_AUX_STATIC_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_COPY_HEADERS",
diff --git a/ui/build/path.go b/ui/build/path.go
index 0e1c02c..c34ba1b 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"io/ioutil"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strings"
@@ -53,6 +54,51 @@
 	return ret
 }
 
+// A "lite" version of SetupPath used for dumpvars, or other places that need
+// minimal overhead (but at the expense of logging).
+func SetupLitePath(ctx Context, config Config) {
+	if config.pathReplaced {
+		return
+	}
+
+	ctx.BeginTrace(metrics.RunSetupTool, "litepath")
+	defer ctx.EndTrace()
+
+	origPath, _ := config.Environment().Get("PATH")
+	myPath, _ := config.Environment().Get("TMPDIR")
+	myPath = filepath.Join(myPath, "path")
+	ensureEmptyDirectoriesExist(ctx, myPath)
+
+	os.Setenv("PATH", origPath)
+	for name, pathConfig := range paths.Configuration {
+		if !pathConfig.Symlink {
+			continue
+		}
+
+		origExec, err := exec.LookPath(name)
+		if err != nil {
+			continue
+		}
+		origExec, err = filepath.Abs(origExec)
+		if err != nil {
+			continue
+		}
+
+		err = os.Symlink(origExec, filepath.Join(myPath, name))
+		if err != nil {
+			ctx.Fatalln("Failed to create symlink:", err)
+		}
+	}
+
+	myPath, _ = filepath.Abs(myPath)
+
+	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
+	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+
+	config.Environment().Set("PATH", myPath)
+	config.pathReplaced = true
+}
+
 func SetupPath(ctx Context, config Config) {
 	if config.pathReplaced {
 		return
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index e44600e..1af816b 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -122,6 +122,9 @@
 	"pgrep": LinuxOnlyPrebuilt,
 	"pkill": LinuxOnlyPrebuilt,
 	"ps":    LinuxOnlyPrebuilt,
+
+	// The toybox xargs is currently breaking the mac build.
+	"xargs": LinuxOnlyPrebuilt,
 }
 
 func init() {
diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go
index 57f71ab..efcfd43 100644
--- a/ui/terminal/smart_status.go
+++ b/ui/terminal/smart_status.go
@@ -189,7 +189,7 @@
 		fmt.Fprintf(s.writer, ansi.resetScrollingMargins())
 		_, height, _ := termSize(s.writer)
 		// Move the cursor to the top of the now-blank, previously non-scrolling region
-		fmt.Fprintf(s.writer, ansi.setCursor(height-s.tableHeight, 0))
+		fmt.Fprintf(s.writer, ansi.setCursor(height-s.tableHeight, 1))
 		// Turn the cursor back on
 		fmt.Fprintf(s.writer, ansi.showCursor())
 	}
@@ -334,52 +334,44 @@
 	scrollingHeight := s.termHeight - s.tableHeight
 
 	// Update the scrolling region in case the height of the terminal changed
-	fmt.Fprint(s.writer, ansi.setScrollingMargins(0, scrollingHeight))
-	// Move the cursor to the first line of the non-scrolling region
-	fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight+1, 0))
+
+	fmt.Fprint(s.writer, ansi.setScrollingMargins(1, scrollingHeight))
 
 	// Write as many status lines as fit in the table
-	var tableLine int
-	var runningAction actionTableEntry
-	for tableLine, runningAction = range s.runningActions {
+	for tableLine := 0; tableLine < s.tableHeight; tableLine++ {
 		if tableLine >= s.tableHeight {
 			break
 		}
+		// Move the cursor to the correct line of the non-scrolling region
+		fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight+1+tableLine, 1))
 
-		seconds := int(time.Since(runningAction.startTime).Round(time.Second).Seconds())
+		if tableLine < len(s.runningActions) {
+			runningAction := s.runningActions[tableLine]
 
-		desc := runningAction.action.Description
-		if desc == "" {
-			desc = runningAction.action.Command
+			seconds := int(time.Since(runningAction.startTime).Round(time.Second).Seconds())
+
+			desc := runningAction.action.Description
+			if desc == "" {
+				desc = runningAction.action.Command
+			}
+
+			color := ""
+			if seconds >= 60 {
+				color = ansi.red() + ansi.bold()
+			} else if seconds >= 30 {
+				color = ansi.yellow() + ansi.bold()
+			}
+
+			durationStr := fmt.Sprintf("   %2d:%02d ", seconds/60, seconds%60)
+			desc = elide(desc, s.termWidth-len(durationStr))
+			durationStr = color + durationStr + ansi.regular()
+			fmt.Fprint(s.writer, durationStr, desc)
 		}
-
-		color := ""
-		if seconds >= 60 {
-			color = ansi.red() + ansi.bold()
-		} else if seconds >= 30 {
-			color = ansi.yellow() + ansi.bold()
-		}
-
-		durationStr := fmt.Sprintf("   %2d:%02d ", seconds/60, seconds%60)
-		desc = elide(desc, s.termWidth-len(durationStr))
-		durationStr = color + durationStr + ansi.regular()
-
-		fmt.Fprint(s.writer, durationStr, desc, ansi.clearToEndOfLine())
-		if tableLine < s.tableHeight-1 {
-			fmt.Fprint(s.writer, "\n")
-		}
-	}
-
-	// Clear any remaining lines in the table
-	for ; tableLine < s.tableHeight; tableLine++ {
 		fmt.Fprint(s.writer, ansi.clearToEndOfLine())
-		if tableLine < s.tableHeight-1 {
-			fmt.Fprint(s.writer, "\n")
-		}
 	}
 
 	// Move the cursor back to the last line of the scrolling region
-	fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight, 0))
+	fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight, 1))
 }
 
 var ansi = ansiImpl{}
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 69a2a09..60dfc70 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -26,10 +26,10 @@
 //
 // statusFormat takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
-func NewStatusOutput(w io.Writer, statusFormat string, quietBuild bool) status.StatusOutput {
+func NewStatusOutput(w io.Writer, statusFormat string, forceDumbOutput, quietBuild bool) status.StatusOutput {
 	formatter := newFormatter(statusFormat, quietBuild)
 
-	if isSmartTerminal(w) {
+	if !forceDumbOutput && isSmartTerminal(w) {
 		return NewSmartStatusOutput(w, formatter)
 	} else {
 		return NewDumbStatusOutput(w, formatter)
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index 81aa238..9f60829 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -94,7 +94,7 @@
 
 			t.Run("smart", func(t *testing.T) {
 				smart := &fakeSmartTerminal{termWidth: 40}
-				stat := NewStatusOutput(smart, "", false)
+				stat := NewStatusOutput(smart, "", false, false)
 				tt.calls(stat)
 				stat.Flush()
 
@@ -105,7 +105,7 @@
 
 			t.Run("dumb", func(t *testing.T) {
 				dumb := &bytes.Buffer{}
-				stat := NewStatusOutput(dumb, "", false)
+				stat := NewStatusOutput(dumb, "", false, false)
 				tt.calls(stat)
 				stat.Flush()
 
@@ -113,6 +113,17 @@
 					t.Errorf("want:\n%q\ngot:\n%q", w, g)
 				}
 			})
+
+			t.Run("force dumb", func(t *testing.T) {
+				smart := &fakeSmartTerminal{termWidth: 40}
+				stat := NewStatusOutput(smart, "", true, false)
+				tt.calls(stat)
+				stat.Flush()
+
+				if g, w := smart.String(), tt.dumb; g != w {
+					t.Errorf("want:\n%q\ngot:\n%q", w, g)
+				}
+			})
 		})
 	}
 }
@@ -258,7 +269,7 @@
 	os.Setenv(tableHeightEnVar, "")
 
 	smart := &fakeSmartTerminal{termWidth: 40}
-	stat := NewStatusOutput(smart, "", false)
+	stat := NewStatusOutput(smart, "", false, false)
 	smartStat := stat.(*smartStatusOutput)
 	smartStat.sigwinchHandled = make(chan bool)
 
diff --git a/vnames.json b/vnames.json
new file mode 100644
index 0000000..f9d3adc
--- /dev/null
+++ b/vnames.json
@@ -0,0 +1,18 @@
+[
+  {
+    "pattern": "out/(.*)",
+    "vname": {
+      "corpus": "android.googlesource.com/platform/superproject",
+      "root": "out",
+      "path": "@1@"
+    }
+  },
+  {
+    "pattern": "(.*)",
+    "vname": {
+      "corpus": "android.googlesource.com/platform/superproject",
+      "path": "@1@"
+    }
+  }
+]
+
diff --git a/xml/xml_test.go b/xml/xml_test.go
index ae3e9fe..f2a440f 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -15,15 +15,40 @@
 package xml
 
 import (
-	"android/soong/android"
 	"io/ioutil"
 	"os"
 	"testing"
+
+	"android/soong/android"
 )
 
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_xml_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
+
 func testXml(t *testing.T, bp string) *android.TestContext {
-	config, buildDir := setup(t)
-	defer teardown(buildDir)
+	config := android.TestArchConfig(buildDir, nil)
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory))
 	ctx.RegisterModuleType("prebuilt_etc_xml", android.ModuleFactoryAdaptor(PrebuiltEtcXmlFactory))
@@ -45,21 +70,6 @@
 	return ctx
 }
 
-func setup(t *testing.T) (config android.Config, buildDir string) {
-	buildDir, err := ioutil.TempDir("", "soong_xml_test")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	config = android.TestArchConfig(buildDir, nil)
-
-	return
-}
-
-func teardown(buildDir string) {
-	os.RemoveAll(buildDir)
-}
-
 func assertEqual(t *testing.T, name, expected, actual string) {
 	t.Helper()
 	if expected != actual {
@@ -103,5 +113,5 @@
 	}
 
 	m := ctx.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
-	assertEqual(t, "installDir", "target/product/test_device/system/etc", m.InstallDirPath().RelPathString())
+	assertEqual(t, "installDir", buildDir+"/target/product/test_device/system/etc", m.InstallDirPath().String())
 }