Merge "Add missing soong_docs inputs"
diff --git a/Android.bp b/Android.bp
index 0b44198..3075d67 100644
--- a/Android.bp
+++ b/Android.bp
@@ -106,15 +106,24 @@
         },
         arm64: {
             src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a",
-            repack_objects_to_keep: ["unwind-dw2.o", "unwind-dw2-fde-dip.o"],
+            repack_objects_to_keep: [
+                "unwind-dw2.o",
+                "unwind-dw2-fde-dip.o",
+            ],
         },
         x86: {
             src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcc.a",
-            repack_objects_to_keep: ["unwind-dw2.o", "unwind-dw2-fde-dip.o"],
+            repack_objects_to_keep: [
+                "unwind-dw2.o",
+                "unwind-dw2-fde-dip.o",
+            ],
         },
         x86_64: {
             src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcc.a",
-            repack_objects_to_keep: ["unwind-dw2.o", "unwind-dw2-fde-dip.o"],
+            repack_objects_to_keep: [
+                "unwind-dw2.o",
+                "unwind-dw2-fde-dip.o",
+            ],
         },
     },
 }
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 928ca03..ff85661 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,3 @@
 [Builtin Hooks]
 gofmt = true
+bpfmt = true
diff --git a/android/androidmk.go b/android/androidmk.go
index d579e30..045cb59 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -46,7 +46,7 @@
 type AndroidMkData struct {
 	Class           string
 	SubName         string
-	DistFile        OptionalPath
+	DistFiles       TaggedDistFiles
 	OutputFile      OptionalPath
 	Disabled        bool
 	Include         string
@@ -58,7 +58,7 @@
 
 	Extra []AndroidMkExtraFunc
 
-	preamble bytes.Buffer
+	Entries AndroidMkEntries
 }
 
 type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
@@ -72,7 +72,7 @@
 type AndroidMkEntries struct {
 	Class           string
 	SubName         string
-	DistFile        OptionalPath
+	DistFiles       TaggedDistFiles
 	OutputFile      OptionalPath
 	Disabled        bool
 	Include         string
@@ -137,6 +137,96 @@
 	a.EntryMap[name] = append(a.EntryMap[name], value...)
 }
 
+// Compute the list of Make strings to declare phone goals and dist-for-goals
+// calls from the module's dist and dists properties.
+func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string {
+	amod := mod.(Module).base()
+	name := amod.BaseModuleName()
+
+	var ret []string
+
+	availableTaggedDists := TaggedDistFiles{}
+	if a.DistFiles != nil && len(a.DistFiles[""]) > 0 {
+		availableTaggedDists = a.DistFiles
+	} else if a.OutputFile.Valid() {
+		availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
+	}
+
+	// Iterate over this module's dist structs, merged from the dist and dists properties.
+	for _, dist := range amod.Dists() {
+		// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
+		goals := strings.Join(dist.Targets, " ")
+
+		// Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map"
+		var tag string
+		if dist.Tag == nil {
+			// If the dist struct does not specify a tag, use the default output files tag.
+			tag = ""
+		} else {
+			tag = *dist.Tag
+		}
+
+		// Get the paths of the output files to be dist'd, represented by the tag.
+		// Can be an empty list.
+		tagPaths := availableTaggedDists[tag]
+		if len(tagPaths) == 0 {
+			// Nothing to dist for this tag, continue to the next dist.
+			continue
+		}
+
+		if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
+			errorMessage := "Cannot apply dest/suffix for more than one dist " +
+				"file for %s goals in module %s. The list of dist files, " +
+				"which should have a single element, is:\n%s"
+			panic(fmt.Errorf(errorMessage, goals, name, tagPaths))
+		}
+
+		ret = append(ret, fmt.Sprintf(".PHONY: %s\n", goals))
+
+		// Create dist-for-goals calls for each path in the dist'd files.
+		for _, path := range tagPaths {
+			// It's possible that the Path is nil from errant modules. Be defensive here.
+			if path == nil {
+				tagName := "default" // for error message readability
+				if dist.Tag != nil {
+					tagName = *dist.Tag
+				}
+				panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name))
+			}
+
+			dest := filepath.Base(path.String())
+
+			if dist.Dest != nil {
+				var err error
+				if dest, err = validateSafePath(*dist.Dest); err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			if dist.Suffix != nil {
+				ext := filepath.Ext(dest)
+				suffix := *dist.Suffix
+				dest = strings.TrimSuffix(dest, ext) + suffix + ext
+			}
+
+			if dist.Dir != nil {
+				var err error
+				if dest, err = validateSafePath(*dist.Dir, dest); err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			ret = append(
+				ret,
+				fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", goals, path.String(), dest))
+		}
+	}
+
+	return ret
+}
+
 func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
 	a.EntryMap = make(map[string][]string)
 	amod := mod.(Module).base()
@@ -149,42 +239,8 @@
 	a.Host_required = append(a.Host_required, amod.commonProperties.Host_required...)
 	a.Target_required = append(a.Target_required, amod.commonProperties.Target_required...)
 
-	// Fill in the header part.
-	if len(amod.commonProperties.Dist.Targets) > 0 {
-		distFile := a.DistFile
-		if !distFile.Valid() {
-			distFile = a.OutputFile
-		}
-		if distFile.Valid() {
-			dest := filepath.Base(distFile.String())
-
-			if amod.commonProperties.Dist.Dest != nil {
-				var err error
-				if dest, err = validateSafePath(*amod.commonProperties.Dist.Dest); err != nil {
-					// This was checked in ModuleBase.GenerateBuildActions
-					panic(err)
-				}
-			}
-
-			if amod.commonProperties.Dist.Suffix != nil {
-				ext := filepath.Ext(dest)
-				suffix := *amod.commonProperties.Dist.Suffix
-				dest = strings.TrimSuffix(dest, ext) + suffix + ext
-			}
-
-			if amod.commonProperties.Dist.Dir != nil {
-				var err error
-				if dest, err = validateSafePath(*amod.commonProperties.Dist.Dir, dest); err != nil {
-					// This was checked in ModuleBase.GenerateBuildActions
-					panic(err)
-				}
-			}
-
-			goals := strings.Join(amod.commonProperties.Dist.Targets, " ")
-			fmt.Fprintln(&a.header, ".PHONY:", goals)
-			fmt.Fprintf(&a.header, "$(call dist-for-goals,%s,%s:%s)\n",
-				goals, distFile.String(), dest)
-		}
+	for _, distString := range a.GetDistForGoals(mod) {
+		fmt.Fprintf(&a.header, distString)
 	}
 
 	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
@@ -427,10 +483,10 @@
 
 func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprint.Module) {
 	// Get the preamble content through AndroidMkEntries logic.
-	entries := AndroidMkEntries{
+	data.Entries = AndroidMkEntries{
 		Class:           data.Class,
 		SubName:         data.SubName,
-		DistFile:        data.DistFile,
+		DistFiles:       data.DistFiles,
 		OutputFile:      data.OutputFile,
 		Disabled:        data.Disabled,
 		Include:         data.Include,
@@ -438,16 +494,12 @@
 		Host_required:   data.Host_required,
 		Target_required: data.Target_required,
 	}
-	entries.fillInEntries(config, bpPath, mod)
-
-	// preamble doesn't need the footer content.
-	entries.footer = bytes.Buffer{}
-	entries.write(&data.preamble)
+	data.Entries.fillInEntries(config, bpPath, mod)
 
 	// copy entries back to data since it is used in Custom
-	data.Required = entries.Required
-	data.Host_required = entries.Host_required
-	data.Target_required = entries.Target_required
+	data.Required = data.Entries.Required
+	data.Host_required = data.Entries.Host_required
+	data.Target_required = data.Entries.Target_required
 }
 
 func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
@@ -503,7 +555,9 @@
 		return
 	}
 
-	w.Write(data.preamble.Bytes())
+	// write preamble via Entries
+	data.Entries.footer = bytes.Buffer{}
+	data.Entries.write(w)
 
 	for _, extra := range data.Extra {
 		extra(w, data.OutputFile.Path())
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 71f8020..250f086 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"io"
 	"reflect"
 	"testing"
@@ -22,10 +23,12 @@
 
 type customModule struct {
 	ModuleBase
-	data AndroidMkData
+	data      AndroidMkData
+	distFiles TaggedDistFiles
 }
 
 func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	m.distFiles = m.GenerateTaggedDistFiles(ctx)
 }
 
 func (m *customModule) AndroidMk() AndroidMkData {
@@ -36,6 +39,26 @@
 	}
 }
 
+func (m *customModule) OutputFiles(tag string) (Paths, error) {
+	switch tag {
+	case "":
+		return PathsForTesting("one.out"), nil
+	case ".multiple":
+		return PathsForTesting("two.out", "three/four.out"), nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{
+		{
+			Class:     "CUSTOM_MODULE",
+			DistFiles: m.distFiles,
+		},
+	}
+}
+
 func customModuleFactory() Module {
 	module := &customModule{}
 	InitAndroidModule(module)
@@ -76,3 +99,159 @@
 	assertEqual([]string{"baz"}, m.data.Host_required)
 	assertEqual([]string{"qux"}, m.data.Target_required)
 }
+
+func TestGetDistForGoals(t *testing.T) {
+	testCases := []struct {
+		bp                     string
+		expectedAndroidMkLines []string
+	}{
+		{
+			bp: `
+			custom {
+				name: "foo",
+				dist: {
+					targets: ["my_goal"]
+				}
+			}
+			`,
+			expectedAndroidMkLines: []string{
+				".PHONY: my_goal\n",
+				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
+			},
+		},
+		{
+			bp: `
+			custom {
+				name: "foo",
+				dists: [
+					{
+						targets: ["my_goal"],
+					},
+					{
+						targets: ["my_second_goal", "my_third_goal"],
+					},
+				],
+			}
+			`,
+			expectedAndroidMkLines: []string{
+				".PHONY: my_goal\n",
+				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
+				".PHONY: my_second_goal my_third_goal\n",
+				"$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
+			},
+		},
+		{
+			bp: `
+			custom {
+				name: "foo",
+				dist: {
+					targets: ["my_goal"],
+				},
+				dists: [
+					{
+						targets: ["my_second_goal", "my_third_goal"],
+					},
+				],
+			}
+			`,
+			expectedAndroidMkLines: []string{
+				".PHONY: my_second_goal my_third_goal\n",
+				"$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
+				".PHONY: my_goal\n",
+				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
+			},
+		},
+		{
+			bp: `
+			custom {
+				name: "foo",
+				dist: {
+					targets: ["my_goal", "my_other_goal"],
+					tag: ".multiple",
+				},
+				dists: [
+					{
+						targets: ["my_second_goal"],
+						tag: ".multiple",
+					},
+					{
+						targets: ["my_third_goal"],
+						dir: "test/dir",
+					},
+					{
+						targets: ["my_fourth_goal"],
+						suffix: ".suffix",
+					},
+					{
+						targets: ["my_fifth_goal"],
+						dest: "new-name",
+					},
+					{
+						targets: ["my_sixth_goal"],
+						dest: "new-name",
+						dir: "some/dir",
+						suffix: ".suffix",
+					},
+				],
+			}
+			`,
+			expectedAndroidMkLines: []string{
+				".PHONY: my_second_goal\n",
+				"$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
+				"$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
+				".PHONY: my_third_goal\n",
+				"$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
+				".PHONY: my_fourth_goal\n",
+				"$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
+				".PHONY: my_fifth_goal\n",
+				"$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
+				".PHONY: my_sixth_goal\n",
+				"$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
+				".PHONY: my_goal my_other_goal\n",
+				"$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
+				"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
+			},
+		},
+	}
+
+	for _, testCase := range testCases {
+		config := TestConfig(buildDir, nil, testCase.bp, nil)
+		config.inMake = true // Enable androidmk Singleton
+
+		ctx := NewTestContext()
+		ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
+		ctx.RegisterModuleType("custom", customModuleFactory)
+		ctx.Register(config)
+
+		_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+		FailIfErrored(t, errs)
+		_, errs = ctx.PrepareBuildActions(config)
+		FailIfErrored(t, errs)
+
+		module := ctx.ModuleForTests("foo", "").Module().(*customModule)
+		entries := AndroidMkEntriesForTest(t, config, "", module)
+		if len(entries) != 1 {
+			t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
+		}
+		androidMkLines := entries[0].GetDistForGoals(module)
+
+		if len(androidMkLines) != len(testCase.expectedAndroidMkLines) {
+			t.Errorf(
+				"Expected %d AndroidMk lines, got %d:\n%v",
+				len(testCase.expectedAndroidMkLines),
+				len(androidMkLines),
+				androidMkLines,
+			)
+		}
+		for idx, line := range androidMkLines {
+			expectedLine := testCase.expectedAndroidMkLines[idx]
+			if line != expectedLine {
+				t.Errorf(
+					"Expected AndroidMk line to be '%s', got '%s'",
+					line,
+					expectedLine,
+				)
+			}
+		}
+	}
+}
diff --git a/android/apex.go b/android/apex.go
index 30152db..47f07ca 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -124,6 +124,10 @@
 	// the private part of the listed APEXes even when it is not included in the
 	// APEXes.
 	TestFor() []string
+
+	// Returns nil if this module supports sdkVersion
+	// Otherwise, returns error with reason
+	ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion int) error
 }
 
 type ApexProperties struct {
@@ -477,3 +481,125 @@
 		},
 	})
 }
+
+// TODO(b/158059172): remove minSdkVersion allowlist
+var minSdkVersionAllowlist = map[string]int{
+	"adbd":                  30,
+	"android.net.ipsec.ike": 30,
+	"androidx-constraintlayout_constraintlayout-solver": 30,
+	"androidx.annotation_annotation":                    28,
+	"androidx.arch.core_core-common":                    28,
+	"androidx.collection_collection":                    28,
+	"androidx.lifecycle_lifecycle-common":               28,
+	"apache-commons-compress":                           29,
+	"bouncycastle_ike_digests":                          30,
+	"brotli-java":                                       29,
+	"captiveportal-lib":                                 28,
+	"flatbuffer_headers":                                30,
+	"framework-permission":                              30,
+	"framework-statsd":                                  30,
+	"gemmlowp_headers":                                  30,
+	"ike-internals":                                     30,
+	"kotlinx-coroutines-android":                        28,
+	"kotlinx-coroutines-core":                           28,
+	"libadb_crypto":                                     30,
+	"libadb_pairing_auth":                               30,
+	"libadb_pairing_connection":                         30,
+	"libadb_pairing_server":                             30,
+	"libadb_protos":                                     30,
+	"libadb_tls_connection":                             30,
+	"libadbconnection_client":                           30,
+	"libadbconnection_server":                           30,
+	"libadbd_core":                                      30,
+	"libadbd_services":                                  30,
+	"libadbd":                                           30,
+	"libapp_processes_protos_lite":                      30,
+	"libasyncio":                                        30,
+	"libbrotli":                                         30,
+	"libbuildversion":                                   30,
+	"libcrypto_static":                                  30,
+	"libcrypto_utils":                                   30,
+	"libdiagnose_usb":                                   30,
+	"libeigen":                                          30,
+	"liblz4":                                            30,
+	"libmdnssd":                                         30,
+	"libneuralnetworks_common":                          30,
+	"libneuralnetworks_headers":                         30,
+	"libneuralnetworks":                                 30,
+	"libprocpartition":                                  30,
+	"libprotobuf-java-lite":                             30,
+	"libprotoutil":                                      30,
+	"libqemu_pipe":                                      30,
+	"libstats_jni":                                      30,
+	"libstatslog_statsd":                                30,
+	"libstatsmetadata":                                  30,
+	"libstatspull":                                      30,
+	"libstatssocket":                                    30,
+	"libsync":                                           30,
+	"libtextclassifier_hash_headers":                    30,
+	"libtextclassifier_hash_static":                     30,
+	"libtflite_kernel_utils":                            30,
+	"libwatchdog":                                       29,
+	"libzstd":                                           30,
+	"metrics-constants-protos":                          28,
+	"net-utils-framework-common":                        29,
+	"permissioncontroller-statsd":                       28,
+	"philox_random_headers":                             30,
+	"philox_random":                                     30,
+	"service-permission":                                30,
+	"service-statsd":                                    30,
+	"statsd-aidl-ndk_platform":                          30,
+	"statsd":                                            30,
+	"tensorflow_headers":                                30,
+	"xz-java":                                           29,
+}
+
+// Function called while walking an APEX's payload dependencies.
+//
+// Return true if the `to` module should be visited, false otherwise.
+type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
+
+// UpdatableModule represents updatable APEX/APK
+type UpdatableModule interface {
+	Module
+	WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback)
+}
+
+// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly
+func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion int) {
+	// do not enforce min_sdk_version for host
+	if ctx.Host() {
+		return
+	}
+
+	// do not enforce for coverage build
+	if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled() {
+		return
+	}
+
+	// do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version or
+	// min_sdk_version is not finalized (e.g. current or codenames)
+	if minSdkVersion == FutureApiLevel {
+		return
+	}
+
+	m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
+		if externalDep {
+			// external deps are outside the payload boundary, which is "stable" interface.
+			// We don't have to check min_sdk_version for external dependencies.
+			return false
+		}
+		if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+			return false
+		}
+		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
+			toName := ctx.OtherModuleName(to)
+			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver > minSdkVersion {
+				ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s",
+					minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false))
+				return false
+			}
+		}
+		return true
+	})
+}
diff --git a/android/config.go b/android/config.go
index c25900b..7073a49 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1054,7 +1054,7 @@
 // represents any path.
 func (c *deviceConfig) JavaCoverageEnabledForPath(path string) bool {
 	coverage := false
-	if c.config.productVariables.JavaCoveragePaths == nil ||
+	if len(c.config.productVariables.JavaCoveragePaths) == 0 ||
 		InList("*", c.config.productVariables.JavaCoveragePaths) ||
 		HasAnyPrefix(path, c.config.productVariables.JavaCoveragePaths) {
 		coverage = true
diff --git a/android/hooks.go b/android/hooks.go
index f43a007..85fc081 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -120,6 +120,7 @@
 
 type InstallHookContext interface {
 	ModuleContext
+	SrcPath() Path
 	Path() InstallPath
 	Symlink() bool
 }
@@ -134,10 +135,17 @@
 
 type installHookContext struct {
 	ModuleContext
+	srcPath Path
 	path    InstallPath
 	symlink bool
 }
 
+var _ InstallHookContext = &installHookContext{}
+
+func (x *installHookContext) SrcPath() Path {
+	return x.srcPath
+}
+
 func (x *installHookContext) Path() InstallPath {
 	return x.path
 }
@@ -146,10 +154,11 @@
 	return x.symlink
 }
 
-func (x *hooks) runInstallHooks(ctx ModuleContext, path InstallPath, symlink bool) {
+func (x *hooks) runInstallHooks(ctx ModuleContext, srcPath Path, path InstallPath, symlink bool) {
 	if len(x.install) > 0 {
 		mctx := &installHookContext{
 			ModuleContext: ctx,
+			srcPath:       srcPath,
 			path:          path,
 			symlink:       symlink,
 		}
diff --git a/android/module.go b/android/module.go
index f24047c..06079ca 100644
--- a/android/module.go
+++ b/android/module.go
@@ -315,6 +315,28 @@
 	return qualifiedModuleName{pkg: pkg, name: ""}
 }
 
+type Dist struct {
+	// Copy the output of this module to the $DIST_DIR when `dist` is specified on the
+	// command line and any of these targets are also on the command line, or otherwise
+	// built
+	Targets []string `android:"arch_variant"`
+
+	// The name of the output artifact. This defaults to the basename of the output of
+	// the module.
+	Dest *string `android:"arch_variant"`
+
+	// The directory within the dist directory to store the artifact. Defaults to the
+	// top level directory ("").
+	Dir *string `android:"arch_variant"`
+
+	// A suffix to add to the artifact file name (before any extension).
+	Suffix *string `android:"arch_variant"`
+
+	// A string tag to select the OutputFiles associated with the tag. Defaults to the
+	// the empty "" string.
+	Tag *string `android:"arch_variant"`
+}
+
 type nameProperties struct {
 	// The name of the module.  Must be unique across all modules.
 	Name *string
@@ -454,23 +476,13 @@
 	// relative path to a file to include in the list of notices for the device
 	Notice *string `android:"path"`
 
-	Dist struct {
-		// copy the output of this module to the $DIST_DIR when `dist` is specified on the
-		// command line and  any of these targets are also on the command line, or otherwise
-		// built
-		Targets []string `android:"arch_variant"`
+	// configuration to distribute output files from this module to the distribution
+	// directory (default: $OUT/dist, configurable with $DIST_DIR)
+	Dist Dist `android:"arch_variant"`
 
-		// The name of the output artifact. This defaults to the basename of the output of
-		// the module.
-		Dest *string `android:"arch_variant"`
-
-		// The directory within the dist directory to store the artifact. Defaults to the
-		// top level directory ("").
-		Dir *string `android:"arch_variant"`
-
-		// A suffix to add to the artifact file name (before any extension).
-		Suffix *string `android:"arch_variant"`
-	} `android:"arch_variant"`
+	// a list of configurations to distribute output files from this module to the
+	// distribution directory (default: $OUT/dist, configurable with $DIST_DIR)
+	Dists []Dist `android:"arch_variant"`
 
 	// The OsType of artifacts that this module variant is responsible for creating.
 	//
@@ -537,6 +549,14 @@
 	ImageVariation string `blueprint:"mutated"`
 }
 
+// A map of OutputFile tag keys to Paths, for disting purposes.
+type TaggedDistFiles map[string]Paths
+
+func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
+	// The default OutputFile tag is the empty "" string.
+	return TaggedDistFiles{"": paths}
+}
+
 type hostAndDeviceProperties struct {
 	// If set to true, build a variant of the module for the host.  Defaults to false.
 	Host_supported *bool
@@ -815,6 +835,41 @@
 	return m.visibilityPropertyInfo
 }
 
+func (m *ModuleBase) Dists() []Dist {
+	if len(m.commonProperties.Dist.Targets) > 0 {
+		// Make a copy of the underlying Dists slice to protect against
+		// backing array modifications with repeated calls to this method.
+		distsCopy := append([]Dist(nil), m.commonProperties.Dists...)
+		return append(distsCopy, m.commonProperties.Dist)
+	} else {
+		return m.commonProperties.Dists
+	}
+}
+
+func (m *ModuleBase) GenerateTaggedDistFiles(ctx BaseModuleContext) TaggedDistFiles {
+	distFiles := make(TaggedDistFiles)
+	for _, dist := range m.Dists() {
+		var tag string
+		var distFilesForTag Paths
+		if dist.Tag == nil {
+			tag = ""
+		} else {
+			tag = *dist.Tag
+		}
+		distFilesForTag, err := m.base().module.(OutputFileProducer).OutputFiles(tag)
+		if err != nil {
+			ctx.PropertyErrorf("dist.tag", "%s", err.Error())
+		}
+		for _, distFile := range distFilesForTag {
+			if distFile != nil && !distFiles[tag].containsPath(distFile) {
+				distFiles[tag] = append(distFiles[tag], distFile)
+			}
+		}
+	}
+
+	return distFiles
+}
+
 func (m *ModuleBase) Target() Target {
 	return m.commonProperties.CompileTarget
 }
@@ -1959,7 +2014,7 @@
 	rule blueprint.Rule, deps []Path) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
-	m.module.base().hooks.runInstallHooks(m, fullInstallPath, false)
+	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
 
 	if !m.skipInstall(fullInstallPath) {
 
@@ -1993,7 +2048,7 @@
 
 func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
-	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
+	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true)
 
 	if !m.skipInstall(fullInstallPath) {
 
@@ -2022,7 +2077,7 @@
 // (e.g. /apex/...)
 func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath {
 	fullInstallPath := installPath.Join(m, name)
-	m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
+	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
 	if !m.skipInstall(fullInstallPath) {
 		m.Build(pctx, BuildParams{
diff --git a/android/neverallow.go b/android/neverallow.go
index 26e42e6..526d399 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -145,11 +145,18 @@
 		"prebuilts",
 	}
 
-	// Core library constraints. The sdk_version: "none" can only be used in core library projects.
+	// Additional whitelisted path only used for ART testing, which needs access to core library
+	// targets. This does not affect the contents of a device image (system, vendor, etc.).
+	var artTests = []string{
+		"art/test",
+	}
+
+	// Core library constraints. The sdk_version: "none" can only be used in core library projects and ART tests.
 	// Access to core library targets is restricted using visibility rules.
 	rules := []Rule{
 		NeverAllow().
 			NotIn(coreLibraryProjects...).
+			NotIn(artTests...).
 			With("sdk_version", "none").
 			WithoutMatcher("name", Regexp("^android_.*stubs_current$")),
 	}
diff --git a/android/override_module.go b/android/override_module.go
index 7e58890..3994084 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,6 +28,7 @@
 // module based on it.
 
 import (
+	"sort"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -42,6 +43,11 @@
 	setOverridingProperties(properties []interface{})
 
 	getOverrideModuleProperties() *OverrideModuleProperties
+
+	// Internal funcs to handle interoperability between override modules and prebuilts.
+	// i.e. cases where an overriding module, too, is overridden by a prebuilt module.
+	setOverriddenByPrebuilt(overridden bool)
+	getOverriddenByPrebuilt() bool
 }
 
 // Base module struct for override module types
@@ -49,6 +55,8 @@
 	moduleProperties OverrideModuleProperties
 
 	overridingProperties []interface{}
+
+	overriddenByPrebuilt bool
 }
 
 type OverrideModuleProperties struct {
@@ -74,6 +82,14 @@
 	return proptools.String(o.moduleProperties.Base)
 }
 
+func (o *OverrideModuleBase) setOverriddenByPrebuilt(overridden bool) {
+	o.overriddenByPrebuilt = overridden
+}
+
+func (o *OverrideModuleBase) getOverriddenByPrebuilt() bool {
+	return o.overriddenByPrebuilt
+}
+
 func InitOverrideModule(m OverrideModule) {
 	m.setOverridingProperties(m.GetProperties())
 
@@ -146,6 +162,11 @@
 
 // Should NOT be used in the same mutator as addOverride.
 func (b *OverridableModuleBase) getOverrides() []OverrideModule {
+	b.overridesLock.Lock()
+	sort.Slice(b.overrides, func(i, j int) bool {
+		return b.overrides[i].Name() < b.overrides[j].Name()
+	})
+	b.overridesLock.Unlock()
 	return b.overrides
 }
 
@@ -208,21 +229,24 @@
 // next phase.
 func overrideModuleDepsMutator(ctx BottomUpMutatorContext) {
 	if module, ok := ctx.Module().(OverrideModule); ok {
-		// Skip this overriding module if there's a prebuilt module that overrides it with prefer flag.
-		overriddenByPrebuilt := false
+		base := String(module.getOverrideModuleProperties().Base)
+		if !ctx.OtherModuleExists(base) {
+			ctx.PropertyErrorf("base", "%q is not a valid module name", base)
+			return
+		}
+		// See if there's a prebuilt module that overrides this override module with prefer flag,
+		// in which case we call SkipInstall on the corresponding variant later.
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
 			prebuilt, ok := dep.(PrebuiltInterface)
 			if !ok {
 				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
 			}
 			if prebuilt.Prebuilt().UsePrebuilt() {
-				overriddenByPrebuilt = true
+				module.setOverriddenByPrebuilt(true)
 				return
 			}
 		})
-		if !overriddenByPrebuilt {
-			ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)
-		}
+		ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)
 	}
 }
 
@@ -258,6 +282,10 @@
 		ctx.AliasVariation(variants[0])
 		for i, o := range overrides {
 			mods[i+1].(OverridableModule).override(ctx, o)
+			if o.getOverriddenByPrebuilt() {
+				// The overriding module itself, too, is overridden by a prebuilt. Skip its installation.
+				mods[i+1].SkipInstall()
+			}
 		}
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
 		// Create a variant of the overriding module with its own name. This matches the above local
diff --git a/android/paths.go b/android/paths.go
index 8d2725b..066baf2 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -220,6 +220,15 @@
 // Paths is a slice of Path objects, with helpers to operate on the collection.
 type Paths []Path
 
+func (paths Paths) containsPath(path Path) bool {
+	for _, p := range paths {
+		if p == path {
+			return true
+		}
+	}
+	return false
+}
+
 // PathsForSource returns Paths rooted from SrcDir
 func PathsForSource(ctx PathContext, paths []string) Paths {
 	ret := make(Paths, len(paths))
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index dcd67d8..03cf74d 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -458,6 +458,9 @@
 			}
 			file.defs = append(file.defs, a)
 		} else {
+			if _, ok := file.globalAssignments[name]; ok {
+				return fmt.Errorf("cannot assign a variable multiple times: \"%s\"", name)
+			}
 			a := &bpparser.Assignment{
 				Name:      name,
 				NamePos:   pos,
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 4f307c4..2448acc 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -876,7 +876,7 @@
 prebuilt_etc {
 	name: "etc.test1",
 	src: "mymod",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 
 }
 `,
@@ -896,7 +896,7 @@
 	name: "etc.test1",
 
 	src: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 
 }
 `,
@@ -913,7 +913,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
     device_specific: true,
 
 }
@@ -931,7 +931,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	product_specific: true,
 
 
@@ -950,7 +950,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	product_specific: true,
 
 }
@@ -968,7 +968,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	system_ext_specific: true,
 
 }
@@ -986,7 +986,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	system_ext_specific: true,
 
 
@@ -1005,7 +1005,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	proprietary: true,
 
 }
@@ -1023,7 +1023,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	proprietary: true,
 
 }
@@ -1041,7 +1041,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	proprietary: true,
 
 }
@@ -1059,7 +1059,7 @@
 		expected: `
 prebuilt_etc {
 	name: "etc.test1",
-	sub_dir: "foo/bar",
+	relative_install_path: "foo/bar",
 	recovery: true,
 
 }
@@ -1098,7 +1098,7 @@
 	name: "foo",
 
 	src: "foo.txt",
-	sub_dir: "bar",
+	relative_install_path: "bar",
 }
 `,
 	},
@@ -1174,7 +1174,7 @@
 	name: "foo",
 
 	src: "foo.txt",
-	sub_dir: "bar",
+	relative_install_path: "bar",
 }
 `,
 	},
@@ -1193,7 +1193,7 @@
 	name: "foo",
 
 	src: "foo.fw",
-	sub_dir: "bar",
+	relative_install_path: "bar",
 }
 `,
 	},
@@ -1212,7 +1212,7 @@
 	name: "foo",
 
 	src: "foo.fw",
-	sub_dir: "bar",
+	relative_install_path: "bar",
 }
 `,
 	},
@@ -1231,7 +1231,7 @@
 	name: "foo",
 
 	src: "foo.fw",
-	sub_dir: "bar",
+	relative_install_path: "bar",
 	proprietary: true,
 }
 `,
@@ -1251,7 +1251,7 @@
 	name: "foo",
 
 	src: "foo.fw",
-	sub_dir: "bar",
+	relative_install_path: "bar",
 	proprietary: true,
 }
 `,
@@ -1389,6 +1389,35 @@
 `,
 	},
 	{
+		desc: "variableReassigned",
+		in: `
+include $(CLEAR_VARS)
+
+src_files:= a.cpp
+
+LOCAL_SRC_FILES:= $(src_files)
+LOCAL_MODULE:= test
+include $(BUILD_EXECUTABLE)
+
+# clear locally used variable
+src_files:=
+`,
+		expected: `
+
+
+src_files = ["a.cpp"]
+cc_binary {
+    name: "test",
+
+    srcs: src_files,
+}
+
+// clear locally used variable
+// ANDROIDMK TRANSLATION ERROR: cannot assign a variable multiple times: "src_files"
+// src_files :=
+`,
+	},
+	{
 		desc: "undefined_boolean_var",
 		in: `
 include $(CLEAR_VARS)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 4dd14d8..7595238 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -33,14 +33,7 @@
 			Disabled: true,
 		}
 	}
-	writers := []android.AndroidMkData{}
-	writers = append(writers, a.androidMkForType())
-	return android.AndroidMkData{
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			for _, data := range writers {
-				data.Custom(w, name, prefix, moduleDir, data)
-			}
-		}}
+	return a.androidMkForType()
 }
 
 func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string) []string {
@@ -308,6 +301,20 @@
 				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())
+
+				// Because apex writes .mk with Custom(), we need to write manually some common properties
+				// which are available via data.Entries
+				commonProperties := []string{
+					"LOCAL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
+					"LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE",
+					"LOCAL_MODULE_OWNER",
+				}
+				for _, name := range commonProperties {
+					if value, ok := data.Entries.EntryMap[name]; ok {
+						fmt.Fprintln(w, name+" := "+strings.Join(value, " "))
+					}
+				}
+
 				if len(a.overridableProperties.Overrides) > 0 {
 					fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.overridableProperties.Overrides, " "))
 				}
diff --git a/apex/apex.go b/apex/apex.go
index e308d48..d0c1a09 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"path"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -369,7 +368,6 @@
 		"libbinderthreadstateutils",
 		"libbluetooth-types-header",
 		"libbufferhub_headers",
-		"libc_scudo",
 		"libcodec2",
 		"libcodec2_headers",
 		"libcodec2_hidl@1.0",
@@ -1243,7 +1241,7 @@
 	container_certificate_file android.Path
 	container_private_key_file android.Path
 
-	fileContexts android.Path
+	fileContexts android.WritablePath
 
 	// list of files to be included in this apex
 	filesInfo []apexFile
@@ -1331,6 +1329,7 @@
 
 	targets := ctx.MultiTargets()
 	config := ctx.DeviceConfig()
+	imageVariation := a.getImageVariation(ctx)
 
 	a.combineProperties(ctx)
 
@@ -1350,13 +1349,13 @@
 				Jni_libs:           a.properties.Jni_libs,
 				Binaries:           nil,
 			},
-			target, a.getImageVariation(config))
+			target, imageVariation)
 
 		// Add native modules targetting both ABIs
 		addDependenciesForNativeModules(ctx,
 			a.properties.Multilib.Both,
 			target,
-			a.getImageVariation(config))
+			imageVariation)
 
 		isPrimaryAbi := i == 0
 		if isPrimaryAbi {
@@ -1369,13 +1368,13 @@
 					Jni_libs:           nil,
 					Binaries:           a.properties.Binaries,
 				},
-				target, a.getImageVariation(config))
+				target, imageVariation)
 
 			// Add native modules targetting the first ABI
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.First,
 				target,
-				a.getImageVariation(config))
+				imageVariation)
 		}
 
 		switch target.Arch.ArchType.Multilib {
@@ -1384,24 +1383,24 @@
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.Lib32,
 				target,
-				a.getImageVariation(config))
+				imageVariation)
 
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.Prefer32,
 				target,
-				a.getImageVariation(config))
+				imageVariation)
 		case "lib64":
 			// Add native modules targetting 64-bit ABI
 			addDependenciesForNativeModules(ctx,
 				a.properties.Multilib.Lib64,
 				target,
-				a.getImageVariation(config))
+				imageVariation)
 
 			if !has32BitTarget {
 				addDependenciesForNativeModules(ctx,
 					a.properties.Multilib.Prefer32,
 					target,
-					a.getImageVariation(config))
+					imageVariation)
 			}
 		}
 	}
@@ -1503,15 +1502,33 @@
 	return proptools.Bool(a.properties.Test_only_unsigned_payload)
 }
 
-func (a *apexBundle) getImageVariation(config android.DeviceConfig) string {
+func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
+	deviceConfig := ctx.DeviceConfig()
 	if a.vndkApex {
-		return cc.VendorVariationPrefix + a.vndkVersion(config)
+		return cc.VendorVariationPrefix + a.vndkVersion(deviceConfig)
 	}
-	if config.VndkVersion() != "" && proptools.Bool(a.properties.Use_vendor) {
-		return cc.VendorVariationPrefix + config.PlatformVndkVersion()
-	} else {
-		return android.CoreVariation
+
+	var prefix string
+	var vndkVersion string
+	if deviceConfig.VndkVersion() != "" {
+		if proptools.Bool(a.properties.Use_vendor) {
+			prefix = cc.VendorVariationPrefix
+			vndkVersion = deviceConfig.PlatformVndkVersion()
+		} else if a.SocSpecific() || a.DeviceSpecific() {
+			prefix = cc.VendorVariationPrefix
+			vndkVersion = deviceConfig.VndkVersion()
+		} else if a.ProductSpecific() {
+			prefix = cc.ProductVariationPrefix
+			vndkVersion = deviceConfig.ProductVndkVersion()
+		}
 	}
+	if vndkVersion == "current" {
+		vndkVersion = deviceConfig.PlatformVndkVersion()
+	}
+	if vndkVersion != "" {
+		return prefix + vndkVersion
+	}
+	return android.CoreVariation
 }
 
 func (a *apexBundle) EnableSanitizer(sanitizerName string) {
@@ -1543,7 +1560,7 @@
 		for _, target := range ctx.MultiTargets() {
 			if target.Arch.ArchType.Multilib == "lib64" {
 				ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
-					{Mutator: "image", Variation: a.getImageVariation(ctx.DeviceConfig())},
+					{Mutator: "image", Variation: a.getImageVariation(ctx)},
 					{Mutator: "link", Variation: "shared"},
 					{Mutator: "version", Variation: ""}, // "" is the non-stub variant
 				}...), sharedLibTag, "libclang_rt.hwasan-aarch64-android")
@@ -1725,13 +1742,8 @@
 	return true
 }
 
-// Function called while walking an APEX's payload dependencies.
-//
-// Return true if the `to` module should be visited, false otherwise.
-type payloadDepsCallback func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool
-
 // Visit dependencies that contributes to the payload of this APEX
-func (a *apexBundle) walkPayloadDeps(ctx android.ModuleContext, do payloadDepsCallback) {
+func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		am, ok := child.(android.ApexModule)
 		if !ok || !am.CanHaveApexVariants() {
@@ -1757,7 +1769,21 @@
 }
 
 func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) int {
-	ver := proptools.StringDefault(a.properties.Min_sdk_version, "current")
+	ver := proptools.String(a.properties.Min_sdk_version)
+	if ver == "" {
+		return android.FutureApiLevel
+	}
+	// Treat the current codenames as "current", which means future API version (10000)
+	// Otherwise, ApiStrToNum converts codename(non-finalized) to a value from [9000...]
+	// and would fail to build against "current".
+	if android.InList(ver, ctx.Config().PlatformVersionActiveCodenames()) {
+		return android.FutureApiLevel
+	}
+	// In "REL" branch, "current" is mapped to finalized sdk version
+	if ctx.Config().PlatformSdkCodename() == "REL" && ver == "current" {
+		return ctx.Config().PlatformSdkVersionInt()
+	}
+	// Finalized codenames are OKAY and will be converted to int
 	intVer, err := android.ApiStrToNum(ctx, ver)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
@@ -1778,6 +1804,12 @@
 		return
 	}
 
+	// Because APEXes targeting other than system/system_ext partitions
+	// can't set apex_available, we skip checks for these APEXes
+	if ctx.SocSpecific() || ctx.DeviceSpecific() || ctx.ProductSpecific() {
+		return
+	}
+
 	// Coverage build adds additional dependencies for the coverage-only runtime libraries.
 	// Requiring them and their transitive depencies with apex_available is not right
 	// because they just add noise.
@@ -1785,7 +1817,7 @@
 		return
 	}
 
-	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if externalDep {
 			// As soon as the dependency graph crosses the APEX boundary, don't go further.
 			return false
@@ -1821,6 +1853,17 @@
 	}
 }
 
+func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) {
+	if a.testApex || a.vndkApex {
+		return
+	}
+	// Meaningless to check min_sdk_version when building use_vendor modules against non-Trebleized targets
+	if proptools.Bool(a.properties.Use_vendor) && ctx.DeviceConfig().VndkVersion() == "" {
+		return
+	}
+	android.CheckMinSdkVersion(a, ctx, a.minSdkVersion(ctx))
+}
+
 // Ensures that a lib providing stub isn't statically linked
 func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
 	// Practically, we only care about regular APEXes on the device.
@@ -1828,7 +1871,7 @@
 		return
 	}
 
-	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if ccm, ok := to.(*cc.Module); ok {
 			apexName := ctx.ModuleName()
 			fromName := ctx.OtherModuleName(from)
@@ -1867,7 +1910,7 @@
 }
 
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
+	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuildApps()
 	switch a.properties.ApexType {
 	case imageApex:
 		if buildFlattenedAsDefault {
@@ -1903,6 +1946,7 @@
 
 	a.checkApexAvailability(ctx)
 	a.checkUpdatable(ctx)
+	a.checkMinSdkVersion(ctx)
 	a.checkStaticLinkingToStubLibraries(ctx)
 
 	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
@@ -1936,7 +1980,7 @@
 	})
 
 	var filesInfo []apexFile
-	// TODO(jiyong) do this using walkPayloadDeps
+	// TODO(jiyong) do this using WalkPayloadDeps
 	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
 		depTag := ctx.OtherModuleDependencyTag(child)
 		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
@@ -2080,7 +2124,7 @@
 						}
 						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
 						af.transitiveDep = true
-						if !a.Host() && !android.DirectlyInApex(ctx.ModuleName(), ctx.OtherModuleName(cc)) && (cc.IsStubs() || cc.HasStubsVariants()) {
+						if !a.Host() && !android.DirectlyInApex(ctx.ModuleName(), depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
 							// If the dependency is a stubs lib, don't include it in this APEX,
 							// but make sure that the lib is installed on the device.
 							// In case no APEX is having the lib, the lib is installed to the system
@@ -2088,8 +2132,17 @@
 							//
 							// Always include if we are a host-apex however since those won't have any
 							// system libraries.
-							if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.BaseModuleName(), a.requiredDeps) {
-								a.requiredDeps = append(a.requiredDeps, cc.BaseModuleName())
+							if !android.DirectlyInAnyApex(ctx, depName) {
+								// we need a module name for Make
+								name := cc.BaseModuleName() + cc.Properties.SubName
+								if proptools.Bool(a.properties.Use_vendor) {
+									// we don't use subName(.vendor) for a "use_vendor: true" apex
+									// which is supposed to be installed in /system
+									name = cc.BaseModuleName()
+								}
+								if !android.InList(name, a.requiredDeps) {
+									a.requiredDeps = append(a.requiredDeps, name)
+								}
 							}
 							requireNativeLibs = append(requireNativeLibs, af.Stem())
 							// Don't track further
@@ -2175,22 +2228,6 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
-	if a.properties.ApexType != zipApex {
-		if a.properties.File_contexts == nil {
-			a.fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
-		} else {
-			a.fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
-			if a.Platform() {
-				if matched, err := path.Match("system/sepolicy/**/*", a.fileContexts.String()); err != nil || !matched {
-					ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but %q", a.fileContexts)
-				}
-			}
-		}
-		if !android.ExistentPathForSource(ctx, a.fileContexts.String()).Valid() {
-			ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", a.fileContexts)
-			return
-		}
-	}
 	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
 	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
 	// the same library in the system partition, thus effectively sharing the same libraries
@@ -2200,6 +2237,12 @@
 		a.installable() &&
 		!proptools.Bool(a.properties.Use_vendor)
 
+	// APEXes targeting other than system/system_ext partitions use vendor/product variants.
+	// So we can't link them to /system/lib libs which are core variants.
+	if a.SocSpecific() || a.DeviceSpecific() || a.ProductSpecific() {
+		a.linkToSystemLib = false
+	}
+
 	// We don't need the optimization for updatable APEXes, as it might give false signal
 	// to the system health when the APEXes are still bundled (b/149805758)
 	if a.Updatable() && a.properties.ApexType == imageApex {
@@ -2214,6 +2257,8 @@
 	// prepare apex_manifest.json
 	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
 
+	a.buildFileContexts(ctx)
+
 	a.setCertificateAndPrivateKey(ctx)
 	if a.properties.ApexType == flattenedApex {
 		a.buildFlattenedApex(ctx)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 3d5886e..befb814 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -206,6 +206,7 @@
 	config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
 	config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
 	config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
+	config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
 	config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
 
 	for _, handler := range handlers {
@@ -1104,13 +1105,13 @@
 	}{
 		{
 			name:          "should link to the latest",
-			minSdkVersion: "current",
+			minSdkVersion: "",
 			shouldLink:    "30",
 			shouldNotLink: []string{"29"},
 		},
 		{
 			name:          "should link to llndk#29",
-			minSdkVersion: "29",
+			minSdkVersion: "min_sdk_version: \"29\",",
 			shouldLink:    "29",
 			shouldNotLink: []string{"30"},
 		},
@@ -1123,7 +1124,7 @@
 				key: "myapex.key",
 				use_vendor: true,
 				native_shared_libs: ["mylib"],
-				min_sdk_version: "`+tc.minSdkVersion+`",
+				`+tc.minSdkVersion+`
 			}
 
 			apex_key {
@@ -1140,6 +1141,7 @@
 				system_shared_libs: [],
 				stl: "none",
 				apex_available: [ "myapex" ],
+				min_sdk_version: "29",
 			}
 
 			cc_library {
@@ -1270,24 +1272,24 @@
 	ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_shared/libdl.so")
 }
 
-func TestApexUseStubsAccordingToMinSdkVersionInUnbundledBuild(t *testing.T) {
+func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) {
 	// there are three links between liba --> libz
-	// 1) myapex -> libx -> liba -> libz    : this should be #2 link, but fallback to #1
-	// 2) otherapex -> liby -> liba -> libz : this should be #3 link
+	// 1) myapex -> libx -> liba -> libz    : this should be #29 link, but fallback to #28
+	// 2) otherapex -> liby -> liba -> libz : this should be #30 link
 	// 3) (platform) -> liba -> libz        : this should be non-stub link
 	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["libx"],
-			min_sdk_version: "2",
+			min_sdk_version: "29",
 		}
 
 		apex {
 			name: "otherapex",
 			key: "myapex.key",
 			native_shared_libs: ["liby"],
-			min_sdk_version: "3",
+			min_sdk_version: "30",
 		}
 
 		apex_key {
@@ -1302,6 +1304,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
 		}
 
 		cc_library {
@@ -1310,6 +1313,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "otherapex" ],
+			min_sdk_version: "29",
 		}
 
 		cc_library {
@@ -1321,6 +1325,7 @@
 				"//apex_available:anyapex",
 				"//apex_available:platform",
 			],
+			min_sdk_version: "29",
 		}
 
 		cc_library {
@@ -1328,10 +1333,10 @@
 			system_shared_libs: [],
 			stl: "none",
 			stubs: {
-				versions: ["1", "3"],
+				versions: ["28", "30"],
 			},
 		}
-	`, withUnbundledBuild)
+	`)
 
 	expectLink := func(from, from_variant, to, to_variant string) {
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
@@ -1343,13 +1348,13 @@
 	}
 	// platform liba is linked to non-stub version
 	expectLink("liba", "shared", "libz", "shared")
-	// liba in myapex is linked to #1
-	expectLink("liba", "shared_myapex", "libz", "shared_1")
-	expectNoLink("liba", "shared_myapex", "libz", "shared_3")
+	// liba in myapex is linked to #28
+	expectLink("liba", "shared_myapex", "libz", "shared_28")
+	expectNoLink("liba", "shared_myapex", "libz", "shared_30")
 	expectNoLink("liba", "shared_myapex", "libz", "shared")
-	// liba in otherapex is linked to #3
-	expectLink("liba", "shared_otherapex", "libz", "shared_3")
-	expectNoLink("liba", "shared_otherapex", "libz", "shared_1")
+	// liba in otherapex is linked to #30
+	expectLink("liba", "shared_otherapex", "libz", "shared_30")
+	expectNoLink("liba", "shared_otherapex", "libz", "shared_28")
 	expectNoLink("liba", "shared_otherapex", "libz", "shared")
 }
 
@@ -1374,6 +1379,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
+			min_sdk_version: "R",
 		}
 
 		cc_library {
@@ -1407,7 +1413,7 @@
 	expectNoLink("libx", "shared_myapex", "libz", "shared")
 }
 
-func TestApexMinSdkVersionDefaultsToLatest(t *testing.T) {
+func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
@@ -1516,6 +1522,7 @@
 			name: "libx",
 			shared_libs: ["libbar"],
 			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
 		}
 
 		cc_library {
@@ -1553,6 +1560,7 @@
 		cc_library {
 			name: "libx",
 			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
 		}
 	`)
 
@@ -1564,7 +1572,7 @@
 	ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
 }
 
-func TestInvalidMinSdkVersion(t *testing.T) {
+func TestApexMinSdkVersion_ErrorIfIncompatibleStubs(t *testing.T) {
 	testApexError(t, `"libz" .*: not found a version\(<=29\)`, `
 		apex {
 			name: "myapex",
@@ -1585,6 +1593,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
 		}
 
 		cc_library {
@@ -1596,12 +1605,15 @@
 			},
 		}
 	`)
+}
 
-	testApexError(t, `"myapex" .*: min_sdk_version: SDK version should be .*`, `
+func TestApexMinSdkVersion_ErrorIfIncompatibleVersion(t *testing.T) {
+	testApexError(t, `module "mylib".*: should support min_sdk_version\(29\)`, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			min_sdk_version: "abc",
+			native_shared_libs: ["mylib"],
+			min_sdk_version: "29",
 		}
 
 		apex_key {
@@ -1609,6 +1621,67 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+			],
+			min_sdk_version: "30",
+		}
+	`)
+}
+
+func TestApexMinSdkVersion_Okay(t *testing.T) {
+	testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			java_libs: ["libbar"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libfoo_dep"],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+		}
+
+		cc_library {
+			name: "libfoo_dep",
+			srcs: ["mylib.cpp"],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+		}
+
+		java_library {
+			name: "libbar",
+			sdk_version: "current",
+			srcs: ["a.java"],
+			static_libs: ["libbar_dep"],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+		}
+
+		java_library {
+			name: "libbar_dep",
+			sdk_version: "current",
+			srcs: ["a.java"],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+		}
 	`)
 }
 
@@ -1659,6 +1732,7 @@
 					srcs: ["foo/bar/MyClass.java"],
 					sdk_version: "current",
 					apex_available: ["myapex"],
+					min_sdk_version: "29",
 				}
 			`,
 		},
@@ -1728,6 +1802,135 @@
 	}
 }
 
+func TestApexMinSdkVersion_ErrorIfDepIsNewer(t *testing.T) {
+	testApexError(t, `module "mylib2".*: should support min_sdk_version\(29\) for "myapex"`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["mylib2"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+			],
+			min_sdk_version: "29",
+		}
+
+		// indirect part of the apex
+		cc_library {
+			name: "mylib2",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex",
+			],
+			min_sdk_version: "30",
+		}
+	`)
+}
+
+func TestApexMinSdkVersion_ErrorIfDepIsNewer_Java(t *testing.T) {
+	testApexError(t, `module "bar".*: should support min_sdk_version\(29\) for "myapex"`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: ["AppFoo"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_app {
+			name: "AppFoo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "current",
+			min_sdk_version: "29",
+			system_modules: "none",
+			stl: "none",
+			static_libs: ["bar"],
+			apex_available: [ "myapex" ],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+			srcs: ["a.java"],
+			apex_available: [ "myapex" ],
+		}
+	`)
+}
+
+func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		// mylib in myapex will link to mylib2#29
+		// mylib in otherapex will link to mylib2(non-stub) in otherapex as well
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["mylib2"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: ["myapex", "otherapex"],
+			min_sdk_version: "29",
+		}
+
+		cc_library {
+			name: "mylib2",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: ["otherapex"],
+			stubs: { versions: ["29", "30"] },
+			min_sdk_version: "30",
+		}
+
+		apex {
+			name: "otherapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib", "mylib2"],
+			min_sdk_version: "30",
+		}
+	`)
+	expectLink := func(from, from_variant, to, to_variant string) {
+		ld := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld")
+		libFlags := ld.Args["libFlags"]
+		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
+	}
+	expectLink("mylib", "shared_myapex", "mylib2", "shared_29")
+	expectLink("mylib", "shared_otherapex", "mylib2", "shared_otherapex")
+}
+
 func TestFilesInSubDir(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -1902,7 +2105,7 @@
 	ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_myapex/mylib2.so")
 }
 
-func TestUseVendorRestriction(t *testing.T) {
+func TestUseVendorNotAllowedForSystemApexes(t *testing.T) {
 	testApexError(t, `module "myapex" .*: use_vendor: not allowed`, `
 		apex {
 			name: "myapex",
@@ -1958,6 +2161,141 @@
 	`)
 }
 
+func TestVendorApex(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			binaries: ["mybin"],
+			vendor: true,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		cc_binary {
+			name: "mybin",
+			vendor: true,
+			shared_libs: ["libfoo"],
+		}
+		cc_library {
+			name: "libfoo",
+			proprietary: true,
+		}
+	`)
+
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"bin/mybin",
+		"lib64/libfoo.so",
+		// TODO(b/159195575): Add an option to use VNDK libs from VNDK APEX
+		"lib64/libc++.so",
+	})
+
+	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	name := apexBundle.BaseModuleName()
+	prefix := "TARGET_"
+	var builder strings.Builder
+	data.Custom(&builder, name, prefix, "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, `LOCAL_MODULE_PATH := /tmp/target/product/test_device/vendor/apex`)
+}
+
+func TestAndroidMk_UseVendorRequired(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			use_vendor: true,
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			vendor_available: true,
+			apex_available: ["myapex"],
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorAllowListForTest(config, []string{"myapex"})
+	})
+
+	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	name := apexBundle.BaseModuleName()
+	prefix := "TARGET_"
+	var builder strings.Builder
+	data.Custom(&builder, name, prefix, "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += libc libm libdl\n")
+}
+
+func TestAndroidMk_VendorApexRequired(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			vendor: true,
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			vendor_available: true,
+		}
+	`)
+
+	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	name := apexBundle.BaseModuleName()
+	prefix := "TARGET_"
+	var builder strings.Builder
+	data.Custom(&builder, name, prefix, "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += libc.vendor libm.vendor libdl.vendor\n")
+}
+
+func TestAndroidMkWritesCommonProperties(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			vintf_fragments: ["fragment.xml"],
+			init_rc: ["init.rc"],
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		cc_binary {
+			name: "mybin",
+		}
+	`)
+
+	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	name := apexBundle.BaseModuleName()
+	prefix := "TARGET_"
+	var builder strings.Builder
+	data.Custom(&builder, name, prefix, "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_VINTF_FRAGMENTS := fragment.xml\n")
+	ensureContains(t, androidMk, "LOCAL_INIT_RC := init.rc\n")
+}
+
 func TestStaticLinking(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -2217,6 +2555,7 @@
 				"otherapex",
 			],
 			recovery_available: true,
+			min_sdk_version: "29",
 		}
 		cc_library {
 			name: "mylib2",
@@ -2228,6 +2567,7 @@
 				"otherapex",
 			],
 			use_apex_name_macro: true,
+			min_sdk_version: "29",
 		}
 	`)
 
@@ -3275,110 +3615,104 @@
 	}
 }
 
-func TestFileContexts(t *testing.T) {
+func TestFileContexts_FindInDefaultLocationIfNotSet(t *testing.T) {
 	ctx, _ := testApex(t, `
-	apex {
-		name: "myapex",
-		key: "myapex.key",
-	}
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+		}
 
-	apex_key {
-		name: "myapex.key",
-		public_key: "testkey.avbpubkey",
-		private_key: "testkey.pem",
-	}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
 	`)
 	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
-	apexRule := module.Rule("apexRule")
-	actual := apexRule.Args["file_contexts"]
-	expected := "system/sepolicy/apex/myapex-file_contexts"
-	if actual != expected {
-		t.Errorf("wrong file_contexts. expected %q. actual %q", expected, actual)
-	}
+	rule := module.Output("file_contexts")
+	ensureContains(t, rule.RuleParams.Command, "cat system/sepolicy/apex/myapex-file_contexts")
+}
 
+func TestFileContexts_ShouldBeUnderSystemSepolicyForSystemApexes(t *testing.T) {
 	testApexError(t, `"myapex" .*: file_contexts: should be under system/sepolicy`, `
-	apex {
-		name: "myapex",
-		key: "myapex.key",
-		file_contexts: "my_own_file_contexts",
-	}
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			file_contexts: "my_own_file_contexts",
+		}
 
-	apex_key {
-		name: "myapex.key",
-		public_key: "testkey.avbpubkey",
-		private_key: "testkey.pem",
-	}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
 	`, withFiles(map[string][]byte{
 		"my_own_file_contexts": nil,
 	}))
+}
 
+func TestFileContexts_ProductSpecificApexes(t *testing.T) {
 	testApexError(t, `"myapex" .*: file_contexts: cannot find`, `
-	apex {
-		name: "myapex",
-		key: "myapex.key",
-		product_specific: true,
-		file_contexts: "product_specific_file_contexts",
-	}
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			product_specific: true,
+			file_contexts: "product_specific_file_contexts",
+		}
 
-	apex_key {
-		name: "myapex.key",
-		public_key: "testkey.avbpubkey",
-		private_key: "testkey.pem",
-	}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
 	`)
 
-	ctx, _ = testApex(t, `
-	apex {
-		name: "myapex",
-		key: "myapex.key",
-		product_specific: true,
-		file_contexts: "product_specific_file_contexts",
-	}
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			product_specific: true,
+			file_contexts: "product_specific_file_contexts",
+		}
 
-	apex_key {
-		name: "myapex.key",
-		public_key: "testkey.avbpubkey",
-		private_key: "testkey.pem",
-	}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
 	`, withFiles(map[string][]byte{
 		"product_specific_file_contexts": nil,
 	}))
-	module = ctx.ModuleForTests("myapex", "android_common_myapex_image")
-	apexRule = module.Rule("apexRule")
-	actual = apexRule.Args["file_contexts"]
-	expected = "product_specific_file_contexts"
-	if actual != expected {
-		t.Errorf("wrong file_contexts. expected %q. actual %q", expected, actual)
-	}
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	rule := module.Output("file_contexts")
+	ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts")
+}
 
-	ctx, _ = testApex(t, `
-	apex {
-		name: "myapex",
-		key: "myapex.key",
-		product_specific: true,
-		file_contexts: ":my-file-contexts",
-	}
+func TestFileContexts_SetViaFileGroup(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			product_specific: true,
+			file_contexts: ":my-file-contexts",
+		}
 
-	apex_key {
-		name: "myapex.key",
-		public_key: "testkey.avbpubkey",
-		private_key: "testkey.pem",
-	}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
 
-	filegroup {
-		name: "my-file-contexts",
-		srcs: ["product_specific_file_contexts"],
-	}
+		filegroup {
+			name: "my-file-contexts",
+			srcs: ["product_specific_file_contexts"],
+		}
 	`, withFiles(map[string][]byte{
 		"product_specific_file_contexts": nil,
 	}))
-	module = ctx.ModuleForTests("myapex", "android_common_myapex_image")
-	apexRule = module.Rule("apexRule")
-	actual = apexRule.Args["file_contexts"]
-	expected = "product_specific_file_contexts"
-	if actual != expected {
-		t.Errorf("wrong file_contexts. expected %q. actual %q", expected, actual)
-	}
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	rule := module.Output("file_contexts")
+	ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts")
 }
 
 func TestApexKeyFromOtherModule(t *testing.T) {
@@ -4349,6 +4683,7 @@
 			stl: "libc++",
 			system_shared_libs: [],
 			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
 		}
 	`, withUnbundledBuild)
 
@@ -4749,6 +5084,7 @@
 				"myapex.updatable",
 				"//apex_available:platform",
 			],
+			min_sdk_version: "current",
 		}
 
 		cc_library {
@@ -4761,6 +5097,7 @@
 				"myapex.updatable",
 				"//apex_available:platform",
 			],
+			min_sdk_version: "current",
 		}
 
 		java_library {
@@ -4774,6 +5111,7 @@
 				"myapex.updatable",
 				"//apex_available:platform",
 			],
+			min_sdk_version: "current",
 		}
 
 		java_library {
@@ -4786,6 +5124,7 @@
 				"myapex.updatable",
 				"//apex_available:platform",
 			],
+			min_sdk_version: "current",
 		}
 	`
 
@@ -5393,7 +5732,7 @@
 	apexKeysText := ctx.SingletonForTests("apex_keys_text")
 	content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"]
 	ensureContains(t, content, `name="myapex_set.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`)
-	ensureNotContains(t, content, "myapex.apex")
+	ensureContains(t, content, `name="myapex.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`)
 }
 
 func TestAllowedFiles(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index af43417..5fb9a5f 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -17,6 +17,7 @@
 import (
 	"encoding/json"
 	"fmt"
+	"path"
 	"path/filepath"
 	"runtime"
 	"sort"
@@ -231,10 +232,47 @@
 	})
 }
 
+func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) {
+	if a.properties.ApexType == zipApex {
+		return
+	}
+	var fileContexts android.Path
+	if a.properties.File_contexts == nil {
+		fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
+	} else {
+		fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
+	}
+	if a.Platform() {
+		if matched, err := path.Match("system/sepolicy/**/*", fileContexts.String()); err != nil || !matched {
+			ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but %q", fileContexts)
+			return
+		}
+	}
+	if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() {
+		ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", a.fileContexts)
+		return
+	}
+
+	output := android.PathForModuleOut(ctx, "file_contexts")
+	rule := android.NewRuleBuilder()
+	// remove old file
+	rule.Command().Text("rm").FlagWithOutput("-f ", output)
+	// copy file_contexts
+	rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output)
+	// new line
+	rule.Command().Text("echo").Text(">>").Output(output)
+	// force-label /apex_manifest.pb and / as system_file so that apexd can read them
+	rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output)
+	rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output)
+	rule.Build(pctx, ctx, "file_contexts."+a.Name(), "Generate file_contexts")
+
+	a.fileContexts = output.OutputPath
+}
+
 func (a *apexBundle) buildNoticeFiles(ctx android.ModuleContext, apexFileName string) android.NoticeOutputs {
 	var noticeFiles android.Paths
 
-	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if externalDep {
 			// As soon as the dependency graph crosses the APEX boundary, don't go further.
 			return false
@@ -733,7 +771,7 @@
 	}
 
 	depInfos := android.DepNameToDepInfoMap{}
-	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if from.Name() == to.Name() {
 			// This can happen for cc.reuseObjTag. We are not interested in tracking this.
 			// As soon as the dependency graph crosses the APEX boundary, don't go further.
diff --git a/apex/key.go b/apex/key.go
index a68f6e1..d2d5786 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -160,12 +160,6 @@
 				presigned: true,
 				partition: m.PartitionTag(ctx.DeviceConfig()),
 			}
-
-			for _, om := range m.Overrides() {
-				if _, ok := apexKeyMap[om]; ok {
-					delete(apexKeyMap, om)
-				}
-			}
 			apexKeyMap[m.BaseModuleName()] = entry
 		}
 	})
diff --git a/bpfix/Android.bp b/bpfix/Android.bp
index e291578..b244e3a 100644
--- a/bpfix/Android.bp
+++ b/bpfix/Android.bp
@@ -44,11 +44,9 @@
         "bpfix/bpfix.go",
     ],
     testSrcs: [
-      "bpfix/bpfix_test.go",
+        "bpfix/bpfix_test.go",
     ],
     deps: [
         "blueprint-parser",
     ],
 }
-
-
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index e731750..689cbd1 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -533,7 +533,7 @@
 		updated = true
 	} else if trimmedPath := strings.TrimPrefix(path, f.prefix+"/"); trimmedPath != path {
 		m.Properties = append(m.Properties, &parser.Property{
-			Name:  "sub_dir",
+			Name:  "relative_install_path",
 			Value: &parser.String{Value: trimmedPath},
 		})
 		updated = true
@@ -581,6 +581,8 @@
 		// 'srcs' --> 'src' conversion
 		convertToSingleSource(mod, "src")
 
+		renameProperty(mod, "sub_dir", "relative_install_dir")
+
 		// The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
 		// 'local_module_path'. Analyze its contents and create the correct sub_dir:,
 		// filename: and boolean attributes combination
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 64a7b93..8988177 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -742,6 +742,22 @@
 		}
 		`,
 		},
+		{
+			name: "prebuilt_etc sub_dir",
+			in: `
+			prebuilt_etc {
+			name: "foo",
+			src: "bar",
+			sub_dir: "baz",
+		}
+		`,
+			out: `prebuilt_etc {
+			name: "foo",
+			src: "bar",
+			relative_install_dir: "baz",
+		}
+		`,
+		},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index b3ad610..3f812c2 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -249,7 +249,10 @@
 		entries.Class = "HEADER_LIBRARIES"
 	}
 
-	entries.DistFile = library.distFile
+	if library.distFile != nil {
+		entries.DistFiles = android.MakeDefaultDistFiles(library.distFile)
+	}
+
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 		library.androidMkWriteExportedFlags(entries)
 		library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
@@ -318,7 +321,7 @@
 	ctx.subAndroidMk(entries, binary.baseInstaller)
 
 	entries.Class = "EXECUTABLES"
-	entries.DistFile = binary.distFile
+	entries.DistFiles = binary.distFiles
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 		entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", binary.unstrippedOutputFile.String())
 		if len(binary.symlinks) > 0 {
diff --git a/cc/binary.go b/cc/binary.go
index 251b7f0..565cb8a 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -98,8 +98,8 @@
 	// Output archive of gcno coverage information
 	coverageOutputFile android.OptionalPath
 
-	// Location of the file that should be copied to dist dir when requested
-	distFile android.OptionalPath
+	// Location of the files that should be copied to dist dir when requested
+	distFiles android.TaggedDistFiles
 
 	post_install_cmds []string
 }
@@ -367,11 +367,11 @@
 			binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
 		} else {
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
-			binary.distFile = android.OptionalPathForPath(versionedOutputFile)
+			binary.distFiles = android.MakeDefaultDistFiles(versionedOutputFile)
 
 			if binary.stripper.needsStrip(ctx) {
 				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
-				binary.distFile = android.OptionalPathForPath(out)
+				binary.distFiles = android.MakeDefaultDistFiles(out)
 				binary.stripper.stripExecutableOrSharedLib(ctx, versionedOutputFile, out, builderFlags)
 			}
 
diff --git a/cc/cc.go b/cc/cc.go
index ebdb46c..e52adaf 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -136,7 +136,6 @@
 
 	// Paths to generated source files
 	GeneratedSources android.Paths
-	GeneratedHeaders android.Paths
 	GeneratedDeps    android.Paths
 
 	Flags                      []string
@@ -873,7 +872,7 @@
 }
 
 func (c *Module) IsNdk() bool {
-	return inList(c.Name(), ndkMigratedLibs)
+	return inList(c.Name(), ndkKnownLibs)
 }
 
 func (c *Module) isLlndk(config android.Config) bool {
@@ -1759,8 +1758,6 @@
 	variantNdkLibs := []string{}
 	variantLateNdkLibs := []string{}
 	if ctx.Os() == android.Android {
-		version := ctx.sdkVersion()
-
 		// rewriteLibs takes a list of names of shared libraries and scans it for three types
 		// of names:
 		//
@@ -1802,12 +1799,8 @@
 			for _, entry := range list {
 				// strip #version suffix out
 				name, _ := StubsLibNameAndVersion(entry)
-				if ctx.useSdk() && inList(name, ndkPrebuiltSharedLibraries) {
-					if !inList(name, ndkMigratedLibs) {
-						nonvariantLibs = append(nonvariantLibs, name+".ndk."+version)
-					} else {
-						variantLibs = append(variantLibs, name+ndkLibrarySuffix)
-					}
+				if ctx.useSdk() && inList(name, ndkKnownLibs) {
+					variantLibs = append(variantLibs, name+ndkLibrarySuffix)
 				} else if ctx.useVndk() {
 					nonvariantLibs = append(nonvariantLibs, rewriteVendorLibs(entry))
 				} else if (ctx.Platform() || ctx.ProductSpecific()) && inList(name, *vendorPublicLibraries) {
@@ -2234,8 +2227,6 @@
 				fallthrough
 			case genHeaderDepTag, genHeaderExportDepTag:
 				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
-					depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders,
-						genRule.GeneratedSourceFiles()...)
 					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps,
 						genRule.GeneratedDeps()...)
 					dirs := genRule.GeneratedHeaderDirs()
@@ -2423,7 +2414,6 @@
 			if _, ok := ccDep.(*Module); ok {
 				if i, ok := ccDep.(*Module).linker.(exportedFlagsProducer); ok {
 					depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, i.exportedSystemDirs()...)
-					depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders, i.exportedGeneratedHeaders()...)
 					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, i.exportedDeps()...)
 					depPaths.Flags = append(depPaths.Flags, i.exportedFlags()...)
 
@@ -2653,7 +2643,6 @@
 	depPaths.Flags = android.FirstUniqueStrings(depPaths.Flags)
 	depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs)
 	depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs)
-	depPaths.GeneratedHeaders = android.FirstUniquePaths(depPaths.GeneratedHeaders)
 	depPaths.GeneratedDeps = android.FirstUniquePaths(depPaths.GeneratedDeps)
 	depPaths.ReexportedDirs = android.FirstUniquePaths(depPaths.ReexportedDirs)
 	depPaths.ReexportedSystemDirs = android.FirstUniquePaths(depPaths.ReexportedSystemDirs)
@@ -2905,6 +2894,54 @@
 	return true
 }
 
+// b/154667674: refactor this to handle "current" in a consistent way
+func decodeSdkVersionString(ctx android.BaseModuleContext, versionString string) (int, error) {
+	if versionString == "" {
+		return 0, fmt.Errorf("not specified")
+	}
+	if versionString == "current" {
+		if ctx.Config().PlatformSdkCodename() == "REL" {
+			return ctx.Config().PlatformSdkVersionInt(), nil
+		}
+		return android.FutureApiLevel, nil
+	}
+	return android.ApiStrToNum(ctx, versionString)
+}
+
+func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	// We ignore libclang_rt.* prebuilt libs since they declare sdk_version: 14(b/121358700)
+	if strings.HasPrefix(ctx.OtherModuleName(c), "libclang_rt") {
+		return nil
+	}
+	// b/154569636: set min_sdk_version correctly for toolchain_libraries
+	if c.ToolchainLibrary() {
+		return nil
+	}
+	// We don't check for prebuilt modules
+	if _, ok := c.linker.(prebuiltLinkerInterface); ok {
+		return nil
+	}
+	minSdkVersion := c.MinSdkVersion()
+	if minSdkVersion == "apex_inherit" {
+		return nil
+	}
+	if minSdkVersion == "" {
+		// JNI libs within APK-in-APEX fall into here
+		// Those are okay to set sdk_version instead
+		// We don't have to check if this is a SDK variant because
+		// non-SDK variant resets sdk_version, which works too.
+		minSdkVersion = c.SdkVersion()
+	}
+	ver, err := decodeSdkVersionString(ctx, minSdkVersion)
+	if err != nil {
+		return err
+	}
+	if ver > sdkVersion {
+		return fmt.Errorf("newer SDK(%v)", ver)
+	}
+	return nil
+}
+
 //
 // Defaults
 //
@@ -2940,6 +2977,7 @@
 		&BinaryLinkerProperties{},
 		&TestProperties{},
 		&TestBinaryProperties{},
+		&BenchmarkProperties{},
 		&FuzzProperties{},
 		&StlProperties{},
 		&SanitizeProperties{},
@@ -2966,6 +3004,9 @@
 
 		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
 			lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs...)
+
+		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
+			lib.baseCompiler.Properties.Target.Vendor.Exclude_generated_sources...)
 	}
 }
 
@@ -2976,6 +3017,9 @@
 
 		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
 			lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+
+		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
+			lib.baseCompiler.Properties.Target.Recovery.Exclude_generated_sources...)
 	}
 }
 
diff --git a/cc/compiler.go b/cc/compiler.go
index b5f297c..d5ea2c3 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -86,6 +86,10 @@
 	// genrule modules.
 	Generated_sources []string `android:"arch_variant"`
 
+	// list of generated sources that should not be used to build the C/C++ module.
+	// This is most useful in the arch/multilib variants to remove non-common files
+	Exclude_generated_sources []string `android:"arch_variant"`
+
 	// list of generated headers to add to the include path. These are the names
 	// of genrule modules.
 	Generated_headers []string `android:"arch_variant"`
@@ -150,6 +154,10 @@
 			// List of additional cflags that should be used to build the vendor
 			// variant of the C/C++ module.
 			Cflags []string
+
+			// list of generated sources that should not be used to
+			// build the vendor variant of the C/C++ module.
+			Exclude_generated_sources []string
 		}
 		Recovery struct {
 			// list of source files that should only be used in the
@@ -163,6 +171,10 @@
 			// List of additional cflags that should be used to build the recovery
 			// variant of the C/C++ module.
 			Cflags []string
+
+			// list of generated sources that should not be used to
+			// build the recovery variant of the C/C++ module.
+			Exclude_generated_sources []string
 		}
 	}
 
@@ -227,6 +239,7 @@
 
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
+	deps.GeneratedSources = removeListFromList(deps.GeneratedSources, compiler.Properties.Exclude_generated_sources)
 	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
diff --git a/cc/library.go b/cc/library.go
index 968702e..98f4d48 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -369,7 +369,7 @@
 	unstrippedOutputFile android.Path
 
 	// Location of the file that should be copied to dist dir when requested
-	distFile android.OptionalPath
+	distFile android.Path
 
 	versionScriptPath android.ModuleGenPath
 
@@ -894,7 +894,7 @@
 			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
 		} else {
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
-			library.distFile = android.OptionalPathForPath(versionedOutputFile)
+			library.distFile = versionedOutputFile
 			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
 		}
 	}
@@ -988,11 +988,11 @@
 			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
 		} else {
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
-			library.distFile = android.OptionalPathForPath(versionedOutputFile)
+			library.distFile = versionedOutputFile
 
 			if library.stripper.needsStrip(ctx) {
 				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
-				library.distFile = android.OptionalPathForPath(out)
+				library.distFile = out
 				library.stripper.stripExecutableOrSharedLib(ctx, versionedOutputFile, out, builderFlags)
 			}
 
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 4410302..9c54399 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -380,7 +380,11 @@
 	// Make sure that the include directories are unique.
 	p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs)
 	p.exportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs)
-	p.ExportedSystemIncludeDirs = android.FirstUniquePaths(ccModule.ExportedSystemIncludeDirs())
+
+	// Take a copy before filtering out duplicates to avoid changing the slice owned by the
+	// ccModule.
+	dirs := append(android.Paths(nil), ccModule.ExportedSystemIncludeDirs()...)
+	p.ExportedSystemIncludeDirs = android.FirstUniquePaths(dirs)
 
 	p.ExportedFlags = ccModule.ExportedFlags()
 	if ccModule.linker != nil {
diff --git a/cc/linker.go b/cc/linker.go
index c9cbd9b..58f8a29 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -298,17 +298,6 @@
 			}
 		}
 
-		if inList("libc_scudo", deps.SharedLibs) {
-			// libc_scudo is an alternate implementation of all
-			// allocation functions (malloc, free), that uses
-			// the scudo allocator instead of the default native
-			// allocator. If this library is in the list, make
-			// sure it's first so it properly overrides the
-			// allocation functions of all other shared libraries.
-			_, deps.SharedLibs = removeFromList("libc_scudo", deps.SharedLibs)
-			deps.SharedLibs = append([]string{"libc_scudo"}, deps.SharedLibs...)
-		}
-
 		// If libc and libdl are both in system_shared_libs make sure libdl comes after libc
 		// to avoid loading libdl before libc.
 		if inList("libdl", systemSharedLibs) && inList("libc", systemSharedLibs) &&
diff --git a/cc/makevars.go b/cc/makevars.go
index 0f9f4c1..968eeb5 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -97,7 +97,6 @@
 	ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ClangExternalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideClangGlobalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
-	ctx.Strict("NDK_PREBUILT_SHARED_LIBRARIES", strings.Join(ndkPrebuiltSharedLibs, " "))
 
 	ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
 
@@ -174,8 +173,8 @@
 	ctx.StrictRaw("SRC_HEADERS", strings.Join(includes, " "))
 	ctx.StrictRaw("SRC_SYSTEM_HEADERS", strings.Join(systemIncludes, " "))
 
-	sort.Strings(ndkMigratedLibs)
-	ctx.Strict("NDK_MIGRATED_LIBS", strings.Join(ndkMigratedLibs, " "))
+	sort.Strings(ndkKnownLibs)
+	ctx.Strict("NDK_KNOWN_LIBS", strings.Join(ndkKnownLibs, " "))
 
 	hostTargets := ctx.Config().Targets[android.BuildOs]
 	makeVarsToolchain(ctx, "", hostTargets[0])
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 5ef9a78..6299b00 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -47,37 +47,10 @@
 
 	ndkLibrarySuffix = ".ndk"
 
-	ndkPrebuiltSharedLibs = []string{
-		"aaudio",
-		"amidi",
-		"android",
-		"binder_ndk",
-		"c",
-		"camera2ndk",
-		"dl",
-		"EGL",
-		"GLESv1_CM",
-		"GLESv2",
-		"GLESv3",
-		"jnigraphics",
-		"log",
-		"mediandk",
-		"nativewindow",
-		"m",
-		"neuralnetworks",
-		"OpenMAXAL",
-		"OpenSLES",
-		"stdc++",
-		"sync",
-		"vulkan",
-		"z",
-	}
-	ndkPrebuiltSharedLibraries = addPrefix(append([]string(nil), ndkPrebuiltSharedLibs...), "lib")
-
-	// These libraries have migrated over to the new ndk_library, which is added
-	// as a variation dependency via depsMutator.
-	ndkMigratedLibs     = []string{}
-	ndkMigratedLibsLock sync.Mutex // protects ndkMigratedLibs writes during parallel BeginMutator
+	// Added as a variation dependency via depsMutator.
+	ndkKnownLibs = []string{}
+	// protects ndkKnownLibs writes during parallel BeginMutator.
+	ndkKnownLibsLock sync.Mutex
 )
 
 // Creates a stub shared library based on the provided version file.
@@ -257,14 +230,14 @@
 		ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix)
 	}
 
-	ndkMigratedLibsLock.Lock()
-	defer ndkMigratedLibsLock.Unlock()
-	for _, lib := range ndkMigratedLibs {
+	ndkKnownLibsLock.Lock()
+	defer ndkKnownLibsLock.Unlock()
+	for _, lib := range ndkKnownLibs {
 		if lib == name {
 			return
 		}
 	}
-	ndkMigratedLibs = append(ndkMigratedLibs, name)
+	ndkKnownLibs = append(ndkKnownLibs, name)
 }
 
 func addStubLibraryCompilerFlags(flags Flags) Flags {
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index c4d7708..acdc581 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -145,6 +145,7 @@
 		libraryDecorator: library,
 	}
 	module.installer = nil
+	module.Properties.Sdk_version = StringPtr("minimum")
 	module.Properties.HideFromMake = true
 	module.Properties.AlwaysSdk = true
 	module.Properties.Sdk_version = StringPtr("current")
diff --git a/cc/pgo.go b/cc/pgo.go
index 88903bb..9298e7a 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -34,7 +34,7 @@
 
 	globalPgoProfileProjects = []string{
 		"toolchain/pgo-profiles",
-		"vendor/google_data/pgo-profiles",
+		"vendor/google_data/pgo_profile",
 	}
 )
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index aaaf694..72ad6d7 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -408,16 +408,6 @@
 		return deps
 	}
 
-	if ctx.Device() {
-		if Bool(sanitize.Properties.Sanitize.Address) {
-			// Compiling asan and having libc_scudo in the same
-			// executable will cause the executable to crash.
-			// Remove libc_scudo since it is only used to override
-			// allocation functions which asan already overrides.
-			_, deps.SharedLibs = removeFromList("libc_scudo", deps.SharedLibs)
-		}
-	}
-
 	return deps
 }
 
diff --git a/cc/testing.go b/cc/testing.go
index 479b424..b0c3c162 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -32,6 +32,7 @@
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
+	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 }
 
 func GatherRequiredDepsForTest(oses ...android.OsType) string {
@@ -273,6 +274,7 @@
 			vendor_available: true,
 			recovery_available: true,
 			host_supported: true,
+			min_sdk_version: "29",
 			apex_available: [
 				"//apex_available:platform",
 				"//apex_available:anyapex",
@@ -287,6 +289,7 @@
 			vendor_available: true,
 			recovery_available: true,
 			host_supported: true,
+			min_sdk_version: "29",
 			vndk: {
 				enabled: true,
 				support_system_process: true,
@@ -305,6 +308,7 @@
 			host_supported: false,
 			vendor_available: true,
 			recovery_available: true,
+			min_sdk_version: "29",
 			apex_available: [
 				"//apex_available:platform",
 				"//apex_available:anyapex",
@@ -338,6 +342,7 @@
 			recovery_available: true,
 			vendor_available: true,
 			native_bridge_supported: true,
+			min_sdk_version: "29",
 			stl: "none",
 		}
 
@@ -365,6 +370,7 @@
 			recovery_available: true,
 			vendor_available: true,
 			native_bridge_supported: true,
+			min_sdk_version: "29",
 			stl: "none",
 		}
 
@@ -388,25 +394,22 @@
 			system_shared_libs: [],
 		}
 
-		cc_library {
-			name: "libc.ndk.current",
-			sdk_version: "current",
-			stl: "none",
-			system_shared_libs: [],
+		ndk_library {
+			name: "libc",
+			first_version: "minimum",
+			symbol_file: "libc.map.txt",
 		}
 
-		cc_library {
-			name: "libm.ndk.current",
-			sdk_version: "current",
-			stl: "none",
-			system_shared_libs: [],
+		ndk_library {
+			name: "libm",
+			first_version: "minimum",
+			symbol_file: "libm.map.txt",
 		}
 
-		cc_library {
-			name: "libdl.ndk.current",
-			sdk_version: "current",
-			stl: "none",
-			system_shared_libs: [],
+		ndk_library {
+			name: "libdl",
+			first_version: "minimum",
+			symbol_file: "libdl.map.txt",
 		}
 
 		ndk_prebuilt_object {
@@ -498,7 +501,6 @@
 	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
 	ctx.RegisterModuleType("cc_test", TestFactory)
 	ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
-	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 4dad730..fec0c8b 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -569,7 +569,13 @@
 			return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
 		}
 		if l.shared() {
-			return m.outputFile.Valid() && !m.IsVndk()
+			if !m.outputFile.Valid() {
+				return false
+			}
+			if !m.IsVndk() {
+				return true
+			}
+			return m.isVndkExt()
 		}
 		return true
 	}
@@ -669,7 +675,16 @@
 
 		// Common properties among snapshots.
 		prop.ModuleName = ctx.ModuleName(m)
-		prop.RelativeInstallPath = m.RelativeInstallPath()
+		if m.isVndkExt() {
+			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
+			if m.isVndkSp() {
+				prop.RelativeInstallPath = "vndk-sp"
+			} else {
+				prop.RelativeInstallPath = "vndk"
+			}
+		} else {
+			prop.RelativeInstallPath = m.RelativeInstallPath()
+		}
 		prop.RuntimeLibs = m.Properties.SnapshotRuntimeLibs
 		prop.Required = m.RequiredModuleNames()
 		for _, path := range m.InitRc() {
diff --git a/cmd/extract_apks/Android.bp b/cmd/extract_apks/Android.bp
index 90548cd..f8fe864 100644
--- a/cmd/extract_apks/Android.bp
+++ b/cmd/extract_apks/Android.bp
@@ -6,7 +6,7 @@
         "golang-protobuf-proto",
         "soong-cmd-extract_apks-proto",
     ],
-    testSrcs: ["main_test.go"]
+    testSrcs: ["main_test.go"],
 }
 
 bootstrap_go_package {
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index e9a850e..db54ffb 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -24,6 +24,7 @@
 	"math"
 	"os"
 	"regexp"
+	"sort"
 	"strings"
 
 	"github.com/golang/protobuf/proto"
@@ -355,7 +356,7 @@
 
 // Writes out selected entries, renaming them as needed
 func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
-	writer Zip2ZipWriter) error {
+	writer Zip2ZipWriter, partition string) ([]string, error) {
 	// Renaming rules:
 	//  splits/MODULE-master.apk to STEM.apk
 	// else
@@ -389,10 +390,11 @@
 	}
 
 	entryOrigin := make(map[string]string) // output entry to input entry
+	var apkcerts []string
 	for _, apk := range selected.entries {
 		apkFile, ok := apkSet.entries[apk]
 		if !ok {
-			return fmt.Errorf("TOC refers to an entry %s which does not exist", apk)
+			return nil, fmt.Errorf("TOC refers to an entry %s which does not exist", apk)
 		}
 		inName := apkFile.Name
 		outName, ok := renamer(inName)
@@ -405,10 +407,15 @@
 		}
 		entryOrigin[outName] = inName
 		if err := writer.CopyFrom(apkFile, outName); err != nil {
-			return err
+			return nil, err
+		}
+		if partition != "" {
+			apkcerts = append(apkcerts, fmt.Sprintf(
+				`name="%s" certificate="PRESIGNED" private_key="" partition="%s"`, outName, partition))
 		}
 	}
-	return nil
+	sort.Strings(apkcerts)
+	return apkcerts, nil
 }
 
 func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error {
@@ -433,6 +440,9 @@
 	}
 	extractSingle = flag.Bool("extract-single", false,
 		"extract a single target and output it uncompressed. only available for standalone apks and apexes.")
+	apkcertsOutput = flag.String("apkcerts", "",
+		"optional apkcerts.txt output file containing signing info of all outputted apks")
+	partition = flag.String("partition", "", "partition string. required when -apkcerts is used.")
 )
 
 // Parse abi values
@@ -485,7 +495,8 @@
 func processArgs() {
 	flag.Usage = func() {
 		fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+
-			`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] <APK set>`)
+			`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `+
+			`[-apkcerts <apkcerts output file> -partition <partition>] <APK set>`)
 		flag.PrintDefaults()
 		os.Exit(2)
 	}
@@ -498,7 +509,8 @@
 		"allow prereleased")
 	flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
 	flag.Parse()
-	if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 || (targetConfig.stem == "" && !*extractSingle) {
+	if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 ||
+		(targetConfig.stem == "" && !*extractSingle) || (*apkcertsOutput != "" && *partition == "") {
 		flag.Usage()
 	}
 	targetConfig.sdkVersion = int32(*version)
@@ -536,7 +548,20 @@
 				log.Fatal(err)
 			}
 		}()
-		err = apkSet.writeApks(sel, targetConfig, writer)
+		apkcerts, err := apkSet.writeApks(sel, targetConfig, writer, *partition)
+		if err == nil && *apkcertsOutput != "" {
+			apkcertsFile, err := os.Create(*apkcertsOutput)
+			if err != nil {
+				log.Fatal(err)
+			}
+			defer apkcertsFile.Close()
+			for _, a := range apkcerts {
+				_, err = apkcertsFile.WriteString(a + "\n")
+				if err != nil {
+					log.Fatal(err)
+				}
+			}
+		}
 	}
 	if err != nil {
 		log.Fatal(err)
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
index bdd4bec..c3e6a2d 100644
--- a/cmd/extract_apks/main_test.go
+++ b/cmd/extract_apks/main_test.go
@@ -16,10 +16,11 @@
 
 import (
 	"fmt"
-	"github.com/golang/protobuf/proto"
 	"reflect"
 	"testing"
 
+	"github.com/golang/protobuf/proto"
+
 	bp "android/soong/cmd/extract_apks/bundle_proto"
 	"android/soong/third_party/zip"
 )
@@ -430,48 +431,63 @@
 	return nil
 }
 
-type testCaseWriteZip struct {
+type testCaseWriteApks struct {
 	name       string
 	moduleName string
 	stem       string
+	partition  string
 	// what we write from what
-	expected map[string]string
+	expectedZipEntries map[string]string
+	expectedApkcerts   []string
 }
 
-func TestWriteZip(t *testing.T) {
-	testCases := []testCaseWriteZip{
+func TestWriteApks(t *testing.T) {
+	testCases := []testCaseWriteApks{
 		{
 			name:       "splits",
 			moduleName: "mybase",
 			stem:       "Foo",
-			expected: map[string]string{
+			partition:  "system",
+			expectedZipEntries: map[string]string{
 				"Foo.apk":       "splits/mybase-master.apk",
 				"Foo-xhdpi.apk": "splits/mybase-xhdpi.apk",
 			},
+			expectedApkcerts: []string{
+				`name="Foo-xhdpi.apk" certificate="PRESIGNED" private_key="" partition="system"`,
+				`name="Foo.apk" certificate="PRESIGNED" private_key="" partition="system"`,
+			},
 		},
 		{
 			name:       "universal",
 			moduleName: "base",
 			stem:       "Bar",
-			expected: map[string]string{
+			partition:  "product",
+			expectedZipEntries: map[string]string{
 				"Bar.apk": "universal.apk",
 			},
+			expectedApkcerts: []string{
+				`name="Bar.apk" certificate="PRESIGNED" private_key="" partition="product"`,
+			},
 		},
 	}
 	for _, testCase := range testCases {
 		apkSet := ApkSet{entries: make(map[string]*zip.File)}
 		sel := SelectionResult{moduleName: testCase.moduleName}
-		for _, in := range testCase.expected {
+		for _, in := range testCase.expectedZipEntries {
 			apkSet.entries[in] = &zip.File{FileHeader: zip.FileHeader{Name: in}}
 			sel.entries = append(sel.entries, in)
 		}
 		writer := testZip2ZipWriter{make(map[string]string)}
 		config := TargetConfig{stem: testCase.stem}
-		if err := apkSet.writeApks(sel, config, writer); err != nil {
+		apkcerts, err := apkSet.writeApks(sel, config, writer, testCase.partition)
+		if err != nil {
 			t.Error(err)
 		}
-		if !reflect.DeepEqual(testCase.expected, writer.entries) {
-			t.Errorf("expected %v, got %v", testCase.expected, writer.entries)
+		if !reflect.DeepEqual(testCase.expectedZipEntries, writer.entries) {
+			t.Errorf("expected zip entries %v, got %v", testCase.expectedZipEntries, writer.entries)
+		}
+		if !reflect.DeepEqual(testCase.expectedApkcerts, apkcerts) {
+			t.Errorf("expected apkcerts %v, got %v", testCase.expectedApkcerts, apkcerts)
 		}
 	}
 }
diff --git a/cmd/extract_jar_packages/Android.bp b/cmd/extract_jar_packages/Android.bp
index ea0cbbf..4ea8798 100644
--- a/cmd/extract_jar_packages/Android.bp
+++ b/cmd/extract_jar_packages/Android.bp
@@ -22,4 +22,3 @@
         "extract_jar_packages.go",
     ],
 }
-
diff --git a/cmd/extract_linker/Android.bp b/cmd/extract_linker/Android.bp
index fe76ae4..690c4fa 100644
--- a/cmd/extract_linker/Android.bp
+++ b/cmd/extract_linker/Android.bp
@@ -17,4 +17,3 @@
     srcs: ["main.go"],
     testSrcs: ["main_test.go"],
 }
-
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index f70c86e..8c97b6d 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -15,10 +15,10 @@
 blueprint_go_binary {
     name: "merge_zips",
     deps: [
-      "android-archive-zip",
-      "blueprint-pathtools",
-      "soong-jar",
-      "soong-zip",
+        "android-archive-zip",
+        "blueprint-pathtools",
+        "soong-jar",
+        "soong-zip",
     ],
     srcs: [
         "merge_zips.go",
@@ -27,4 +27,3 @@
         "merge_zips_test.go",
     ],
 }
-
diff --git a/cmd/sbox/Android.bp b/cmd/sbox/Android.bp
index a706810d..6fa304e 100644
--- a/cmd/sbox/Android.bp
+++ b/cmd/sbox/Android.bp
@@ -19,4 +19,3 @@
         "sbox.go",
     ],
 }
-
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 6b79823..b559bac 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -29,4 +29,3 @@
     ],
     primaryBuilder: true,
 }
-
diff --git a/cmd/soong_env/Android.bp b/cmd/soong_env/Android.bp
index 4cdc396..4db0da3 100644
--- a/cmd/soong_env/Android.bp
+++ b/cmd/soong_env/Android.bp
@@ -22,4 +22,3 @@
     ],
     default: true,
 }
-
diff --git a/cmd/zip2zip/Android.bp b/cmd/zip2zip/Android.bp
index 68d8bc7..2c4cd82 100644
--- a/cmd/zip2zip/Android.bp
+++ b/cmd/zip2zip/Android.bp
@@ -24,4 +24,3 @@
     ],
     testSrcs: ["zip2zip_test.go"],
 }
-
diff --git a/cmd/zipsync/Android.bp b/cmd/zipsync/Android.bp
index 22feadc..49b5f3e 100644
--- a/cmd/zipsync/Android.bp
+++ b/cmd/zipsync/Android.bp
@@ -22,4 +22,3 @@
         "zipsync.go",
     ],
 }
-
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index bc44b21..2cf65fe 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -383,7 +383,7 @@
 		SoongZip:         ctx.Config().HostToolPath(ctx, "soong_zip"),
 		Zip2zip:          ctx.Config().HostToolPath(ctx, "zip2zip"),
 		ManifestCheck:    ctx.Config().HostToolPath(ctx, "manifest_check"),
-		ConstructContext: android.PathForSource(ctx, "build/make/core/construct_context.sh"),
+		ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"),
 	}
 }
 
@@ -574,7 +574,7 @@
 			SoongZip:         android.PathForTesting("soong_zip"),
 			Zip2zip:          android.PathForTesting("zip2zip"),
 			ManifestCheck:    android.PathForTesting("manifest_check"),
-			ConstructContext: android.PathForTesting("construct_context.sh"),
+			ConstructContext: android.PathForTesting("construct_context"),
 		}
 	}).(*GlobalSoongConfig)
 }
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 9cbe6e5..e49fa98 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -208,7 +208,7 @@
 // targetSdkVersion in the manifest or APK is less than that API version.
 type classLoaderContextMap map[int]*classLoaderContext
 
-const anySdkVersion int = -1
+const anySdkVersion int = 9999 // should go last in class loader context
 
 func (m classLoaderContextMap) getSortedKeys() []int {
 	keys := make([]int, 0, len(m))
@@ -276,14 +276,30 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
+	classLoaderContexts := make(classLoaderContextMap)
 	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
 
-	classLoaderContexts := make(classLoaderContextMap)
+	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
+	rule.Command().FlagWithOutput("rm -f ", odexPath)
 
-	// A flag indicating if the '&' class loader context is used.
-	unknownClassLoaderContext := false
+	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
+		// System server jars should be dexpreopted together: class loader context of each jar
+		// should include all preceding jars on the system server classpath.
+		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
 
-	if module.EnforceUsesLibraries {
+		// Copy the system server jar to a predefined location where dex2oat will find it.
+		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
+		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
+		rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
+
+		checkSystemServerOrder(ctx, jarIndex)
+
+		clc := classLoaderContexts[anySdkVersion]
+		rule.Command().
+			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
+			Implicits(clc.Host).
+			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
+	} else if module.EnforceUsesLibraries {
 		// Unconditional class loader context.
 		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
 		classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...)
@@ -306,41 +322,8 @@
 		if !contains(usesLibs, testBase) {
 			classLoaderContexts.addLibs(30, module, testBase)
 		}
-	} else if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
-		// System server jars should be dexpreopted together: class loader context of each jar
-		// should include all preceding jars on the system server classpath.
-		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
 
-		// Copy the system server jar to a predefined location where dex2oat will find it.
-		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
-		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
-		rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
-
-		checkSystemServerOrder(ctx, jarIndex)
-	} else {
-		// Pass special class loader context to skip the classpath and collision check.
-		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
-		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
-		// to the &.
-		unknownClassLoaderContext = true
-	}
-
-	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
-	rule.Command().FlagWithOutput("rm -f ", odexPath)
-	// Set values in the environment of the rule.  These may be modified by construct_context.sh.
-	if unknownClassLoaderContext {
-		rule.Command().
-			Text(`class_loader_context_arg=--class-loader-context=\&`).
-			Text(`stored_class_loader_context_arg=""`)
-	} else {
-		clc := classLoaderContexts[anySdkVersion]
-		rule.Command().
-			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
-			Implicits(clc.Host).
-			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
-	}
-
-	if module.EnforceUsesLibraries {
+		// Generate command that saves target SDK version in a shell variable.
 		if module.ManifestPath != nil {
 			rule.Command().Text(`target_sdk_version="$(`).
 				Tool(globalSoong.ManifestCheck).
@@ -356,20 +339,30 @@
 				Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
 				Text(`)"`)
 		}
+
+		// Generate command that saves host and target class loader context in shell variables.
+		cmd := rule.Command().
+			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
+			Text(` --target-sdk-version ${target_sdk_version}`)
 		for _, ver := range classLoaderContexts.getSortedKeys() {
 			clc := classLoaderContexts.getValue(ver)
-			var varHost, varTarget string
+			verString := fmt.Sprintf("%d", ver)
 			if ver == anySdkVersion {
-				varHost = "dex_preopt_host_libraries"
-				varTarget = "dex_preopt_target_libraries"
-			} else {
-				varHost = fmt.Sprintf("conditional_host_libs_%d", ver)
-				varTarget = fmt.Sprintf("conditional_target_libs_%d", ver)
+				verString = "any" // a special keyword that means any SDK version
 			}
-			rule.Command().Textf(varHost+`="%s"`, strings.Join(clc.Host.Strings(), " ")).Implicits(clc.Host)
-			rule.Command().Textf(varTarget+`="%s"`, strings.Join(clc.Target, " "))
+			cmd.Textf(`--host-classpath-for-sdk %s %s`, verString, strings.Join(clc.Host.Strings(), ":")).
+				Implicits(clc.Host).
+				Textf(`--target-classpath-for-sdk %s %s`, verString, strings.Join(clc.Target, ":"))
 		}
-		rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath)
+		cmd.Text(`)"`)
+	} else {
+		// Pass special class loader context to skip the classpath and collision check.
+		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
+		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
+		// to the &.
+		rule.Command().
+			Text(`class_loader_context_arg=--class-loader-context=\&`).
+			Text(`stored_class_loader_context_arg=""`)
 	}
 
 	// Devices that do not have a product partition use a symlink from /product to /system/product.
diff --git a/dexpreopt/dexpreopt_gen/Android.bp b/dexpreopt/dexpreopt_gen/Android.bp
index 0790391..3f0619c 100644
--- a/dexpreopt/dexpreopt_gen/Android.bp
+++ b/dexpreopt/dexpreopt_gen/Android.bp
@@ -8,4 +8,4 @@
         "blueprint-pathtools",
         "blueprint-proptools",
     ],
-}
\ No newline at end of file
+}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index df6d79d..0f7b8df 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -42,9 +42,12 @@
 	// Source file of this prebuilt.
 	Src *string `android:"path,arch_variant"`
 
-	// optional subdirectory under which this file is installed into
+	// optional subdirectory under which this file is installed into, cannot be specified with relative_install_path, prefer relative_install_path
 	Sub_dir *string `android:"arch_variant"`
 
+	// optional subdirectory under which this file is installed into, cannot be specified with sub_dir
+	Relative_install_path *string `android:"arch_variant"`
+
 	// optional name for the installed file. If unspecified, name of the module is used as the file name
 	Filename *string `android:"arch_variant"`
 
@@ -158,7 +161,10 @@
 }
 
 func (p *PrebuiltEtc) SubDir() string {
-	return android.String(p.properties.Sub_dir)
+	if subDir := proptools.String(p.properties.Sub_dir); subDir != "" {
+		return subDir
+	}
+	return proptools.String(p.properties.Relative_install_path)
 }
 
 func (p *PrebuiltEtc) Installable() bool {
@@ -181,13 +187,17 @@
 	}
 	p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
 
+	if p.properties.Sub_dir != nil && p.properties.Relative_install_path != nil {
+		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
+	}
+
 	// If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
 	// socInstallDirBase.
 	installBaseDir := p.installDirBase
 	if ctx.SocSpecific() && p.socInstallDirBase != "" {
 		installBaseDir = p.socInstallDirBase
 	}
-	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, proptools.String(p.properties.Sub_dir))
+	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir())
 
 	// This ensures that outputFilePath has the correct name for others to
 	// use, as the source file may have a different name.
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 4ce1984..8fc36c2 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -49,7 +49,7 @@
 	os.Exit(run())
 }
 
-func testPrebuiltEtc(t *testing.T, bp string) (*android.TestContext, android.Config) {
+func testPrebuiltEtcContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
 	fs := map[string][]byte{
 		"foo.conf": nil,
 		"bar.conf": nil,
@@ -67,6 +67,14 @@
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
 	ctx.Register(config)
+
+	return ctx, config
+}
+
+func testPrebuiltEtc(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	t.Helper()
+
+	ctx, config := testPrebuiltEtcContext(t, bp)
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -75,6 +83,24 @@
 	return ctx, config
 }
 
+func testPrebuiltEtcError(t *testing.T, pattern, bp string) {
+	t.Helper()
+
+	ctx, config := testPrebuiltEtcContext(t, bp)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+
+	_, errs = ctx.PrepareBuildActions(config)
+	if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+
+	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
 func TestPrebuiltEtcVariants(t *testing.T) {
 	ctx, _ := testPrebuiltEtc(t, `
 		prebuilt_etc {
@@ -184,6 +210,33 @@
 	}
 }
 
+func TestPrebuiltEtcRelativeInstallPathInstallDirPath(t *testing.T) {
+	ctx, _ := testPrebuiltEtc(t, `
+		prebuilt_etc {
+			name: "foo.conf",
+			src: "foo.conf",
+			relative_install_path: "bar",
+		}
+	`)
+
+	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+	expected := buildDir + "/target/product/test_device/system/etc/bar"
+	if p.installDirPath.String() != expected {
+		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
+	}
+}
+
+func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) {
+	testPrebuiltEtcError(t, "relative_install_path is set. Cannot set sub_dir", `
+		prebuilt_etc {
+			name: "foo.conf",
+			src: "foo.conf",
+			sub_dir: "bar",
+			relative_install_path: "bar",
+		}
+	`)
+}
+
 func TestPrebuiltEtcHost(t *testing.T) {
 	ctx, _ := testPrebuiltEtc(t, `
 		prebuilt_etc_host {
diff --git a/finder/Android.bp b/finder/Android.bp
index 47b47c9..a5d7fd4 100644
--- a/finder/Android.bp
+++ b/finder/Android.bp
@@ -17,7 +17,7 @@
 //
 
 subdirs = [
-    "cmd"
+    "cmd",
 ]
 
 bootstrap_go_package {
@@ -30,8 +30,6 @@
         "finder_test.go",
     ],
     deps: [
-      "soong-finder-fs",
+        "soong-finder-fs",
     ],
 }
-
-
diff --git a/finder/cmd/Android.bp b/finder/cmd/Android.bp
index 9dc84ae..e066c39 100644
--- a/finder/cmd/Android.bp
+++ b/finder/cmd/Android.bp
@@ -22,8 +22,6 @@
         "finder.go",
     ],
     deps: [
-        "soong-finder"
+        "soong-finder",
     ],
 }
-
-
diff --git a/finder/finder.go b/finder/finder.go
index 89be0f5..f20bed2 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -1393,17 +1393,25 @@
 	for _, child := range children {
 		linkBits := child.Mode() & os.ModeSymlink
 		isLink := linkBits != 0
-		if child.IsDir() {
-			if !isLink {
+		if isLink {
+			childPath := filepath.Join(path, child.Name())
+			childStat, err := f.filesystem.Stat(childPath)
+			if err != nil {
+				// If stat fails this is probably a broken or dangling symlink, treat it as a file.
+				subfiles = append(subfiles, child.Name())
+			} else if childStat.IsDir() {
 				// Skip symlink dirs.
 				// We don't have to support symlink dirs because
 				// that would cause duplicates.
-				subdirs = append(subdirs, child.Name())
+			} else {
+				// We do have to support symlink files because the link name might be
+				// different than the target name
+				// (for example, Android.bp -> build/soong/root.bp)
+				subfiles = append(subfiles, child.Name())
 			}
+		} else if child.IsDir() {
+			subdirs = append(subdirs, child.Name())
 		} else {
-			// We do have to support symlink files because the link name might be
-			// different than the target name
-			// (for example, Android.bp -> build/soong/root.bp)
 			subfiles = append(subfiles, child.Name())
 		}
 
diff --git a/finder/finder_test.go b/finder/finder_test.go
index f6d0aa9..88b0c05 100644
--- a/finder/finder_test.go
+++ b/finder/finder_test.go
@@ -20,11 +20,8 @@
 	"log"
 	"os"
 	"path/filepath"
-	"reflect"
-	"runtime/debug"
 	"sort"
 	"testing"
-	"time"
 
 	"android/soong/finder/fs"
 )
@@ -41,7 +38,7 @@
 func newFinderWithNumThreads(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams, numThreads int) *Finder {
 	f, err := newFinderAndErr(t, filesystem, cacheParams, numThreads)
 	if err != nil {
-		fatal(t, err.Error())
+		t.Fatal(err.Error())
 	}
 	return f
 }
@@ -62,7 +59,7 @@
 func finderWithSameParams(t *testing.T, original *Finder) *Finder {
 	f, err := finderAndErrorWithSameParams(t, original)
 	if err != nil {
-		fatal(t, err.Error())
+		t.Fatal(err.Error())
 	}
 	return f
 }
@@ -78,146 +75,13 @@
 	return f, err
 }
 
-func write(t *testing.T, path string, content string, filesystem *fs.MockFs) {
-	parent := filepath.Dir(path)
-	filesystem.MkDirs(parent)
-	err := filesystem.WriteFile(path, []byte(content), 0777)
-	if err != nil {
-		fatal(t, err.Error())
-	}
-}
-
-func create(t *testing.T, path string, filesystem *fs.MockFs) {
-	write(t, path, "hi", filesystem)
-}
-
-func delete(t *testing.T, path string, filesystem *fs.MockFs) {
-	err := filesystem.Remove(path)
-	if err != nil {
-		fatal(t, err.Error())
-	}
-}
-
-func removeAll(t *testing.T, path string, filesystem *fs.MockFs) {
-	err := filesystem.RemoveAll(path)
-	if err != nil {
-		fatal(t, err.Error())
-	}
-}
-
-func move(t *testing.T, oldPath string, newPath string, filesystem *fs.MockFs) {
-	err := filesystem.Rename(oldPath, newPath)
-	if err != nil {
-		fatal(t, err.Error())
-	}
-}
-
-func link(t *testing.T, newPath string, oldPath string, filesystem *fs.MockFs) {
-	parentPath := filepath.Dir(newPath)
-	err := filesystem.MkDirs(parentPath)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	err = filesystem.Symlink(oldPath, newPath)
-	if err != nil {
-		fatal(t, err.Error())
-	}
-}
-func read(t *testing.T, path string, filesystem *fs.MockFs) string {
-	reader, err := filesystem.Open(path)
-	if err != nil {
-		t.Fatalf(err.Error())
-	}
-	bytes, err := ioutil.ReadAll(reader)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	return string(bytes)
-}
-func modTime(t *testing.T, path string, filesystem *fs.MockFs) time.Time {
-	stats, err := filesystem.Lstat(path)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	return stats.ModTime()
-}
-func setReadable(t *testing.T, path string, readable bool, filesystem *fs.MockFs) {
-	err := filesystem.SetReadable(path, readable)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-}
-
-func setReadErr(t *testing.T, path string, readErr error, filesystem *fs.MockFs) {
-	err := filesystem.SetReadErr(path, readErr)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-}
-
-func fatal(t *testing.T, message string) {
-	t.Error(message)
-	debug.PrintStack()
-	t.FailNow()
-}
-
-func assertSameResponse(t *testing.T, actual []string, expected []string) {
-	sort.Strings(actual)
-	sort.Strings(expected)
-	if !reflect.DeepEqual(actual, expected) {
-		fatal(
-			t,
-			fmt.Sprintf(
-				"Expected Finder to return these %v paths:\n  %v,\ninstead returned these %v paths:  %v\n",
-				len(expected), expected, len(actual), actual),
-		)
-	}
-}
-
-func assertSameStatCalls(t *testing.T, actual []string, expected []string) {
-	sort.Strings(actual)
-	sort.Strings(expected)
-
-	if !reflect.DeepEqual(actual, expected) {
-		fatal(
-			t,
-			fmt.Sprintf(
-				"Finder made incorrect Stat calls.\n"+
-					"Actual:\n"+
-					"%v\n"+
-					"Expected:\n"+
-					"%v\n"+
-					"\n",
-				actual, expected),
-		)
-	}
-}
-func assertSameReadDirCalls(t *testing.T, actual []string, expected []string) {
-	sort.Strings(actual)
-	sort.Strings(expected)
-
-	if !reflect.DeepEqual(actual, expected) {
-		fatal(
-			t,
-			fmt.Sprintf(
-				"Finder made incorrect ReadDir calls.\n"+
-					"Actual:\n"+
-					"%v\n"+
-					"Expected:\n"+
-					"%v\n"+
-					"\n",
-				actual, expected),
-		)
-	}
-}
-
 // runSimpleTests creates a few files, searches for findme.txt, and checks for the expected matches
 func runSimpleTest(t *testing.T, existentPaths []string, expectedMatches []string) {
 	filesystem := newFs()
 	root := "/tmp"
 	filesystem.MkDirs(root)
 	for _, path := range existentPaths {
-		create(t, filepath.Join(root, path), filesystem)
+		fs.Create(t, filepath.Join(root, path), filesystem)
 	}
 
 	finder := newFinder(t,
@@ -237,7 +101,7 @@
 	for i := range expectedMatches {
 		absoluteMatches = append(absoluteMatches, filepath.Join(root, expectedMatches[i]))
 	}
-	assertSameResponse(t, foundPaths, absoluteMatches)
+	fs.AssertSameResponse(t, foundPaths, absoluteMatches)
 }
 
 // testAgainstSeveralThreadcounts runs the given test for each threadcount that we care to test
@@ -288,7 +152,7 @@
 func TestEmptyPath(t *testing.T) {
 	filesystem := newFs()
 	root := "/tmp"
-	create(t, filepath.Join(root, "findme.txt"), filesystem)
+	fs.Create(t, filepath.Join(root, "findme.txt"), filesystem)
 
 	finder := newFinder(
 		t,
@@ -302,7 +166,7 @@
 
 	foundPaths := finder.FindNamedAt("", "findme.txt")
 
-	assertSameResponse(t, foundPaths, []string{})
+	fs.AssertSameResponse(t, foundPaths, []string{})
 }
 
 func TestFilesystemRoot(t *testing.T) {
@@ -311,7 +175,7 @@
 		filesystem := newFs()
 		root := "/"
 		createdPath := "/findme.txt"
-		create(t, createdPath, filesystem)
+		fs.Create(t, createdPath, filesystem)
 
 		finder := newFinderWithNumThreads(
 			t,
@@ -326,7 +190,7 @@
 
 		foundPaths := finder.FindNamedAt(root, "findme.txt")
 
-		assertSameResponse(t, foundPaths, []string{createdPath})
+		fs.AssertSameResponse(t, foundPaths, []string{createdPath})
 	}
 
 	testAgainstSeveralThreadcounts(t, testWithNumThreads)
@@ -334,7 +198,7 @@
 
 func TestNonexistentDir(t *testing.T) {
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
 
 	_, err := newFinderAndErr(
 		t,
@@ -346,18 +210,18 @@
 		1,
 	)
 	if err == nil {
-		fatal(t, "Did not fail when given a nonexistent root directory")
+		t.Fatal("Did not fail when given a nonexistent root directory")
 	}
 }
 
 func TestExcludeDirs(t *testing.T) {
 	filesystem := newFs()
-	create(t, "/tmp/exclude/findme.txt", filesystem)
-	create(t, "/tmp/exclude/subdir/findme.txt", filesystem)
-	create(t, "/tmp/subdir/exclude/findme.txt", filesystem)
-	create(t, "/tmp/subdir/subdir/findme.txt", filesystem)
-	create(t, "/tmp/subdir/findme.txt", filesystem)
-	create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/exclude/findme.txt", filesystem)
+	fs.Create(t, "/tmp/exclude/subdir/findme.txt", filesystem)
+	fs.Create(t, "/tmp/subdir/exclude/findme.txt", filesystem)
+	fs.Create(t, "/tmp/subdir/subdir/findme.txt", filesystem)
+	fs.Create(t, "/tmp/subdir/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -372,7 +236,7 @@
 
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt",
 			"/tmp/subdir/findme.txt",
 			"/tmp/subdir/subdir/findme.txt"})
@@ -380,15 +244,15 @@
 
 func TestPruneFiles(t *testing.T) {
 	filesystem := newFs()
-	create(t, "/tmp/out/findme.txt", filesystem)
-	create(t, "/tmp/out/.ignore-out-dir", filesystem)
-	create(t, "/tmp/out/child/findme.txt", filesystem)
+	fs.Create(t, "/tmp/out/findme.txt", filesystem)
+	fs.Create(t, "/tmp/out/.ignore-out-dir", filesystem)
+	fs.Create(t, "/tmp/out/child/findme.txt", filesystem)
 
-	create(t, "/tmp/out2/.ignore-out-dir", filesystem)
-	create(t, "/tmp/out2/sub/findme.txt", filesystem)
+	fs.Create(t, "/tmp/out2/.ignore-out-dir", filesystem)
+	fs.Create(t, "/tmp/out2/sub/findme.txt", filesystem)
 
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/include/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/include/findme.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -403,7 +267,7 @@
 
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt",
 			"/tmp/include/findme.txt"})
 }
@@ -412,10 +276,10 @@
 // tests of the filesystem root are in TestFilesystemRoot
 func TestRootDir(t *testing.T) {
 	filesystem := newFs()
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/subdir/findme.txt", filesystem)
-	create(t, "/tmp/b/findme.txt", filesystem)
-	create(t, "/tmp/b/subdir/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/subdir/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/subdir/findme.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -429,17 +293,17 @@
 
 	foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
 
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/a/findme.txt",
 			"/tmp/a/subdir/findme.txt"})
 }
 
 func TestUncachedDir(t *testing.T) {
 	filesystem := newFs()
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/subdir/findme.txt", filesystem)
-	create(t, "/tmp/b/findme.txt", filesystem)
-	create(t, "/tmp/b/subdir/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/subdir/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/subdir/findme.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -456,7 +320,7 @@
 	// fail to notice its slowness. Instead, we only ever search the cache for files
 	// to return, which enforces that we can determine which files will be
 	// interesting upfront.
-	assertSameResponse(t, foundPaths, []string{})
+	fs.AssertSameResponse(t, foundPaths, []string{})
 
 	finder.Shutdown()
 }
@@ -464,9 +328,9 @@
 func TestSearchingForFilesExcludedFromCache(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/misc.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/misc.txt", filesystem)
 
 	// set up the finder and run it
 	finder := newFinder(
@@ -483,7 +347,7 @@
 	// fail to notice its slowness. Instead, we only ever search the cache for files
 	// to return, which enforces that we can determine which files will be
 	// interesting upfront.
-	assertSameResponse(t, foundPaths, []string{})
+	fs.AssertSameResponse(t, foundPaths, []string{})
 
 	finder.Shutdown()
 }
@@ -491,12 +355,12 @@
 func TestRelativeFilePaths(t *testing.T) {
 	filesystem := newFs()
 
-	create(t, "/tmp/ignore/hi.txt", filesystem)
-	create(t, "/tmp/include/hi.txt", filesystem)
-	create(t, "/cwd/hi.txt", filesystem)
-	create(t, "/cwd/a/hi.txt", filesystem)
-	create(t, "/cwd/a/a/hi.txt", filesystem)
-	create(t, "/rel/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/ignore/hi.txt", filesystem)
+	fs.Create(t, "/tmp/include/hi.txt", filesystem)
+	fs.Create(t, "/cwd/hi.txt", filesystem)
+	fs.Create(t, "/cwd/a/hi.txt", filesystem)
+	fs.Create(t, "/cwd/a/a/hi.txt", filesystem)
+	fs.Create(t, "/rel/a/hi.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -509,25 +373,25 @@
 	defer finder.Shutdown()
 
 	foundPaths := finder.FindNamedAt("a", "hi.txt")
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"a/hi.txt",
 			"a/a/hi.txt"})
 
 	foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
-	assertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
 
 	foundPaths = finder.FindNamedAt(".", "hi.txt")
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"hi.txt",
 			"a/hi.txt",
 			"a/a/hi.txt"})
 
 	foundPaths = finder.FindNamedAt("/rel", "hi.txt")
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/rel/a/hi.txt"})
 
 	foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
-	assertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
 }
 
 // have to run this test with the race-detector (`go test -race src/android/soong/finder/*.go`)
@@ -535,7 +399,7 @@
 func TestRootDirsContainedInOtherRootDirs(t *testing.T) {
 	filesystem := newFs()
 
-	create(t, "/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -549,15 +413,15 @@
 
 	foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
 
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt"})
 }
 
 func TestFindFirst(t *testing.T) {
 	filesystem := newFs()
-	create(t, "/tmp/a/hi.txt", filesystem)
-	create(t, "/tmp/b/hi.txt", filesystem)
-	create(t, "/tmp/b/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/b/hi.txt", filesystem)
+	fs.Create(t, "/tmp/b/a/hi.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -571,7 +435,7 @@
 
 	foundPaths := finder.FindFirstNamed("hi.txt")
 
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/a/hi.txt",
 			"/tmp/b/hi.txt"},
 	)
@@ -593,7 +457,7 @@
 		}
 		sort.Strings(paths)
 		for _, path := range paths {
-			create(t, path, filesystem)
+			fs.Create(t, path, filesystem)
 		}
 
 		// set up a finder
@@ -621,7 +485,7 @@
 		// check that each response was correct
 		for i := 0; i < numTests; i++ {
 			foundPaths := <-results
-			assertSameResponse(t, foundPaths, paths)
+			fs.AssertSameResponse(t, foundPaths, paths)
 		}
 	}
 
@@ -649,7 +513,7 @@
 	}
 	sort.Strings(allFiles)
 	for _, path := range allFiles {
-		create(t, path, filesystem)
+		fs.Create(t, path, filesystem)
 	}
 
 	// set up a finder
@@ -687,16 +551,16 @@
 	// check that each response was correct
 	for i := 0; i < numTests; i++ {
 		testRun := <-testRuns
-		assertSameResponse(t, testRun.foundMatches, testRun.correctMatches)
+		fs.AssertSameResponse(t, testRun.foundMatches, testRun.correctMatches)
 	}
 }
 
 func TestStrangelyFormattedPaths(t *testing.T) {
 	filesystem := newFs()
 
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/b/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/findme.txt", filesystem)
 
 	finder := newFinder(
 		t,
@@ -710,7 +574,7 @@
 
 	foundPaths := finder.FindNamedAt("//tmp//a//..", "findme.txt")
 
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/a/findme.txt",
 			"/tmp/b/findme.txt",
 			"/tmp/findme.txt"})
@@ -719,9 +583,9 @@
 func TestCorruptedCacheHeader(t *testing.T) {
 	filesystem := newFs()
 
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	write(t, "/finder/finder-db", "sample header", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Write(t, "/finder/finder-db", "sample header", filesystem)
 
 	finder := newFinder(
 		t,
@@ -735,7 +599,7 @@
 
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/a/findme.txt",
 			"/tmp/findme.txt"})
 }
@@ -743,8 +607,8 @@
 func TestCanUseCache(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -759,11 +623,11 @@
 	// check the response of the first finder
 	correctResponse := []string{"/tmp/a/findme.txt",
 		"/tmp/findme.txt"}
-	assertSameResponse(t, foundPaths, correctResponse)
+	fs.AssertSameResponse(t, foundPaths, correctResponse)
 	finder.Shutdown()
 
 	// check results
-	cacheText := read(t, finder.DbPath, filesystem)
+	cacheText := fs.Read(t, finder.DbPath, filesystem)
 	if len(cacheText) < 1 {
 		t.Fatalf("saved cache db is empty\n")
 	}
@@ -780,8 +644,8 @@
 	finder2 := finderWithSameParams(t, finder)
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 	// check results
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
-	assertSameReadDirCalls(t, filesystem.StatCalls, statCalls)
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
+	fs.AssertSameReadDirCalls(t, filesystem.StatCalls, statCalls)
 
 	finder2.Shutdown()
 }
@@ -789,8 +653,8 @@
 func TestCorruptedCacheBody(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -807,7 +671,7 @@
 	// check the response of the first finder
 	correctResponse := []string{"/tmp/a/findme.txt",
 		"/tmp/findme.txt"}
-	assertSameResponse(t, foundPaths, correctResponse)
+	fs.AssertSameResponse(t, foundPaths, correctResponse)
 	numStatCalls := len(filesystem.StatCalls)
 	numReadDirCalls := len(filesystem.ReadDirCalls)
 
@@ -828,7 +692,7 @@
 	finder2 := finderWithSameParams(t, finder)
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 	// check results
-	assertSameResponse(t, foundPaths, correctResponse)
+	fs.AssertSameResponse(t, foundPaths, correctResponse)
 	numNewStatCalls := len(filesystem.StatCalls)
 	numNewReadDirCalls := len(filesystem.ReadDirCalls)
 	// It's permissable to make more Stat calls with a corrupted cache because
@@ -853,7 +717,7 @@
 func TestStatCalls(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
 
 	// run finder
 	finder := newFinder(
@@ -868,19 +732,19 @@
 	finder.Shutdown()
 
 	// check response
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
-	assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
+	fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
 }
 
 func TestFileAdded(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/ignoreme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/b/ignore.txt", filesystem)
-	create(t, "/tmp/b/c/nope.txt", filesystem)
-	create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
+	fs.Create(t, "/tmp/ignoreme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/ignore.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -895,11 +759,11 @@
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	create(t, "/tmp/b/c/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/findme.txt", filesystem)
 	filesystem.Clock.Tick()
 	filesystem.ClearMetrics()
 
@@ -908,9 +772,9 @@
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/c/findme.txt"})
-	assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/c/findme.txt"})
+	fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c"})
 	finder2.Shutdown()
 
 }
@@ -918,11 +782,11 @@
 func TestDirectoriesAdded(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/ignoreme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/b/ignore.txt", filesystem)
-	create(t, "/tmp/b/c/nope.txt", filesystem)
-	create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
+	fs.Create(t, "/tmp/ignoreme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/ignore.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -936,13 +800,13 @@
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	create(t, "/tmp/b/c/new/findme.txt", filesystem)
-	create(t, "/tmp/b/c/new/new2/findme.txt", filesystem)
-	create(t, "/tmp/b/c/new/new2/ignoreme.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/new/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/new/new2/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/new/new2/ignoreme.txt", filesystem)
 	filesystem.ClearMetrics()
 
 	// run the second finder
@@ -950,11 +814,11 @@
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 
 	// check results
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/a/findme.txt", "/tmp/b/c/new/findme.txt", "/tmp/b/c/new/new2/findme.txt"})
-	assertSameStatCalls(t, filesystem.StatCalls,
+	fs.AssertSameStatCalls(t, filesystem.StatCalls,
 		[]string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
 
 	finder2.Shutdown()
 }
@@ -962,8 +826,8 @@
 func TestDirectoryAndSubdirectoryBothUpdated(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/hi1.txt", filesystem)
-	create(t, "/tmp/a/hi1.txt", filesystem)
+	fs.Create(t, "/tmp/hi1.txt", filesystem)
+	fs.Create(t, "/tmp/a/hi1.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -977,12 +841,12 @@
 	foundPaths := finder.FindNamedAt("/tmp", "hi1.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths, []string{"/tmp/hi1.txt", "/tmp/a/hi1.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi1.txt", "/tmp/a/hi1.txt"})
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	create(t, "/tmp/hi2.txt", filesystem)
-	create(t, "/tmp/a/hi2.txt", filesystem)
+	fs.Create(t, "/tmp/hi2.txt", filesystem)
+	fs.Create(t, "/tmp/a/hi2.txt", filesystem)
 	filesystem.ClearMetrics()
 
 	// run the second finder
@@ -990,11 +854,11 @@
 	foundPaths = finder2.FindAll()
 
 	// check results
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/hi1.txt", "/tmp/hi2.txt", "/tmp/a/hi1.txt", "/tmp/a/hi2.txt"})
-	assertSameStatCalls(t, filesystem.StatCalls,
+	fs.AssertSameStatCalls(t, filesystem.StatCalls,
 		[]string{"/tmp", "/tmp/a"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
 
 	finder2.Shutdown()
 }
@@ -1002,11 +866,11 @@
 func TestFileDeleted(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/ignoreme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/b/findme.txt", filesystem)
-	create(t, "/tmp/b/c/nope.txt", filesystem)
-	create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
+	fs.Create(t, "/tmp/ignoreme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
+	fs.Create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1020,11 +884,11 @@
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/findme.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/findme.txt"})
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	delete(t, "/tmp/b/findme.txt", filesystem)
+	fs.Delete(t, "/tmp/b/findme.txt", filesystem)
 	filesystem.ClearMetrics()
 
 	// run the second finder
@@ -1032,9 +896,9 @@
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
-	assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
+	fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
 
 	finder2.Shutdown()
 }
@@ -1042,11 +906,11 @@
 func TestDirectoriesDeleted(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/1/findme.txt", filesystem)
-	create(t, "/tmp/a/1/2/findme.txt", filesystem)
-	create(t, "/tmp/b/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/findme.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1060,7 +924,7 @@
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt",
 			"/tmp/a/findme.txt",
 			"/tmp/a/1/findme.txt",
@@ -1069,7 +933,7 @@
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	removeAll(t, "/tmp/a/1", filesystem)
+	fs.RemoveAll(t, "/tmp/a/1", filesystem)
 	filesystem.ClearMetrics()
 
 	// run the second finder
@@ -1077,7 +941,7 @@
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 
 	// check results
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/b/findme.txt"})
 	// Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
 	// if the Finder detects the nonexistence of /tmp/a/1
@@ -1087,9 +951,9 @@
 	// The Finder is currently implemented to always restat every dir and
 	// to not short-circuit due to nonexistence of parents (but it will remove
 	// missing dirs from the cache for next time)
-	assertSameStatCalls(t, filesystem.StatCalls,
+	fs.AssertSameStatCalls(t, filesystem.StatCalls,
 		[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/a"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/a"})
 
 	finder2.Shutdown()
 }
@@ -1097,11 +961,11 @@
 func TestDirectoriesMoved(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/1/findme.txt", filesystem)
-	create(t, "/tmp/a/1/2/findme.txt", filesystem)
-	create(t, "/tmp/b/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/findme.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1115,7 +979,7 @@
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt",
 			"/tmp/a/findme.txt",
 			"/tmp/a/1/findme.txt",
@@ -1124,7 +988,7 @@
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	move(t, "/tmp/a", "/tmp/c", filesystem)
+	fs.Move(t, "/tmp/a", "/tmp/c", filesystem)
 	filesystem.ClearMetrics()
 
 	// run the second finder
@@ -1132,7 +996,7 @@
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 
 	// check results
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt",
 			"/tmp/b/findme.txt",
 			"/tmp/c/findme.txt",
@@ -1146,20 +1010,20 @@
 	// The Finder is currently implemented to always restat every dir and
 	// to not short-circuit due to nonexistence of parents (but it will remove
 	// missing dirs from the cache for next time)
-	assertSameStatCalls(t, filesystem.StatCalls,
+	fs.AssertSameStatCalls(t, filesystem.StatCalls,
 		[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
 	finder2.Shutdown()
 }
 
 func TestDirectoriesSwapped(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/1/findme.txt", filesystem)
-	create(t, "/tmp/a/1/2/findme.txt", filesystem)
-	create(t, "/tmp/b/findme.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
+	fs.Create(t, "/tmp/b/findme.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1173,7 +1037,7 @@
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt",
 			"/tmp/a/findme.txt",
 			"/tmp/a/1/findme.txt",
@@ -1182,9 +1046,9 @@
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	move(t, "/tmp/a", "/tmp/temp", filesystem)
-	move(t, "/tmp/b", "/tmp/a", filesystem)
-	move(t, "/tmp/temp", "/tmp/b", filesystem)
+	fs.Move(t, "/tmp/a", "/tmp/temp", filesystem)
+	fs.Move(t, "/tmp/b", "/tmp/a", filesystem)
+	fs.Move(t, "/tmp/temp", "/tmp/b", filesystem)
 	filesystem.ClearMetrics()
 
 	// run the second finder
@@ -1192,7 +1056,7 @@
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 
 	// check results
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt",
 			"/tmp/a/findme.txt",
 			"/tmp/b/findme.txt",
@@ -1206,9 +1070,9 @@
 	// The Finder is currently implemented to always restat every dir and
 	// to not short-circuit due to nonexistence of parents (but it will remove
 	// missing dirs from the cache for next time)
-	assertSameStatCalls(t, filesystem.StatCalls,
+	fs.AssertSameStatCalls(t, filesystem.StatCalls,
 		[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
 	finder2.Shutdown()
 }
 
@@ -1217,15 +1081,15 @@
 // runFsReplacementTest is a helper method called by other tests
 func runFsReplacementTest(t *testing.T, fs1 *fs.MockFs, fs2 *fs.MockFs) {
 	// setup fs1
-	create(t, "/tmp/findme.txt", fs1)
-	create(t, "/tmp/a/findme.txt", fs1)
-	create(t, "/tmp/a/a/findme.txt", fs1)
+	fs.Create(t, "/tmp/findme.txt", fs1)
+	fs.Create(t, "/tmp/a/findme.txt", fs1)
+	fs.Create(t, "/tmp/a/a/findme.txt", fs1)
 
 	// setup fs2 to have the same directories but different files
-	create(t, "/tmp/findme.txt", fs2)
-	create(t, "/tmp/a/findme.txt", fs2)
-	create(t, "/tmp/a/a/ignoreme.txt", fs2)
-	create(t, "/tmp/a/b/findme.txt", fs2)
+	fs.Create(t, "/tmp/findme.txt", fs2)
+	fs.Create(t, "/tmp/a/findme.txt", fs2)
+	fs.Create(t, "/tmp/a/a/ignoreme.txt", fs2)
+	fs.Create(t, "/tmp/a/b/findme.txt", fs2)
 
 	// run the first finder
 	finder := newFinder(
@@ -1239,12 +1103,12 @@
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
 	// check the response of the first finder
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/a/findme.txt"})
 
 	// copy the cache data from the first filesystem to the second
-	cacheContent := read(t, finder.DbPath, fs1)
-	write(t, finder.DbPath, cacheContent, fs2)
+	cacheContent := fs.Read(t, finder.DbPath, fs1)
+	fs.Write(t, finder.DbPath, cacheContent, fs2)
 
 	// run the second finder, with the same config and same cache contents but a different filesystem
 	finder2 := newFinder(
@@ -1258,11 +1122,11 @@
 	foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
 
 	// check results
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/b/findme.txt"})
-	assertSameStatCalls(t, fs2.StatCalls,
+	fs.AssertSameStatCalls(t, fs2.StatCalls,
 		[]string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
-	assertSameReadDirCalls(t, fs2.ReadDirCalls,
+	fs.AssertSameReadDirCalls(t, fs2.ReadDirCalls,
 		[]string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
 	finder2.Shutdown()
 }
@@ -1292,7 +1156,7 @@
 	// setup filesystem
 	filesystem := newFs()
 	for i := 0; i < 5; i++ {
-		create(t, fmt.Sprintf("/tmp/%v/findme.txt", i), filesystem)
+		fs.Create(t, fmt.Sprintf("/tmp/%v/findme.txt", i), filesystem)
 	}
 
 	// run the first finder
@@ -1308,7 +1172,7 @@
 	finder.Shutdown()
 
 	// read db file
-	string1 := read(t, finder.DbPath, filesystem)
+	string1 := fs.Read(t, finder.DbPath, filesystem)
 
 	err := filesystem.Remove(finder.DbPath)
 	if err != nil {
@@ -1320,7 +1184,7 @@
 	finder2.FindNamedAt("/tmp", "findme.txt")
 	finder2.Shutdown()
 
-	string2 := read(t, finder.DbPath, filesystem)
+	string2 := fs.Read(t, finder.DbPath, filesystem)
 
 	if string1 != string2 {
 		t.Errorf("Running Finder twice generated two dbs not having identical contents.\n"+
@@ -1343,9 +1207,9 @@
 func TestNumSyscallsOfSecondFind(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/misc.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/misc.txt", filesystem)
 
 	// set up the finder and run it once
 	finder := newFinder(
@@ -1357,15 +1221,15 @@
 		},
 	)
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
-	assertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
 
 	filesystem.ClearMetrics()
 
 	// run the finder again and confirm it doesn't check the filesystem
 	refoundPaths := finder.FindNamedAt("/tmp", "findme.txt")
-	assertSameResponse(t, refoundPaths, foundPaths)
-	assertSameStatCalls(t, filesystem.StatCalls, []string{})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
+	fs.AssertSameResponse(t, refoundPaths, foundPaths)
+	fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
 
 	finder.Shutdown()
 }
@@ -1373,9 +1237,9 @@
 func TestChangingParamsOfSecondFind(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/findme.txt", filesystem)
-	create(t, "/tmp/a/findme.txt", filesystem)
-	create(t, "/tmp/a/metoo.txt", filesystem)
+	fs.Create(t, "/tmp/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/findme.txt", filesystem)
+	fs.Create(t, "/tmp/a/metoo.txt", filesystem)
 
 	// set up the finder and run it once
 	finder := newFinder(
@@ -1387,15 +1251,15 @@
 		},
 	)
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
-	assertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
 
 	filesystem.ClearMetrics()
 
 	// run the finder again and confirm it gets the right answer without asking the filesystem
 	refoundPaths := finder.FindNamedAt("/tmp", "metoo.txt")
-	assertSameResponse(t, refoundPaths, []string{"/tmp/a/metoo.txt"})
-	assertSameStatCalls(t, filesystem.StatCalls, []string{})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
+	fs.AssertSameResponse(t, refoundPaths, []string{"/tmp/a/metoo.txt"})
+	fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
 
 	finder.Shutdown()
 }
@@ -1403,15 +1267,15 @@
 func TestSymlinkPointingToFile(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/a/hi.txt", filesystem)
-	create(t, "/tmp/a/ignoreme.txt", filesystem)
-	link(t, "/tmp/hi.txt", "a/hi.txt", filesystem)
-	link(t, "/tmp/b/hi.txt", "../a/hi.txt", filesystem)
-	link(t, "/tmp/c/hi.txt", "/tmp/hi.txt", filesystem)
-	link(t, "/tmp/d/hi.txt", "../a/bye.txt", filesystem)
-	link(t, "/tmp/d/bye.txt", "../a/hi.txt", filesystem)
-	link(t, "/tmp/e/bye.txt", "../a/bye.txt", filesystem)
-	link(t, "/tmp/f/hi.txt", "somethingThatDoesntExist", filesystem)
+	fs.Create(t, "/tmp/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/a/ignoreme.txt", filesystem)
+	fs.Link(t, "/tmp/hi.txt", "a/hi.txt", filesystem)
+	fs.Link(t, "/tmp/b/hi.txt", "../a/hi.txt", filesystem)
+	fs.Link(t, "/tmp/c/hi.txt", "/tmp/hi.txt", filesystem)
+	fs.Link(t, "/tmp/d/hi.txt", "../a/bye.txt", filesystem)
+	fs.Link(t, "/tmp/d/bye.txt", "../a/hi.txt", filesystem)
+	fs.Link(t, "/tmp/e/bye.txt", "../a/bye.txt", filesystem)
+	fs.Link(t, "/tmp/f/hi.txt", "somethingThatDoesntExist", filesystem)
 
 	// set up the finder and run it once
 	finder := newFinder(
@@ -1432,20 +1296,21 @@
 		"/tmp/d/hi.txt",
 		"/tmp/f/hi.txt",
 	}
-	assertSameResponse(t, foundPaths, correctResponse)
+	fs.AssertSameResponse(t, foundPaths, correctResponse)
 
 }
 
 func TestSymlinkPointingToDirectory(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/dir/hi.txt", filesystem)
-	create(t, "/tmp/dir/ignoreme.txt", filesystem)
+	fs.Create(t, "/tmp/dir/hi.txt", filesystem)
+	fs.Create(t, "/tmp/dir/ignoreme.txt", filesystem)
 
-	link(t, "/tmp/links/dir", "../dir", filesystem)
-	link(t, "/tmp/links/link", "../dir", filesystem)
-	link(t, "/tmp/links/broken", "nothingHere", filesystem)
-	link(t, "/tmp/links/recursive", "recursive", filesystem)
+	fs.Link(t, "/tmp/links/dir", "../dir", filesystem)
+	fs.Link(t, "/tmp/links/link", "../dir", filesystem)
+	fs.Link(t, "/tmp/links/hi.txt", "../dir", filesystem)
+	fs.Link(t, "/tmp/links/broken", "nothingHere", filesystem)
+	fs.Link(t, "/tmp/links/recursive", "recursive", filesystem)
 
 	// set up the finder and run it once
 	finder := newFinder(
@@ -1463,7 +1328,7 @@
 	correctResponse := []string{
 		"/tmp/dir/hi.txt",
 	}
-	assertSameResponse(t, foundPaths, correctResponse)
+	fs.AssertSameResponse(t, foundPaths, correctResponse)
 
 }
 
@@ -1472,9 +1337,9 @@
 func TestAddPruneFile(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/out/hi.txt", filesystem)
-	create(t, "/tmp/out/a/hi.txt", filesystem)
-	create(t, "/tmp/hi.txt", filesystem)
+	fs.Create(t, "/tmp/out/hi.txt", filesystem)
+	fs.Create(t, "/tmp/out/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/hi.txt", filesystem)
 
 	// do find
 	finder := newFinder(
@@ -1490,7 +1355,7 @@
 	foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
 
 	// check result
-	assertSameResponse(t, foundPaths,
+	fs.AssertSameResponse(t, foundPaths,
 		[]string{"/tmp/hi.txt",
 			"/tmp/out/hi.txt",
 			"/tmp/out/a/hi.txt"},
@@ -1499,19 +1364,19 @@
 
 	// modify filesystem
 	filesystem.Clock.Tick()
-	create(t, "/tmp/out/.ignore-out-dir", filesystem)
+	fs.Create(t, "/tmp/out/.ignore-out-dir", filesystem)
 	// run another find and check its result
 	finder2 := finderWithSameParams(t, finder)
 	foundPaths = finder2.FindNamedAt("/tmp", "hi.txt")
-	assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
 	finder2.Shutdown()
 }
 
 func TestUpdatingDbIffChanged(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/a/hi.txt", filesystem)
-	create(t, "/tmp/b/bye.txt", filesystem)
+	fs.Create(t, "/tmp/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/b/bye.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1526,11 +1391,11 @@
 	foundPaths := finder.FindAll()
 	finder.Shutdown()
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
-	create(t, "/tmp/b/hi.txt", filesystem)
+	fs.Create(t, "/tmp/b/hi.txt", filesystem)
 	filesystem.Clock.Tick()
 	filesystem.ClearMetrics()
 
@@ -1539,10 +1404,10 @@
 	foundPaths = finder2.FindAll()
 	finder2.Shutdown()
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
 	expectedDbWriteTime := filesystem.Clock.Time()
-	actualDbWriteTime := modTime(t, finder2.DbPath, filesystem)
+	actualDbWriteTime := fs.ModTime(t, finder2.DbPath, filesystem)
 	if actualDbWriteTime != expectedDbWriteTime {
 		t.Fatalf("Expected to write db at %v, actually wrote db at %v\n",
 			expectedDbWriteTime, actualDbWriteTime)
@@ -1556,10 +1421,10 @@
 	foundPaths = finder3.FindAll()
 
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
-	assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
+	fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
 	finder3.Shutdown()
-	actualDbWriteTime = modTime(t, finder3.DbPath, filesystem)
+	actualDbWriteTime = fs.ModTime(t, finder3.DbPath, filesystem)
 	if actualDbWriteTime != expectedDbWriteTime {
 		t.Fatalf("Re-wrote db even when contents did not change")
 	}
@@ -1569,10 +1434,10 @@
 func TestDirectoryNotPermitted(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/hi.txt", filesystem)
-	create(t, "/tmp/a/hi.txt", filesystem)
-	create(t, "/tmp/a/a/hi.txt", filesystem)
-	create(t, "/tmp/b/hi.txt", filesystem)
+	fs.Create(t, "/tmp/hi.txt", filesystem)
+	fs.Create(t, "/tmp/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/a/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/b/hi.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1588,12 +1453,12 @@
 	finder.Shutdown()
 	allPaths := []string{"/tmp/hi.txt", "/tmp/a/hi.txt", "/tmp/a/a/hi.txt", "/tmp/b/hi.txt"}
 	// check results
-	assertSameResponse(t, foundPaths, allPaths)
+	fs.AssertSameResponse(t, foundPaths, allPaths)
 
 	// modify the filesystem
 	filesystem.Clock.Tick()
 
-	setReadable(t, "/tmp/a", false, filesystem)
+	fs.SetReadable(t, "/tmp/a", false, filesystem)
 	filesystem.Clock.Tick()
 
 	// run the second finder
@@ -1601,24 +1466,24 @@
 	foundPaths = finder2.FindAll()
 	finder2.Shutdown()
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt", "/tmp/b/hi.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt", "/tmp/b/hi.txt"})
 
 	// modify the filesystem back
-	setReadable(t, "/tmp/a", true, filesystem)
+	fs.SetReadable(t, "/tmp/a", true, filesystem)
 
 	// run the third finder
 	finder3 := finderWithSameParams(t, finder2)
 	foundPaths = finder3.FindAll()
 	finder3.Shutdown()
 	// check results
-	assertSameResponse(t, foundPaths, allPaths)
+	fs.AssertSameResponse(t, foundPaths, allPaths)
 }
 
 func TestFileNotPermitted(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/hi.txt", filesystem)
-	setReadable(t, "/tmp/hi.txt", false, filesystem)
+	fs.Create(t, "/tmp/hi.txt", filesystem)
+	fs.SetReadable(t, "/tmp/hi.txt", false, filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1633,13 +1498,13 @@
 	foundPaths := finder.FindAll()
 	finder.Shutdown()
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
 }
 
 func TestCacheEntryPathUnexpectedError(t *testing.T) {
 	// setup filesystem
 	filesystem := newFs()
-	create(t, "/tmp/a/hi.txt", filesystem)
+	fs.Create(t, "/tmp/a/hi.txt", filesystem)
 
 	// run the first finder
 	finder := newFinder(
@@ -1654,14 +1519,14 @@
 	foundPaths := finder.FindAll()
 	finder.Shutdown()
 	// check results
-	assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
+	fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
 
 	// make the directory not readable
-	setReadErr(t, "/tmp/a", os.ErrInvalid, filesystem)
+	fs.SetReadErr(t, "/tmp/a", os.ErrInvalid, filesystem)
 
 	// run the second finder
 	_, err := finderAndErrorWithSameParams(t, finder)
 	if err == nil {
-		fatal(t, "Failed to detect unexpected filesystem error")
+		t.Fatal("Failed to detect unexpected filesystem error")
 	}
 }
diff --git a/finder/fs/Android.bp b/finder/fs/Android.bp
index 27e3c7d..85929ae 100644
--- a/finder/fs/Android.bp
+++ b/finder/fs/Android.bp
@@ -22,8 +22,10 @@
     srcs: [
         "fs.go",
         "readdir.go",
+        "test.go",
     ],
     testSrcs: [
+        "fs_test.go",
         "readdir_test.go",
     ],
     darwin: {
@@ -37,4 +39,3 @@
         ],
     },
 }
-
diff --git a/finder/fs/fs.go b/finder/fs/fs.go
index 071f764..d2e3e4a 100644
--- a/finder/fs/fs.go
+++ b/finder/fs/fs.go
@@ -51,6 +51,7 @@
 	// getting information about files
 	Open(name string) (file io.ReadCloser, err error)
 	Lstat(path string) (stats os.FileInfo, err error)
+	Stat(path string) (stats os.FileInfo, err error)
 	ReadDir(path string) (contents []DirEntryInfo, err error)
 
 	InodeNumber(info os.FileInfo) (number uint64, err error)
@@ -99,6 +100,10 @@
 	return os.Lstat(path)
 }
 
+func (osFs) Stat(path string) (stats os.FileInfo, err error) {
+	return os.Stat(path)
+}
+
 func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
 	entries, err := readdir(path)
 	if err != nil {
@@ -376,7 +381,7 @@
 	size         int64
 	modTime      time.Time // time at which the inode's contents were modified
 	permTime     time.Time // time at which the inode's permissions were modified
-	isDir        bool
+	mode         os.FileMode
 	inodeNumber  uint64
 	deviceNumber uint64
 }
@@ -390,7 +395,7 @@
 }
 
 func (m *mockFileInfo) Mode() os.FileMode {
-	return 0
+	return m.mode
 }
 
 func (m *mockFileInfo) ModTime() time.Time {
@@ -398,7 +403,7 @@
 }
 
 func (m *mockFileInfo) IsDir() bool {
-	return m.isDir
+	return m.mode&os.ModeDir != 0
 }
 
 func (m *mockFileInfo) Sys() interface{} {
@@ -407,11 +412,11 @@
 
 func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
 	return &mockFileInfo{
-		path:         path,
+		path:         filepath.Base(path),
 		size:         1,
 		modTime:      d.modTime,
 		permTime:     d.permTime,
-		isDir:        true,
+		mode:         os.ModeDir,
 		inodeNumber:  d.inodeNumber,
 		deviceNumber: m.deviceNumber,
 	}
@@ -420,11 +425,11 @@
 
 func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
 	return &mockFileInfo{
-		path:         path,
+		path:         filepath.Base(path),
 		size:         1,
 		modTime:      f.modTime,
 		permTime:     f.permTime,
-		isDir:        false,
+		mode:         0,
 		inodeNumber:  f.inodeNumber,
 		deviceNumber: m.deviceNumber,
 	}
@@ -432,11 +437,11 @@
 
 func (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) {
 	return &mockFileInfo{
-		path:         path,
+		path:         filepath.Base(path),
 		size:         1,
 		modTime:      l.modTime,
 		permTime:     l.permTime,
-		isDir:        false,
+		mode:         os.ModeSymlink,
 		inodeNumber:  l.inodeNumber,
 		deviceNumber: m.deviceNumber,
 	}
@@ -485,6 +490,16 @@
 	}
 }
 
+func (m *MockFs) Stat(path string) (stats os.FileInfo, err error) {
+	// resolve symlinks
+	path, err = m.resolve(path, true)
+	if err != nil {
+		return nil, err
+	}
+
+	return m.Lstat(path)
+}
+
 func (m *MockFs) InodeNumber(info os.FileInfo) (number uint64, err error) {
 	mockInfo, ok := info.(*mockFileInfo)
 	if ok {
diff --git a/finder/fs/fs_test.go b/finder/fs/fs_test.go
new file mode 100644
index 0000000..22a4d7a
--- /dev/null
+++ b/finder/fs/fs_test.go
@@ -0,0 +1,76 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fs
+
+import (
+	"os"
+	"testing"
+)
+
+func TestMockFs_LstatStatSymlinks(t *testing.T) {
+	// setup filesystem
+	filesystem := NewMockFs(nil)
+	Create(t, "/tmp/realdir/hi.txt", filesystem)
+	Create(t, "/tmp/realdir/ignoreme.txt", filesystem)
+
+	Link(t, "/tmp/links/dir", "../realdir", filesystem)
+	Link(t, "/tmp/links/file", "../realdir/hi.txt", filesystem)
+	Link(t, "/tmp/links/broken", "nothingHere", filesystem)
+	Link(t, "/tmp/links/recursive", "recursive", filesystem)
+
+	assertStat := func(t *testing.T, stat os.FileInfo, err error, wantName string, wantMode os.FileMode) {
+		t.Helper()
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if g, w := stat.Name(), wantName; g != w {
+			t.Errorf("want name %q, got %q", w, g)
+		}
+		if g, w := stat.Mode(), wantMode; g != w {
+			t.Errorf("%s: want mode %q, got %q", wantName, w, g)
+		}
+	}
+
+	assertErr := func(t *testing.T, err error, wantErr string) {
+		if err == nil || err.Error() != wantErr {
+			t.Errorf("want error %q, got %q", wantErr, err)
+		}
+	}
+
+	stat, err := filesystem.Lstat("/tmp/links/dir")
+	assertStat(t, stat, err, "dir", os.ModeSymlink)
+
+	stat, err = filesystem.Stat("/tmp/links/dir")
+	assertStat(t, stat, err, "realdir", os.ModeDir)
+
+	stat, err = filesystem.Lstat("/tmp/links/file")
+	assertStat(t, stat, err, "file", os.ModeSymlink)
+
+	stat, err = filesystem.Stat("/tmp/links/file")
+	assertStat(t, stat, err, "hi.txt", 0)
+
+	stat, err = filesystem.Lstat("/tmp/links/broken")
+	assertStat(t, stat, err, "broken", os.ModeSymlink)
+
+	stat, err = filesystem.Stat("/tmp/links/broken")
+	assertErr(t, err, "stat /tmp/links/nothingHere: file does not exist")
+
+	stat, err = filesystem.Lstat("/tmp/links/recursive")
+	assertStat(t, stat, err, "recursive", os.ModeSymlink)
+
+	stat, err = filesystem.Stat("/tmp/links/recursive")
+	assertErr(t, err, "read /tmp/links/recursive: too many levels of symbolic links")
+}
diff --git a/finder/fs/test.go b/finder/fs/test.go
new file mode 100644
index 0000000..cb2140e
--- /dev/null
+++ b/finder/fs/test.go
@@ -0,0 +1,146 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fs
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"reflect"
+	"sort"
+	"testing"
+	"time"
+)
+
+func Write(t *testing.T, path string, content string, filesystem *MockFs) {
+	parent := filepath.Dir(path)
+	filesystem.MkDirs(parent)
+	err := filesystem.WriteFile(path, []byte(content), 0777)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+}
+
+func Create(t *testing.T, path string, filesystem *MockFs) {
+	Write(t, path, "hi", filesystem)
+}
+
+func Delete(t *testing.T, path string, filesystem *MockFs) {
+	err := filesystem.Remove(path)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+}
+
+func RemoveAll(t *testing.T, path string, filesystem *MockFs) {
+	err := filesystem.RemoveAll(path)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+}
+
+func Move(t *testing.T, oldPath string, newPath string, filesystem *MockFs) {
+	err := filesystem.Rename(oldPath, newPath)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+}
+
+func Link(t *testing.T, newPath string, oldPath string, filesystem *MockFs) {
+	parentPath := filepath.Dir(newPath)
+	err := filesystem.MkDirs(parentPath)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+	err = filesystem.Symlink(oldPath, newPath)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+}
+
+func Read(t *testing.T, path string, filesystem *MockFs) string {
+	reader, err := filesystem.Open(path)
+	if err != nil {
+		t.Fatalf(err.Error())
+	}
+	bytes, err := ioutil.ReadAll(reader)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+	return string(bytes)
+}
+
+func ModTime(t *testing.T, path string, filesystem *MockFs) time.Time {
+	stats, err := filesystem.Lstat(path)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+	return stats.ModTime()
+}
+
+func SetReadable(t *testing.T, path string, readable bool, filesystem *MockFs) {
+	err := filesystem.SetReadable(path, readable)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+}
+
+func SetReadErr(t *testing.T, path string, readErr error, filesystem *MockFs) {
+	err := filesystem.SetReadErr(path, readErr)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+}
+
+func AssertSameResponse(t *testing.T, actual []string, expected []string) {
+	t.Helper()
+	sort.Strings(actual)
+	sort.Strings(expected)
+	if !reflect.DeepEqual(actual, expected) {
+		t.Fatalf("Expected Finder to return these %v paths:\n  %v,\ninstead returned these %v paths:  %v\n",
+			len(expected), expected, len(actual), actual)
+	}
+}
+
+func AssertSameStatCalls(t *testing.T, actual []string, expected []string) {
+	t.Helper()
+	sort.Strings(actual)
+	sort.Strings(expected)
+
+	if !reflect.DeepEqual(actual, expected) {
+		t.Fatalf("Finder made incorrect Stat calls.\n"+
+			"Actual:\n"+
+			"%v\n"+
+			"Expected:\n"+
+			"%v\n"+
+			"\n",
+			actual, expected)
+	}
+}
+
+func AssertSameReadDirCalls(t *testing.T, actual []string, expected []string) {
+	t.Helper()
+	sort.Strings(actual)
+	sort.Strings(expected)
+
+	if !reflect.DeepEqual(actual, expected) {
+		t.Fatalf("Finder made incorrect ReadDir calls.\n"+
+			"Actual:\n"+
+			"%v\n"+
+			"Expected:\n"+
+			"%v\n"+
+			"\n",
+			actual, expected)
+	}
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index f6904f1..00baa57 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -83,7 +83,6 @@
 
 type generatorProperties struct {
 	// The command to run on one or more input files. Cmd supports substitution of a few variables
-	// (the actual substitution is implemented in GenerateAndroidBuildActions below)
 	//
 	// Available variables for substitution:
 	//
@@ -94,9 +93,6 @@
 	//  $(depfile): a file to which dependencies will be written, if the depfile property is set to true
 	//  $(genDir): the sandbox directory for this tool; contains $(out)
 	//  $$: a literal $
-	//
-	// All files used must be declared as inputs (to ensure proper up-to-date checks).
-	// Use "$(in)" directly in Cmd to ensure that all inputs used are declared.
 	Cmd *string
 
 	// Enable reading a file containing dependencies in gcc format after the command completes
@@ -559,6 +555,12 @@
 	}
 }
 
+func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	// Because generated outputs are checked by client modules(e.g. cc_library, ...)
+	// we can safely ignore the check here.
+	return nil
+}
+
 func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module {
 	module := &Module{
 		taskGenerator: taskGenerator,
diff --git a/java/aar.go b/java/aar.go
index 074ead4..500788f 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -762,6 +762,10 @@
 	return a.depIsInSameApex(ctx, dep)
 }
 
+func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	return nil
+}
+
 var _ android.PrebuiltInterface = (*Import)(nil)
 
 // android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
diff --git a/java/androidmk.go b/java/androidmk.go
index 62cf169..618e15d 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -91,7 +91,7 @@
 	} else {
 		mainEntries = android.AndroidMkEntries{
 			Class:      "JAVA_LIBRARIES",
-			DistFile:   android.OptionalPathForPath(library.distFile),
+			DistFiles:  library.distFiles,
 			OutputFile: android.OptionalPathForPath(library.outputFile),
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -550,14 +550,14 @@
 	// needed because an invalid output file would prevent the make entries from
 	// being written.
 	// TODO(b/146727827): Revert when we do not need to generate stubs and API separately.
-	distFile := android.OptionalPathForPath(dstubs.apiFile)
+	distFile := dstubs.apiFile
 	outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar)
 	if !outputFile.Valid() {
-		outputFile = distFile
+		outputFile = android.OptionalPathForPath(distFile)
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		DistFile:   distFile,
+		DistFiles:  android.MakeDefaultDistFiles(distFile),
 		OutputFile: outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -719,6 +719,7 @@
 				func(entries *android.AndroidMkEntries) {
 					entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
 					entries.SetString("LOCAL_APK_SET_MASTER_FILE", apkSet.masterFile)
+					entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
 					entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...)
 				},
 			},
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index d471fb7..075b7aa 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -156,17 +156,158 @@
 		}
 	`)
 
-	without_tag_entries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_without_tag", "android_common").Module())
-	with_tag_entries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_with_tag", "android_common").Module())
+	withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_without_tag", "android_common").Module())
+	withTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_with_tag", "android_common").Module())
 
-	if len(without_tag_entries) != 2 || len(with_tag_entries) != 2 {
-		t.Errorf("two mk entries per module expected, got %d and %d", len(without_tag_entries), len(with_tag_entries))
+	if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 {
+		t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries))
 	}
-	if !with_tag_entries[0].DistFile.Valid() || !strings.Contains(with_tag_entries[0].DistFile.String(), "/javac/foo_with_tag.jar") {
-		t.Errorf("expected classes.jar DistFile, got %v", with_tag_entries[0].DistFile)
+	if len(withTagEntries[0].DistFiles[".jar"]) != 1 ||
+		!strings.Contains(withTagEntries[0].DistFiles[".jar"][0].String(), "/javac/foo_with_tag.jar") {
+		t.Errorf("expected DistFiles to contain classes.jar, got %v", withTagEntries[0].DistFiles)
 	}
-	if without_tag_entries[0].DistFile.Valid() {
-		t.Errorf("did not expect explicit DistFile, got %v", without_tag_entries[0].DistFile)
+	if len(withoutTagEntries[0].DistFiles[".jar"]) > 0 {
+		t.Errorf("did not expect explicit DistFile for .jar tag, got %v", withoutTagEntries[0].DistFiles[".jar"])
+	}
+}
+
+func TestDistWithDest(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+			dist: {
+				targets: ["my_goal"],
+				dest: "my/custom/dest/dir",
+			},
+		}
+	`)
+
+	module := ctx.ModuleForTests("foo", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", module)
+	if len(entries) != 2 {
+		t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries))
+	}
+
+	distStrings := entries[0].GetDistForGoals(module)
+
+	if len(distStrings) != 2 {
+		t.Errorf("Expected 2 entries for dist: PHONY and dist-for-goals, but got %q", distStrings)
+	}
+
+	if distStrings[0] != ".PHONY: my_goal\n" {
+		t.Errorf("Expected .PHONY entry to declare my_goal, but got: %s", distStrings[0])
+	}
+
+	if !strings.Contains(distStrings[1], "$(call dist-for-goals,my_goal") ||
+		!strings.Contains(distStrings[1], ".intermediates/foo/android_common/dex/foo.jar:my/custom/dest/dir") {
+		t.Errorf(
+			"Expected dist-for-goals entry to contain my_goal and new dest dir, but got: %s", distStrings[1])
+	}
+}
+
+func TestDistsWithAllProperties(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+			dist: {
+				targets: ["baz"],
+			},
+			dists: [
+				{
+					targets: ["bar"],
+					tag: ".jar",
+					dest: "bar.jar",
+					dir: "bar/dir",
+					suffix: ".qux",
+				},
+			]
+		}
+	`)
+
+	module := ctx.ModuleForTests("foo", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", module)
+	if len(entries) != 2 {
+		t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries))
+	}
+
+	distStrings := entries[0].GetDistForGoals(module)
+
+	if len(distStrings) != 4 {
+		t.Errorf("Expected 4 entries for dist: PHONY and dist-for-goals, but got %d", len(distStrings))
+	}
+
+	if distStrings[0] != ".PHONY: bar\n" {
+		t.Errorf("Expected .PHONY entry to declare bar, but got: %s", distStrings[0])
+	}
+
+	if !strings.Contains(distStrings[1], "$(call dist-for-goals,bar") ||
+		!strings.Contains(
+			distStrings[1],
+			".intermediates/foo/android_common/javac/foo.jar:bar/dir/bar.qux.jar") {
+		t.Errorf(
+			"Expected dist-for-goals entry to contain bar and new dest dir, but got: %s", distStrings[1])
+	}
+
+	if distStrings[2] != ".PHONY: baz\n" {
+		t.Errorf("Expected .PHONY entry to declare baz, but got: %s", distStrings[2])
+	}
+
+	if !strings.Contains(distStrings[3], "$(call dist-for-goals,baz") ||
+		!strings.Contains(distStrings[3], ".intermediates/foo/android_common/dex/foo.jar:foo.jar") {
+		t.Errorf(
+			"Expected dist-for-goals entry to contain my_other_goal and new dest dir, but got: %s",
+			distStrings[3])
+	}
+}
+
+func TestDistsWithTag(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_library {
+			name: "foo_without_tag",
+			srcs: ["a.java"],
+			compile_dex: true,
+			dists: [
+				{
+					targets: ["hi"],
+				},
+			],
+		}
+		java_library {
+			name: "foo_with_tag",
+			srcs: ["a.java"],
+			compile_dex: true,
+			dists: [
+				{
+					targets: ["hi"],
+					tag: ".jar",
+				},
+			],
+		}
+	`)
+
+	moduleWithoutTag := ctx.ModuleForTests("foo_without_tag", "android_common").Module()
+	moduleWithTag := ctx.ModuleForTests("foo_with_tag", "android_common").Module()
+
+	withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithoutTag)
+	withTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithTag)
+
+	if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 {
+		t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries))
+	}
+
+	distFilesWithoutTag := withoutTagEntries[0].DistFiles
+	distFilesWithTag := withTagEntries[0].DistFiles
+
+	if len(distFilesWithTag[".jar"]) != 1 ||
+		!strings.Contains(distFilesWithTag[".jar"][0].String(), "/javac/foo_with_tag.jar") {
+		t.Errorf("expected foo_with_tag's .jar-tagged DistFiles to contain classes.jar, got %v", distFilesWithTag[".jar"])
+	}
+	if len(distFilesWithoutTag[".jar"]) > 0 {
+		t.Errorf("did not expect foo_without_tag's .jar-tagged DistFiles to contain files, but got %v", distFilesWithoutTag[".jar"])
 	}
 }
 
diff --git a/java/app.go b/java/app.go
index c568516..98bce94 100755
--- a/java/app.go
+++ b/java/app.go
@@ -79,6 +79,7 @@
 	properties   AndroidAppSetProperties
 	packedOutput android.WritablePath
 	masterFile   string
+	apkcertsFile android.ModuleOutPath
 }
 
 func (as *AndroidAppSet) Name() string {
@@ -130,6 +131,7 @@
 
 func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+	as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
 	// We are assuming here that the master file in the APK
 	// set has `.apk` suffix. If it doesn't the build will fail.
 	// APK sets containing APEX files are handled elsewhere.
@@ -142,16 +144,19 @@
 	// TODO(asmundak): do we support device features
 	ctx.Build(pctx,
 		android.BuildParams{
-			Rule:        extractMatchingApks,
-			Description: "Extract APKs from APK set",
-			Output:      as.packedOutput,
-			Inputs:      android.Paths{as.prebuilt.SingleSourcePath(ctx)},
+			Rule:           extractMatchingApks,
+			Description:    "Extract APKs from APK set",
+			Output:         as.packedOutput,
+			ImplicitOutput: as.apkcertsFile,
+			Inputs:         android.Paths{as.prebuilt.SingleSourcePath(ctx)},
 			Args: map[string]string{
 				"abis":              strings.Join(SupportedAbis(ctx), ","),
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
 				"screen-densities":  screenDensities,
 				"sdk-version":       ctx.Config().PlatformSdkVersion(),
 				"stem":              as.BaseModuleName(),
+				"apkcerts":          as.apkcertsFile.String(),
+				"partition":         as.PartitionTag(ctx.DeviceConfig()),
 			},
 		})
 }
@@ -421,8 +426,10 @@
 		if String(a.deviceProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
+
 		if minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx); err == nil {
 			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
+			android.CheckMinSdkVersion(a, ctx, int(minSdkVersion))
 		} else {
 			ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
 		}
@@ -862,13 +869,13 @@
 	return jniLibs, certificates
 }
 
-func (a *AndroidApp) walkPayloadDeps(ctx android.ModuleContext,
-	do func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool)) {
-
+func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		isExternal := !a.DepIsInSameApex(ctx, child)
 		if am, ok := child.(android.ApexModule); ok {
-			do(ctx, parent, am, isExternal)
+			if !do(ctx, parent, am, isExternal) {
+				return false
+			}
 		}
 		return !isExternal
 	})
@@ -880,7 +887,7 @@
 	}
 
 	depsInfo := android.DepNameToDepInfoMap{}
-	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) {
+	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		depName := to.Name()
 		if info, exist := depsInfo[depName]; exist {
 			info.From = append(info.From, from.Name())
@@ -900,6 +907,7 @@
 				MinSdkVersion: toMinSdkVersion,
 			}
 		}
+		return true
 	})
 
 	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(), depsInfo)
@@ -1563,6 +1571,11 @@
 	return sdkSpecFrom("")
 }
 
+func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
+	return nil
+}
+
 func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
 	props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
 
@@ -1852,9 +1865,8 @@
 		// creating a cyclic dependency:
 		//     e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
 		if hasFrameworkLibs {
-			// dexpreopt/dexpreopt.go needs the paths to the dex jars of these libraries in case construct_context.sh needs
-			// to pass them to dex2oat.  Add them as a dependency so we can determine the path to the dex jar of each
-			// library to dexpreopt.
+			// Dexpreopt needs paths to the dex jars of these libraries in order to construct
+			// class loader context for dex2oat. Add them as a dependency with a special tag.
 			ctx.AddVariationDependencies(nil, usesLibTag,
 				"org.apache.http.legacy",
 				"android.hidl.base-V1.0-java",
diff --git a/java/app_test.go b/java/app_test.go
index e45ba70..d4323bb 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -147,7 +147,7 @@
 			name: "foo",
 			set: "prebuilts/apks/app.apks",
 			prerelease: true,
-        }`)
+		}`)
 	module := ctx.ModuleForTests("foo", "android_common")
 	const packedSplitApks = "foo.zip"
 	params := module.Output(packedSplitApks)
@@ -157,6 +157,9 @@
 	if s := params.Args["allow-prereleased"]; s != "true" {
 		t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
 	}
+	if s := params.Args["partition"]; s != "system" {
+		t.Errorf("wrong partition value: '%s', expected 'system'", s)
+	}
 	mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
 	actualMaster := mkEntries.EntryMap["LOCAL_APK_SET_MASTER_FILE"]
 	expectedMaster := []string{"foo.apk"}
@@ -473,6 +476,24 @@
 	}
 }
 
+func TestUpdatableApps_TransitiveDepsShouldSetMinSdkVersion(t *testing.T) {
+	testJavaError(t, `module "bar".*: should support min_sdk_version\(29\)`, cc.GatherRequiredDepsForTest(android.Android)+`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",
+			static_libs: ["bar"],
+		}
+
+		java_library {
+			name: "bar",
+			sdk_version: "current",
+		}
+	`)
+}
+
 func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
 	testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		android_app {
@@ -2569,13 +2590,13 @@
 	// Test that only present libraries are preopted
 	cmd = app.Rule("dexpreopt").RuleParams.Command
 
-	if w := `dex_preopt_target_libraries="/system/framework/foo.jar /system/framework/bar.jar"`; !strings.Contains(cmd, w) {
+	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
 
 	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
 
-	if w := `dex_preopt_target_libraries="/system/framework/foo.jar /system/framework/bar.jar"`; !strings.Contains(cmd, w) {
+	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
 }
diff --git a/java/builder.go b/java/builder.go
index a27e5c3..7318fcb 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -120,10 +120,11 @@
 				`${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
 				`-sdk-version=${sdk-version} -abis=${abis} ` +
 				`--screen-densities=${screen-densities} --stem=${stem} ` +
+				`-apkcerts=${apkcerts} -partition=${partition} ` +
 				`${in}`,
 			CommandDeps: []string{"${config.ExtractApksCmd}"},
 		},
-		"abis", "allow-prereleased", "screen-densities", "sdk-version", "stem")
+		"abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition")
 
 	turbine, turbineRE = remoteexec.StaticRules(pctx, "turbine",
 		blueprint.RuleParams{
diff --git a/java/config/config.go b/java/config/config.go
index bb5be3a..0fe74c8 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -28,14 +28,14 @@
 var (
 	pctx = android.NewPackageContext("android/soong/java/config")
 
-	// TODO(b/157640067): Don't depend on the legacy API by default in the long term.
-	DefaultBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
-	DefaultSystemModules          = "legacy-core-platform-api-stubs-system-modules"
-	DefaultLibraries              = []string{"ext", "framework"}
-	DefaultLambdaStubsLibrary     = "core-lambda-stubs"
-	SdkLambdaStubsPath            = "prebuilts/sdk/tools/core-lambda-stubs.jar"
+	LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
+	LegacyCorePlatformSystemModules          = "legacy-core-platform-api-stubs-system-modules"
+	FrameworkLibraries                       = []string{"ext", "framework"}
+	DefaultLambdaStubsLibrary                = "core-lambda-stubs"
+	SdkLambdaStubsPath                       = "prebuilts/sdk/tools/core-lambda-stubs.jar"
 
-	DefaultJacocoExcludeFilter = []string{"org.junit.*", "org.jacoco.*", "org.mockito.*"}
+	DefaultMakeJacocoExcludeFilter = []string{"org.junit.*", "org.jacoco.*", "org.mockito.*"}
+	DefaultJacocoExcludeFilter     = []string{"org.junit.**", "org.jacoco.**", "org.mockito.**"}
 
 	InstrumentFrameworkModules = []string{
 		"framework",
@@ -110,7 +110,7 @@
 	pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
-	pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh")
+	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file.py")
 
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh")
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 981a736..708a72a 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -25,9 +25,11 @@
 }
 
 func makeVarsProvider(ctx android.MakeVarsContext) {
-	ctx.Strict("TARGET_DEFAULT_JAVA_LIBRARIES", strings.Join(DefaultLibraries, " "))
-	ctx.Strict("TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES", strings.Join(DefaultBootclasspathLibraries, " "))
-	ctx.Strict("DEFAULT_SYSTEM_MODULES", DefaultSystemModules)
+	ctx.Strict("FRAMEWORK_LIBRARIES", strings.Join(FrameworkLibraries, " "))
+
+	// These are used by make when LOCAL_PRIVATE_PLATFORM_APIS is set (equivalent to platform_apis in blueprint):
+	ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES", strings.Join(LegacyCorePlatformBootclasspathLibraries, " "))
+	ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES", LegacyCorePlatformSystemModules)
 
 	ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
 	ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}")
@@ -64,7 +66,7 @@
 	ctx.Strict("ZIPSYNC", "${ZipSyncCmd}")
 
 	ctx.Strict("JACOCO_CLI_JAR", "${JacocoCLIJar}")
-	ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultJacocoExcludeFilter, ","))
+	ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultMakeJacocoExcludeFilter, ","))
 
 	ctx.Strict("EXTRACT_JAR_PACKAGES", "${ExtractJarPackagesCmd}")
 
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 7f1afd6..f1b7178 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -173,7 +173,7 @@
 			profileBootListing = android.ExistentPathForSource(ctx,
 				ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
 			profileIsTextListing = true
-		} else {
+		} else if global.ProfileDir != "" {
 			profileClassListing = android.ExistentPathForSource(ctx,
 				global.ProfileDir, ctx.ModuleName()+".prof")
 		}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index a0b7edf..99bfb6d 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -440,16 +440,11 @@
 func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
 		sdkDep := decodeSdkDep(ctx, sdkContext(j))
-		if sdkDep.useDefaultLibs {
-			ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
-			ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
-			if sdkDep.hasFrameworkLibs() {
-				ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
-			}
-		} else if sdkDep.useModule {
+		if sdkDep.useModule {
 			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
 			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
 			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
+			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
 		}
 	}
 
@@ -1325,7 +1320,6 @@
 			cmd.Flag("--exclude-documentation-from-stubs")
 		}
 	}
-	cmd.FlagWithArg("--hide ", "ShowingMemberInHiddenClass") // b/159121253 -- remove it once all the violations are fixed.
 }
 
 func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index ce624bf..130b634 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -59,10 +59,9 @@
 
 var _ hiddenAPIIntf = (*hiddenAPI)(nil)
 
-func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, dexJar android.ModuleOutPath,
+func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bool, dexJar android.ModuleOutPath,
 	implementationJar android.Path, uncompressDex bool) android.ModuleOutPath {
 	if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		name := ctx.ModuleName()
 
 		// Modules whose names are of the format <x>-hiddenapi provide hiddenapi information
 		// for the boot jar module <x>. Otherwise, the module provides information for itself.
@@ -90,7 +89,14 @@
 			// the gathered information in the generated dex file.
 			if name == bootJarName {
 				hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar")
-				h.bootDexJarPath = dexJar
+
+				// More than one library with the same classes can be encoded but only one can
+				// be added to the global set of flags, otherwise it will result in duplicate
+				// classes which is an error. Therefore, only add the dex jar of one of them
+				// to the global set of flags.
+				if primary {
+					h.bootDexJarPath = dexJar
+				}
 				hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
 				dexJar = hiddenAPIJar
 			}
diff --git a/java/jacoco.go b/java/jacoco.go
index bce9822..9162161 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -25,6 +25,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/java/config"
 )
 
 var (
@@ -76,7 +77,8 @@
 	if err != nil {
 		ctx.PropertyErrorf("jacoco.include_filter", "%s", err.Error())
 	}
-	excludes, err := jacocoFiltersToSpecs(j.properties.Jacoco.Exclude_filter)
+	// Also include the default list of classes to exclude from instrumentation.
+	excludes, err := jacocoFiltersToSpecs(append(j.properties.Jacoco.Exclude_filter, config.DefaultJacocoExcludeFilter...))
 	if err != nil {
 		ctx.PropertyErrorf("jacoco.exclude_filter", "%s", err.Error())
 	}
diff --git a/java/java.go b/java/java.go
index 7a42557..0d46672 100644
--- a/java/java.go
+++ b/java/java.go
@@ -44,7 +44,7 @@
 			PropertyName: "java_libs",
 		},
 		func(j *Library) android.Path {
-			implementationJars := j.ImplementationJars()
+			implementationJars := j.ImplementationAndResourcesJars()
 			if len(implementationJars) != 1 {
 				panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
 			}
@@ -342,6 +342,12 @@
 	// otherwise provides defaults libraries to add to the bootclasspath.
 	System_modules *string
 
+	// The name of the module as used in build configuration.
+	//
+	// Allows a library to separate its actual name from the name used in
+	// build configuration, e.g.ctx.Config().BootJars().
+	ConfigurationName *string `blueprint:"mutated"`
+
 	// set the name of the output
 	Stem *string
 
@@ -479,7 +485,7 @@
 	// list of the xref extraction files
 	kytheFiles android.Paths
 
-	distFile android.Path
+	distFiles android.TaggedDistFiles
 
 	// Collect the module directory for IDE info in java/jdeps.go.
 	modulePaths []string
@@ -591,7 +597,7 @@
 }
 
 type sdkDep struct {
-	useModule, useFiles, useDefaultLibs, invalidVersion bool
+	useModule, useFiles, invalidVersion bool
 
 	// The modules that will be added to the bootclasspath when targeting 1.8 or lower
 	bootclasspath []string
@@ -600,7 +606,11 @@
 	// modules are to be used.
 	systemModules string
 
+	// The modules that will be added to the classpath regardless of the Java language level targeted
+	classpath []string
+
 	// The modules that will be added ot the classpath when targeting 1.9 or higher
+	// (normally these will be on the bootclasspath when targeting 1.8 or lower)
 	java9Classpath []string
 
 	frameworkResModule string
@@ -694,18 +704,15 @@
 		j.linter.deps(ctx)
 
 		sdkDep := decodeSdkDep(ctx, sdkContext(j))
-		if sdkDep.useDefaultLibs {
-			ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
-			ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
-			if sdkDep.hasFrameworkLibs() {
-				ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
-			}
-		} else if sdkDep.useModule {
+		if sdkDep.useModule {
 			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
 			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
+			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
 			if j.deviceProperties.EffectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
-				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultBootclasspathLibraries...)
-				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
+				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
+			}
+			if j.deviceProperties.EffectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
+				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
 			}
 		}
 		if sdkDep.systemModules != "" {
@@ -1631,8 +1638,11 @@
 			return
 		}
 
+		configurationName := j.ConfigurationName()
+		primary := configurationName == ctx.ModuleName()
+
 		// Hidden API CSV generation and dex encoding
-		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
+		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile,
 			proptools.Bool(j.deviceProperties.Uncompress_dex))
 
 		// merge dex jar with resources if necessary
@@ -1887,10 +1897,32 @@
 	return j.depIsInSameApex(ctx, dep)
 }
 
+func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	sdkSpec := j.minSdkVersion()
+	if !sdkSpec.specified() {
+		return fmt.Errorf("min_sdk_version is not specified")
+	}
+	if sdkSpec.kind == sdkCore {
+		return nil
+	}
+	ver, err := sdkSpec.effectiveVersion(ctx)
+	if err != nil {
+		return err
+	}
+	if int(ver) > sdkVersion {
+		return fmt.Errorf("newer SDK(%v)", ver)
+	}
+	return nil
+}
+
 func (j *Module) Stem() string {
 	return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
 }
 
+func (j *Module) ConfigurationName() string {
+	return proptools.StringDefault(j.deviceProperties.ConfigurationName, j.BaseModuleName())
+}
+
 func (j *Module) JacocoReportClassesFile() android.Path {
 	return j.jacocoReportClassesFile
 }
@@ -1903,18 +1935,9 @@
 // Java libraries (.jar file)
 //
 
-type LibraryProperties struct {
-	Dist struct {
-		// The tag of the output of this module that should be output.
-		Tag *string `android:"arch_variant"`
-	} `android:"arch_variant"`
-}
-
 type Library struct {
 	Module
 
-	libraryProperties LibraryProperties
-
 	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
 }
 
@@ -1976,14 +1999,7 @@
 			j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
 
-	// Verify Dist.Tag is set to a supported output
-	if j.libraryProperties.Dist.Tag != nil {
-		distFiles, err := j.OutputFiles(*j.libraryProperties.Dist.Tag)
-		if err != nil {
-			ctx.PropertyErrorf("dist.tag", "%s", err.Error())
-		}
-		j.distFile = distFiles[0]
-	}
+	j.distFiles = j.GenerateTaggedDistFiles(ctx)
 }
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -2101,7 +2117,6 @@
 	module := &Library{}
 
 	module.addHostAndDeviceProperties()
-	module.AddProperties(&module.libraryProperties)
 
 	module.initModuleAndImport(&module.ModuleBase)
 
@@ -2651,6 +2666,11 @@
 	return j.depIsInSameApex(ctx, dep)
 }
 
+func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
+	return nil
+}
+
 // Add compile time check for interface implementation
 var _ android.IDEInfo = (*Import)(nil)
 var _ android.IDECustomizedModuleName = (*Import)(nil)
@@ -2820,6 +2840,11 @@
 	return j.dexJarFile
 }
 
+func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	// we don't check prebuilt modules for sdk_version
+	return nil
+}
+
 // dex_import imports a `.jar` file containing classes.dex files.
 //
 // A dex_import module cannot be used as a dependency of a java_* or android_* module, it can only be installed
@@ -2892,6 +2917,7 @@
 		&appProperties{},
 		&appTestProperties{},
 		&overridableAppProperties{},
+		&testProperties{},
 		&ImportProperties{},
 		&AARImportProperties{},
 		&sdkLibraryProperties{},
diff --git a/java/java_test.go b/java/java_test.go
index 1d07e70..def42db 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -25,6 +25,7 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -171,6 +172,20 @@
 	}
 }
 
+func checkModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
+	t.Helper()
+	module := ctx.ModuleForTests(name, variant).Module()
+	deps := []string{}
+	ctx.VisitDirectDeps(module, func(m blueprint.Module) {
+		deps = append(deps, m.Name())
+	})
+	sort.Strings(deps)
+
+	if actual := deps; !reflect.DeepEqual(expected, actual) {
+		t.Errorf("expected %#q, found %#q", expected, actual)
+	}
+}
+
 func TestJavaLinkType(t *testing.T) {
 	testJava(t, `
 		java_library {
@@ -630,6 +645,90 @@
 			t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], sdklibStubsJar.String())
 		}
 	}
+
+	checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+		`prebuilt_sdklib.stubs`,
+		`prebuilt_sdklib.stubs.source.test`,
+		`prebuilt_sdklib.stubs.system`,
+		`prebuilt_sdklib.stubs.test`,
+	})
+}
+
+func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "sdklib",
+			public: {
+				jars: ["a.jar"],
+			},
+		}
+		`)
+
+	checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+		`dex2oatd`,
+		`prebuilt_sdklib`,
+		`sdklib.impl`,
+		`sdklib.stubs`,
+		`sdklib.stubs.source`,
+		`sdklib.xml`,
+	})
+
+	checkModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
+		`sdklib.impl`,
+		// This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the
+		// dependency is added after prebuilts may have been renamed and so has to use
+		// the renamed name.
+		`sdklib.stubs`,
+		`sdklib.xml`,
+	})
+}
+
+func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "sdklib",
+			prefer: true,
+			public: {
+				jars: ["a.jar"],
+			},
+		}
+		`)
+
+	checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+		`dex2oatd`,
+		`prebuilt_sdklib`,
+		// This should be sdklib.stubs but is switched to the prebuilt because it is preferred.
+		`prebuilt_sdklib.stubs`,
+		`sdklib.impl`,
+		`sdklib.stubs.source`,
+		`sdklib.xml`,
+	})
+
+	checkModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
+		`prebuilt_sdklib.stubs`,
+		`sdklib.impl`,
+		`sdklib.xml`,
+	})
 }
 
 func TestDefaults(t *testing.T) {
@@ -1379,6 +1478,28 @@
 		`)
 }
 
+func TestJavaSdkLibrary_Deps(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+		`)
+
+	checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+		`dex2oatd`,
+		`sdklib.impl`,
+		`sdklib.stubs`,
+		`sdklib.stubs.source`,
+		`sdklib.xml`,
+	})
+}
+
 func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
 	testJava(t, `
 		java_sdk_library_import {
diff --git a/java/kotlin.go b/java/kotlin.go
index 673970b..e3356be 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -31,7 +31,9 @@
 		Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` +
 			`mkdir -p "$classesDir" "$srcJarDir" "$emptyDir" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
-			`${config.GenKotlinBuildFileCmd} $classpath "$name" $classesDir $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
+			`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
+			` --out_dir "$classesDir" --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
+			` --out "$kotlinBuildFile" && ` +
 			`${config.KotlincCmd} ${config.JavacHeapFlags} $kotlincFlags ` +
 			`-jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile -kotlin-home $emptyDir && ` +
 			`${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir && ` +
@@ -74,7 +76,7 @@
 		Inputs:      srcFiles,
 		Implicits:   deps,
 		Args: map[string]string{
-			"classpath":       flags.kotlincClasspath.FormJavaClassPath("-classpath"),
+			"classpath":       flags.kotlincClasspath.FormJavaClassPath(""),
 			"kotlincFlags":    flags.kotlincFlags,
 			"srcJars":         strings.Join(srcJars.Strings(), " "),
 			"classesDir":      android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
@@ -93,7 +95,9 @@
 		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` +
 			`mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
-			`${config.GenKotlinBuildFileCmd} $classpath "$name" "" $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
+			`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
+			` --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
+			` --out "$kotlinBuildFile" && ` +
 			`${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} $kotlincFlags ` +
 			`-Xplugin=${config.KotlinKaptJar} ` +
 			`-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` +
@@ -162,7 +166,7 @@
 		Inputs:         srcFiles,
 		Implicits:      deps,
 		Args: map[string]string{
-			"classpath":         flags.kotlincClasspath.FormJavaClassPath("-classpath"),
+			"classpath":         flags.kotlincClasspath.FormJavaClassPath(""),
 			"kotlincFlags":      flags.kotlincFlags,
 			"srcJars":           strings.Join(srcJars.Strings(), " "),
 			"srcJarDir":         android.PathForModuleOut(ctx, "kapt", "srcJars").String(),
diff --git a/java/lint.go b/java/lint.go
index fac9a19..b73d6a5 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -88,7 +88,7 @@
 }
 
 func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
-	rule *android.RuleBuilder) (projectXMLPath, configXMLPath, cacheDir android.WritablePath, deps android.Paths) {
+	rule *android.RuleBuilder) (projectXMLPath, configXMLPath, cacheDir, homeDir android.WritablePath, deps android.Paths) {
 
 	var resourcesList android.WritablePath
 	if len(l.resources) > 0 {
@@ -106,6 +106,7 @@
 	// Lint looks for a lint.xml file next to the project.xml file, give it one.
 	configXMLPath = android.PathForModuleOut(ctx, "lint", "lint.xml")
 	cacheDir = android.PathForModuleOut(ctx, "lint", "cache")
+	homeDir = android.PathForModuleOut(ctx, "lint", "home")
 
 	srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
 	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
@@ -154,8 +155,11 @@
 	cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
 	deps = append(deps, l.extraLintCheckJars...)
 
-	// The cache tag in project.xml is relative to the project.xml file.
-	cmd.FlagWithArg("--cache_dir ", "cache")
+	cmd.FlagWithArg("--root_dir ", "$PWD")
+
+	// The cache tag in project.xml is relative to the root dir, or the project.xml file if
+	// the root dir is not set.
+	cmd.FlagWithArg("--cache_dir ", cacheDir.String())
 
 	cmd.FlagWithInput("@",
 		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
@@ -165,7 +169,7 @@
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
-	return projectXMLPath, configXMLPath, cacheDir, deps
+	return projectXMLPath, configXMLPath, cacheDir, homeDir, deps
 }
 
 // generateManifest adds a command to the rule to write a dummy manifest cat contains the
@@ -207,18 +211,19 @@
 		l.manifest = manifest
 	}
 
-	projectXML, lintXML, cacheDir, deps := l.writeLintProjectXML(ctx, rule)
+	projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule)
 
 	l.outputs.html = android.PathForModuleOut(ctx, "lint-report.html")
 	l.outputs.text = android.PathForModuleOut(ctx, "lint-report.txt")
 	l.outputs.xml = android.PathForModuleOut(ctx, "lint-report.xml")
 
-	rule.Command().Text("rm -rf").Flag(cacheDir.String())
-	rule.Command().Text("mkdir -p").Flag(cacheDir.String())
+	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
+	rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
 
 	rule.Command().
 		Text("(").
 		Flag("JAVA_OPTS=-Xmx2048m").
+		FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
 		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath(ctx)).
 		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXmlPath(ctx)).
 		Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")).
@@ -239,7 +244,7 @@
 		Text("|| (").Text("cat").Input(l.outputs.text).Text("; exit 7)").
 		Text(")")
 
-	rule.Command().Text("rm -rf").Flag(cacheDir.String())
+	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
 
 	rule.Build(pctx, ctx, "lint", "lint")
 }
diff --git a/java/sdk.go b/java/sdk.go
index 2a08f32..6564f6d 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -94,9 +94,9 @@
 	case sdkCorePlatform:
 		return "core_platform"
 	case sdkModule:
-		return "module"
+		return "module-lib"
 	case sdkSystemServer:
-		return "system_server"
+		return "system-server"
 	default:
 		return "invalid"
 	}
@@ -412,7 +412,10 @@
 	switch sdkVersion.kind {
 	case sdkPrivate:
 		return sdkDep{
-			useDefaultLibs:     true,
+			useModule:          true,
+			systemModules:      config.LegacyCorePlatformSystemModules,
+			bootclasspath:      config.LegacyCorePlatformBootclasspathLibraries,
+			classpath:          config.FrameworkLibraries,
 			frameworkResModule: "framework-res",
 		}
 	case sdkNone:
@@ -434,9 +437,10 @@
 		}
 	case sdkCorePlatform:
 		return sdkDep{
-			useDefaultLibs:     true,
-			frameworkResModule: "framework-res",
-			noFrameworksLibs:   true,
+			useModule:        true,
+			systemModules:    config.LegacyCorePlatformSystemModules,
+			bootclasspath:    config.LegacyCorePlatformBootclasspathLibraries,
+			noFrameworksLibs: true,
 		}
 	case sdkPublic:
 		return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
@@ -445,7 +449,12 @@
 	case sdkTest:
 		return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
 	case sdkCore:
-		return toModule([]string{"core.current.stubs"}, "", nil)
+		return sdkDep{
+			useModule:        true,
+			bootclasspath:    []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
+			systemModules:    "core-current-stubs-system-modules",
+			noFrameworksLibs: true,
+		}
 	case sdkModule:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
 		return toModule([]string{"android_module_lib_stubs_current"}, "framework-res", nonUpdatableFrameworkAidlPath(ctx))
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 676557e..8f8f8ce 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1087,15 +1087,22 @@
 
 // Creates the implementation java library
 func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
+
+	moduleNamePtr := proptools.StringPtr(module.BaseModuleName())
+
 	props := struct {
-		Name       *string
-		Visibility []string
-		Instrument bool
+		Name              *string
+		Visibility        []string
+		Instrument        bool
+		ConfigurationName *string
 	}{
 		Name:       proptools.StringPtr(module.implLibraryModuleName()),
 		Visibility: module.sdkLibraryProperties.Impl_library_visibility,
 		// Set the instrument property to ensure it is instrumented when instrumentation is required.
 		Instrument: true,
+
+		// Make the created library behave as if it had the same name as this module.
+		ConfigurationName: moduleNamePtr,
 	}
 
 	properties := []interface{}{
@@ -1905,6 +1912,11 @@
 	return false
 }
 
+func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	// we don't check prebuilt modules for sdk_version
+	return nil
+}
+
 func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
 	return module.commonOutputFiles(tag)
 }
@@ -2071,6 +2083,11 @@
 	// do nothing
 }
 
+func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
+	return nil
+}
+
 // File path to the runtime implementation library
 func (module *sdkLibraryXml) implPath() string {
 	implName := proptools.String(module.properties.Lib_name)
diff --git a/java/sdk_test.go b/java/sdk_test.go
index e5d322c..1f23b14 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -49,27 +49,27 @@
 	}{
 		{
 			name:           "default",
-			bootclasspath:  config.DefaultBootclasspathLibraries,
-			system:         config.DefaultSystemModules,
-			java8classpath: config.DefaultLibraries,
-			java9classpath: config.DefaultLibraries,
+			bootclasspath:  config.LegacyCorePlatformBootclasspathLibraries,
+			system:         config.LegacyCorePlatformSystemModules,
+			java8classpath: config.FrameworkLibraries,
+			java9classpath: config.FrameworkLibraries,
 			aidl:           "-Iframework/aidl",
 		},
 		{
 			name:           `sdk_version:"core_platform"`,
 			properties:     `sdk_version:"core_platform"`,
-			bootclasspath:  config.DefaultBootclasspathLibraries,
-			system:         config.DefaultSystemModules,
+			bootclasspath:  config.LegacyCorePlatformBootclasspathLibraries,
+			system:         config.LegacyCorePlatformSystemModules,
 			java8classpath: []string{},
 			aidl:           "",
 		},
 		{
 			name:           "blank sdk version",
 			properties:     `sdk_version: "",`,
-			bootclasspath:  config.DefaultBootclasspathLibraries,
-			system:         config.DefaultSystemModules,
-			java8classpath: config.DefaultLibraries,
-			java9classpath: config.DefaultLibraries,
+			bootclasspath:  config.LegacyCorePlatformBootclasspathLibraries,
+			system:         config.LegacyCorePlatformSystemModules,
+			java8classpath: config.FrameworkLibraries,
+			java9classpath: config.FrameworkLibraries,
 			aidl:           "-Iframework/aidl",
 		},
 		{
@@ -139,11 +139,10 @@
 		},
 		{
 
-			name:           "core_current",
-			properties:     `sdk_version: "core_current",`,
-			bootclasspath:  []string{"core.current.stubs", "core-lambda-stubs"},
-			system:         "core-current-stubs-system-modules",
-			java9classpath: []string{"core.current.stubs"},
+			name:          "core_current",
+			properties:    `sdk_version: "core_current",`,
+			bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"},
+			system:        "core-current-stubs-system-modules",
 		},
 		{
 
diff --git a/java/testing.go b/java/testing.go
index 6fc10da..f5688e6 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -197,6 +197,7 @@
 	systemModules := []string{
 		"core-current-stubs-system-modules",
 		"legacy-core-platform-api-stubs-system-modules",
+		"stable-core-platform-api-stubs-system-modules",
 	}
 
 	for _, extra := range systemModules {
diff --git a/python/python.go b/python/python.go
index 8b912be..a6c9e2a 100644
--- a/python/python.go
+++ b/python/python.go
@@ -251,6 +251,18 @@
 	return android.OptionalPathForPath(p.installer.(*binaryDecorator).path)
 }
 
+func (p *Module) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		if outputFile := p.installSource; outputFile.Valid() {
+			return android.Paths{outputFile.Path()}, nil
+		}
+		return android.Paths{}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
 func (p *Module) isEmbeddedLauncherEnabled(actual_version string) bool {
 	switch actual_version {
 	case pyVersion2:
diff --git a/rust/Android.bp b/rust/Android.bp
index b06ea8e..d56de87 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -9,24 +9,26 @@
     ],
     srcs: [
         "androidmk.go",
-        "compiler.go",
-        "coverage.go",
         "binary.go",
         "builder.go",
+        "clippy.go",
+        "compiler.go",
+        "coverage.go",
         "library.go",
         "prebuilt.go",
         "proc_macro.go",
-	"project_json.go",
+        "project_json.go",
         "rust.go",
         "test.go",
         "testing.go",
     ],
     testSrcs: [
         "binary_test.go",
+        "clippy_test.go",
         "compiler_test.go",
         "coverage_test.go",
         "library_test.go",
-	"project_json_test.go",
+        "project_json_test.go",
         "rust_test.go",
         "test_test.go",
     ],
diff --git a/rust/OWNERS b/rust/OWNERS
index afd06e4..b5b795c 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -1,5 +1,5 @@
 # Additional owner/reviewers for rust rules, including parent directory owners.
-per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, srhines@google.com
+per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
 
 # Limited owners/reviewers of the allowed list.
-per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, srhines@google.com
+per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 69d0df5..aea899b 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -86,8 +86,11 @@
 func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ctx.subAndroidMk(ret, binary.baseCompiler)
 
+	if binary.distFile.Valid() {
+		ret.DistFiles = android.MakeDefaultDistFiles(binary.distFile.Path())
+	}
+
 	ret.Class = "EXECUTABLES"
-	ret.DistFile = binary.distFile
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String())
 		if binary.coverageOutputZipFile.Valid() {
@@ -127,7 +130,10 @@
 		ret.Class = "SHARED_LIBRARIES"
 	}
 
-	ret.DistFile = library.distFile
+	if library.distFile.Valid() {
+		ret.DistFiles = android.MakeDefaultDistFiles(library.distFile.Path())
+	}
+
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		if !library.rlib() {
 			fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
@@ -143,7 +149,9 @@
 	ctx.subAndroidMk(ret, procMacro.baseCompiler)
 
 	ret.Class = "PROC_MACRO_LIBRARIES"
-	ret.DistFile = procMacro.distFile
+	if procMacro.distFile.Valid() {
+		ret.DistFiles = android.MakeDefaultDistFiles(procMacro.distFile.Path())
+	}
 
 }
 
diff --git a/rust/binary.go b/rust/binary.go
index 56d6f0b..a1cd410 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -131,3 +131,11 @@
 func (binary *binaryDecorator) coverageOutputZipPath() android.OptionalPath {
 	return binary.coverageOutputZipFile
 }
+
+func (binary *binaryDecorator) autoDep() autoDep {
+	if binary.preferDynamic() {
+		return dylibAutoDep
+	} else {
+		return rlibAutoDep
+	}
+}
diff --git a/rust/builder.go b/rust/builder.go
index 7dbb59d..16d7306 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -39,6 +39,18 @@
 		},
 		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd")
 
+	_            = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
+	clippyDriver = pctx.AndroidStaticRule("clippy",
+		blueprint.RuleParams{
+			Command: "$clippyCmd " +
+				// Because clippy-driver uses rustc as backend, we need to have some output even during the linting.
+				// Use the metadata output as it has the smallest footprint.
+				"--emit metadata -o $out $in ${libFlags} " +
+				"$rustcFlags $clippyFlags",
+			CommandDeps: []string{"$clippyCmd"},
+		},
+		"rustcFlags", "libFlags", "clippyFlags")
+
 	zip = pctx.AndroidStaticRule("zip",
 		blueprint.RuleParams{
 			Command:        "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
@@ -57,38 +69,38 @@
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
 }
 
-func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) buildOutput {
+func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, linkDirs []string) buildOutput {
 	flags.RustFlags = append(flags.RustFlags, "-C lto")
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", linkDirs)
 }
 
-func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs)
+func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, linkDirs []string) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", linkDirs)
 }
 
-func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs)
+func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, linkDirs []string) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", linkDirs)
 }
 
-func TransformSrctoStatic(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) buildOutput {
+func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, linkDirs []string) buildOutput {
 	flags.RustFlags = append(flags.RustFlags, "-C lto")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", linkDirs)
 }
 
-func TransformSrctoShared(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, includeDirs []string) buildOutput {
+func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, linkDirs []string) buildOutput {
 	flags.RustFlags = append(flags.RustFlags, "-C lto")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", linkDirs)
 }
 
-func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps,
-	flags Flags, outputFile android.WritablePath, includeDirs []string) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs)
+func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
+	flags Flags, outputFile android.WritablePath, linkDirs []string) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", linkDirs)
 }
 
 func rustLibsToPaths(libs RustLibraries) android.Paths {
@@ -99,8 +111,8 @@
 	return paths
 }
 
-func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, crate_type string, includeDirs []string) buildOutput {
+func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, crate_type string, linkDirs []string) buildOutput {
 
 	var inputs android.Paths
 	var implicits android.Paths
@@ -109,8 +121,8 @@
 	var implicitOutputs android.WritablePaths
 
 	output.outputFile = outputFile
-	crate_name := ctx.(ModuleContext).CrateName()
-	targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
+	crate_name := ctx.RustModule().CrateName()
+	targetTriple := ctx.toolchain().RustTriple()
 
 	inputs = append(inputs, main)
 
@@ -125,12 +137,10 @@
 		rustcFlags = append(rustcFlags, "--target="+targetTriple)
 		linkFlags = append(linkFlags, "-target "+targetTriple)
 	}
-	// TODO once we have static libraries in the host prebuilt .bp, this
-	// should be unconditionally added.
-	if !(ctx.Host() && ctx.TargetPrimary()) {
-		// If we're not targeting the host primary arch, do not use an implicit sysroot
-		rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
-	}
+
+	// Suppress an implicit sysroot
+	rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
+
 	// Collect linker flags
 	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
 	linkFlags = append(linkFlags, flags.LinkFlags...)
@@ -146,7 +156,7 @@
 		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
 	}
 
-	for _, path := range includeDirs {
+	for _, path := range linkDirs {
 		libFlags = append(libFlags, "-L "+path)
 	}
 
@@ -179,6 +189,25 @@
 		output.coverageFile = gcnoFile
 	}
 
+	if flags.Clippy {
+		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:            clippyDriver,
+			Description:     "clippy " + main.Rel(),
+			Output:          clippyFile,
+			ImplicitOutputs: nil,
+			Inputs:          inputs,
+			Implicits:       implicits,
+			Args: map[string]string{
+				"rustcFlags":  strings.Join(rustcFlags, " "),
+				"libFlags":    strings.Join(libFlags, " "),
+				"clippyFlags": strings.Join(flags.ClippyFlags, " "),
+			},
+		})
+		// Declare the clippy build as an implicit dependency of the original crate.
+		implicits = append(implicits, clippyFile)
+	}
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rustc,
 		Description:     "rustc " + main.Rel(),
@@ -198,7 +227,7 @@
 	return output
 }
 
-func TransformCoverageFilesToZip(ctx android.ModuleContext,
+func TransformCoverageFilesToZip(ctx ModuleContext,
 	covFiles android.Paths, baseName string) android.OptionalPath {
 	if len(covFiles) > 0 {
 
diff --git a/rust/clippy.go b/rust/clippy.go
new file mode 100644
index 0000000..e1f567d
--- /dev/null
+++ b/rust/clippy.go
@@ -0,0 +1,42 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/rust/config"
+)
+
+type ClippyProperties struct {
+	// whether to run clippy.
+	Clippy *bool
+}
+
+type clippy struct {
+	Properties ClippyProperties
+}
+
+func (c *clippy) props() []interface{} {
+	return []interface{}{&c.Properties}
+}
+
+func (c *clippy) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+	if c.Properties.Clippy != nil && !*c.Properties.Clippy {
+		return flags, deps
+	}
+	enabled, lints := config.ClippyLintsForDir(ctx.ModuleDir())
+	flags.Clippy = enabled
+	flags.ClippyFlags = append(flags.ClippyFlags, lints)
+	return flags, deps
+}
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
new file mode 100644
index 0000000..3144173
--- /dev/null
+++ b/rust/clippy_test.go
@@ -0,0 +1,46 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"testing"
+)
+
+func TestClippy(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		rust_library {
+			name: "libfoobar",
+			srcs: ["foo.rs"],
+			crate_name: "foobar",
+			clippy: false,
+		}`)
+
+	ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Output("libfoo.dylib.so")
+	fooClippy := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+	if fooClippy.Rule.String() != "android/soong/rust.clippy" {
+		t.Errorf("Clippy output (default) for libfoo was not generated: %+v", fooClippy)
+	}
+
+	ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").Output("libfoobar.dylib.so")
+	foobarClippy := ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+	if foobarClippy.Rule != nil {
+		t.Errorf("Clippy output for libfoobar is not empty")
+	}
+}
diff --git a/rust/compiler.go b/rust/compiler.go
index efc1ce4..92a3b07 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -28,10 +28,6 @@
 	return proptools.StringDefault(compiler.Properties.Edition, config.DefaultEdition)
 }
 
-func getDenyWarnings(compiler *baseCompiler) bool {
-	return BoolDefault(compiler.Properties.Deny_warnings, config.DefaultDenyWarnings)
-}
-
 func (compiler *baseCompiler) setNoStdlibs() {
 	compiler.Properties.No_stdlibs = proptools.BoolPtr(true)
 }
@@ -56,8 +52,8 @@
 	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs)
 	Srcs []string `android:"path,arch_variant"`
 
-	// whether to pass "-D warnings" to rustc. Defaults to true.
-	Deny_warnings *bool
+	// whether to suppress the standard lint flags - default to false
+	No_lint *bool
 
 	// flags to pass to rustc
 	Flags []string `android:"path,arch_variant"`
@@ -71,6 +67,9 @@
 	// list of rust dylib crate dependencies
 	Dylibs []string `android:"arch_variant"`
 
+	// list of rust automatic crate dependencies
+	Rustlibs []string `android:"arch_variant"`
+
 	// list of rust proc_macro crate dependencies
 	Proc_macros []string `android:"arch_variant"`
 
@@ -104,8 +103,6 @@
 
 type baseCompiler struct {
 	Properties   BaseCompilerProperties
-	depFlags     []string
-	linkDirs     []string
 	coverageFile android.Path //rustc generates a single gcno file
 
 	// Install related
@@ -145,8 +142,8 @@
 
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 
-	if getDenyWarnings(compiler) {
-		flags.RustFlags = append(flags.RustFlags, "-D warnings")
+	if !Bool(compiler.Properties.No_lint) {
+		flags.RustFlags = append(flags.RustFlags, config.RustcLintsForDir(ctx.ModuleDir()))
 	}
 	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
 	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
@@ -182,6 +179,7 @@
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
 	deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
+	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
 	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
 	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
 	deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
@@ -193,17 +191,7 @@
 				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
 			}
 
-			// This check is technically insufficient - on the host, where
-			// static linking is the default, if one of our static
-			// dependencies uses a dynamic library, we need to dynamically
-			// link the stdlib as well.
-			if (len(deps.Dylibs) > 0) || ctx.Device() {
-				// Dynamically linked stdlib
-				deps.Dylibs = append(deps.Dylibs, stdlib)
-			} else if ctx.Host() && !ctx.TargetPrimary() {
-				// Otherwise use the static in-tree stdlib for host secondary arch
-				deps.Rlibs = append(deps.Rlibs, stdlib+".static")
-			}
+			deps.Rustlibs = append(deps.Rustlibs, stdlib)
 		}
 	}
 	return deps
@@ -253,7 +241,7 @@
 }
 
 func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string {
-	stem := ctx.baseModuleName()
+	stem := ctx.ModuleName()
 	if String(compiler.Properties.Stem) != "" {
 		stem = String(compiler.Properties.Stem)
 	}
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 5026da3..bcfac7c 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -9,6 +9,7 @@
         "arm_device.go",
         "arm64_device.go",
         "global.go",
+        "lints.go",
         "toolchain.go",
         "allowed_list.go",
         "x86_darwin_host.go",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index a339050..0204cd2 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -15,13 +15,15 @@
 		"rust_library",
 		"rust_library_dylib",
 		"rust_library_rlib",
-		"rust_library_shared",
-		"rust_library_static",
+		"rust_ffi",
+		"rust_ffi_shared",
+		"rust_ffi_static",
 		"rust_library_host",
 		"rust_library_host_dylib",
 		"rust_library_host_rlib",
-		"rust_library_host_shared",
-		"rust_library_host_static",
+		"rust_ffi_host",
+		"rust_ffi_host_shared",
+		"rust_ffi_host_static",
 		"rust_proc_macro",
 		"rust_test",
 		"rust_test_host",
diff --git a/rust/config/global.go b/rust/config/global.go
index 63624c0..e1b1775 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.43.0"
+	RustDefaultVersion = "1.44.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
@@ -32,8 +32,6 @@
 		"libtest",
 	}
 
-	DefaultDenyWarnings = true
-
 	GlobalRustFlags = []string{
 		"--remap-path-prefix $$(pwd)=",
 		"-C codegen-units=1",
diff --git a/rust/config/lints.go b/rust/config/lints.go
new file mode 100644
index 0000000..529d094
--- /dev/null
+++ b/rust/config/lints.go
@@ -0,0 +1,150 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+// Overarching principles for Rust lints on Android:
+// The Android build system tries to avoid reporting warnings during the build.
+// Therefore, by default, we upgrade warnings to denials. For some of these
+// lints, an allow exception is setup, using the variables below.
+// There are different default lints depending on the repository location (see
+// DefaultLocalClippyChecks).
+// The lints are split into two categories. The first one contains the built-in
+// lints (https://doc.rust-lang.org/rustc/lints/index.html). The second is
+// specific to Clippy lints (https://rust-lang.github.io/rust-clippy/master/).
+//
+// When developing a module, it is possible to use the `no_lint` property to
+// disable the built-in lints configuration. It is also possible to set
+// `clippy` to false to disable the clippy verification. Expect some
+// questioning during review if you enable one of these options. For external/
+// code, if you need to use them, it is likely a bug. Otherwise, it may be
+// useful to add an exception (that is, move a lint from deny to allow).
+var (
+	// Default Rust lints that applies to Google-authored modules.
+	defaultRustcLints = []string{
+		"-A deprecated",
+		"-D missing-docs",
+		"-D warnings",
+	}
+	// Default Clippy lints. These are applied on top of defaultRustcLints.
+	// It should be assumed that any warning lint will be promoted to a
+	// deny.
+	defaultClippyLints = []string{
+		"-A clippy::type-complexity",
+	}
+
+	// Rust lints for vendor code.
+	defaultRustcVendorLints = []string{
+		"-A deprecated",
+		"-D warnings",
+	}
+	// Clippy lints for vendor source. These are applied on top of
+	// defaultRustcVendorLints.  It should be assumed that any warning lint
+	// will be promoted to a deny.
+	defaultClippyVendorLints = []string{
+		"-A clippy::complexity",
+		"-A clippy::perf",
+		"-A clippy::style",
+	}
+
+	// For prebuilts/ and external/, no linting is expected. If a warning
+	// or a deny is reported, it should be fixed upstream.
+	allowAllLints = []string{
+		"--cap-lints allow",
+	}
+)
+
+func init() {
+	// Default Rust lints. These apply to all Google-authored modules.
+	pctx.VariableFunc("RustDefaultLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("RUST_DEFAULT_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultRustcLints, " ")
+	})
+	pctx.VariableFunc("ClippyDefaultLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("CLIPPY_DEFAULT_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultClippyLints, " ")
+	})
+
+	// Rust lints that only applies to external code.
+	pctx.VariableFunc("RustVendorLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("RUST_VENDOR_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultRustcVendorLints, " ")
+	})
+	pctx.VariableFunc("ClippyVendorLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("CLIPPY_VENDOR_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultClippyVendorLints, " ")
+	})
+	pctx.StaticVariable("RustAllowAllLints", strings.Join(allowAllLints, " "))
+}
+
+type PathBasedClippyConfig struct {
+	PathPrefix    string
+	RustcConfig   string
+	ClippyEnabled bool
+	ClippyConfig  string
+}
+
+const noLint = ""
+const rustcDefault = "${config.RustDefaultLints}"
+const rustcVendor = "${config.RustVendorLints}"
+const rustcAllowAll = "${config.RustAllowAllLints}"
+const clippyDefault = "${config.ClippyDefaultLints}"
+const clippyVendor = "${config.ClippyVendorLints}"
+
+// This is a map of local path prefixes to a set of parameters for the linting:
+// - a string for the lints to apply to rustc.
+// - a boolean to indicate if clippy should be executed.
+// - a string for the lints to apply to clippy.
+// The first entry matching will be used.
+var DefaultLocalClippyChecks = []PathBasedClippyConfig{
+	{"external/", rustcAllowAll, false, noLint},
+	{"hardware/", rustcVendor, true, clippyVendor},
+	{"prebuilts/", rustcAllowAll, false, noLint},
+	{"vendor/google", rustcDefault, true, clippyDefault},
+	{"vendor/", rustcVendor, true, clippyVendor},
+}
+
+// ClippyLintsForDir returns a boolean if Clippy should be executed and if so, the lints to be used.
+func ClippyLintsForDir(dir string) (bool, string) {
+	for _, pathCheck := range DefaultLocalClippyChecks {
+		if strings.HasPrefix(dir, pathCheck.PathPrefix) {
+			return pathCheck.ClippyEnabled, pathCheck.ClippyConfig
+		}
+	}
+	return true, clippyDefault
+}
+
+// RustcLintsForDir returns the standard lints to be used for a repository.
+func RustcLintsForDir(dir string) string {
+	for _, pathCheck := range DefaultLocalClippyChecks {
+		if strings.HasPrefix(dir, pathCheck.PathPrefix) {
+			return pathCheck.RustcConfig
+		}
+	}
+	return rustcDefault
+}
diff --git a/rust/coverage.go b/rust/coverage.go
index 4e3977b..223ba4f 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -67,6 +67,6 @@
 		// Host coverage not yet supported.
 	} else {
 		// Update useSdk and sdkVersion args if Rust modules become SDK aware.
-		cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), false, "")
+		cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.RustModule().nativeCoverage(), false, "")
 	}
 }
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 27acad3..357c2e8 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -105,6 +105,12 @@
 			rlibs: ["librlib"],
 			crate_name: "foo",
 		}
+                rust_ffi_static {
+                        name: "libbaz",
+                        srcs: ["foo.rs"],
+                        rlibs: ["librlib"],
+                        crate_name: "baz",
+                }
 		rust_library_rlib {
 			name: "librlib",
 			srcs: ["foo.rs"],
@@ -113,17 +119,17 @@
 		rust_binary {
 			name: "fizz",
 			rlibs: ["librlib"],
-			static_libs: ["libfoo"],
+			static_libs: ["libbaz"],
 			srcs: ["foo.rs"],
 		}
 		cc_binary {
 			name: "buzz",
-			static_libs: ["libfoo"],
+			static_libs: ["libbaz"],
 			srcs: ["foo.c"],
 		}
 		cc_library {
 			name: "libbar",
-			static_libs: ["libfoo"],
+			static_libs: ["libbaz"],
 			compile_multilib: "64",
 			srcs: ["foo.c"],
 		}`)
@@ -149,7 +155,7 @@
 
 	// Make sure the expected inputs are provided to the zip rule.
 	if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
-		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") ||
+		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") ||
 		!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_cov/fizz.gcno") {
 		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
 	}
@@ -158,11 +164,11 @@
 		t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
 	}
 	if !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_cov/obj/foo.gcno") ||
-		!android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") {
+		!android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") {
 		t.Fatalf("missing expected coverage files for cc 'buzz' binary: %#v", buzzZipInputs)
 	}
 	if !android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/obj/foo.gcno") ||
-		!android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") {
+		!android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") {
 		t.Fatalf("missing expected coverage files for cc 'libbar' library: %#v", libbarZipInputs)
 	}
 }
diff --git a/rust/library.go b/rust/library.go
index 3c948ea..8b8e797 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -19,7 +19,6 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/rust/config"
 )
 
 func init() {
@@ -29,14 +28,17 @@
 	android.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
 	android.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
 	android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
-	android.RegisterModuleType("rust_library_shared", RustLibrarySharedFactory)
-	android.RegisterModuleType("rust_library_static", RustLibraryStaticFactory)
-	android.RegisterModuleType("rust_library_host_shared", RustLibrarySharedHostFactory)
-	android.RegisterModuleType("rust_library_host_static", RustLibraryStaticHostFactory)
+	android.RegisterModuleType("rust_ffi", RustFFIFactory)
+	android.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
+	android.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	android.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
+	android.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
+	android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
 }
 
 type VariantLibraryProperties struct {
-	Enabled *bool `android:"arch_variant"`
+	Enabled *bool    `android:"arch_variant"`
+	Srcs    []string `android:"path,arch_variant"`
 }
 
 type LibraryCompilerProperties struct {
@@ -71,6 +73,7 @@
 
 type libraryDecorator struct {
 	*baseCompiler
+	*flagExporter
 
 	Properties        LibraryCompilerProperties
 	MutatedProperties LibraryMutatedProperties
@@ -96,6 +99,8 @@
 	setStatic()
 
 	// Build a specific library variant
+	BuildOnlyFFI()
+	BuildOnlyRust()
 	BuildOnlyRlib()
 	BuildOnlyDylib()
 	BuildOnlyStatic()
@@ -106,22 +111,6 @@
 	return true
 }
 
-func (library *libraryDecorator) exportedDirs() []string {
-	return library.linkDirs
-}
-
-func (library *libraryDecorator) exportedDepFlags() []string {
-	return library.depFlags
-}
-
-func (library *libraryDecorator) reexportDirs(dirs ...string) {
-	library.linkDirs = android.FirstUniqueStrings(append(library.linkDirs, dirs...))
-}
-
-func (library *libraryDecorator) reexportDepFlags(flags ...string) {
-	library.depFlags = android.FirstUniqueStrings(append(library.depFlags, flags...))
-}
-
 func (library *libraryDecorator) rlib() bool {
 	return library.MutatedProperties.VariantIsRlib
 }
@@ -182,12 +171,31 @@
 	library.MutatedProperties.VariantIsDylib = false
 }
 
+func (library *libraryDecorator) autoDep() autoDep {
+	if library.rlib() || library.static() {
+		return rlibAutoDep
+	} else if library.dylib() || library.shared() {
+		return dylibAutoDep
+	} else {
+		return rlibAutoDep
+	}
+}
+
 var _ compiler = (*libraryDecorator)(nil)
 var _ libraryInterface = (*libraryDecorator)(nil)
+var _ exportedFlagsProducer = (*libraryDecorator)(nil)
 
-// rust_library produces all variants.
+// rust_library produces all rust variants.
 func RustLibraryFactory() android.Module {
-	module, _ := NewRustLibrary(android.HostAndDeviceSupported)
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyRust()
+	return module.Init()
+}
+
+// rust_ffi produces all ffi variants.
+func RustFFIFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyFFI()
 	return module.Init()
 }
 
@@ -205,23 +213,31 @@
 	return module.Init()
 }
 
-// rust_library_shared produces a shared library.
-func RustLibrarySharedFactory() android.Module {
+// rust_ffi_shared produces a shared library.
+func RustFFISharedFactory() android.Module {
 	module, library := NewRustLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyShared()
 	return module.Init()
 }
 
-// rust_library_static produces a static library.
-func RustLibraryStaticFactory() android.Module {
+// rust_ffi_static produces a static library.
+func RustFFIStaticFactory() android.Module {
 	module, library := NewRustLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyStatic()
 	return module.Init()
 }
 
-// rust_library_host produces all variants.
+// rust_library_host produces all rust variants.
 func RustLibraryHostFactory() android.Module {
-	module, _ := NewRustLibrary(android.HostSupported)
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyRust()
+	return module.Init()
+}
+
+// rust_ffi_host produces all FFI variants.
+func RustFFIHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyFFI()
 	return module.Init()
 }
 
@@ -239,44 +255,60 @@
 	return module.Init()
 }
 
-// rust_library_static_host produces a static library.
-func RustLibraryStaticHostFactory() android.Module {
+// rust_ffi_static_host produces a static library.
+func RustFFIStaticHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
 	library.BuildOnlyStatic()
 	return module.Init()
 }
 
-// rust_library_shared_host produces an shared library.
-func RustLibrarySharedHostFactory() android.Module {
+// rust_ffi_shared_host produces an shared library.
+func RustFFISharedHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
 	library.BuildOnlyShared()
 	return module.Init()
 }
 
+func (library *libraryDecorator) BuildOnlyFFI() {
+	library.MutatedProperties.BuildDylib = false
+	library.MutatedProperties.BuildRlib = false
+	library.MutatedProperties.BuildShared = true
+	library.MutatedProperties.BuildStatic = true
+}
+
+func (library *libraryDecorator) BuildOnlyRust() {
+	library.MutatedProperties.BuildDylib = true
+	library.MutatedProperties.BuildRlib = true
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildStatic = false
+}
+
 func (library *libraryDecorator) BuildOnlyDylib() {
+	library.MutatedProperties.BuildDylib = true
 	library.MutatedProperties.BuildRlib = false
 	library.MutatedProperties.BuildShared = false
 	library.MutatedProperties.BuildStatic = false
-
 }
 
 func (library *libraryDecorator) BuildOnlyRlib() {
 	library.MutatedProperties.BuildDylib = false
+	library.MutatedProperties.BuildRlib = true
 	library.MutatedProperties.BuildShared = false
 	library.MutatedProperties.BuildStatic = false
 }
 
 func (library *libraryDecorator) BuildOnlyStatic() {
-	library.MutatedProperties.BuildShared = false
 	library.MutatedProperties.BuildRlib = false
 	library.MutatedProperties.BuildDylib = false
-
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildStatic = true
 }
 
 func (library *libraryDecorator) BuildOnlyShared() {
-	library.MutatedProperties.BuildStatic = false
 	library.MutatedProperties.BuildRlib = false
 	library.MutatedProperties.BuildDylib = false
+	library.MutatedProperties.BuildStatic = false
+	library.MutatedProperties.BuildShared = true
 }
 
 func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
@@ -284,12 +316,13 @@
 
 	library := &libraryDecorator{
 		MutatedProperties: LibraryMutatedProperties{
-			BuildDylib:  true,
-			BuildRlib:   true,
-			BuildShared: true,
-			BuildStatic: true,
+			BuildDylib:  false,
+			BuildRlib:   false,
+			BuildShared: false,
+			BuildStatic: false,
 		},
 		baseCompiler: NewBaseCompiler("lib", "lib64", InstallInSystem),
+		flagExporter: NewFlagExporter(),
 	}
 
 	module.compiler = library
@@ -304,15 +337,6 @@
 }
 
 func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
-
-	// TODO(b/155498724) Remove if C static libraries no longer require libstd as an rlib dependency.
-	if !ctx.Host() && library.static() {
-		library.setNoStdlibs()
-		for _, stdlib := range config.Stdlibs {
-			deps.Rlibs = append(deps.Rlibs, stdlib+".static")
-		}
-	}
-
 	deps = library.baseCompiler.compilerDeps(ctx, deps)
 
 	if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
@@ -329,7 +353,7 @@
 }
 
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.baseModuleName())
+	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName())
 	flags = library.baseCompiler.compilerFlags(ctx, flags)
 	if library.shared() || library.static() {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
@@ -391,8 +415,8 @@
 	library.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, library.getStem(ctx))
 
 	if library.rlib() || library.dylib() {
-		library.reexportDirs(deps.linkDirs...)
-		library.reexportDepFlags(deps.depFlags...)
+		library.exportLinkDirs(deps.linkDirs...)
+		library.exportDepFlags(deps.depFlags...)
 	}
 	library.unstrippedOutputFile = outputFile
 
diff --git a/rust/library_test.go b/rust/library_test.go
index 9d2f6c0..8a91cf1 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -29,13 +29,18 @@
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
-		}`)
+		}
+                rust_ffi_host {
+                        name: "libfoo.ffi",
+                        srcs: ["foo.rs"],
+                        crate_name: "foo"
+                }`)
 
 	// Test all variants are being built.
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib").Output("libfoo.rlib")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
-	libfooStatic := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_static").Output("libfoo.a")
-	libfooShared := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_shared").Output("libfoo.so")
+	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Output("libfoo.ffi.a")
+	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Output("libfoo.ffi.so")
 
 	rlibCrateType := "rlib"
 	dylibCrateType := "dylib"
@@ -119,7 +124,7 @@
 
 func TestSharedLibrary(t *testing.T) {
 	ctx := testRust(t, `
-		rust_library {
+		rust_ffi_shared {
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
@@ -138,3 +143,50 @@
 			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
 	}
 }
+
+// Test that variants pull in the right type of rustlib autodep
+func TestAutoDeps(t *testing.T) {
+
+	ctx := testRust(t, `
+                rust_library_host {
+                        name: "libbar",
+                        srcs: ["bar.rs"],
+                        crate_name: "bar",
+                }
+		rust_library_host {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+                        rustlibs: ["libbar"],
+		}
+                rust_ffi_host {
+                        name: "libfoo.ffi",
+                        srcs: ["foo.rs"],
+                        crate_name: "foo",
+                        rustlibs: ["libbar"],
+                }`)
+
+	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib")
+	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
+	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
+	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
+
+	for _, static := range []android.TestingModule{libfooRlib, libfooStatic} {
+		if !android.InList("libbar", static.Module().(*Module).Properties.AndroidMkRlibs) {
+			t.Errorf("libbar not present as static dependency in static lib")
+		}
+		if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) {
+			t.Errorf("libbar present as dynamic dependency in static lib")
+		}
+	}
+
+	for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} {
+		if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) {
+			t.Errorf("libbar not present as dynamic dependency in dynamic lib")
+		}
+		if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
+			t.Errorf("libbar present as static dependency in dynamic lib")
+		}
+
+	}
+}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 1d97650..67d649d 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -19,12 +19,16 @@
 )
 
 func init() {
+	android.RegisterModuleType("rust_prebuilt_library", PrebuiltLibraryFactory)
 	android.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
+	android.RegisterModuleType("rust_prebuilt_rlib", PrebuiltRlibFactory)
 }
 
 type PrebuiltProperties struct {
 	// path to the prebuilt file
 	Srcs []string `android:"path,arch_variant"`
+	// directories containing associated rlib dependencies
+	Link_dirs []string `android:"path,arch_variant"`
 }
 
 type prebuiltLibraryDecorator struct {
@@ -33,32 +37,65 @@
 }
 
 var _ compiler = (*prebuiltLibraryDecorator)(nil)
+var _ exportedFlagsProducer = (*prebuiltLibraryDecorator)(nil)
+
+func PrebuiltLibraryFactory() android.Module {
+	module, _ := NewPrebuiltLibrary(android.HostAndDeviceSupported)
+	return module.Init()
+}
 
 func PrebuiltDylibFactory() android.Module {
 	module, _ := NewPrebuiltDylib(android.HostAndDeviceSupported)
 	return module.Init()
 }
 
-func NewPrebuiltDylib(hod android.HostOrDeviceSupported) (*Module, *prebuiltLibraryDecorator) {
+func PrebuiltRlibFactory() android.Module {
+	module, _ := NewPrebuiltRlib(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *prebuiltLibraryDecorator) {
 	module, library := NewRustLibrary(hod)
-	library.BuildOnlyDylib()
+	library.BuildOnlyRust()
 	library.setNoStdlibs()
-	library.setDylib()
 	prebuilt := &prebuiltLibraryDecorator{
 		libraryDecorator: library,
 	}
 	module.compiler = prebuilt
-	module.AddProperties(&library.Properties)
+	return module, prebuilt
+}
+
+func NewPrebuiltDylib(hod android.HostOrDeviceSupported) (*Module, *prebuiltLibraryDecorator) {
+	module, library := NewRustLibrary(hod)
+	library.BuildOnlyDylib()
+	library.setNoStdlibs()
+	prebuilt := &prebuiltLibraryDecorator{
+		libraryDecorator: library,
+	}
+	module.compiler = prebuilt
+	return module, prebuilt
+}
+
+func NewPrebuiltRlib(hod android.HostOrDeviceSupported) (*Module, *prebuiltLibraryDecorator) {
+	module, library := NewRustLibrary(hod)
+	library.BuildOnlyRlib()
+	library.setNoStdlibs()
+	prebuilt := &prebuiltLibraryDecorator{
+		libraryDecorator: library,
+	}
+	module.compiler = prebuilt
 	return module, prebuilt
 }
 
 func (prebuilt *prebuiltLibraryDecorator) compilerProps() []interface{} {
-	return append(prebuilt.baseCompiler.compilerProps(),
+	return append(prebuilt.libraryDecorator.compilerProps(),
 		&prebuilt.Properties)
 }
 
 func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
-	srcPath := srcPathFromModuleSrcs(ctx, prebuilt.Properties.Srcs)
+	prebuilt.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
+
+	srcPath := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
 
 	prebuilt.unstrippedOutputFile = srcPath
 
@@ -73,3 +110,15 @@
 func (prebuilt *prebuiltLibraryDecorator) nativeCoverage() bool {
 	return false
 }
+
+func (prebuilt *prebuiltLibraryDecorator) prebuiltSrcs() []string {
+	srcs := prebuilt.Properties.Srcs
+	if prebuilt.rlib() {
+		srcs = append(srcs, prebuilt.libraryDecorator.Properties.Rlib.Srcs...)
+	}
+	if prebuilt.dylib() {
+		srcs = append(srcs, prebuilt.libraryDecorator.Properties.Dylib.Srcs...)
+	}
+
+	return srcs
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 42c8537..2719161 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -27,6 +27,7 @@
 
 type procMacroDecorator struct {
 	*baseCompiler
+	*flagExporter
 
 	Properties ProcMacroCompilerProperties
 }
@@ -35,6 +36,7 @@
 }
 
 var _ compiler = (*procMacroDecorator)(nil)
+var _ exportedFlagsProducer = (*procMacroDecorator)(nil)
 
 func ProcMacroFactory() android.Module {
 	module, _ := NewProcMacro(android.HostSupportedNoCross)
@@ -46,6 +48,7 @@
 
 	procMacro := &procMacroDecorator{
 		baseCompiler: NewBaseCompiler("lib", "lib64", InstallInSystem),
+		flagExporter: NewFlagExporter(),
 	}
 
 	module.compiler = procMacro
@@ -76,3 +79,7 @@
 
 	return stem + String(procMacro.baseCompiler.Properties.Suffix)
 }
+
+func (procMacro *procMacroDecorator) autoDep() autoDep {
+	return rlibAutoDep
+}
diff --git a/rust/rust.go b/rust/rust.go
index 7b82b1f..72301a7 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -49,8 +49,10 @@
 	GlobalLinkFlags []string // Flags that apply globally to linker
 	RustFlags       []string // Flags that apply to rust
 	LinkFlags       []string // Flags that apply to linker
+	ClippyFlags     []string // Flags that apply to clippy-driver, during the linting
 	Toolchain       config.Toolchain
 	Coverage        bool
+	Clippy          bool
 }
 
 type BaseProperties struct {
@@ -75,6 +77,7 @@
 
 	compiler         compiler
 	coverage         *coverage
+	clippy           *clippy
 	cachedToolchain  config.Toolchain
 	subAndroidMkOnce map[subAndroidMkProvider]bool
 	outputFile       android.OptionalPath
@@ -214,6 +217,7 @@
 type Deps struct {
 	Dylibs     []string
 	Rlibs      []string
+	Rustlibs   []string
 	ProcMacros []string
 	SharedLibs []string
 	StaticLibs []string
@@ -258,6 +262,43 @@
 	nativeCoverage() bool
 }
 
+type exportedFlagsProducer interface {
+	exportedLinkDirs() []string
+	exportedDepFlags() []string
+	exportLinkDirs(...string)
+	exportDepFlags(...string)
+}
+
+type flagExporter struct {
+	depFlags []string
+	linkDirs []string
+}
+
+func (flagExporter *flagExporter) exportedLinkDirs() []string {
+	return flagExporter.linkDirs
+}
+
+func (flagExporter *flagExporter) exportedDepFlags() []string {
+	return flagExporter.depFlags
+}
+
+func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
+	flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
+}
+
+func (flagExporter *flagExporter) exportDepFlags(flags ...string) {
+	flagExporter.depFlags = android.FirstUniqueStrings(append(flagExporter.depFlags, flags...))
+}
+
+var _ exportedFlagsProducer = (*flagExporter)(nil)
+
+func NewFlagExporter() *flagExporter {
+	return &flagExporter{
+		depFlags: []string{},
+		linkDirs: []string{},
+	}
+}
+
 func (mod *Module) isCoverageVariant() bool {
 	return mod.coverage.Properties.IsCoverageVariant
 }
@@ -306,6 +347,7 @@
 		&PrebuiltProperties{},
 		&TestProperties{},
 		&cc.CoverageProperties{},
+		&ClippyProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -411,7 +453,7 @@
 func (mod *Module) StubsVersions() []string {
 	// For now, Rust has no stubs versions.
 	if mod.compiler != nil {
-		if _, ok := mod.compiler.(*libraryDecorator); ok {
+		if _, ok := mod.compiler.(libraryInterface); ok {
 			return []string{}
 		}
 	}
@@ -435,6 +477,9 @@
 
 func (mod *Module) CoverageFiles() android.Paths {
 	if mod.compiler != nil {
+		if !mod.compiler.nativeCoverage() {
+			return android.Paths{}
+		}
 		if library, ok := mod.compiler.(*libraryDecorator); ok {
 			if library.coverageFile != nil {
 				return android.Paths{library.coverageFile}
@@ -456,6 +501,9 @@
 	if mod.coverage != nil {
 		mod.AddProperties(mod.coverage.props()...)
 	}
+	if mod.clippy != nil {
+		mod.AddProperties(mod.clippy.props()...)
+	}
 
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
 
@@ -487,6 +535,7 @@
 func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	module := newBaseModule(hod, multilib)
 	module.coverage = &coverage{}
+	module.clippy = &clippy{}
 	return module
 }
 
@@ -506,39 +555,50 @@
 }
 
 type ModuleContextIntf interface {
+	RustModule() *Module
 	toolchain() config.Toolchain
-	baseModuleName() string
-	CrateName() string
-	nativeCoverage() bool
 }
 
 type depsContext struct {
 	android.BottomUpMutatorContext
-	moduleContextImpl
 }
 
 type moduleContext struct {
 	android.ModuleContext
-	moduleContextImpl
 }
 
-func (ctx *moduleContextImpl) nativeCoverage() bool {
-	return ctx.mod.nativeCoverage()
+type baseModuleContext struct {
+	android.BaseModuleContext
+}
+
+func (ctx *moduleContext) RustModule() *Module {
+	return ctx.Module().(*Module)
+}
+
+func (ctx *moduleContext) toolchain() config.Toolchain {
+	return ctx.RustModule().toolchain(ctx)
+}
+
+func (ctx *depsContext) RustModule() *Module {
+	return ctx.Module().(*Module)
+}
+
+func (ctx *depsContext) toolchain() config.Toolchain {
+	return ctx.RustModule().toolchain(ctx)
+}
+
+func (ctx *baseModuleContext) RustModule() *Module {
+	return ctx.Module().(*Module)
+}
+
+func (ctx *baseModuleContext) toolchain() config.Toolchain {
+	return ctx.RustModule().toolchain(ctx)
 }
 
 func (mod *Module) nativeCoverage() bool {
 	return mod.compiler != nil && mod.compiler.nativeCoverage()
 }
 
-type moduleContextImpl struct {
-	mod *Module
-	ctx BaseModuleContext
-}
-
-func (ctx *moduleContextImpl) toolchain() config.Toolchain {
-	return ctx.mod.toolchain(ctx.ctx)
-}
-
 func (mod *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
 	if mod.cachedToolchain == nil {
 		mod.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
@@ -552,11 +612,7 @@
 func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
 	ctx := &moduleContext{
 		ModuleContext: actx,
-		moduleContextImpl: moduleContextImpl{
-			mod: mod,
-		},
 	}
-	ctx.ctx = ctx
 
 	toolchain := mod.toolchain(ctx)
 
@@ -576,6 +632,9 @@
 	if mod.coverage != nil {
 		flags, deps = mod.coverage.flags(ctx, flags, deps)
 	}
+	if mod.clippy != nil {
+		flags, deps = mod.clippy.flags(ctx, flags, deps)
+	}
 
 	if mod.compiler != nil {
 		outputFile := mod.compiler.compile(ctx, flags, deps)
@@ -599,6 +658,7 @@
 
 	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
 	deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
+	deps.Rustlibs = android.LastUniqueStrings(deps.Rustlibs)
 	deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
 	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
@@ -607,14 +667,6 @@
 
 }
 
-func (ctx *moduleContextImpl) baseModuleName() string {
-	return ctx.mod.ModuleBase.BaseModuleName()
-}
-
-func (ctx *moduleContextImpl) CrateName() string {
-	return ctx.mod.CrateName()
-}
-
 type dependencyTag struct {
 	blueprint.BaseDependencyTag
 	name       string
@@ -629,6 +681,20 @@
 	testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
 )
 
+type autoDep struct {
+	variation string
+	depTag    dependencyTag
+}
+
+var (
+	rlibAutoDep  = autoDep{variation: "rlib", depTag: rlibDepTag}
+	dylibAutoDep = autoDep{variation: "dylib", depTag: dylibDepTag}
+)
+
+type autoDeppable interface {
+	autoDep() autoDep
+}
+
 func (mod *Module) begin(ctx BaseModuleContext) {
 	if mod.coverage != nil {
 		mod.coverage.begin(ctx)
@@ -679,19 +745,15 @@
 			}
 
 			//Append the dependencies exportedDirs
-			if lib, ok := rustDep.compiler.(*libraryDecorator); ok {
-				depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedDirs()...)
+			if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok {
+				depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedLinkDirs()...)
 				depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
 			}
 
-			// Append this dependencies output to this mod's linkDirs so they can be exported to dependencies
-			// This can be probably be refactored by defining a common exporter interface similar to cc's
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
 				linkDir := linkPathFromFilePath(linkFile.Path())
-				if lib, ok := mod.compiler.(*libraryDecorator); ok {
-					lib.linkDirs = append(lib.linkDirs, linkDir)
-				} else if procMacro, ok := mod.compiler.(*procMacroDecorator); ok {
-					procMacro.linkDirs = append(procMacro.linkDirs, linkDir)
+				if lib, ok := mod.compiler.(exportedFlagsProducer); ok {
+					lib.exportLinkDirs(linkDir)
 				}
 			}
 
@@ -742,14 +804,10 @@
 			}
 
 			// Make sure these dependencies are propagated
-			if lib, ok := mod.compiler.(*libraryDecorator); ok && exportDep {
-				lib.linkDirs = append(lib.linkDirs, linkPath)
-				lib.depFlags = append(lib.depFlags, depFlag)
-			} else if procMacro, ok := mod.compiler.(*procMacroDecorator); ok && exportDep {
-				procMacro.linkDirs = append(procMacro.linkDirs, linkPath)
-				procMacro.depFlags = append(procMacro.depFlags, depFlag)
+			if lib, ok := mod.compiler.(exportedFlagsProducer); ok && exportDep {
+				lib.exportLinkDirs(linkPath)
+				lib.exportDepFlags(depFlag)
 			}
-
 		}
 	})
 
@@ -811,11 +869,7 @@
 func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	ctx := &depsContext{
 		BottomUpMutatorContext: actx,
-		moduleContextImpl: moduleContextImpl{
-			mod: mod,
-		},
 	}
-	ctx.ctx = ctx
 
 	deps := mod.deps(ctx)
 	commonDepVariations := []blueprint.Variation{}
@@ -838,6 +892,15 @@
 			{Mutator: "link", Variation: ""}}...),
 		dylibDepTag, deps.Dylibs...)
 
+	if deps.Rustlibs != nil {
+		autoDep := mod.compiler.(autoDeppable).autoDep()
+		actx.AddVariationDependencies(
+			append(commonDepVariations, []blueprint.Variation{
+				{Mutator: "rust_libraries", Variation: autoDep.variation},
+				{Mutator: "link", Variation: ""}}...),
+			autoDep.depTag, deps.Rustlibs...)
+	}
+
 	actx.AddVariationDependencies(append(commonDepVariations,
 		blueprint.Variation{Mutator: "link", Variation: "shared"}),
 		cc.SharedDepTag, deps.SharedLibs...)
@@ -862,19 +925,10 @@
 	}
 }
 
-type baseModuleContext struct {
-	android.BaseModuleContext
-	moduleContextImpl
-}
-
 func (mod *Module) beginMutator(actx android.BottomUpMutatorContext) {
 	ctx := &baseModuleContext{
 		BaseModuleContext: actx,
-		moduleContextImpl: moduleContextImpl{
-			mod: mod,
-		},
 	}
-	ctx.ctx = ctx
 
 	mod.begin(ctx)
 }
@@ -889,6 +943,18 @@
 	return name
 }
 
+var _ android.HostToolProvider = (*Module)(nil)
+
+func (mod *Module) HostToolPath() android.OptionalPath {
+	if !mod.Host() {
+		return android.OptionalPath{}
+	}
+	if _, ok := mod.compiler.(*binaryDecorator); ok {
+		return mod.outputFile
+	}
+	return android.OptionalPath{}
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 703aaed..08bc8ca 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -93,7 +93,6 @@
 		config.TestProductVariables.NativeCoveragePaths = []string{"*"}
 	}
 
-	t.Helper()
 	ctx := CreateTestContext()
 	ctx.Register(config)
 
@@ -165,12 +164,12 @@
 // Test to make sure dependencies are being picked up correctly.
 func TestDepsTracking(t *testing.T) {
 	ctx := testRust(t, `
-		rust_library_host_static {
+		rust_ffi_host_static {
 			name: "libstatic",
 			srcs: ["foo.rs"],
 			crate_name: "static",
 		}
-		rust_library_host_shared {
+		rust_ffi_host_shared {
 			name: "libshared",
 			srcs: ["foo.rs"],
 			crate_name: "shared",
diff --git a/rust/test.go b/rust/test.go
index 416c557..e27a70c 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -105,6 +105,10 @@
 	return flags
 }
 
+func (test *testDecorator) autoDep() autoDep {
+	return rlibAutoDep
+}
+
 func init() {
 	// Rust tests are binary files built with --test.
 	android.RegisterModuleType("rust_test", RustTestFactory)
diff --git a/rust/testing.go b/rust/testing.go
index 4e186d3..3d583e1 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -21,14 +21,26 @@
 
 func GatherRequiredDepsForTest() string {
 	bp := `
-		rust_prebuilt_dylib {
+		rust_prebuilt_library {
 				name: "libstd_x86_64-unknown-linux-gnu",
-				srcs: [""],
+                                crate_name: "std",
+                                rlib: {
+                                    srcs: ["libstd.rlib"],
+                                },
+                                dylib: {
+                                    srcs: ["libstd.so"],
+                                },
 				host_supported: true,
 		}
-		rust_prebuilt_dylib {
+		rust_prebuilt_library {
 				name: "libtest_x86_64-unknown-linux-gnu",
-				srcs: [""],
+                                crate_name: "test",
+                                rlib: {
+                                    srcs: ["libtest.rlib"],
+                                },
+                                dylib: {
+                                    srcs: ["libtest.so"],
+                                },
 				host_supported: true,
 		}
 
@@ -41,34 +53,21 @@
 			nocrt: true,
 			system_shared_libs: [],
 		}
-		rust_library_dylib {
+		rust_library {
 			name: "libstd",
 			crate_name: "std",
 			srcs: ["foo.rs"],
 			no_stdlibs: true,
 			host_supported: true,
+                        native_coverage: false,
 		}
-		rust_library_rlib {
-			name: "libstd.static",
-			crate_name: "std",
-			srcs: ["foo.rs"],
-			no_stdlibs: true,
-			host_supported: true,
-		}
-		rust_library_dylib {
+		rust_library {
 			name: "libtest",
 			crate_name: "test",
 			srcs: ["foo.rs"],
 			no_stdlibs: true,
 			host_supported: true,
-
-		}
-		rust_library_rlib {
-			name: "libtest.static",
-			crate_name: "test",
-			srcs: ["foo.rs"],
-			no_stdlibs: true,
-			host_supported: true,
+                        native_coverage: false,
 		}
 
 ` + cc.GatherRequiredDepsForTest(android.NoOsType)
@@ -83,17 +82,21 @@
 	ctx.RegisterModuleType("rust_test", RustTestFactory)
 	ctx.RegisterModuleType("rust_test_host", RustTestHostFactory)
 	ctx.RegisterModuleType("rust_library", RustLibraryFactory)
-	ctx.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
-	ctx.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
-	ctx.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
-	ctx.RegisterModuleType("rust_library_rlib", RustLibraryRlibFactory)
 	ctx.RegisterModuleType("rust_library_dylib", RustLibraryDylibFactory)
-	ctx.RegisterModuleType("rust_library_shared", RustLibrarySharedFactory)
-	ctx.RegisterModuleType("rust_library_static", RustLibraryStaticFactory)
-	ctx.RegisterModuleType("rust_library_host_shared", RustLibrarySharedHostFactory)
-	ctx.RegisterModuleType("rust_library_host_static", RustLibraryStaticHostFactory)
+	ctx.RegisterModuleType("rust_library_rlib", RustLibraryRlibFactory)
+	ctx.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
+	ctx.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
+	ctx.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
+	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
+	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
+	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
+	ctx.RegisterModuleType("rust_prebuilt_library", PrebuiltLibraryFactory)
 	ctx.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
+	ctx.RegisterModuleType("rust_prebuilt_rlib", PrebuiltRlibFactory)
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		// rust mutators
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 1f55030..7782c68 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -106,7 +106,7 @@
         py3: {
             enabled: false,
         },
-    }
+    },
 }
 
 python_binary_host {
@@ -150,7 +150,59 @@
 }
 
 python_binary_host {
+    name: "construct_context",
+    main: "construct_context.py",
+    srcs: [
+        "construct_context.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+    libs: [
+        "manifest_utils",
+    ],
+}
+
+python_test_host {
+    name: "construct_context_test",
+    main: "construct_context_test.py",
+    srcs: [
+        "construct_context_test.py",
+        "construct_context.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+    libs: [
+        "manifest_utils",
+    ],
+    test_suites: ["general-tests"],
+}
+
+python_binary_host {
     name: "lint-project-xml",
     main: "lint-project-xml.py",
-    srcs: ["lint-project-xml.py"],
+    srcs: [
+        "lint-project-xml.py",
+        "ninja_rsp.py",
+    ],
+}
+
+python_binary_host {
+    name: "gen-kotlin-build-file.py",
+    main: "gen-kotlin-build-file.py",
+    srcs: [
+        "gen-kotlin-build-file.py",
+        "ninja_rsp.py",
+    ],
 }
diff --git a/scripts/OWNERS b/scripts/OWNERS
index dd0966f..8c64424 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1,3 +1,4 @@
 per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
 per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
 per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
+per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 9b68a82..1bc78e6 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -23,6 +23,15 @@
   runtime-module-host-exports
   i18n-module-test-exports
   i18n-module-sdk
+  platform-mainline-sdk
+  platform-mainline-host-exports
+)
+
+# List of libraries installed on the platform that are needed for ART chroot
+# testing.
+PLATFORM_LIBRARIES=(
+  liblog
+  libartpalette-system
 )
 
 # We want to create apex modules for all supported architectures.
@@ -49,7 +58,8 @@
 for product in "${PRODUCTS[@]}"; do
   echo_and_run build/soong/soong_ui.bash --make-mode $@ \
     TARGET_PRODUCT=${product} \
-    ${MAINLINE_MODULES[@]}
+    ${MAINLINE_MODULES[@]} \
+    ${PLATFORM_LIBRARIES[@]}
 
   PRODUCT_OUT=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var PRODUCT_OUT)
   TARGET_ARCH=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var TARGET_ARCH)
@@ -58,9 +68,11 @@
   for module in "${MAINLINE_MODULES[@]}"; do
     echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/
   done
+  for library in "${PLATFORM_LIBRARIES[@]}"; do
+    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/lib/${library}.so ${DIST_DIR}/${TARGET_ARCH}/
+  done
 done
 
-
 # Create multi-archs SDKs in a different out directory. The multi-arch script
 # uses Soong in --skip-make mode which cannot use the same directory as normal
 # mode with make.
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
new file mode 100755
index 0000000..8717fe3
--- /dev/null
+++ b/scripts/construct_context.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A tool for constructing class loader context."""
+
+from __future__ import print_function
+
+import argparse
+import sys
+
+from manifest import compare_version_gt
+
+
+def parse_args(args):
+  """Parse commandline arguments."""
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--target-sdk-version', default='', dest='sdk',
+    help='specify target SDK version (as it appears in the manifest)')
+  parser.add_argument('--host-classpath-for-sdk', dest='host_classpaths',
+    action='append', nargs=2, metavar=('sdk','classpath'),
+    help='specify classpath on host for a given SDK version or "any" version')
+  parser.add_argument('--target-classpath-for-sdk', dest='target_classpaths',
+    action='append', nargs=2, metavar=('sdk','classpath'),
+    help='specify classpath on target for a given SDK version or "any" version')
+  return parser.parse_args(args)
+
+# The hidl.manager shared library has a dependency on hidl.base. We manually
+# add that information to the class loader context if we see those libraries.
+HIDL_MANAGER = 'android.hidl.manager-V1.0-java'
+HIDL_BASE    = 'android.hidl.base-V1.0-java'
+
+# Special keyword that means that the classpath should be added to class loader
+# context regardless of the target SDK version.
+any_sdk = 'any'
+
+# We assume that the order of classpath arguments passed to this script is
+# correct (matches the order computed by package manager). It is possible to
+# sort them here, but Soong needs to use deterministic order anyway, so it can
+# as well use the correct order.
+def construct_context(versioned_classpaths, target_sdk):
+  context = []
+  for [sdk, classpath] in versioned_classpaths:
+    if sdk == any_sdk or compare_version_gt(sdk, target_sdk):
+      for jar in classpath.split(':'):
+        pcl = 'PCL[%s]' % jar
+        if HIDL_MANAGER in jar:
+          pcl += '{PCL[%s]}' % jar.replace(HIDL_MANAGER, HIDL_BASE, 1)
+        context.append(pcl)
+  return context
+
+def construct_contexts(args):
+  host_context = construct_context(args.host_classpaths, args.sdk)
+  target_context = construct_context(args.target_classpaths, args.sdk)
+  context_sep = '#'
+  return ('class_loader_context_arg=--class-loader-context=PCL[]{%s} ; ' % context_sep.join(host_context) +
+    'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{%s}' % context_sep.join(target_context))
+
+def main():
+  """Program entry point."""
+  try:
+    args = parse_args(sys.argv[1:])
+    if not args.sdk:
+      raise SystemExit('target sdk version is not set')
+    if not args.host_classpaths:
+      raise SystemExit('host classpath is not set')
+    if not args.target_classpaths:
+      raise SystemExit('target classpath is not set')
+
+    print(construct_contexts(args))
+
+  # pylint: disable=broad-except
+  except Exception as err:
+    print('error: ' + str(err), file=sys.stderr)
+    sys.exit(-1)
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/construct_context_test.py b/scripts/construct_context_test.py
new file mode 100755
index 0000000..0b0b0a3
--- /dev/null
+++ b/scripts/construct_context_test.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Unit tests for construct_context.py."""
+
+import sys
+import unittest
+
+import construct_context as cc
+
+sys.dont_write_bytecode = True
+
+def construct_contexts(arglist):
+  args = cc.parse_args(arglist)
+  return cc.construct_contexts(args)
+
+classpaths = [
+  '--host-classpath-for-sdk', '28', 'out/zdir/z.jar',
+  '--target-classpath-for-sdk', '28', '/system/z.jar',
+  '--host-classpath-for-sdk', '29', 'out/xdir/x.jar:out/ydir/y.jar',
+  '--target-classpath-for-sdk', '29', '/system/x.jar:/product/y.jar',
+  '--host-classpath-for-sdk', 'any', 'out/adir/a.jar:out/android.hidl.manager-V1.0-java.jar:out/bdir/b.jar',
+  '--target-classpath-for-sdk', 'any', '/system/a.jar:/system/android.hidl.manager-V1.0-java.jar:/product/b.jar',
+]
+
+class ConstructContextTest(unittest.TestCase):
+  def test_construct_context_28(self):
+    args = ['--target-sdk-version', '28'] + classpaths
+    result = construct_contexts(args)
+    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/xdir/x.jar]'
+      '#PCL[out/ydir/y.jar]'
+      '#PCL[out/adir/a.jar]'
+      '#PCL[out/android.hidl.manager-V1.0-java.jar]{PCL[out/android.hidl.base-V1.0-java.jar]}'
+      '#PCL[out/bdir/b.jar]}'
+      ' ; '
+      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/x.jar]'
+      '#PCL[/product/y.jar]'
+      '#PCL[/system/a.jar]'
+      '#PCL[/system/android.hidl.manager-V1.0-java.jar]{PCL[/system/android.hidl.base-V1.0-java.jar]}'
+      '#PCL[/product/b.jar]}')
+    self.assertEqual(result, expect)
+
+  def test_construct_context_29(self):
+    args = ['--target-sdk-version', '29'] + classpaths
+    result = construct_contexts(args)
+    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]'
+      '#PCL[out/android.hidl.manager-V1.0-java.jar]{PCL[out/android.hidl.base-V1.0-java.jar]}'
+      '#PCL[out/bdir/b.jar]}'
+      ' ; '
+      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]'
+      '#PCL[/system/android.hidl.manager-V1.0-java.jar]{PCL[/system/android.hidl.base-V1.0-java.jar]}'
+      '#PCL[/product/b.jar]}')
+    self.assertEqual(result, expect)
+
+  def test_construct_context_S(self):
+    args = ['--target-sdk-version', 'S'] + classpaths
+    result = construct_contexts(args)
+    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]'
+      '#PCL[out/android.hidl.manager-V1.0-java.jar]{PCL[out/android.hidl.base-V1.0-java.jar]}'
+      '#PCL[out/bdir/b.jar]}'
+      ' ; '
+      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]'
+      '#PCL[/system/android.hidl.manager-V1.0-java.jar]{PCL[/system/android.hidl.base-V1.0-java.jar]}'
+      '#PCL[/product/b.jar]}')
+    self.assertEqual(result, expect)
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/scripts/gen-kotlin-build-file.py b/scripts/gen-kotlin-build-file.py
new file mode 100644
index 0000000..83b4cd8
--- /dev/null
+++ b/scripts/gen-kotlin-build-file.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 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.
+
+# Generates kotlinc module xml file to drive kotlinc
+
+import argparse
+import os
+
+from ninja_rsp import NinjaRspFileReader
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  def convert_arg_line_to_args(arg_line):
+    for arg in arg_line.split():
+      if arg.startswith('#'):
+        return
+      if not arg.strip():
+        continue
+      yield arg
+
+  parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
+  parser.convert_arg_line_to_args = convert_arg_line_to_args
+  parser.add_argument('--out', dest='out',
+                      help='file to which the module.xml contents will be written.')
+  parser.add_argument('--classpath', dest='classpath', action='append', default=[],
+                      help='classpath to pass to kotlinc.')
+  parser.add_argument('--name', dest='name',
+                      help='name of the module.')
+  parser.add_argument('--out_dir', dest='out_dir',
+                      help='directory to which kotlinc will write output files.')
+  parser.add_argument('--srcs', dest='srcs', action='append', default=[],
+                      help='file containing whitespace separated list of source files.')
+  parser.add_argument('--common_srcs', dest='common_srcs', action='append', default=[],
+                      help='file containing whitespace separated list of common multiplatform source files.')
+
+  return parser.parse_args()
+
+def main():
+  """Program entry point."""
+  args = parse_args()
+
+  if not args.out:
+    raise RuntimeError('--out argument is required')
+
+  if not args.name:
+    raise RuntimeError('--name argument is required')
+
+  with open(args.out, 'w') as f:
+    # Print preamble
+    f.write('<modules>\n')
+    f.write('  <module name="%s" type="java-production" outputDir="%s">\n' % (args.name, args.out_dir or ''))
+
+    # Print classpath entries
+    for c in args.classpath:
+      for entry in c.split(':'):
+        path = os.path.abspath(entry)
+        f.write('    <classpath path="%s"/>\n' % path)
+
+    # For each rsp file, print source entries
+    for rsp_file in args.srcs:
+      for src in NinjaRspFileReader(rsp_file):
+        path = os.path.abspath(src)
+        if src.endswith('.java'):
+          f.write('    <javaSourceRoots path="%s"/>\n' % path)
+        elif src.endswith('.kt'):
+          f.write('    <sources path="%s"/>\n' % path)
+        else:
+          raise RuntimeError('unknown source file type %s' % file)
+
+    for rsp_file in args.common_srcs:
+      for src in NinjaRspFileReader(rsp_file):
+        path = os.path.abspath(src)
+        f.write('    <sources path="%s"/>\n' % path)
+        f.write('    <commonSources path="%s"/>\n' % path)
+
+    f.write('  </module>\n')
+    f.write('</modules>\n')
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/gen-kotlin-build-file.sh b/scripts/gen-kotlin-build-file.sh
deleted file mode 100755
index 177ca1b..0000000
--- a/scripts/gen-kotlin-build-file.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/bash -e
-
-# Copyright 2018 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.
-
-# Generates kotlinc module xml file to standard output based on rsp files
-
-if [[ -z "$1" ]]; then
-  echo "usage: $0 <classpath> <name> <outDir> <rspFiles>..." >&2
-  exit 1
-fi
-
-# Classpath variable has a tendency to be prefixed by "-classpath", remove it.
-if [[ $1 == "-classpath" ]]; then
-  shift
-fi;
-
-classpath=$1
-name=$2
-out_dir=$3
-shift 3
-
-# Path in the build file may be relative to the build file, we need to make them
-# absolute
-prefix="$(pwd)"
-
-get_abs_path () {
-  local file="$1"
-  if [[ "${file:0:1}" == '/' ]] ; then
-    echo "${file}"
-  else
-    echo "${prefix}/${file}"
-  fi
-}
-
-# Print preamble
-echo "<modules><module name=\"${name}\" type=\"java-production\" outputDir=\"${out_dir}\">"
-
-# Print classpath entries
-for file in $(echo "$classpath" | tr ":" "\n"); do
-  path="$(get_abs_path "$file")"
-  echo "  <classpath path=\"${path}\"/>"
-done
-
-# For each rsp file, print source entries
-while (( "$#" )); do
-  for file in $(cat "$1"); do
-    path="$(get_abs_path "$file")"
-    if [[ $file == *.java ]]; then
-      echo "  <javaSourceRoots path=\"${path}\"/>"
-    elif [[ $file == *.kt ]]; then
-      echo "  <sources path=\"${path}\"/>"
-    else
-      echo "Unknown source file type ${file}"
-      exit 1
-    fi
-  done
-
-  shift
-done
-
-echo "</module></modules>"
diff --git a/scripts/lint-project-xml.py b/scripts/lint-project-xml.py
index 7ab4f01..f1ef85d 100755
--- a/scripts/lint-project-xml.py
+++ b/scripts/lint-project-xml.py
@@ -19,6 +19,8 @@
 
 import argparse
 
+from ninja_rsp import NinjaRspFileReader
+
 
 def check_action(check_type):
   """
@@ -77,6 +79,8 @@
                       help='mark the module as a test.')
   parser.add_argument('--cache_dir', dest='cache_dir',
                       help='directory to use for cached file.')
+  parser.add_argument('--root_dir', dest='root_dir',
+                      help='directory to use for root dir.')
   group = parser.add_argument_group('check arguments', 'later arguments override earlier ones.')
   group.add_argument('--fatal_check', dest='checks', action=check_action('fatal'), default=[],
                      help='treat a lint issue as a fatal error.')
@@ -89,79 +93,13 @@
   return parser.parse_args()
 
 
-class NinjaRspFileReader:
-  """
-  Reads entries from a Ninja rsp file.  Ninja escapes any entries in the file that contain a
-  non-standard character by surrounding the whole entry with single quotes, and then replacing
-  any single quotes in the entry with the escape sequence '\''.
-  """
-
-  def __init__(self, filename):
-    self.f = open(filename, 'r')
-    self.r = self.character_reader(self.f)
-
-  def __iter__(self):
-    return self
-
-  def character_reader(self, f):
-    """Turns a file into a generator that returns one character at a time."""
-    while True:
-      c = f.read(1)
-      if c:
-        yield c
-      else:
-        return
-
-  def __next__(self):
-    entry = self.read_entry()
-    if entry:
-      return entry
-    else:
-      raise StopIteration
-
-  def read_entry(self):
-    c = next(self.r, "")
-    if not c:
-      return ""
-    elif c == "'":
-      return self.read_quoted_entry()
-    else:
-      entry = c
-      for c in self.r:
-        if c == " " or c == "\n":
-          break
-        entry += c
-      return entry
-
-  def read_quoted_entry(self):
-    entry = ""
-    for c in self.r:
-      if c == "'":
-        # Either the end of the quoted entry, or the beginning of an escape sequence, read the next
-        # character to find out.
-        c = next(self.r)
-        if not c or c == " " or c == "\n":
-          # End of the item
-          return entry
-        elif c == "\\":
-          # Escape sequence, expect a '
-          c = next(self.r)
-          if c != "'":
-            # Malformed escape sequence
-            raise "malformed escape sequence %s'\\%s" % (entry, c)
-          entry += "'"
-        else:
-          raise "malformed escape sequence %s'%s" % (entry, c)
-      else:
-        entry += c
-    raise "unterminated quoted entry %s" % entry
-
-
 def write_project_xml(f, args):
   test_attr = "test='true' " if args.test else ""
 
   f.write("<?xml version='1.0' encoding='utf-8'?>\n")
   f.write("<project>\n")
+  if args.root_dir:
+    f.write("  <root dir='%s' />\n" % args.root_dir)
   f.write("  <module name='%s' android='true' %sdesugar='full' >\n" % (args.name, "library='true' " if args.library else ""))
   if args.manifest:
     f.write("    <manifest file='%s' %s/>\n" % (args.manifest, test_attr))
diff --git a/scripts/ninja_rsp.py b/scripts/ninja_rsp.py
new file mode 100644
index 0000000..004ce47
--- /dev/null
+++ b/scripts/ninja_rsp.py
@@ -0,0 +1,83 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""This file reads entries from a Ninja rsp file."""
+
+class NinjaRspFileReader:
+  """
+  Reads entries from a Ninja rsp file.  Ninja escapes any entries in the file that contain a
+  non-standard character by surrounding the whole entry with single quotes, and then replacing
+  any single quotes in the entry with the escape sequence '\''.
+  """
+
+  def __init__(self, filename):
+    self.f = open(filename, 'r')
+    self.r = self.character_reader(self.f)
+
+  def __iter__(self):
+    return self
+
+  def character_reader(self, f):
+    """Turns a file into a generator that returns one character at a time."""
+    while True:
+      c = f.read(1)
+      if c:
+        yield c
+      else:
+        return
+
+  def __next__(self):
+    entry = self.read_entry()
+    if entry:
+      return entry
+    else:
+      raise StopIteration
+
+  def read_entry(self):
+    c = next(self.r, "")
+    if not c:
+      return ""
+    elif c == "'":
+      return self.read_quoted_entry()
+    else:
+      entry = c
+      for c in self.r:
+        if c == " " or c == "\n":
+          break
+        entry += c
+      return entry
+
+  def read_quoted_entry(self):
+    entry = ""
+    for c in self.r:
+      if c == "'":
+        # Either the end of the quoted entry, or the beginning of an escape sequence, read the next
+        # character to find out.
+        c = next(self.r)
+        if not c or c == " " or c == "\n":
+          # End of the item
+          return entry
+        elif c == "\\":
+          # Escape sequence, expect a '
+          c = next(self.r)
+          if c != "'":
+            # Malformed escape sequence
+            raise "malformed escape sequence %s'\\%s" % (entry, c)
+          entry += "'"
+        else:
+          raise "malformed escape sequence %s'%s" % (entry, c)
+      else:
+        entry += c
+    raise "unterminated quoted entry %s" % entry
diff --git a/scripts/rustfmt.toml b/scripts/rustfmt.toml
new file mode 100644
index 0000000..617d425
--- /dev/null
+++ b/scripts/rustfmt.toml
@@ -0,0 +1,5 @@
+# Android Format Style
+
+edition = "2018"
+use_small_heuristics = "Max"
+newline_style = "Unix"
diff --git a/scripts/transitive-deps.sh b/scripts/transitive-deps.sh
index c6f8b76..ba36ba4 100755
--- a/scripts/transitive-deps.sh
+++ b/scripts/transitive-deps.sh
@@ -56,7 +56,7 @@
                     temporary / intermediate files.
   -sep=<delim>      Use 'delim' as output field separator between notice
                     checksum and notice filename in notice output.
-                    e.g. sep='\t'
+                    e.g. sep='\\t'
                     (Default space)
   -csv              Shorthand for -sep=','
   -directories=<f>  Output directory names of dependencies to 'f'.
@@ -280,7 +280,7 @@
           notices=)
             notices_out=$(expr "${flag}" : '^.*=\(.*\)$');;
           *)
-            die "Unknown flag ${1}";;
+            die "${usage}\n\nUnknown flag ${1}";;
         esac
       ;;
       *)
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 77a4e94..7496b20 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -23,6 +23,7 @@
 
 	fs := map[string][]byte{
 		"Test.java":              nil,
+		"resource.test":          nil,
 		"aidl/foo/bar/Test.aidl": nil,
 
 		// For java_sdk_library
@@ -348,6 +349,7 @@
 		java_library {
 			name: "myjavalib",
 			srcs: ["Test.java"],
+			java_resources: ["resource.txt"],
 			aidl: {
 				export_include_dirs: ["aidl"],
 			},
@@ -381,7 +383,7 @@
 
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib/android_common/javac/myjavalib.jar -> java/myjavalib.jar
+.intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
 `),
 	)
diff --git a/sdk/sdk.go b/sdk/sdk.go
index cb5a605..b9b8199 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -291,7 +291,7 @@
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "FAKE",
 		OutputFile: s.snapshotFile,
-		DistFile:   s.snapshotFile,
+		DistFiles:  android.MakeDefaultDistFiles(s.snapshotFile.Path()),
 		Include:    "$(BUILD_PHONY_PACKAGE)",
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
 			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 9f27647..768c8e5 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -310,6 +310,10 @@
 		}}
 }
 
+func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error {
+	return fmt.Errorf("sysprop_library is not supposed to be part of apex modules")
+}
+
 // sysprop_library creates schematized APIs from sysprop description files (.sysprop).
 // Both Java and C++ modules can link against sysprop_library, and API stability check
 // against latest APIs (see build/soong/scripts/freeze-sysprop-api-files.sh)
diff --git a/ui/build/build.go b/ui/build/build.go
index 89c3fad..396f54c 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -243,6 +243,8 @@
 	// Write combined ninja file
 	createCombinedBuildNinjaFile(ctx, config)
 
+	distGzipFile(ctx, config, config.CombinedNinjaFile())
+
 	if what&RunBuildTests != 0 {
 		testForDanglingRules(ctx, config)
 	}
@@ -256,3 +258,47 @@
 		runNinja(ctx, config)
 	}
 }
+
+// distGzipFile writes a compressed copy of src to the distDir if dist is enabled.  Failures
+// are printed but non-fatal.
+func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
+	if !config.Dist() {
+		return
+	}
+
+	subDir := filepath.Join(subDirs...)
+	destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
+
+	err := os.MkdirAll(destDir, 0777)
+	if err != nil {
+		ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
+
+	}
+
+	err = gzipFileToDir(src, destDir)
+	if err != nil {
+		ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
+	}
+}
+
+// distFile writes a copy of src to the distDir if dist is enabled.  Failures are printed but
+// non-fatal.
+func distFile(ctx Context, config Config, src string, subDirs ...string) {
+	if !config.Dist() {
+		return
+	}
+
+	subDir := filepath.Join(subDirs...)
+	destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
+
+	err := os.MkdirAll(destDir, 0777)
+	if err != nil {
+		ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
+
+	}
+
+	_, err = copyFile(src, filepath.Join(destDir, filepath.Base(src)))
+	if err != nil {
+		ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
+	}
+}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 0f34b5e..6786337 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -86,7 +86,7 @@
 	os.MkdirAll(dumpDir, 0777)
 
 	androidMks := f.FindFirstNamedAt(".", "Android.mk")
-	err := dumpListToFile(androidMks, filepath.Join(dumpDir, "Android.mk.list"))
+	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
 	if err != nil {
 		ctx.Fatalf("Could not export module list: %v", err)
 	}
@@ -94,25 +94,25 @@
 	androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk")
 	androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...)
 	androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...)
-	err = dumpListToFile(androidProductsMks, filepath.Join(dumpDir, "AndroidProducts.mk.list"))
+	err = dumpListToFile(ctx, config, androidProductsMks, filepath.Join(dumpDir, "AndroidProducts.mk.list"))
 	if err != nil {
 		ctx.Fatalf("Could not export product list: %v", err)
 	}
 
 	cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
-	err = dumpListToFile(cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
+	err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
 	if err != nil {
 		ctx.Fatalf("Could not export module list: %v", err)
 	}
 
 	owners := f.FindNamedAt(".", "OWNERS")
-	err = dumpListToFile(owners, filepath.Join(dumpDir, "OWNERS.list"))
+	err = dumpListToFile(ctx, config, owners, filepath.Join(dumpDir, "OWNERS.list"))
 	if err != nil {
 		ctx.Fatalf("Could not find OWNERS: %v", err)
 	}
 
 	testMappings := f.FindNamedAt(".", "TEST_MAPPING")
-	err = dumpListToFile(testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
+	err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
 	if err != nil {
 		ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
 	}
@@ -122,18 +122,24 @@
 	if len(androidBps) == 0 {
 		ctx.Fatalf("No Android.bp found")
 	}
-	err = dumpListToFile(androidBps, filepath.Join(dumpDir, "Android.bp.list"))
+	err = dumpListToFile(ctx, config, androidBps, filepath.Join(dumpDir, "Android.bp.list"))
 	if err != nil {
 		ctx.Fatalf("Could not find modules: %v", err)
 	}
 }
 
-func dumpListToFile(list []string, filePath string) (err error) {
+func dumpListToFile(ctx Context, config Config, list []string, filePath string) (err error) {
 	desiredText := strings.Join(list, "\n")
 	desiredBytes := []byte(desiredText)
 	actualBytes, readErr := ioutil.ReadFile(filePath)
 	if readErr != nil || !bytes.Equal(desiredBytes, actualBytes) {
 		err = ioutil.WriteFile(filePath, desiredBytes, 0777)
+		if err != nil {
+			return err
+		}
 	}
-	return err
+
+	distFile(ctx, config, filePath, "module_paths")
+
+	return nil
 }
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 2eb7850..1cd5fea 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -156,6 +156,8 @@
 
 	runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
 
+	distGzipFile(ctx, config, config.KatiBuildNinjaFile())
+
 	cleanCopyHeaders(ctx, config)
 	cleanOldInstalledFiles(ctx, config)
 }
@@ -251,6 +253,8 @@
 			env.Set("DIST_DIR", config.DistDir())
 		}
 	})
+
+	distGzipFile(ctx, config, config.KatiPackageNinjaFile())
 }
 
 func runKatiCleanSpec(ctx Context, config Config) {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 6a12add..749acb3 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -139,6 +139,13 @@
 	soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
 	logSoongBuildMetrics(ctx, soongBuildMetrics)
 
+	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
+
+	if !config.SkipMake() {
+		distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
+		distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong")
+	}
+
 	if ctx.Metrics != nil {
 		ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
 	}
diff --git a/ui/build/util.go b/ui/build/util.go
index d44cd6d..d4c3431 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -15,6 +15,8 @@
 package build
 
 import (
+	"compress/gzip"
+	"fmt"
 	"io"
 	"os"
 	"path/filepath"
@@ -142,3 +144,29 @@
 
 	return io.Copy(destination, source)
 }
+
+// gzipFileToDir writes a compressed copy of src to destDir with the suffix ".gz".
+func gzipFileToDir(src, destDir string) error {
+	in, err := os.Open(src)
+	if err != nil {
+		return fmt.Errorf("failed to open %s: %s", src, err.Error())
+	}
+	defer in.Close()
+
+	dest := filepath.Join(destDir, filepath.Base(src)+".gz")
+
+	out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0666)
+	if err != nil {
+		return fmt.Errorf("failed to open %s: %s", dest, err.Error())
+	}
+	defer out.Close()
+	gz := gzip.NewWriter(out)
+	defer gz.Close()
+
+	_, err = io.Copy(gz, in)
+	if err != nil {
+		return fmt.Errorf("failed to gzip %s: %s", dest, err.Error())
+	}
+
+	return nil
+}
diff --git a/zip/Android.bp b/zip/Android.bp
index 259e010..5081e91 100644
--- a/zip/Android.bp
+++ b/zip/Android.bp
@@ -27,7 +27,6 @@
         "rate_limit.go",
     ],
     testSrcs: [
-      "zip_test.go",
+        "zip_test.go",
     ],
 }
-