Merge "Deprecate VNDK-Lite"
diff --git a/android/Android.bp b/android/Android.bp
index 977345b..96f0983 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -39,6 +39,7 @@
         "paths.go",
         "phony.go",
         "prebuilt.go",
+        "prebuilt_build_tool.go",
         "proto.go",
         "register.go",
         "rule_builder.go",
@@ -46,6 +47,7 @@
         "sdk.go",
         "singleton.go",
         "soong_config_modules.go",
+        "test_suites.go",
         "testing.go",
         "util.go",
         "variable.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index dfc68c4..7e86140 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -126,6 +126,26 @@
 	}
 }
 
+func (a *AndroidMkEntries) SetPaths(name string, paths Paths) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.entryOrder = append(a.entryOrder, name)
+	}
+	a.EntryMap[name] = paths.Strings()
+}
+
+func (a *AndroidMkEntries) SetOptionalPaths(name string, paths Paths) {
+	if len(paths) > 0 {
+		a.SetPaths(name, paths)
+	}
+}
+
+func (a *AndroidMkEntries) AddPaths(name string, paths Paths) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.entryOrder = append(a.entryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
+}
+
 func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
 	if flag {
 		if _, ok := a.EntryMap[name]; !ok {
@@ -165,7 +185,7 @@
 	var ret []string
 
 	availableTaggedDists := TaggedDistFiles{}
-	if a.DistFiles != nil && len(a.DistFiles[""]) > 0 {
+	if a.DistFiles != nil {
 		availableTaggedDists = a.DistFiles
 	} else if a.OutputFile.Valid() {
 		availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 250f086..a558f45 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -45,6 +45,8 @@
 		return PathsForTesting("one.out"), nil
 	case ".multiple":
 		return PathsForTesting("two.out", "three/four.out"), nil
+	case ".another-tag":
+		return PathsForTesting("another.out"), nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
@@ -123,6 +125,38 @@
 			bp: `
 			custom {
 				name: "foo",
+				dist: {
+					targets: ["my_goal"],
+					tag: ".another-tag",
+				}
+			}
+			`,
+			expectedAndroidMkLines: []string{
+				".PHONY: my_goal\n",
+				"$(call dist-for-goals,my_goal,another.out:another.out)\n",
+			},
+		},
+		{
+			bp: `
+			custom {
+				name: "foo",
+				dists: [
+					{
+						targets: ["my_goal"],
+						tag: ".another-tag",
+					},
+				],
+			}
+			`,
+			expectedAndroidMkLines: []string{
+				".PHONY: my_goal\n",
+				"$(call dist-for-goals,my_goal,another.out:another.out)\n",
+			},
+		},
+		{
+			bp: `
+			custom {
+				name: "foo",
 				dists: [
 					{
 						targets: ["my_goal"],
diff --git a/android/apex.go b/android/apex.go
index 47f07ca..a7570dc 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -65,9 +65,9 @@
 
 	apexModuleBase() *ApexModuleBase
 
-	// Marks that this module should be built for the specified APEXes.
+	// Marks that this module should be built for the specified APEX.
 	// Call this before apex.apexMutator is run.
-	BuildForApexes(apexes []ApexInfo)
+	BuildForApex(apex ApexInfo)
 
 	// Returns the APEXes that this module will be built for
 	ApexVariations() []ApexInfo
@@ -96,7 +96,7 @@
 	IsInstallableToApex() bool
 
 	// Mutate this module into one or more variants each of which is built
-	// for an APEX marked via BuildForApexes().
+	// for an APEX marked via BuildForApex().
 	CreateApexVariations(mctx BottomUpMutatorContext) []Module
 
 	// Tests if this module is available for the specified APEX or ":platform"
@@ -178,18 +178,15 @@
 	return nil
 }
 
-func (m *ApexModuleBase) BuildForApexes(apexes []ApexInfo) {
+func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
 	m.apexVariationsLock.Lock()
 	defer m.apexVariationsLock.Unlock()
-nextApex:
-	for _, apex := range apexes {
-		for _, v := range m.apexVariations {
-			if v.ApexName == apex.ApexName {
-				continue nextApex
-			}
+	for _, v := range m.apexVariations {
+		if v.ApexName == apex.ApexName {
+			return
 		}
-		m.apexVariations = append(m.apexVariations, apex)
 	}
+	m.apexVariations = append(m.apexVariations, apex)
 }
 
 func (m *ApexModuleBase) ApexVariations() []ApexInfo {
@@ -327,17 +324,15 @@
 // depended on by the specified APEXes. Directly depending means that a module
 // is explicitly listed in the build definition of the APEX via properties like
 // native_shared_libs, java_libs, etc.
-func UpdateApexDependency(apexes []ApexInfo, moduleName string, directDep bool) {
+func UpdateApexDependency(apex ApexInfo, moduleName string, directDep bool) {
 	apexNamesMapMutex.Lock()
 	defer apexNamesMapMutex.Unlock()
-	for _, apex := range apexes {
-		apexesForModule, ok := apexNamesMap()[moduleName]
-		if !ok {
-			apexesForModule = make(map[string]bool)
-			apexNamesMap()[moduleName] = apexesForModule
-		}
-		apexesForModule[apex.ApexName] = apexesForModule[apex.ApexName] || directDep
+	apexesForModule, ok := apexNamesMap()[moduleName]
+	if !ok {
+		apexesForModule = make(map[string]bool)
+		apexNamesMap()[moduleName] = apexesForModule
 	}
+	apexesForModule[apex.ApexName] = apexesForModule[apex.ApexName] || directDep
 }
 
 // TODO(b/146393795): remove this when b/146393795 is fixed
diff --git a/android/defs.go b/android/defs.go
index 4552224..83daa03 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -69,7 +69,7 @@
 	// A symlink rule.
 	Symlink = pctx.AndroidStaticRule("Symlink",
 		blueprint.RuleParams{
-			Command:     "ln -f -s $fromPath $out",
+			Command:     "rm -f $out && ln -f -s $fromPath $out",
 			Description: "symlink $out",
 		},
 		"fromPath")
diff --git a/android/paths.go b/android/paths.go
index 20ff55e..65f129c 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1555,3 +1555,15 @@
 	}
 	return filepath.Join(absSrcDir, path)
 }
+
+// A DataPath represents the path of a file to be used as data, for example
+// a test library to be installed alongside a test.
+// The data file should be installed (copied from `<SrcPath>`) to
+// `<install_root>/<RelativeInstallPath>/<filename>`, or
+// `<install_root>/<filename>` if RelativeInstallPath is empty.
+type DataPath struct {
+	// The path of the data file that should be copied into the data directory
+	SrcPath Path
+	// The install path of the data file, relative to the install root.
+	RelativeInstallPath string
+}
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
new file mode 100644
index 0000000..d457da4
--- /dev/null
+++ b/android/prebuilt_build_tool.go
@@ -0,0 +1,100 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"path"
+	"path/filepath"
+)
+
+func init() {
+	RegisterModuleType("prebuilt_build_tool", prebuiltBuildToolFactory)
+}
+
+type prebuiltBuildToolProperties struct {
+	// Source file to be executed for this build tool
+	Src *string `android:"path,arch_variant"`
+
+	// Extra files that should trigger rules using this tool to rebuild
+	Deps []string `android:"path,arch_variant"`
+}
+
+type prebuiltBuildTool struct {
+	ModuleBase
+	prebuilt Prebuilt
+
+	properties prebuiltBuildToolProperties
+
+	toolPath OptionalPath
+}
+
+func (t *prebuiltBuildTool) Name() string {
+	return t.prebuilt.Name(t.ModuleBase.Name())
+}
+
+func (t *prebuiltBuildTool) Prebuilt() *Prebuilt {
+	return &t.prebuilt
+}
+
+func (t *prebuiltBuildTool) DepsMutator(ctx BottomUpMutatorContext) {
+	if t.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+	}
+}
+
+func (t *prebuiltBuildTool) GenerateAndroidBuildActions(ctx ModuleContext) {
+	sourcePath := t.prebuilt.SingleSourcePath(ctx)
+	installedPath := PathForModuleOut(ctx, t.ModuleBase.Name())
+	deps := PathsForModuleSrc(ctx, t.properties.Deps)
+
+	var relPath string
+	if filepath.IsAbs(installedPath.String()) {
+		relPath = filepath.Join(absSrcDir, sourcePath.String())
+	} else {
+		var err error
+		relPath, err = filepath.Rel(path.Dir(installedPath.String()), sourcePath.String())
+		if err != nil {
+			ctx.ModuleErrorf("Unable to generate symlink between %q and %q: %s", installedPath.String(), sourcePath.String(), err)
+		}
+	}
+
+	ctx.Build(pctx, BuildParams{
+		Rule:      Symlink,
+		Output:    installedPath,
+		Input:     sourcePath,
+		Implicits: deps,
+		Args: map[string]string{
+			"fromPath": relPath,
+		},
+	})
+
+	t.toolPath = OptionalPathForPath(installedPath)
+}
+
+func (t *prebuiltBuildTool) HostToolPath() OptionalPath {
+	return t.toolPath
+}
+
+var _ HostToolProvider = &prebuiltBuildTool{}
+
+// prebuilt_build_tool is to declare prebuilts to be used during the build, particularly for use
+// in genrules with the "tools" property.
+func prebuiltBuildToolFactory() Module {
+	module := &prebuiltBuildTool{}
+	module.AddProperties(&module.properties)
+	InitSingleSourcePrebuiltModule(module, &module.properties, "Src")
+	InitAndroidArchModule(module, HostSupportedNoCross, MultilibFirst)
+	return module
+}
diff --git a/android/sdk.go b/android/sdk.go
index 8115b69..28f5cd5 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -34,10 +34,8 @@
 	RequiredSdks() SdkRefs
 }
 
-// SdkAware is the interface that must be supported by any module to become a member of SDK or to be
-// built with SDK
-type SdkAware interface {
-	Module
+// Provided to improve code navigation with the IDE.
+type sdkAwareWithoutModule interface {
 	RequiredSdks
 
 	sdkBase() *SdkBase
@@ -48,6 +46,13 @@
 	BuildWithSdks(sdks SdkRefs)
 }
 
+// SdkAware is the interface that must be supported by any module to become a member of SDK or to be
+// built with SDK
+type SdkAware interface {
+	Module
+	sdkAwareWithoutModule
+}
+
 // SdkRef refers to a version of an SDK
 type SdkRef struct {
 	Name    string
@@ -466,8 +471,7 @@
 
 // Base structure for all implementations of SdkMemberProperties.
 //
-// Contains common properties that apply across many different member types. These
-// are not affected by the optimization to extract common values.
+// Contains common properties that apply across many different member types.
 type SdkMemberPropertiesBase struct {
 	// The number of unique os types supported by the member variants.
 	//
@@ -489,9 +493,7 @@
 	Os OsType `sdk:"keep"`
 
 	// The setting to use for the compile_multilib property.
-	//
-	// This property is set after optimization so there is no point in trying to optimize it.
-	Compile_multilib string `sdk:"keep"`
+	Compile_multilib string `android:"arch_variant"`
 }
 
 // The os prefix to use for any file paths in the sdk.
diff --git a/android/test_suites.go b/android/test_suites.go
new file mode 100644
index 0000000..7b2d7dc
--- /dev/null
+++ b/android/test_suites.go
@@ -0,0 +1,75 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+func init() {
+	RegisterSingletonType("testsuites", testSuiteFilesFactory)
+}
+
+func testSuiteFilesFactory() Singleton {
+	return &testSuiteFiles{}
+}
+
+type testSuiteFiles struct {
+	robolectric WritablePath
+}
+
+type TestSuiteModule interface {
+	Module
+	TestSuites() []string
+}
+
+func (t *testSuiteFiles) GenerateBuildActions(ctx SingletonContext) {
+	files := make(map[string]map[string]InstallPaths)
+
+	ctx.VisitAllModules(func(m Module) {
+		if tsm, ok := m.(TestSuiteModule); ok {
+			for _, testSuite := range tsm.TestSuites() {
+				if files[testSuite] == nil {
+					files[testSuite] = make(map[string]InstallPaths)
+				}
+				name := ctx.ModuleName(m)
+				files[testSuite][name] = append(files[testSuite][name], tsm.filesToInstall()...)
+			}
+		}
+	})
+
+	t.robolectric = robolectricTestSuite(ctx, files["robolectric-tests"])
+
+	ctx.Phony("robolectric-tests", t.robolectric)
+}
+
+func (t *testSuiteFiles) MakeVars(ctx MakeVarsContext) {
+	ctx.DistForGoal("robolectric-tests", t.robolectric)
+}
+
+func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath {
+	var installedPaths InstallPaths
+	for _, module := range SortedStringKeys(files) {
+		installedPaths = append(installedPaths, files[module]...)
+	}
+	testCasesDir := pathForInstall(ctx, BuildOs, "testcases", false).ToMakePath()
+
+	outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
+	rule := NewRuleBuilder()
+	rule.Command().BuiltTool(ctx, "soong_zip").
+		FlagWithOutput("-o ", outputFile).
+		FlagWithArg("-P ", "host/testcases").
+		FlagWithArg("-C ", testCasesDir.String()).
+		FlagWithRspFileInputList("-l ", installedPaths.Paths())
+	rule.Build(pctx, ctx, "robolectric_tests_zip", "robolectric-tests.zip")
+
+	return outputFile
+}
diff --git a/apex/androidmk.go b/apex/androidmk.go
index b9bcc3a..e739e2b 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -116,9 +116,9 @@
 			if len(fi.symlinks) > 0 {
 				fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
 			}
-			newDataPaths := []android.Path{}
+			newDataPaths := []android.DataPath{}
 			for _, path := range fi.dataPaths {
-				dataOutPath := modulePath + ":" + path.Rel()
+				dataOutPath := modulePath + ":" + path.SrcPath.Rel()
 				if ok := seenDataOutPaths[dataOutPath]; !ok {
 					newDataPaths = append(newDataPaths, path)
 					seenDataOutPaths[dataOutPath] = true
@@ -353,6 +353,10 @@
 				if apexType == imageApex {
 					fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String())
 				}
+				if len(a.lintReports) > 0 {
+					fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS :=",
+						strings.Join(a.lintReports.Strings(), " "))
+				}
 
 				if a.installedFilesFile != nil {
 					goal := "checkbuild"
diff --git a/apex/apex.go b/apex/apex.go
index 045ae82..fa986cd 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -669,7 +669,7 @@
 }
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("apex_deps", apexDepsMutator)
+	ctx.TopDown("apex_deps", apexDepsMutator).Parallel()
 	ctx.BottomUp("apex", apexMutator).Parallel()
 	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
 	ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
@@ -682,33 +682,30 @@
 	if !mctx.Module().Enabled() {
 		return
 	}
-	var apexBundles []android.ApexInfo
-	var directDep bool
-	if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
-		apexBundles = []android.ApexInfo{{
-			ApexName:      mctx.ModuleName(),
-			MinSdkVersion: a.minSdkVersion(mctx),
-			Updatable:     a.Updatable(),
-		}}
-		directDep = true
-	} else if am, ok := mctx.Module().(android.ApexModule); ok {
-		apexBundles = am.ApexVariations()
-		directDep = false
-	}
-
-	if len(apexBundles) == 0 {
+	a, ok := mctx.Module().(*apexBundle)
+	if !ok || a.vndkApex {
 		return
 	}
-
-	cur := mctx.Module().(android.DepIsInSameApex)
-
-	mctx.VisitDirectDeps(func(child android.Module) {
-		depName := mctx.OtherModuleName(child)
-		if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() &&
-			(cur.DepIsInSameApex(mctx, child) || inAnySdk(child)) {
-			android.UpdateApexDependency(apexBundles, depName, directDep)
-			am.BuildForApexes(apexBundles)
+	apexInfo := android.ApexInfo{
+		ApexName:      mctx.ModuleName(),
+		MinSdkVersion: a.minSdkVersion(mctx),
+		Updatable:     a.Updatable(),
+	}
+	mctx.WalkDeps(func(child, parent android.Module) bool {
+		am, ok := child.(android.ApexModule)
+		if !ok || !am.CanHaveApexVariants() {
+			return false
 		}
+		if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) && !inAnySdk(child) {
+			return false
+		}
+
+		depName := mctx.OtherModuleName(child)
+		// If the parent is apexBundle, this child is directly depended.
+		_, directDep := parent.(*apexBundle)
+		android.UpdateApexDependency(apexInfo, depName, directDep)
+		am.BuildForApex(apexInfo)
+		return true
 	})
 }
 
@@ -1145,7 +1142,7 @@
 	module     android.Module
 	// list of symlinks that will be created in installDir that point to this apexFile
 	symlinks      []string
-	dataPaths     android.Paths
+	dataPaths     []android.DataPath
 	transitiveDep bool
 	moduleDir     string
 
@@ -1154,6 +1151,7 @@
 	hostRequiredModuleNames   []string
 
 	jacocoReportClassesFile android.Path     // only for javalibs and apps
+	lintDepSets             java.LintDepSets // only for javalibs and apps
 	certificate             java.Certificate // only for apps
 	overriddenPackageName   string           // only for apps
 
@@ -1278,6 +1276,9 @@
 
 	// Struct holding the merged notice file paths in different formats
 	mergedNotices android.NoticeOutputs
+
+	// Optional list of lint report zip files for apexes that contain java or app modules
+	lintReports android.Paths
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -1666,9 +1667,16 @@
 type javaDependency interface {
 	DexJarBuildPath() android.Path
 	JacocoReportClassesFile() android.Path
+	LintDepSets() java.LintDepSets
+
 	Stem() string
 }
 
+var _ javaDependency = (*java.Library)(nil)
+var _ javaDependency = (*java.SdkLibrary)(nil)
+var _ javaDependency = (*java.DexImport)(nil)
+var _ javaDependency = (*java.SdkLibraryImport)(nil)
+
 func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaDependency, module android.Module) apexFile {
 	dirInApex := "javalib"
 	fileToCopy := lib.DexJarBuildPath()
@@ -1676,6 +1684,7 @@
 	name := strings.TrimPrefix(module.Name(), "prebuilt_")
 	af := newApexFile(ctx, fileToCopy, name, dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = lib.JacocoReportClassesFile()
+	af.lintDepSets = lib.LintDepSets()
 	af.stem = lib.Stem() + ".jar"
 	return af
 }
@@ -2275,6 +2284,8 @@
 	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
 
 	a.buildApexDependencyInfo(ctx)
+
+	a.buildLintReports(ctx)
 }
 
 // Enforce that Java deps of the apex are using stable SDKs to compile
diff --git a/apex/builder.go b/apex/builder.go
index 49e4642..0a1ec3e 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -403,16 +403,16 @@
 		}
 		for _, d := range fi.dataPaths {
 			// TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible
-			relPath := d.Rel()
-			dataPath := d.String()
+			relPath := d.SrcPath.Rel()
+			dataPath := d.SrcPath.String()
 			if !strings.HasSuffix(dataPath, relPath) {
 				panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath))
 			}
 
-			dataDest := android.PathForModuleOut(ctx, "image"+suffix, fi.apexRelativePath(relPath)).String()
+			dataDest := android.PathForModuleOut(ctx, "image"+suffix, fi.apexRelativePath(relPath), d.RelativeInstallPath).String()
 
-			copyCommands = append(copyCommands, "cp -f "+d.String()+" "+dataDest)
-			implicitInputs = append(implicitInputs, d)
+			copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest)
+			implicitInputs = append(implicitInputs, d.SrcPath)
 		}
 	}
 
@@ -473,7 +473,7 @@
 			if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
 				executablePaths = append(executablePaths, pathInApex)
 				for _, d := range f.dataPaths {
-					readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.Rel()))
+					readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel()))
 				}
 				for _, s := range f.symlinks {
 					executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
@@ -815,3 +815,12 @@
 		},
 	})
 }
+
+func (a *apexBundle) buildLintReports(ctx android.ModuleContext) {
+	depSetsBuilder := java.NewLintDepSetBuilder()
+	for _, fi := range a.filesInfo {
+		depSetsBuilder.Transitive(fi.lintDepSets)
+	}
+
+	a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 3f812c2..e91b40a 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -149,21 +149,25 @@
 	return []android.AndroidMkEntries{entries}
 }
 
-func AndroidMkDataPaths(data android.Paths) []string {
+func AndroidMkDataPaths(data []android.DataPath) []string {
 	var testFiles []string
 	for _, d := range data {
-		rel := d.Rel()
-		path := d.String()
+		rel := d.SrcPath.Rel()
+		path := d.SrcPath.String()
 		if !strings.HasSuffix(path, rel) {
 			panic(fmt.Errorf("path %q does not end with %q", path, rel))
 		}
 		path = strings.TrimSuffix(path, rel)
-		testFiles = append(testFiles, path+":"+rel)
+		testFileString := path + ":" + rel
+		if len(d.RelativeInstallPath) > 0 {
+			testFileString += ":" + d.RelativeInstallPath
+		}
+		testFiles = append(testFiles, testFileString)
 	}
 	return testFiles
 }
 
-func androidMkWriteTestData(data android.Paths, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func androidMkWriteTestData(data []android.DataPath, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	testFiles := AndroidMkDataPaths(data)
 	if len(testFiles) > 0 {
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
@@ -357,8 +361,11 @@
 			entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
 		}
 	})
-
-	androidMkWriteTestData(benchmark.data, ctx, entries)
+	dataPaths := []android.DataPath{}
+	for _, srcPath := range benchmark.data {
+		dataPaths = append(dataPaths, android.DataPath{SrcPath: srcPath})
+	}
+	androidMkWriteTestData(dataPaths, ctx, entries)
 }
 
 func (test *testBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 372a72e..51d8b4e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -140,10 +140,6 @@
 }
 
 func (p *nativeBinaryInfoProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
-	if p.Compile_multilib != "" {
-		propertySet.AddProperty("compile_multilib", p.Compile_multilib)
-	}
-
 	builder := ctx.SnapshotBuilder()
 	if p.outputFile != nil {
 		propertySet.AddProperty("srcs", []string{nativeBinaryPathFor(*p)})
diff --git a/cc/cc.go b/cc/cc.go
index 0d5b6bd..bea851f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -116,6 +116,8 @@
 	// Used for host bionic
 	LinkerFlagsFile string
 	DynamicLinker   string
+
+	Tools []string
 }
 
 type PathDeps struct {
@@ -158,6 +160,8 @@
 
 	// Path to the dynamic linker binary
 	DynamicLinker android.OptionalPath
+
+	Tools map[string]android.Path
 }
 
 // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -425,6 +429,12 @@
 	XrefCcFiles() android.Paths
 }
 
+type ToolDependencyTag struct {
+	blueprint.BaseDependencyTag
+
+	Name string
+}
+
 var (
 	dataLibDepTag         = DependencyTag{Name: "data_lib", Library: true, Shared: true}
 	sharedExportDepTag    = DependencyTag{Name: "shared", Library: true, Shared: true, ReexportFlags: true}
@@ -1401,9 +1411,9 @@
 	return ok && test.isAllTestsVariation()
 }
 
-func (c *Module) DataPaths() android.Paths {
+func (c *Module) DataPaths() []android.DataPath {
 	if p, ok := c.installer.(interface {
-		dataPaths() android.Paths
+		dataPaths() []android.DataPath
 	}); ok {
 		return p.dataPaths()
 	}
@@ -1694,6 +1704,7 @@
 	deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs)
 	deps.HeaderLibs = android.LastUniqueStrings(deps.HeaderLibs)
 	deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
+	deps.Tools = android.LastUniqueStrings(deps.Tools)
 
 	for _, lib := range deps.ReexportSharedLibHeaders {
 		if !inList(lib, deps.SharedLibs) {
@@ -2037,6 +2048,11 @@
 			}, vndkExtDepTag, vndkdep.getVndkExtendsModuleName())
 		}
 	}
+
+	for _, tool := range deps.Tools {
+		actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
+			ToolDependencyTag{Name: tool}, tool)
+	}
 }
 
 func BeginMutator(ctx android.BottomUpMutatorContext) {
@@ -2215,10 +2231,38 @@
 		depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders, exporter.exportedGeneratedHeaders()...)
 	}
 
+	// For the dependency from platform to apex, use the latest stubs
+	c.apexSdkVersion = android.FutureApiLevel
+	if !c.IsForPlatform() {
+		c.apexSdkVersion = c.ApexProperties.Info.MinSdkVersion
+	}
+
+	if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
+		// In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
+		// so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
+		// (b/144430859)
+		c.apexSdkVersion = android.FutureApiLevel
+	}
+
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 
+		if toolDep, ok := depTag.(ToolDependencyTag); ok {
+			if toolMod, ok := dep.(android.HostToolProvider); ok {
+				if depPaths.Tools == nil {
+					depPaths.Tools = make(map[string]android.Path)
+				}
+				toolPath := toolMod.HostToolPath()
+				if !toolPath.Valid() {
+					ctx.ModuleErrorf("Failed to find path for host tool %q", toolDep.Name)
+				}
+				depPaths.Tools[toolDep.Name] = toolPath.Path()
+			} else {
+				ctx.ModuleErrorf("Found module, but not host tool for %q", toolDep.Name)
+			}
+		}
+
 		ccDep, ok := dep.(LinkableInterface)
 		if !ok {
 
@@ -2303,19 +2347,6 @@
 			}
 		}
 
-		// For the dependency from platform to apex, use the latest stubs
-		c.apexSdkVersion = android.FutureApiLevel
-		if !c.IsForPlatform() {
-			c.apexSdkVersion = c.ApexProperties.Info.MinSdkVersion
-		}
-
-		if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
-			// In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
-			// so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
-			// (b/144430859)
-			c.apexSdkVersion = android.FutureApiLevel
-		}
-
 		if depTag == staticUnwinderDepTag {
 			// Use static unwinder for legacy (min_sdk_version = 29) apexes (b/144430859)
 			if c.apexSdkVersion <= android.SdkVersion_Android10 {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 8a1c8ed..38a5c2d 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -539,7 +539,7 @@
 			data_libs: ["test_lib"],
 			gtest: false,
 		}
-  `
+ `
 
 	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -564,7 +564,7 @@
 	}
 
 	outputPath := outputFiles[0].String()
-	testBinaryPath := testBinary.dataPaths()[0].String()
+	testBinaryPath := testBinary.dataPaths()[0].SrcPath.String()
 
 	if !strings.HasSuffix(outputPath, "/main_test") {
 		t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
@@ -576,6 +576,53 @@
 	}
 }
 
+func TestDataLibsRelativeInstallPath(t *testing.T) {
+	bp := `
+		cc_test_library {
+			name: "test_lib",
+			srcs: ["test_lib.cpp"],
+			relative_install_path: "foo/bar/baz",
+			gtest: false,
+		}
+
+		cc_test {
+			name: "main_test",
+			data_libs: ["test_lib"],
+			gtest: false,
+		}
+ `
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
+
+	ctx := testCcWithConfig(t, config)
+	module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
+	testBinary := module.(*Module).linker.(*testBinary)
+	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Fatalf("Expected cc_test to produce output files, error: %s", err)
+	}
+	if len(outputFiles) != 1 {
+		t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
+	}
+	if len(testBinary.dataPaths()) != 1 {
+		t.Errorf("expected exactly one test data file. test data files: [%s]", testBinary.dataPaths())
+	}
+
+	outputPath := outputFiles[0].String()
+
+	if !strings.HasSuffix(outputPath, "/main_test") {
+		t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
+	}
+	entries := android.AndroidMkEntriesForTest(t, config, "", module)[0]
+	if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
+		t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
+			" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
+	}
+}
+
 func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
 	ctx := testCcNoVndk(t, `
 		cc_library {
@@ -2919,6 +2966,52 @@
 	}
 }
 
+func TestDataLibsPrebuiltSharedTestLibrary(t *testing.T) {
+	bp := `
+		cc_prebuilt_test_library_shared {
+			name: "test_lib",
+			relative_install_path: "foo/bar/baz",
+			srcs: ["srcpath/dontusethispath/baz.so"],
+		}
+
+		cc_test {
+			name: "main_test",
+			data_libs: ["test_lib"],
+			gtest: false,
+		}
+ `
+
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
+
+	ctx := testCcWithConfig(t, config)
+	module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
+	testBinary := module.(*Module).linker.(*testBinary)
+	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Fatalf("Expected cc_test to produce output files, error: %s", err)
+	}
+	if len(outputFiles) != 1 {
+		t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
+	}
+	if len(testBinary.dataPaths()) != 1 {
+		t.Errorf("expected exactly one test data file. test data files: [%s]", testBinary.dataPaths())
+	}
+
+	outputPath := outputFiles[0].String()
+
+	if !strings.HasSuffix(outputPath, "/main_test") {
+		t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
+	}
+	entries := android.AndroidMkEntriesForTest(t, config, "", module)[0]
+	if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
+		t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
+			" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
+	}
+}
+
 func TestVersionedStubs(t *testing.T) {
 	ctx := testCc(t, `
 		cc_library_shared {
diff --git a/cc/compiler.go b/cc/compiler.go
index d5ea2c3..ba14dd5 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -251,6 +251,14 @@
 		deps.StaticLibs = append(deps.StaticLibs, "libomp")
 	}
 
+	if compiler.hasSrcExt(".y") || compiler.hasSrcExt(".yy") {
+		deps.Tools = append(deps.Tools, "bison", "m4")
+	}
+
+	if compiler.hasSrcExt(".l") || compiler.hasSrcExt(".ll") {
+		deps.Tools = append(deps.Tools, "flex", "m4")
+	}
+
 	return deps
 }
 
@@ -581,7 +589,7 @@
 
 	srcs := append(android.Paths(nil), compiler.srcsBeforeGen...)
 
-	srcs, genDeps := genSources(ctx, srcs, buildFlags)
+	srcs, genDeps := genSources(ctx, srcs, buildFlags, deps.Tools)
 	pathDeps = append(pathDeps, genDeps...)
 
 	compiler.pathDeps = pathDeps
diff --git a/cc/gen.go b/cc/gen.go
index b0aadc6..6f9036b 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -24,7 +24,6 @@
 )
 
 func init() {
-	pctx.SourcePathVariable("lexCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/flex")
 	pctx.SourcePathVariable("m4Cmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/m4")
 
 	pctx.HostBinToolVariable("aidlCmd", "aidl-cpp")
@@ -32,12 +31,6 @@
 }
 
 var (
-	lex = pctx.AndroidStaticRule("lex",
-		blueprint.RuleParams{
-			Command:     "M4=$m4Cmd $lexCmd -o$out $in",
-			CommandDeps: []string{"$lexCmd", "$m4Cmd"},
-		})
-
 	sysprop = pctx.AndroidStaticRule("sysprop",
 		blueprint.RuleParams{
 			Command: "$syspropCmd --header-dir=$headerOutDir --public-header-dir=$publicOutDir " +
@@ -66,7 +59,8 @@
 }
 
 func genYacc(ctx android.ModuleContext, rule *android.RuleBuilder, yaccFile android.Path,
-	outFile android.ModuleGenPath, props *YaccProperties) (headerFiles android.Paths) {
+	outFile android.ModuleGenPath, props *YaccProperties,
+	tools map[string]android.Path) (headerFiles android.Paths) {
 
 	outDir := android.PathForModuleGen(ctx, "yacc")
 	headerFile := android.GenPathWithExt(ctx, "yacc", yaccFile, "h")
@@ -97,9 +91,17 @@
 		}
 	}
 
-	cmd.Text("BISON_PKGDATADIR=prebuilts/build-tools/common/bison").
-		FlagWithInput("M4=", ctx.Config().PrebuiltBuildTool(ctx, "m4")).
-		PrebuiltBuildTool(ctx, "bison").
+	bison, ok := tools["bison"]
+	if !ok {
+		ctx.ModuleErrorf("Unable to find bison")
+	}
+	m4, ok := tools["m4"]
+	if !ok {
+		ctx.ModuleErrorf("Unable to find m4")
+	}
+
+	cmd.FlagWithInput("M4=", m4).
+		Tool(bison).
 		Flag("-d").
 		Flags(flags).
 		FlagWithOutput("--defines=", headerFile).
@@ -153,13 +155,23 @@
 	}
 }
 
-func genLex(ctx android.ModuleContext, lexFile android.Path, outFile android.ModuleGenPath) {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        lex,
-		Description: "lex " + lexFile.Rel(),
-		Output:      outFile,
-		Input:       lexFile,
-	})
+func genLex(ctx android.ModuleContext, rule *android.RuleBuilder, lexFile android.Path,
+	outFile android.ModuleGenPath, tools map[string]android.Path) {
+
+	flex, ok := tools["flex"]
+	if !ok {
+		ctx.ModuleErrorf("Unable to find flex")
+	}
+	m4, ok := tools["m4"]
+	if !ok {
+		ctx.ModuleErrorf("Unable to find m4")
+	}
+
+	rule.Command().
+		FlagWithInput("M4=", m4).
+		Tool(flex).
+		FlagWithOutput("-o", outFile).
+		Input(lexFile)
 }
 
 func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Paths) {
@@ -206,14 +218,22 @@
 	return rcFile, headerFile
 }
 
-func genSources(ctx android.ModuleContext, srcFiles android.Paths,
-	buildFlags builderFlags) (android.Paths, android.Paths) {
+func genSources(ctx android.ModuleContext, srcFiles android.Paths, buildFlags builderFlags,
+	tools map[string]android.Path) (android.Paths, android.Paths) {
 
 	var deps android.Paths
 	var rsFiles android.Paths
 
 	var aidlRule *android.RuleBuilder
 
+	var lexRule_ *android.RuleBuilder
+	lexRule := func() *android.RuleBuilder {
+		if lexRule_ == nil {
+			lexRule_ = android.NewRuleBuilder().Sbox(android.PathForModuleGen(ctx, "lex"))
+		}
+		return lexRule_
+	}
+
 	var yaccRule_ *android.RuleBuilder
 	yaccRule := func() *android.RuleBuilder {
 		if yaccRule_ == nil {
@@ -227,19 +247,19 @@
 		case ".y":
 			cFile := android.GenPathWithExt(ctx, "yacc", srcFile, "c")
 			srcFiles[i] = cFile
-			deps = append(deps, genYacc(ctx, yaccRule(), srcFile, cFile, buildFlags.yacc)...)
+			deps = append(deps, genYacc(ctx, yaccRule(), srcFile, cFile, buildFlags.yacc, tools)...)
 		case ".yy":
 			cppFile := android.GenPathWithExt(ctx, "yacc", srcFile, "cpp")
 			srcFiles[i] = cppFile
-			deps = append(deps, genYacc(ctx, yaccRule(), srcFile, cppFile, buildFlags.yacc)...)
+			deps = append(deps, genYacc(ctx, yaccRule(), srcFile, cppFile, buildFlags.yacc, tools)...)
 		case ".l":
 			cFile := android.GenPathWithExt(ctx, "lex", srcFile, "c")
 			srcFiles[i] = cFile
-			genLex(ctx, srcFile, cFile)
+			genLex(ctx, lexRule(), srcFile, cFile, tools)
 		case ".ll":
 			cppFile := android.GenPathWithExt(ctx, "lex", srcFile, "cpp")
 			srcFiles[i] = cppFile
-			genLex(ctx, srcFile, cppFile)
+			genLex(ctx, lexRule(), srcFile, cppFile, tools)
 		case ".proto":
 			ccFile, headerFile := genProto(ctx, srcFile, buildFlags)
 			srcFiles[i] = ccFile
@@ -271,6 +291,10 @@
 		aidlRule.Build(pctx, ctx, "aidl", "gen aidl")
 	}
 
+	if lexRule_ != nil {
+		lexRule_.Build(pctx, ctx, "lex", "gen lex")
+	}
+
 	if yaccRule_ != nil {
 		yaccRule_.Build(pctx, ctx, "yacc", "gen yacc")
 	}
diff --git a/cc/pgo.go b/cc/pgo.go
index 9298e7a..674e1bb 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -199,8 +199,8 @@
 		return false
 	}
 
-	// If at least one property exists, validate that all properties exist
-	if !profileKindPresent || !filePresent || !benchmarksPresent {
+	// profileKindPresent and filePresent are mandatory properties.
+	if !profileKindPresent || !filePresent {
 		var missing []string
 		if !profileKindPresent {
 			missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)")
@@ -208,13 +208,15 @@
 		if !filePresent {
 			missing = append(missing, "profile_file property")
 		}
-		if !benchmarksPresent {
-			missing = append(missing, "non-empty benchmarks property")
-		}
 		missingProps := strings.Join(missing, ", ")
 		ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
 	}
 
+	// Benchmark property is mandatory for instrumentation PGO.
+	if isInstrumentation && !benchmarksPresent {
+		ctx.ModuleErrorf("Instrumentation PGO specification is missing benchmark property")
+	}
+
 	if isSampling && isInstrumentation {
 		ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set")
 	}
@@ -288,15 +290,17 @@
 
 	// Add flags to profile this module based on its profile_kind
 	if props.ShouldProfileModule && props.isInstrumentation() {
-		return props.addInstrumentationProfileGatherFlags(ctx, flags)
+		props.addInstrumentationProfileGatherFlags(ctx, flags)
+		// Instrumentation PGO use and gather flags cannot coexist.
+		return flags
 	} else if props.ShouldProfileModule && props.isSampling() {
-		return props.addSamplingProfileGatherFlags(ctx, flags)
+		props.addSamplingProfileGatherFlags(ctx, flags)
 	} else if ctx.DeviceConfig().SamplingPGO() {
-		return props.addSamplingProfileGatherFlags(ctx, flags)
+		props.addSamplingProfileGatherFlags(ctx, flags)
 	}
 
 	if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
-		return props.addProfileUseFlags(ctx, flags)
+		props.addProfileUseFlags(ctx, flags)
 	}
 
 	return flags
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 0751f1c..653b43e 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -26,6 +26,7 @@
 	ctx.RegisterModuleType("cc_prebuilt_library", PrebuiltLibraryFactory)
 	ctx.RegisterModuleType("cc_prebuilt_library_shared", PrebuiltSharedLibraryFactory)
 	ctx.RegisterModuleType("cc_prebuilt_library_static", PrebuiltStaticLibraryFactory)
+	ctx.RegisterModuleType("cc_prebuilt_test_library_shared", PrebuiltSharedTestLibraryFactory)
 	ctx.RegisterModuleType("cc_prebuilt_object", prebuiltObjectFactory)
 	ctx.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
 }
@@ -243,6 +244,16 @@
 	return module.Init()
 }
 
+// cc_prebuilt_test_library_shared installs a precompiled shared library
+// to be used as a data dependency of a test-related module (such as cc_test, or
+// cc_test_library).
+func PrebuiltSharedTestLibraryFactory() android.Module {
+	module, library := NewPrebuiltLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyShared()
+	library.baseInstaller = NewTestInstaller()
+	return module.Init()
+}
+
 func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewPrebuiltLibrary(hod)
 	library.BuildOnlyShared()
diff --git a/cc/test.go b/cc/test.go
index 37afb0c..9b6864a 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -165,7 +165,7 @@
 	return test.baseCompiler.Properties.Srcs
 }
 
-func (test *testBinary) dataPaths() android.Paths {
+func (test *testBinary) dataPaths() []android.DataPath {
 	return test.data
 }
 
@@ -310,7 +310,7 @@
 	*binaryDecorator
 	*baseCompiler
 	Properties TestBinaryProperties
-	data       android.Paths
+	data       []android.DataPath
 	testConfig android.Path
 }
 
@@ -339,7 +339,11 @@
 }
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
-	test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
+	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
+
+	for _, dataSrcPath := range dataSrcPaths {
+		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+	}
 
 	ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
@@ -348,10 +352,14 @@
 		if !ok {
 			ctx.ModuleErrorf("data_lib %q is not a linkable cc module", depName)
 		}
+		ccModule, ok := dep.(*Module)
+		if !ok {
+			ctx.ModuleErrorf("data_lib %q is not a cc module", depName)
+		}
 		if ccDep.OutputFile().Valid() {
-			test.data = append(test.data, ccDep.OutputFile().Path())
-		} else {
-			ctx.ModuleErrorf("data_lib %q has no output file", depName)
+			test.data = append(test.data,
+				android.DataPath{SrcPath: ccDep.OutputFile().Path(),
+					RelativeInstallPath: ccModule.installer.relativeInstallPath()})
 		}
 	})
 
diff --git a/java/Android.bp b/java/Android.bp
index 1fda7f7..fd06c46 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -38,6 +38,7 @@
         "java_resources.go",
         "kotlin.go",
         "lint.go",
+        "legacy_core_platform_api_usage.go",
         "platform_compat_config.go",
         "plugin.go",
         "prebuilt_apis.go",
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 8280cb1..84dee16 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -130,7 +130,7 @@
 		},
 	})
 
-	return fixedManifest
+	return fixedManifest.WithoutRel()
 }
 
 func manifestMerger(ctx android.ModuleContext, manifest android.Path, staticLibManifests android.Paths,
@@ -155,5 +155,5 @@
 		},
 	})
 
-	return mergedManifest
+	return mergedManifest.WithoutRel()
 }
diff --git a/java/androidmk.go b/java/androidmk.go
index 03994bf..25dd329 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -132,9 +132,7 @@
 					}
 					entries.SetString("LOCAL_MODULE_STEM", library.Stem())
 
-					entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveHTMLZip)
-					entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveTextZip)
-					entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveXMLZip)
+					entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", library.linter.reports)
 				},
 			},
 		}
@@ -394,9 +392,7 @@
 					entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", extra.String()+":"+install)
 				}
 
-				entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveHTMLZip)
-				entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveTextZip)
-				entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveXMLZip)
+				entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
diff --git a/java/config/config.go b/java/config/config.go
index 0fe74c8..d2f4513 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -30,6 +30,8 @@
 
 	LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
 	LegacyCorePlatformSystemModules          = "legacy-core-platform-api-stubs-system-modules"
+	StableCorePlatformBootclasspathLibraries = []string{"stable.core.platform.api.stubs", "core-lambda-stubs"}
+	StableCorePlatformSystemModules          = "stable-core-platform-api-stubs-system-modules"
 	FrameworkLibraries                       = []string{"ext", "framework"}
 	DefaultLambdaStubsLibrary                = "core-lambda-stubs"
 	SdkLambdaStubsPath                       = "prebuilts/sdk/tools/core-lambda-stubs.jar"
diff --git a/java/java.go b/java/java.go
index 367b09c..ef9613d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1707,6 +1707,9 @@
 		j.linter.compileSdkVersion = lintSDKVersionString(j.sdkVersion())
 		j.linter.javaLanguageLevel = flags.javaVersion.String()
 		j.linter.kotlinLanguageLevel = "1.3"
+		if j.ApexName() != "" && ctx.Config().UnbundledBuildApps() {
+			j.linter.buildModuleReportZip = true
+		}
 		j.linter.lint(ctx)
 	}
 
@@ -2789,6 +2792,10 @@
 	return nil
 }
 
+func (a *DexImport) LintDepSets() LintDepSets {
+	return LintDepSets{}
+}
+
 func (j *DexImport) IsInstallable() bool {
 	return true
 }
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
new file mode 100644
index 0000000..8af66d0
--- /dev/null
+++ b/java/legacy_core_platform_api_usage.go
@@ -0,0 +1,162 @@
+// 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 java
+
+import (
+	"android/soong/android"
+	"android/soong/java/config"
+)
+
+var legacyCorePlatformApiModules = []string{
+	"ahat-test-dump",
+	"android.car",
+	"android.test.mock",
+	"android.test.mock.impl",
+	"AoapTestDeviceApp",
+	"AoapTestHostApp",
+	"api-stubs-docs",
+	"art_cts_jvmti_test_library",
+	"art-gtest-jars-MyClassNatives",
+	"BackupFrameworksServicesRoboTests",
+	"BandwidthEnforcementTest",
+	"BlockedNumberProvider",
+	"BluetoothInstrumentationTests",
+	"BluetoothMidiService",
+	"car-apps-common",
+	"CertInstaller",
+	"ConnectivityManagerTest",
+	"ContactsProvider",
+	"core-tests-support",
+	"CtsContentTestCases",
+	"CtsIkeTestCases",
+	"CtsLibcoreWycheproofBCTestCases",
+	"CtsMediaTestCases",
+	"CtsNetTestCases",
+	"CtsNetTestCasesLatestSdk",
+	"CtsSecurityTestCases",
+	"CtsUsageStatsTestCases",
+	"DisplayCutoutEmulationEmu01Overlay",
+	"DocumentsUIPerfTests",
+	"DocumentsUITests",
+	"DownloadProvider",
+	"DownloadProviderTests",
+	"DownloadProviderUi",
+	"DynamicSystemInstallationService",
+	"EmergencyInfo-lib",
+	"ethernet-service",
+	"EthernetServiceTests",
+	"ExternalStorageProvider",
+	"ExtServices",
+	"ExtServices-core",
+	"framework-all",
+	"framework-minus-apex",
+	"FrameworksCoreTests",
+	"FrameworksIkeTests",
+	"FrameworksNetCommonTests",
+	"FrameworksNetTests",
+	"FrameworksServicesRoboTests",
+	"FrameworksServicesTests",
+	"FrameworksUtilTests",
+	"hid",
+	"hidl_test_java_java",
+	"hwbinder",
+	"ims",
+	"KeyChain",
+	"ksoap2",
+	"LocalTransport",
+	"lockagent",
+	"mediaframeworktest",
+	"MediaProvider",
+	"MmsService",
+	"MtpDocumentsProvider",
+	"MultiDisplayProvider",
+	"NetworkStackIntegrationTestsLib",
+	"NetworkStackNextIntegrationTests",
+	"NetworkStackNextTests",
+	"NetworkStackTests",
+	"NetworkStackTestsLib",
+	"NfcNci",
+	"platform_library-docs",
+	"PrintSpooler",
+	"RollbackTest",
+	"services",
+	"services.accessibility",
+	"services.backup",
+	"services.core.unboosted",
+	"services.devicepolicy",
+	"services.print",
+	"services.usage",
+	"services.usb",
+	"Settings-core",
+	"SettingsLib",
+	"SettingsProvider",
+	"SettingsProviderTest",
+	"SettingsRoboTests",
+	"Shell",
+	"ShellTests",
+	"sl4a.Common",
+	"StatementService",
+	"SystemUI-core",
+	"SystemUISharedLib",
+	"SystemUI-tests",
+	"Telecom",
+	"TelecomUnitTests",
+	"telephony-common",
+	"TelephonyProvider",
+	"TelephonyProviderTests",
+	"TeleService",
+	"testables",
+	"TetheringTests",
+	"TetheringTestsLib",
+	"time_zone_distro_installer",
+	"time_zone_distro_installer-tests",
+	"time_zone_distro-tests",
+	"time_zone_updater",
+	"TvProvider",
+	"uiautomator-stubs-docs",
+	"UsbHostExternalManagementTestApp",
+	"UserDictionaryProvider",
+	"WallpaperBackup",
+	"wifi-service",
+}
+
+var legacyCorePlatformApiLookup = make(map[string]struct{})
+
+func init() {
+	for _, module := range legacyCorePlatformApiModules {
+		legacyCorePlatformApiLookup[module] = struct{}{}
+	}
+}
+
+func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool {
+	_, found := legacyCorePlatformApiLookup[ctx.ModuleName()]
+	return found
+}
+
+func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
+	if useLegacyCorePlatformApi(ctx) {
+		return config.LegacyCorePlatformSystemModules
+	} else {
+		return config.StableCorePlatformSystemModules
+	}
+}
+
+func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string {
+	if useLegacyCorePlatformApi(ctx) {
+		return config.LegacyCorePlatformBootclasspathLibraries
+	} else {
+		return config.StableCorePlatformBootclasspathLibraries
+	}
+}
diff --git a/java/lint.go b/java/lint.go
index 20a7dc4..6391067 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strings"
 
 	"android/soong/android"
 )
@@ -68,28 +69,78 @@
 	outputs             lintOutputs
 	properties          LintProperties
 
+	reports android.Paths
+
 	buildModuleReportZip bool
 }
 
 type lintOutputs struct {
-	html android.ModuleOutPath
-	text android.ModuleOutPath
-	xml  android.ModuleOutPath
+	html android.Path
+	text android.Path
+	xml  android.Path
 
-	transitiveHTML *android.DepSet
-	transitiveText *android.DepSet
-	transitiveXML  *android.DepSet
-
-	transitiveHTMLZip android.OptionalPath
-	transitiveTextZip android.OptionalPath
-	transitiveXMLZip  android.OptionalPath
+	depSets LintDepSets
 }
 
-type lintOutputIntf interface {
+type lintOutputsIntf interface {
 	lintOutputs() *lintOutputs
 }
 
-var _ lintOutputIntf = (*linter)(nil)
+type lintDepSetsIntf interface {
+	LintDepSets() LintDepSets
+}
+
+type LintDepSets struct {
+	HTML, Text, XML *android.DepSet
+}
+
+type LintDepSetsBuilder struct {
+	HTML, Text, XML *android.DepSetBuilder
+}
+
+func NewLintDepSetBuilder() LintDepSetsBuilder {
+	return LintDepSetsBuilder{
+		HTML: android.NewDepSetBuilder(android.POSTORDER),
+		Text: android.NewDepSetBuilder(android.POSTORDER),
+		XML:  android.NewDepSetBuilder(android.POSTORDER),
+	}
+}
+
+func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder {
+	l.HTML.Direct(html)
+	l.Text.Direct(text)
+	l.XML.Direct(xml)
+	return l
+}
+
+func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder {
+	if depSets.HTML != nil {
+		l.HTML.Transitive(depSets.HTML)
+	}
+	if depSets.Text != nil {
+		l.Text.Transitive(depSets.Text)
+	}
+	if depSets.XML != nil {
+		l.XML.Transitive(depSets.XML)
+	}
+	return l
+}
+
+func (l LintDepSetsBuilder) Build() LintDepSets {
+	return LintDepSets{
+		HTML: l.HTML.Build(),
+		Text: l.Text.Build(),
+		XML:  l.XML.Build(),
+	}
+}
+
+func (l *linter) LintDepSets() LintDepSets {
+	return l.outputs.depSets
+}
+
+var _ lintDepSetsIntf = (*linter)(nil)
+
+var _ lintOutputsIntf = (*linter)(nil)
 
 func (l *linter) lintOutputs() *lintOutputs {
 	return &l.outputs
@@ -104,7 +155,16 @@
 		return
 	}
 
-	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), extraLintCheckTag, l.properties.Lint.Extra_check_modules...)
+	extraCheckModules := l.properties.Lint.Extra_check_modules
+
+	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
+		if checkOnlyModules := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); checkOnlyModules != "" {
+			extraCheckModules = strings.Split(checkOnlyModules, ",")
+		}
+	}
+
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
+		extraLintCheckTag, extraCheckModules...)
 }
 
 func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
@@ -237,16 +297,11 @@
 	text := android.PathForModuleOut(ctx, "lint-report.txt")
 	xml := android.PathForModuleOut(ctx, "lint-report.xml")
 
-	htmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(html)
-	textDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(text)
-	xmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(xml)
+	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
 
 	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
-		if depLint, ok := dep.(lintOutputIntf); ok {
-			depLintOutputs := depLint.lintOutputs()
-			htmlDeps.Transitive(depLintOutputs.transitiveHTML)
-			textDeps.Transitive(depLintOutputs.transitiveText)
-			xmlDeps.Transitive(depLintOutputs.transitiveXML)
+		if depLint, ok := dep.(lintDepSetsIntf); ok {
+			depSetsBuilder.Transitive(depLint.LintDepSets())
 		}
 	})
 
@@ -262,7 +317,7 @@
 		apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
 	}
 
-	rule.Command().
+	cmd := rule.Command().
 		Text("(").
 		Flag("JAVA_OPTS=-Xmx2048m").
 		FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
@@ -282,9 +337,13 @@
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
 		Flag("--exitcode").
 		Flags(l.properties.Lint.Flags).
-		Implicits(deps).
-		Text("|| (").Text("cat").Input(text).Text("; exit 7)").
-		Text(")")
+		Implicits(deps)
+
+	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
+		cmd.FlagWithArg("--check ", checkOnly)
+	}
+
+	cmd.Text("|| (").Text("cat").Input(text).Text("; exit 7)").Text(")")
 
 	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
 
@@ -295,26 +354,35 @@
 		text: text,
 		xml:  xml,
 
-		transitiveHTML: htmlDeps.Build(),
-		transitiveText: textDeps.Build(),
-		transitiveXML:  xmlDeps.Build(),
+		depSets: depSetsBuilder.Build(),
 	}
 
 	if l.buildModuleReportZip {
-		htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
-		l.outputs.transitiveHTMLZip = android.OptionalPathForPath(htmlZip)
-		lintZip(ctx, l.outputs.transitiveHTML.ToSortedList(), htmlZip)
-
-		textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
-		l.outputs.transitiveTextZip = android.OptionalPathForPath(textZip)
-		lintZip(ctx, l.outputs.transitiveText.ToSortedList(), textZip)
-
-		xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
-		l.outputs.transitiveXMLZip = android.OptionalPathForPath(xmlZip)
-		lintZip(ctx, l.outputs.transitiveXML.ToSortedList(), xmlZip)
+		l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
 	}
 }
 
+func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
+	htmlList := depSets.HTML.ToSortedList()
+	textList := depSets.Text.ToSortedList()
+	xmlList := depSets.XML.ToSortedList()
+
+	if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
+		return nil
+	}
+
+	htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
+	lintZip(ctx, htmlList, htmlZip)
+
+	textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
+	lintZip(ctx, textList, textZip)
+
+	xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
+	lintZip(ctx, xmlList, xmlZip)
+
+	return android.Paths{htmlZip, textZip, xmlZip}
+}
+
 type lintSingleton struct {
 	htmlZip android.WritablePath
 	textZip android.WritablePath
@@ -389,7 +457,7 @@
 			return
 		}
 
-		if l, ok := m.(lintOutputIntf); ok {
+		if l, ok := m.(lintOutputsIntf); ok {
 			outputs = append(outputs, l.lintOutputs())
 		}
 	})
@@ -400,7 +468,9 @@
 		var paths android.Paths
 
 		for _, output := range outputs {
-			paths = append(paths, get(output))
+			if p := get(output); p != nil {
+				paths = append(paths, p)
+			}
 		}
 
 		lintZip(ctx, paths, outputPath)
diff --git a/java/robolectric.go b/java/robolectric.go
index c6b07a1..4d68fd9 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -21,10 +21,13 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/java/config"
+	"android/soong/tradefed"
 )
 
 func init() {
 	android.RegisterModuleType("android_robolectric_test", RobolectricTestFactory)
+	android.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory)
 }
 
 var robolectricDefaultLibs = []string{
@@ -32,10 +35,13 @@
 	"Robolectric_all-target",
 	"mockito-robolectric-prebuilt",
 	"truth-prebuilt",
+	// TODO(ccross): this is not needed at link time
+	"junitxml",
 }
 
 var (
-	roboCoverageLibsTag = dependencyTag{name: "roboSrcs"}
+	roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
+	roboRuntimesTag     = dependencyTag{name: "roboRuntimes"}
 )
 
 type robolectricProperties struct {
@@ -58,13 +64,28 @@
 	Library
 
 	robolectricProperties robolectricProperties
+	testProperties        testProperties
 
 	libs  []string
 	tests []string
 
+	manifest    android.Path
+	resourceApk android.Path
+
+	combinedJar android.WritablePath
+
 	roboSrcJar android.Path
+
+	testConfig android.Path
+	data       android.Paths
 }
 
+func (r *robolectricTest) TestSuites() []string {
+	return r.testProperties.Test_suites
+}
+
+var _ android.TestSuiteModule = (*robolectricTest)(nil)
+
 func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) {
 	r.Library.DepsMutator(ctx)
 
@@ -77,9 +98,16 @@
 	ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...)
 
 	ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
+
+	ctx.AddVariationDependencies(nil, roboRuntimesTag, "robolectric-android-all-prebuilts")
 }
 
 func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	r.testConfig = tradefed.AutoGenRobolectricTestConfig(ctx, r.testProperties.Test_config,
+		r.testProperties.Test_config_template, r.testProperties.Test_suites,
+		r.testProperties.Auto_gen_config)
+	r.data = android.PathsForModuleSrc(ctx, r.testProperties.Data)
+
 	roboTestConfig := android.PathForModuleGen(ctx, "robolectric").
 		Join(ctx, "com/android/tools/test_config.properties")
 
@@ -95,6 +123,9 @@
 		ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
 	}
 
+	r.manifest = instrumentedApp.mergedManifestFile
+	r.resourceApk = instrumentedApp.outputFile
+
 	generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
 	r.extraResources = android.Paths{roboTestConfig}
 
@@ -104,10 +135,30 @@
 	r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
 	r.roboSrcJar = roboSrcJar
 
-	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
-		r.libs = append(r.libs, dep.(Dependency).BaseModuleName())
+	roboTestConfigJar := android.PathForModuleOut(ctx, "robolectric_samedir", "samedir_config.jar")
+	generateSameDirRoboTestConfigJar(ctx, roboTestConfigJar)
+
+	combinedJarJars := android.Paths{
+		// roboTestConfigJar comes first so that its com/android/tools/test_config.properties
+		// overrides the one from r.extraResources.  The r.extraResources one can be removed
+		// once the Make test runner is removed.
+		roboTestConfigJar,
+		r.outputFile,
+		instrumentedApp.implementationAndResourcesJar,
 	}
 
+	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
+		m := dep.(Dependency)
+		r.libs = append(r.libs, m.BaseModuleName())
+		if !android.InList(m.BaseModuleName(), config.FrameworkLibraries) {
+			combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars()...)
+		}
+	}
+
+	r.combinedJar = android.PathForModuleOut(ctx, "robolectric_combined", r.outputFile.Base())
+	TransformJarsToJar(ctx, r.combinedJar, "combine jars", combinedJarJars, android.OptionalPath{},
+		false, nil, nil)
+
 	// TODO: this could all be removed if tradefed was used as the test runner, it will find everything
 	// annotated as a test and run it.
 	for _, src := range r.compiledJavaSrcs {
@@ -121,14 +172,38 @@
 		}
 		r.tests = append(r.tests, s)
 	}
+
+	r.data = append(r.data, r.manifest, r.resourceApk)
+
+	runtimes := ctx.GetDirectDepWithTag("robolectric-android-all-prebuilts", roboRuntimesTag)
+
+	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
+
+	installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk)
+	installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest)
+	installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
+
+	var installDeps android.Paths
+	for _, runtime := range runtimes.(*robolectricRuntimes).runtimes {
+		installDeps = append(installDeps, runtime)
+	}
+	installDeps = append(installDeps, installedResourceApk, installedManifest, installedConfig)
+
+	for _, data := range android.PathsForModuleSrc(ctx, r.testProperties.Data) {
+		installedData := ctx.InstallFile(installPath, data.Rel(), data)
+		installDeps = append(installDeps, installedData)
+	}
+
+	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
 }
 
-func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, instrumentedApp *AndroidApp) {
+func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath,
+	instrumentedApp *AndroidApp) {
+	rule := android.NewRuleBuilder()
+
 	manifest := instrumentedApp.mergedManifestFile
 	resourceApk := instrumentedApp.outputFile
 
-	rule := android.NewRuleBuilder()
-
 	rule.Command().Text("rm -f").Output(outputFile)
 	rule.Command().
 		Textf(`echo "android_merged_manifest=%s" >>`, manifest.String()).Output(outputFile).Text("&&").
@@ -141,6 +216,28 @@
 	rule.Build(pctx, ctx, "generate_test_config", "generate test_config.properties")
 }
 
+func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
+	rule := android.NewRuleBuilder()
+
+	outputDir := outputFile.InSameDir(ctx)
+	configFile := outputDir.Join(ctx, "com/android/tools/test_config.properties")
+	rule.Temporary(configFile)
+	rule.Command().Text("rm -f").Output(outputFile).Output(configFile)
+	rule.Command().Textf("mkdir -p $(dirname %s)", configFile.String())
+	rule.Command().
+		Text("(").
+		Textf(`echo "android_merged_manifest=%s-AndroidManifest.xml" &&`, ctx.ModuleName()).
+		Textf(`echo "android_resource_apk=%s.apk"`, ctx.ModuleName()).
+		Text(") >>").Output(configFile)
+	rule.Command().
+		BuiltTool(ctx, "soong_zip").
+		FlagWithArg("-C ", outputDir.String()).
+		FlagWithInput("-f ", configFile).
+		FlagWithOutput("-o ", outputFile)
+
+	rule.Build(pctx, ctx, "generate_test_config_samedir", "generate test_config.properties")
+}
+
 func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath,
 	instrumentedApp *AndroidApp) {
 
@@ -202,7 +299,6 @@
 		fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
 	}
 	fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
-
 }
 
 // An android_robolectric_test module compiles tests against the Robolectric framework that can run on the local host
@@ -218,11 +314,74 @@
 	module.addHostProperties()
 	module.AddProperties(
 		&module.Module.deviceProperties,
-		&module.robolectricProperties)
+		&module.robolectricProperties,
+		&module.testProperties)
 
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.test = true
 
+	module.testProperties.Test_suites = []string{"robolectric-tests"}
+
 	InitJavaModule(module, android.DeviceSupported)
 	return module
 }
+
+func (r *robolectricTest) InstallBypassMake() bool         { return true }
+func (r *robolectricTest) InstallInTestcases() bool        { return true }
+func (r *robolectricTest) InstallForceOS() *android.OsType { return &android.BuildOs }
+
+func robolectricRuntimesFactory() android.Module {
+	module := &robolectricRuntimes{}
+	module.AddProperties(&module.props)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+type robolectricRuntimesProperties struct {
+	Jars []string `android:"path"`
+	Lib  *string
+}
+
+type robolectricRuntimes struct {
+	android.ModuleBase
+
+	props robolectricRuntimesProperties
+
+	runtimes []android.InstallPath
+}
+
+func (r *robolectricRuntimes) TestSuites() []string {
+	return []string{"robolectric-tests"}
+}
+
+var _ android.TestSuiteModule = (*robolectricRuntimes)(nil)
+
+func (r *robolectricRuntimes) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() && r.props.Lib != nil {
+		ctx.AddVariationDependencies(nil, libTag, String(r.props.Lib))
+	}
+}
+
+func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	files := android.PathsForModuleSrc(ctx, r.props.Jars)
+
+	androidAllDir := android.PathForModuleInstall(ctx, "android-all")
+	for _, from := range files {
+		installedRuntime := ctx.InstallFile(androidAllDir, from.Base(), from)
+		r.runtimes = append(r.runtimes, installedRuntime)
+	}
+
+	if !ctx.Config().UnbundledBuildUsePrebuiltSdks() && r.props.Lib != nil {
+		runtimeFromSourceModule := ctx.GetDirectDepWithTag(String(r.props.Lib), libTag)
+		runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "")
+
+		runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar",
+			ctx.Config().PlatformSdkCodename())
+		installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar)
+		r.runtimes = append(r.runtimes, installedRuntime)
+	}
+}
+
+func (r *robolectricRuntimes) InstallBypassMake() bool         { return true }
+func (r *robolectricRuntimes) InstallInTestcases() bool        { return true }
+func (r *robolectricRuntimes) InstallForceOS() *android.OsType { return &android.BuildOs }
diff --git a/java/sdk.go b/java/sdk.go
index 6564f6d..6e67a13 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -413,8 +413,8 @@
 	case sdkPrivate:
 		return sdkDep{
 			useModule:          true,
-			systemModules:      config.LegacyCorePlatformSystemModules,
-			bootclasspath:      config.LegacyCorePlatformBootclasspathLibraries,
+			systemModules:      corePlatformSystemModules(ctx),
+			bootclasspath:      corePlatformBootclasspathLibraries(ctx),
 			classpath:          config.FrameworkLibraries,
 			frameworkResModule: "framework-res",
 		}
@@ -438,8 +438,8 @@
 	case sdkCorePlatform:
 		return sdkDep{
 			useModule:        true,
-			systemModules:    config.LegacyCorePlatformSystemModules,
-			bootclasspath:    config.LegacyCorePlatformBootclasspathLibraries,
+			systemModules:    corePlatformSystemModules(ctx),
+			bootclasspath:    corePlatformBootclasspathLibraries(ctx),
 			noFrameworksLibs: true,
 		}
 	case sdkPublic:
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 58e05e5..8a8d0c9 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -126,25 +126,23 @@
 	// the prebuilt jar.
 	sdkVersion string
 
+	// The annotation that identifies this API level, empty for the public API scope.
+	annotation string
+
 	// Extra arguments to pass to droidstubs for this scope.
-	droidstubsArgs []string
-
-	// The args that must be passed to droidstubs to generate the stubs source
-	// for this scope.
 	//
-	// The stubs source must include the definitions of everything that is in this
-	// api scope and all the scopes that this one extends.
-	droidstubsArgsForGeneratingStubsSource []string
+	// This is not used directly but is used to construct the droidstubsArgs.
+	extraArgs []string
 
-	// The args that must be passed to droidstubs to generate the API for this scope.
+	// The args that must be passed to droidstubs to generate the API and stubs source
+	// for this scope, constructed dynamically by initApiScope().
 	//
 	// The API only includes the additional members that this scope adds over the scope
 	// that it extends.
-	droidstubsArgsForGeneratingApi []string
-
-	// True if the stubs source and api can be created by the same metalava invocation.
-	// TODO(b/146727827) Now that metalava supports "API hierarchy", do we still need it?
-	createStubsSourceAndApiTogether bool
+	//
+	// The stubs source must include the definitions of everything that is in this
+	// api scope and all the scopes that this one extends.
+	droidstubsArgs []string
 
 	// Whether the api scope can be treated as unstable, and should skip compat checks.
 	unstable bool
@@ -181,21 +179,23 @@
 	// To get the args needed to generate the stubs source append all the args from
 	// this scope and all the scopes it extends as each set of args adds additional
 	// members to the stubs.
-	var stubsSourceArgs []string
-	for s := scope; s != nil; s = s.extends {
-		stubsSourceArgs = append(stubsSourceArgs, s.droidstubsArgs...)
+	var scopeSpecificArgs []string
+	if scope.annotation != "" {
+		scopeSpecificArgs = []string{"--show-annotation", scope.annotation}
 	}
-	scope.droidstubsArgsForGeneratingStubsSource = stubsSourceArgs
+	for s := scope; s != nil; s = s.extends {
+		scopeSpecificArgs = append(scopeSpecificArgs, s.extraArgs...)
 
-	// Currently the args needed to generate the API are the same as the args
-	// needed to add additional members.
-	apiArgs := scope.droidstubsArgs
-	scope.droidstubsArgsForGeneratingApi = apiArgs
+		// Ensure that the generated stubs includes all the API elements from the API scope
+		// that this scope extends.
+		if s != scope && s.annotation != "" {
+			scopeSpecificArgs = append(scopeSpecificArgs, "--show-for-stub-purposes-annotation", s.annotation)
+		}
+	}
 
-	// If the args needed to generate the stubs and API are the same then they
-	// can be generated in a single invocation of metalava, otherwise they will
-	// need separate invocations.
-	scope.createStubsSourceAndApiTogether = reflect.DeepEqual(stubsSourceArgs, apiArgs)
+	// Escape any special characters in the arguments. This is needed because droidstubs
+	// passes these directly to the shell command.
+	scope.droidstubsArgs = proptools.ShellEscapeList(scopeSpecificArgs)
 
 	return scope
 }
@@ -250,10 +250,10 @@
 		scopeSpecificProperties: func(module *SdkLibrary) *ApiScopeProperties {
 			return &module.sdkLibraryProperties.System
 		},
-		apiFilePrefix:  "system-",
-		moduleSuffix:   ".system",
-		sdkVersion:     "system_current",
-		droidstubsArgs: []string{"-showAnnotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"},
+		apiFilePrefix: "system-",
+		moduleSuffix:  ".system",
+		sdkVersion:    "system_current",
+		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS)",
 	})
 	apiScopeTest = initApiScope(&apiScope{
 		name:                "test",
@@ -262,11 +262,11 @@
 		scopeSpecificProperties: func(module *SdkLibrary) *ApiScopeProperties {
 			return &module.sdkLibraryProperties.Test
 		},
-		apiFilePrefix:  "test-",
-		moduleSuffix:   ".test",
-		sdkVersion:     "test_current",
-		droidstubsArgs: []string{"-showAnnotation android.annotation.TestApi"},
-		unstable:       true,
+		apiFilePrefix: "test-",
+		moduleSuffix:  ".test",
+		sdkVersion:    "test_current",
+		annotation:    "android.annotation.TestApi",
+		unstable:      true,
 	})
 	apiScopeModuleLib = initApiScope(&apiScope{
 		name:    "module-lib",
@@ -283,10 +283,7 @@
 		apiFilePrefix: "module-lib-",
 		moduleSuffix:  ".module_lib",
 		sdkVersion:    "module_current",
-		droidstubsArgs: []string{
-			"--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
-			"--show-for-stub-purposes-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
-		},
+		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)",
 	})
 	apiScopeSystemServer = initApiScope(&apiScope{
 		name:    "system-server",
@@ -303,11 +300,11 @@
 		apiFilePrefix: "system-server-",
 		moduleSuffix:  ".system_server",
 		sdkVersion:    "system_server_current",
-		droidstubsArgs: []string{
-			"--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\) ",
-			"--hide-annotation android.annotation.Hide",
+		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.SYSTEM_SERVER)",
+		extraArgs: []string{
+			"--hide-annotation", "android.annotation.Hide",
 			// com.android.* classes are okay in this interface"
-			"--hide InternalClasses",
+			"--hide", "InternalClasses",
 		},
 	})
 	allApiScopes = apiScopes{
@@ -987,16 +984,8 @@
 		// Add dependencies to the stubs library
 		ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
 
-		// If the stubs source and API cannot be generated together then add an additional dependency on
-		// the API module.
-		if apiScope.createStubsSourceAndApiTogether {
-			// Add a dependency on the stubs source in order to access both stubs source and api information.
-			ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
-		} else {
-			// Add separate dependencies on the creators of the stubs source files and the API.
-			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, module.stubsSourceModuleName(apiScope))
-			ctx.AddVariationDependencies(nil, apiScope.apiFileTag, module.apiModuleName(apiScope))
-		}
+		// Add a dependency on the stubs source in order to access both stubs source and api information.
+		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
 	}
 
 	if module.requiresRuntimeImplementationLibrary() {
@@ -1199,7 +1188,7 @@
 
 // Creates a droidstubs module that creates stubs source files from the given full source
 // files and also updates and checks the API specification files.
-func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope, name string, createStubSources, createApi bool, scopeSpecificDroidstubsArgs []string) {
+func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
 	props := struct {
 		Name                             *string
 		Visibility                       []string
@@ -1286,64 +1275,57 @@
 	}
 	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
 
-	if !createStubSources {
-		// Stubs are not required.
-		props.Generate_stubs = proptools.BoolPtr(false)
-	}
-
 	// Add in scope specific arguments.
 	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
 	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
 	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
 
-	if createApi {
-		// List of APIs identified from the provided source files are created. They are later
-		// compared against to the not-yet-released (a.k.a current) list of APIs and to the
-		// last-released (a.k.a numbered) list of API.
-		currentApiFileName := apiScope.apiFilePrefix + "current.txt"
-		removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
-		apiDir := module.getApiDir()
-		currentApiFileName = path.Join(apiDir, currentApiFileName)
-		removedApiFileName = path.Join(apiDir, removedApiFileName)
+	// List of APIs identified from the provided source files are created. They are later
+	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
+	// last-released (a.k.a numbered) list of API.
+	currentApiFileName := apiScope.apiFilePrefix + "current.txt"
+	removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
+	apiDir := module.getApiDir()
+	currentApiFileName = path.Join(apiDir, currentApiFileName)
+	removedApiFileName = path.Join(apiDir, removedApiFileName)
 
-		// check against the not-yet-release API
-		props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
-		props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
+	// check against the not-yet-release API
+	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
+	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
 
-		if !apiScope.unstable {
-			// check against the latest released API
-			latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
-			props.Check_api.Last_released.Api_file = latestApiFilegroupName
-			props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
-				module.latestRemovedApiFilegroupName(apiScope))
-			props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
+	if !apiScope.unstable {
+		// check against the latest released API
+		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+		props.Check_api.Last_released.Api_file = latestApiFilegroupName
+		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+			module.latestRemovedApiFilegroupName(apiScope))
+		props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
 
-			if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
-				// Enable api lint.
-				props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
-				props.Check_api.Api_lint.New_since = latestApiFilegroupName
+		if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
+			// Enable api lint.
+			props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
+			props.Check_api.Api_lint.New_since = latestApiFilegroupName
 
-				// If it exists then pass a lint-baseline.txt through to droidstubs.
-				baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
-				baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
-				paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
-				if err != nil {
-					mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
-				}
-				if len(paths) == 1 {
-					props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
-				} else if len(paths) != 0 {
-					mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
-				}
+			// If it exists then pass a lint-baseline.txt through to droidstubs.
+			baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
+			baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
+			paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
+			if err != nil {
+				mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
+			}
+			if len(paths) == 1 {
+				props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
+			} else if len(paths) != 0 {
+				mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
 			}
 		}
+	}
 
-		// Dist the api txt artifact for sdk builds.
-		if !Bool(module.sdkLibraryProperties.No_dist) {
-			props.Dist.Targets = []string{"sdk", "win_sdk"}
-			props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.BaseModuleName()))
-			props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
-		}
+	// Dist the api txt artifact for sdk builds.
+	if !Bool(module.sdkLibraryProperties.No_dist) {
+		props.Dist.Targets = []string{"sdk", "win_sdk"}
+		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.BaseModuleName()))
+		props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
 	}
 
 	mctx.CreateModule(DroidstubsFactory, &props)
@@ -1526,22 +1508,8 @@
 	}
 
 	for _, scope := range generatedScopes {
-		stubsSourceArgs := scope.droidstubsArgsForGeneratingStubsSource
-		stubsSourceModuleName := module.stubsSourceModuleName(scope)
-
-		// If the args needed to generate the stubs and API are the same then they
-		// can be generated in a single invocation of metalava, otherwise they will
-		// need separate invocations.
-		if scope.createStubsSourceAndApiTogether {
-			// Use the stubs source name for legacy reasons.
-			module.createStubsSourcesAndApi(mctx, scope, stubsSourceModuleName, true, true, stubsSourceArgs)
-		} else {
-			module.createStubsSourcesAndApi(mctx, scope, stubsSourceModuleName, true, false, stubsSourceArgs)
-
-			apiArgs := scope.droidstubsArgsForGeneratingApi
-			apiName := module.apiModuleName(scope)
-			module.createStubsSourcesAndApi(mctx, scope, apiName, false, true, apiArgs)
-		}
+		// Use the stubs source name for legacy reasons.
+		module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs)
 
 		module.createStubsLibrary(mctx, scope)
 	}
@@ -2028,6 +1996,15 @@
 }
 
 // to satisfy apex.javaDependency interface
+func (module *SdkLibraryImport) LintDepSets() LintDepSets {
+	if module.implLibraryModule == nil {
+		return LintDepSets{}
+	} else {
+		return module.implLibraryModule.LintDepSets()
+	}
+}
+
+// to satisfy apex.javaDependency interface
 func (module *SdkLibraryImport) Stem() string {
 	return module.BaseModuleName()
 }
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 1f23b14..395da79 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -49,8 +49,8 @@
 	}{
 		{
 			name:           "default",
-			bootclasspath:  config.LegacyCorePlatformBootclasspathLibraries,
-			system:         config.LegacyCorePlatformSystemModules,
+			bootclasspath:  config.StableCorePlatformBootclasspathLibraries,
+			system:         config.StableCorePlatformSystemModules,
 			java8classpath: config.FrameworkLibraries,
 			java9classpath: config.FrameworkLibraries,
 			aidl:           "-Iframework/aidl",
@@ -58,16 +58,16 @@
 		{
 			name:           `sdk_version:"core_platform"`,
 			properties:     `sdk_version:"core_platform"`,
-			bootclasspath:  config.LegacyCorePlatformBootclasspathLibraries,
-			system:         config.LegacyCorePlatformSystemModules,
+			bootclasspath:  config.StableCorePlatformBootclasspathLibraries,
+			system:         config.StableCorePlatformSystemModules,
 			java8classpath: []string{},
 			aidl:           "",
 		},
 		{
 			name:           "blank sdk version",
 			properties:     `sdk_version: "",`,
-			bootclasspath:  config.LegacyCorePlatformBootclasspathLibraries,
-			system:         config.LegacyCorePlatformSystemModules,
+			bootclasspath:  config.StableCorePlatformBootclasspathLibraries,
+			system:         config.StableCorePlatformSystemModules,
 			java8classpath: config.FrameworkLibraries,
 			java9classpath: config.FrameworkLibraries,
 			aidl:           "-Iframework/aidl",
@@ -155,9 +155,9 @@
 		{
 
 			name:           "nostdlib system_modules",
-			properties:     `sdk_version: "none", system_modules: "legacy-core-platform-api-stubs-system-modules"`,
-			system:         "legacy-core-platform-api-stubs-system-modules",
-			bootclasspath:  []string{"legacy-core-platform-api-stubs-system-modules-lib"},
+			properties:     `sdk_version: "none", system_modules: "stable-core-platform-api-stubs-system-modules"`,
+			system:         "stable-core-platform-api-stubs-system-modules",
+			bootclasspath:  []string{"stable-core-platform-api-stubs-system-modules-lib"},
 			java8classpath: []string{},
 		},
 		{
diff --git a/java/testing.go b/java/testing.go
index 94f054e..e761743 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -136,7 +136,7 @@
 				name: "%s",
 				srcs: ["a.java"],
 				sdk_version: "none",
-				system_modules: "legacy-core-platform-api-stubs-system-modules",
+				system_modules: "stable-core-platform-api-stubs-system-modules",
 			}
 		`, extra)
 	}
@@ -146,7 +146,7 @@
 			name: "framework",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
 			aidl: {
 				export_include_dirs: ["framework/aidl"],
 			},
@@ -161,7 +161,7 @@
 			name: "android.hidl.base-V1.0-java",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
 
@@ -169,7 +169,7 @@
 			name: "android.hidl.manager-V1.0-java",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
 
@@ -177,7 +177,7 @@
 			name: "org.apache.http.legacy",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
 
@@ -185,7 +185,7 @@
 			name: "android.test.base",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
   
@@ -193,7 +193,7 @@
 			name: "android.test.mock",
 			srcs: ["a.java"],
 			sdk_version: "none",
-			system_modules: "legacy-core-platform-api-stubs-system-modules",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
 			installable: true,
 		}
 	`
diff --git a/rust/Android.bp b/rust/Android.bp
index d56de87..e03bf4f 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -10,6 +10,7 @@
     srcs: [
         "androidmk.go",
         "binary.go",
+        "bindgen.go",
         "builder.go",
         "clippy.go",
         "compiler.go",
@@ -19,11 +20,14 @@
         "proc_macro.go",
         "project_json.go",
         "rust.go",
+        "source_provider.go",
         "test.go",
         "testing.go",
     ],
     testSrcs: [
         "binary_test.go",
+        "bindgen_test.go",
+        "builder_test.go",
         "clippy_test.go",
         "compiler_test.go",
         "coverage_test.go",
diff --git a/rust/androidmk.go b/rust/androidmk.go
index aea899b..babbf91 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -55,6 +55,7 @@
 	ret := android.AndroidMkData{
 		OutputFile: mod.outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
+		SubName:    mod.subName,
 		Extra: []android.AndroidMkExtraFunc{
 			func(w io.Writer, outputFile android.Path) {
 				if len(mod.Properties.AndroidMkRlibs) > 0 {
@@ -75,9 +76,11 @@
 			},
 		},
 	}
-
-	mod.subAndroidMk(&ret, mod.compiler)
-
+	if mod.compiler != nil {
+		mod.subAndroidMk(&ret, mod.compiler)
+	} else if mod.sourceProvider != nil {
+		mod.subAndroidMk(&ret, mod.sourceProvider)
+	}
 	ret.SubName += mod.Properties.SubName
 
 	return ret
@@ -155,6 +158,25 @@
 
 }
 
+func (sourceProvider *baseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	outFile := sourceProvider.outputFile
+	ret.Class = "ETC"
+	ret.OutputFile = android.OptionalPathForPath(outFile)
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		_, file := filepath.Split(outFile.String())
+		stem, suffix, _ := android.SplitFileExt(file)
+		fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
+		fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+	})
+}
+
+func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	ctx.subAndroidMk(ret, bindgen.baseSourceProvider)
+	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+		fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+	})
+}
+
 func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	// Soong installation is only supported for host modules. Have Make
 	// installation trigger Soong installation.
diff --git a/rust/binary.go b/rust/binary.go
index 9fc52cd..1e9e119 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -106,8 +106,7 @@
 func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
 
-	srcPath, paths := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
-	deps.SrcDeps = paths
+	srcPath, _ := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
 
 	outputFile := android.PathForModuleOut(ctx, fileName)
 	binary.unstrippedOutputFile = outputFile
diff --git a/rust/bindgen.go b/rust/bindgen.go
new file mode 100644
index 0000000..83ad560
--- /dev/null
+++ b/rust/bindgen.go
@@ -0,0 +1,182 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"github.com/google/blueprint"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/cc"
+	ccConfig "android/soong/cc/config"
+)
+
+var (
+	defaultBindgenFlags = []string{""}
+
+	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
+	bindgenClangVersion  = "clang-r383902c"
+	bindgenLibClangSoGit = "11git"
+
+	//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
+	_ = pctx.SourcePathVariable("bindgenCmd", "out/host/${config.HostPrebuiltTag}/bin/bindgen")
+	_ = pctx.SourcePathVariable("bindgenClang",
+		"${ccConfig.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/bin/clang")
+	_ = pctx.SourcePathVariable("bindgenLibClang",
+		"${ccConfig.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/lib64/libclang.so."+bindgenLibClangSoGit)
+
+	//TODO(ivanlozano) Switch this to RuleBuilder
+	bindgen = pctx.AndroidStaticRule("bindgen",
+		blueprint.RuleParams{
+			Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " +
+				"$bindgenCmd $flags $in -o $out -- $cflags",
+			CommandDeps: []string{"$bindgenCmd"},
+		},
+		"flags", "cflags")
+)
+
+func init() {
+	android.RegisterModuleType("rust_bindgen", RustBindgenFactory)
+	android.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory)
+}
+
+var _ SourceProvider = (*bindgenDecorator)(nil)
+
+type BindgenProperties struct {
+	// The wrapper header file
+	Wrapper_src *string `android:"path,arch_variant"`
+
+	// list of bindgen-specific flags and options
+	Flags []string `android:"arch_variant"`
+
+	// list of clang flags required to correctly interpret the headers.
+	Cflags []string `android:"arch_variant"`
+
+	// list of directories relative to the Blueprints file that will
+	// be added to the include path using -I
+	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+
+	// list of static libraries that provide headers for this binding.
+	Static_libs []string `android:"arch_variant,variant_prepend"`
+
+	// list of shared libraries that provide headers for this binding.
+	Shared_libs []string `android:"arch_variant"`
+
+	//TODO(b/161141999) Add support for headers from cc_library_header modules.
+}
+
+type bindgenDecorator struct {
+	*baseSourceProvider
+
+	Properties BindgenProperties
+}
+
+func (b *bindgenDecorator) libraryExports(ctx android.ModuleContext) (android.Paths, []string) {
+	var libraryPaths android.Paths
+	var libraryFlags []string
+
+	for _, static_lib := range b.Properties.Static_libs {
+		if dep, ok := ctx.GetDirectDepWithTag(static_lib, cc.StaticDepTag).(*cc.Module); ok {
+			libraryPaths = append(libraryPaths, dep.ExportedIncludeDirs()...)
+			libraryFlags = append(libraryFlags, dep.ExportedFlags()...)
+		}
+	}
+	for _, shared_lib := range b.Properties.Shared_libs {
+		if dep, ok := ctx.GetDirectDepWithTag(shared_lib, cc.SharedDepTag).(*cc.Module); ok {
+			libraryPaths = append(libraryPaths, dep.ExportedIncludeDirs()...)
+			libraryFlags = append(libraryFlags, dep.ExportedFlags()...)
+		}
+	}
+
+	return libraryPaths, libraryFlags
+}
+
+func (b *bindgenDecorator) generateSource(ctx android.ModuleContext) android.Path {
+	ccToolchain := ccConfig.FindToolchain(ctx.Os(), ctx.Arch())
+	includes, exportedFlags := b.libraryExports(ctx)
+
+	var cflags []string
+	cflags = append(cflags, b.Properties.Cflags...)
+	cflags = append(cflags, "-target "+ccToolchain.ClangTriple())
+	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainClangCflags(), "${config.", "${ccConfig."))
+	cflags = append(cflags, exportedFlags...)
+	for _, include := range includes {
+		cflags = append(cflags, "-I"+include.String())
+	}
+	for _, include := range b.Properties.Local_include_dirs {
+		cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
+	}
+
+	bindgenFlags := defaultBindgenFlags
+	bindgenFlags = append(bindgenFlags, strings.Join(b.Properties.Flags, " "))
+
+	wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src)
+	if !wrapperFile.Valid() {
+		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
+	}
+
+	outputFile := android.PathForModuleOut(ctx, b.baseSourceProvider.getStem(ctx)+".rs")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        bindgen,
+		Description: "bindgen " + wrapperFile.Path().Rel(),
+		Output:      outputFile,
+		Input:       wrapperFile.Path(),
+		Implicits:   includes,
+		Args: map[string]string{
+			"flags":  strings.Join(bindgenFlags, " "),
+			"cflags": strings.Join(cflags, " "),
+		},
+	})
+	b.baseSourceProvider.outputFile = outputFile
+	return outputFile
+}
+
+func (b *bindgenDecorator) sourceProviderProps() []interface{} {
+	return append(b.baseSourceProvider.sourceProviderProps(),
+		&b.Properties)
+}
+
+// rust_bindgen generates Rust FFI bindings to C libraries using bindgen given a wrapper header as the primary input.
+// Bindgen has a number of flags to control the generated source, and additional flags can be passed to clang to ensure
+// the header and generated source is appropriately handled.
+func RustBindgenFactory() android.Module {
+	module, _ := NewRustBindgen(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func RustBindgenHostFactory() android.Module {
+	module, _ := NewRustBindgen(android.HostSupported)
+	return module.Init()
+}
+
+func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) {
+	module := newModule(hod, android.MultilibBoth)
+
+	bindgen := &bindgenDecorator{
+		baseSourceProvider: NewSourceProvider(),
+		Properties:         BindgenProperties{},
+	}
+	module.sourceProvider = bindgen
+
+	return module, bindgen
+}
+
+func (b *bindgenDecorator) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
+	deps = b.baseSourceProvider.sourceProviderDeps(ctx, deps)
+	deps.SharedLibs = append(deps.SharedLibs, b.Properties.Shared_libs...)
+	deps.StaticLibs = append(deps.StaticLibs, b.Properties.Static_libs...)
+	return deps
+}
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
new file mode 100644
index 0000000..18e188f
--- /dev/null
+++ b/rust/bindgen_test.go
@@ -0,0 +1,56 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestRustBindgen(t *testing.T) {
+	ctx := testRust(t, `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			stem: "bindings",
+			flags: ["--bindgen-flag"],
+			cflags: ["--clang-flag"],
+			shared_libs: ["libfoo_shared"],
+			static_libs: ["libfoo_static"],
+		}
+		cc_library_shared {
+			name: "libfoo_shared",
+			export_include_dirs: ["shared_include"],
+		}
+		cc_library_static {
+			name: "libfoo_static",
+			export_include_dirs: ["static_include"],
+		}
+
+	`)
+	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a").Output("bindings.rs")
+	if !strings.Contains(libbindgen.Args["flags"], "--bindgen-flag") {
+		t.Errorf("missing bindgen flags in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
+	}
+	if !strings.Contains(libbindgen.Args["cflags"], "--clang-flag") {
+		t.Errorf("missing clang cflags in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
+	}
+	if !strings.Contains(libbindgen.Args["cflags"], "-Ishared_include") {
+		t.Errorf("missing clang cflags in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
+	}
+	if !strings.Contains(libbindgen.Args["cflags"], "-Istatic_include") {
+		t.Errorf("missing clang cflags in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
+	}
+}
diff --git a/rust/builder.go b/rust/builder.go
index 7f94bb5..45cd268 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -28,7 +28,7 @@
 	_     = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
 	rustc = pctx.AndroidStaticRule("rustc",
 		blueprint.RuleParams{
-			Command: "$rustcCmd " +
+			Command: "$envVars $rustcCmd " +
 				"-C linker=${config.RustLinker} " +
 				"-C link-args=\"${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}\" " +
 				"--emit link -o $out --emit dep-info=$out.d $in ${libFlags} $rustcFlags",
@@ -37,19 +37,19 @@
 			Deps:    blueprint.DepsGCC,
 			Depfile: "$out.d",
 		},
-		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd")
+		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
 
 	_            = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
 	clippyDriver = pctx.AndroidStaticRule("clippy",
 		blueprint.RuleParams{
-			Command: "$clippyCmd " +
+			Command: "$envVars $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")
+		"rustcFlags", "libFlags", "clippyFlags", "envVars")
 
 	zip = pctx.AndroidStaticRule("zip",
 		blueprint.RuleParams{
@@ -58,6 +58,14 @@
 			Rspfile:        "$out.rsp",
 			RspfileContent: "$in",
 		})
+
+	cp = pctx.AndroidStaticRule("cp",
+		blueprint.RuleParams{
+			Command:        "cp `cat $outDir.rsp` $outDir",
+			Rspfile:        "${outDir}.rsp",
+			RspfileContent: "$in",
+		},
+		"outDir")
 )
 
 type buildOutput struct {
@@ -116,6 +124,7 @@
 
 	var inputs android.Paths
 	var implicits android.Paths
+	var envVars []string
 	var output buildOutput
 	var libFlags, rustcFlags, linkFlags []string
 	var implicitOutputs android.WritablePaths
@@ -166,7 +175,7 @@
 	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
 	implicits = append(implicits, deps.StaticLibs...)
 	implicits = append(implicits, deps.SharedLibs...)
-	implicits = append(implicits, deps.SrcDeps...)
+
 	if deps.CrtBegin.Valid() {
 		implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
 	}
@@ -190,6 +199,32 @@
 		output.coverageFile = gcnoFile
 	}
 
+	if len(deps.SrcDeps) > 0 {
+		genSubDir := "out/"
+		moduleGenDir := android.PathForModuleOut(ctx, genSubDir)
+		var outputs android.WritablePaths
+
+		for _, genSrc := range deps.SrcDeps {
+			if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) {
+				ctx.PropertyErrorf("srcs",
+					"multiple source providers generate the same filename output: "+genSrc.Base())
+			}
+			outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base()))
+		}
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        cp,
+			Description: "cp " + moduleGenDir.Rel(),
+			Outputs:     outputs,
+			Inputs:      deps.SrcDeps,
+			Args: map[string]string{
+				"outDir": moduleGenDir.String(),
+			},
+		})
+		implicits = append(implicits, outputs.Paths()...)
+		envVars = append(envVars, "OUT_DIR=$$PWD/"+moduleGenDir.String())
+	}
+
 	if flags.Clippy {
 		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
 		ctx.Build(pctx, android.BuildParams{
@@ -203,6 +238,7 @@
 				"rustcFlags":  strings.Join(rustcFlags, " "),
 				"libFlags":    strings.Join(libFlags, " "),
 				"clippyFlags": strings.Join(flags.ClippyFlags, " "),
+				"envVars":     strings.Join(envVars, " "),
 			},
 		})
 		// Declare the clippy build as an implicit dependency of the original crate.
@@ -222,6 +258,7 @@
 			"libFlags":   strings.Join(libFlags, " "),
 			"crtBegin":   deps.CrtBegin.String(),
 			"crtEnd":     deps.CrtEnd.String(),
+			"envVars":    strings.Join(envVars, " "),
 		},
 	})
 
diff --git a/rust/builder_test.go b/rust/builder_test.go
new file mode 100644
index 0000000..04b67d9
--- /dev/null
+++ b/rust/builder_test.go
@@ -0,0 +1,40 @@
+// 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 TestSourceProviderCollision(t *testing.T) {
+	testRustError(t, "multiple source providers generate the same filename output: bindings.rs", `
+		rust_binary {
+			name: "source_collider",
+			srcs: [
+				"foo.rs",
+				":libbindings1",
+				":libbindings2",
+			],
+		}
+		rust_bindgen {
+			name: "libbindings1",
+			stem: "bindings",
+			wrapper_src: "src/any.h",
+		}
+		rust_bindgen {
+			name: "libbindings2",
+			stem: "bindings",
+			wrapper_src: "src/any.h",
+		}
+	`)
+}
diff --git a/rust/compiler.go b/rust/compiler.go
index c20179b..ab3d2f4 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -46,6 +46,8 @@
 const (
 	InstallInSystem installLocation = 0
 	InstallInData                   = iota
+
+	incorrectSourcesError = "srcs can only contain one path for a rust file and source providers prefixed by \":\""
 )
 
 type BaseCompilerProperties struct {
@@ -253,6 +255,7 @@
 	return String(compiler.Properties.Relative_install_path)
 }
 
+// Returns the Path for the main source file along with Paths for generated source files from modules listed in srcs.
 func srcPathFromModuleSrcs(ctx ModuleContext, srcs []string) (android.Path, android.Paths) {
 	// The srcs can contain strings with prefix ":".
 	// They are dependent modules of this module, with android.SourceDepTag.
@@ -266,11 +269,11 @@
 		}
 	}
 	if numSrcs != 1 {
-		ctx.PropertyErrorf("srcs", "srcs can only contain one path for a rust file")
+		ctx.PropertyErrorf("srcs", incorrectSourcesError)
 	}
 	if srcIndex != 0 {
 		ctx.PropertyErrorf("srcs", "main source file must be the first in srcs")
 	}
 	paths := android.PathsForModuleSrc(ctx, srcs)
-	return paths[srcIndex], paths
+	return paths[srcIndex], paths[1:]
 }
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 58ca52a..b853196 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -43,7 +43,7 @@
 // Test that we reject multiple source files.
 func TestEnforceSingleSourceFile(t *testing.T) {
 
-	singleSrcError := "srcs can only contain one path for a rust file"
+	singleSrcError := "srcs can only contain one path for a rust file and source providers prefixed by \":\""
 
 	// Test libraries
 	testRustError(t, singleSrcError, `
diff --git a/rust/library.go b/rust/library.go
index d718eb8..ac725d7 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -368,8 +368,7 @@
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	var outputFile android.WritablePath
 
-	srcPath, paths := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
-	deps.SrcDeps = paths
+	srcPath, _ := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 3b4f40a..3d081c1 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -96,7 +96,9 @@
 	prebuilt.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
 
 	srcPath, paths := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
-	deps.SrcDeps = paths
+	if len(paths) > 0 {
+		ctx.PropertyErrorf("srcs", "prebuilt libraries can only have one entry in srcs (the prebuilt path)")
+	}
 
 	prebuilt.unstrippedOutputFile = srcPath
 
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 49dbd8d..3dd2521 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -65,8 +65,7 @@
 	fileName := procMacro.getStem(ctx) + ctx.toolchain().ProcMacroSuffix()
 	outputFile := android.PathForModuleOut(ctx, fileName)
 
-	srcPath, paths := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
-	deps.SrcDeps = paths
+	srcPath, _ := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
 
 	procMacro.unstrippedOutputFile = outputFile
 
diff --git a/rust/rust.go b/rust/rust.go
index 7a98c64..89b89e2 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -42,6 +42,7 @@
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
+	pctx.ImportAs("ccConfig", "android/soong/cc/config")
 }
 
 type Flags struct {
@@ -61,9 +62,10 @@
 	AndroidMkProcMacroLibs []string
 	AndroidMkSharedLibs    []string
 	AndroidMkStaticLibs    []string
-	SubName                string `blueprint:"mutated"`
-	PreventInstall         bool
-	HideFromMake           bool
+
+	SubName        string `blueprint:"mutated"`
+	PreventInstall bool
+	HideFromMake   bool
 }
 
 type Module struct {
@@ -79,8 +81,27 @@
 	coverage         *coverage
 	clippy           *clippy
 	cachedToolchain  config.Toolchain
+	sourceProvider   SourceProvider
 	subAndroidMkOnce map[subAndroidMkProvider]bool
 	outputFile       android.OptionalPath
+
+	subName string
+}
+
+func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		if mod.sourceProvider != nil {
+			return mod.sourceProvider.Srcs(), nil
+		} else {
+			if mod.outputFile.Valid() {
+				return android.Paths{mod.outputFile.Path()}, nil
+			}
+			return android.Paths{}, nil
+		}
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
 }
 
 var _ android.ImageInterface = (*Module)(nil)
@@ -348,6 +369,7 @@
 		&LibraryCompilerProperties{},
 		&ProcMacroCompilerProperties{},
 		&PrebuiltProperties{},
+		&SourceProviderProperties{},
 		&TestProperties{},
 		&cc.CoverageProperties{},
 		&ClippyProperties{},
@@ -507,6 +529,9 @@
 	if mod.clippy != nil {
 		mod.AddProperties(mod.clippy.props()...)
 	}
+	if mod.sourceProvider != nil {
+		mod.AddProperties(mod.sourceProvider.sourceProviderProps()...)
+	}
 
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
 
@@ -645,6 +670,10 @@
 		if !mod.Properties.PreventInstall {
 			mod.compiler.install(ctx, mod.outputFile.Path())
 		}
+	} else if mod.sourceProvider != nil {
+		outputFile := mod.sourceProvider.generateSource(ctx)
+		mod.outputFile = android.OptionalPathForPath(outputFile)
+		mod.subName = ctx.ModuleSubDir()
 	}
 }
 
@@ -653,6 +682,8 @@
 
 	if mod.compiler != nil {
 		deps = mod.compiler.compilerDeps(ctx, deps)
+	} else if mod.sourceProvider != nil {
+		deps = mod.sourceProvider.sourceProviderDeps(ctx, deps)
 	}
 
 	if mod.coverage != nil {
@@ -712,6 +743,8 @@
 	directProcMacroDeps := []*Module{}
 	directSharedLibDeps := [](cc.LinkableInterface){}
 	directStaticLibDeps := [](cc.LinkableInterface){}
+	directSrcProvidersDeps := []*Module{}
+	directSrcDeps := [](android.SourceFileProducer){}
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
@@ -745,6 +778,24 @@
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, depName)
+			case android.SourceDepTag:
+				// Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct
+				// OS/Arch variant is used.
+				var helper string
+				if ctx.Host() {
+					helper = "missing 'host_supported'?"
+				} else {
+					helper = "device module defined?"
+				}
+
+				if dep.Target().Os != ctx.Os() {
+					ctx.ModuleErrorf("OS mismatch on dependency %q (%s)", dep.Name(), helper)
+					return
+				} else if dep.Target().Arch.ArchType != ctx.Arch().ArchType {
+					ctx.ModuleErrorf("Arch mismatch on dependency %q (%s)", dep.Name(), helper)
+					return
+				}
+				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
 			}
 
 			//Append the dependencies exportedDirs
@@ -762,6 +813,14 @@
 
 		}
 
+		if srcDep, ok := dep.(android.SourceFileProducer); ok {
+			switch depTag {
+			case android.SourceDepTag:
+				// These are usually genrules which don't have per-target variants.
+				directSrcDeps = append(directSrcDeps, srcDep)
+			}
+		}
+
 		if ccDep, ok := dep.(cc.LinkableInterface); ok {
 			//Handle C dependencies
 			if _, ok := ccDep.(*Module); !ok {
@@ -837,16 +896,26 @@
 		sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
 	}
 
+	var srcProviderDepFiles android.Paths
+	for _, dep := range directSrcProvidersDeps {
+		srcs, _ := dep.OutputFiles("")
+		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
+	}
+	for _, dep := range directSrcDeps {
+		srcs := dep.Srcs()
+		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
+	}
+
 	depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
 	depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
 	depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
 	depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
 	depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
+	depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
 
 	// Dedup exported flags from dependencies
 	depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
 	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
-	depPaths.SrcDeps = android.FirstUniquePaths(depPaths.SrcDeps)
 
 	return depPaths
 }
@@ -963,3 +1032,5 @@
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
 var StringPtr = proptools.StringPtr
+
+var _ android.OutputFileProducer = (*Module)(nil)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index e803925..b3bbddb 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -182,7 +182,7 @@
 		}
 		rust_library_host_rlib {
 			name: "librlib",
-			srcs: ["foo.rs", ":my_generator"],
+			srcs: ["foo.rs"],
 			crate_name: "rlib",
 		}
 		rust_proc_macro {
@@ -190,38 +190,17 @@
 			srcs: ["foo.rs"],
 			crate_name: "pm",
 		}
-		genrule {
-			name: "my_generator",
-			tools: ["any_rust_binary"],
-			cmd: "$(location) -o $(out) $(in)",
-			srcs: ["src/any.h"],
-			out: ["src/any.rs"],
-		}
 		rust_binary_host {
-			name: "fizz-buzz-dep",
+			name: "fizz-buzz",
 			dylibs: ["libdylib"],
 			rlibs: ["librlib"],
 			proc_macros: ["libpm"],
 			static_libs: ["libstatic"],
 			shared_libs: ["libshared"],
-			srcs: [
-				"foo.rs",
-				":my_generator",
-			],
+			srcs: ["foo.rs"],
 		}
 	`)
-	module := ctx.ModuleForTests("fizz-buzz-dep", "linux_glibc_x86_64").Module().(*Module)
-	rlibmodule := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib").Module().(*Module)
-
-	srcs := module.compiler.(*binaryDecorator).baseCompiler.Properties.Srcs
-	if len(srcs) != 2 || !android.InList(":my_generator", srcs) {
-		t.Errorf("missing module dependency in fizz-buzz)")
-	}
-
-	srcs = rlibmodule.compiler.(*libraryDecorator).baseCompiler.Properties.Srcs
-	if len(srcs) != 2 || !android.InList(":my_generator", srcs) {
-		t.Errorf("missing module dependency in rlib")
-	}
+	module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
 
 	// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
 	if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
@@ -245,6 +224,93 @@
 	}
 }
 
+func TestSourceProviderDeps(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz-buzz-dep",
+			srcs: [
+				"foo.rs",
+				":my_generator",
+				":libbindings",
+			],
+		}
+		rust_proc_macro {
+			name: "libprocmacro",
+			srcs: [
+				"foo.rs",
+				":my_generator",
+				":libbindings",
+			],
+			crate_name: "procmacro",
+		}
+		rust_library {
+			name: "libfoo",
+			srcs: [
+				"foo.rs",
+				":my_generator",
+				":libbindings"],
+			crate_name: "foo",
+		}
+		genrule {
+			name: "my_generator",
+			tools: ["any_rust_binary"],
+			cmd: "$(location) -o $(out) $(in)",
+			srcs: ["src/any.h"],
+			out: ["src/any.rs"],
+		}
+		rust_bindgen {
+			name: "libbindings",
+			stem: "bindings",
+			host_supported: true,
+			wrapper_src: "src/any.h",
+        }
+	`)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib").Rule("rustc")
+	if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") {
+		t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
+	}
+	if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/any.rs") {
+		t.Errorf("genrule generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
+	}
+
+	fizzBuzz := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc")
+	if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/bindings.rs") {
+		t.Errorf("rust_bindgen generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
+	}
+	if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/any.rs") {
+		t.Errorf("genrule generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
+	}
+
+	libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc")
+	if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/bindings.rs") {
+		t.Errorf("rust_bindgen generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
+	}
+	if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/any.rs") {
+		t.Errorf("genrule generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
+	}
+}
+
+func TestSourceProviderTargetMismatch(t *testing.T) {
+	// This might error while building the dependency tree or when calling depsToPaths() depending on the lunched
+	// target, which results in two different errors. So don't check the error, just confirm there is one.
+	testRustError(t, ".*", `
+		rust_proc_macro {
+			name: "libprocmacro",
+			srcs: [
+				"foo.rs",
+				":libbindings",
+			],
+			crate_name: "procmacro",
+		}
+		rust_bindgen {
+			name: "libbindings",
+			stem: "bindings",
+			wrapper_src: "src/any.h",
+		}
+	`)
+}
+
 // Test to make sure proc_macros use host variants when building device modules.
 func TestProcMacroDeviceDeps(t *testing.T) {
 	ctx := testRust(t, `
diff --git a/rust/source_provider.go b/rust/source_provider.go
new file mode 100644
index 0000000..e034d2c
--- /dev/null
+++ b/rust/source_provider.go
@@ -0,0 +1,70 @@
+// 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/android"
+)
+
+type SourceProviderProperties struct {
+	// sets name of the output
+	Stem *string `android:"arch_variant"`
+}
+
+type baseSourceProvider struct {
+	Properties SourceProviderProperties
+
+	outputFile       android.Path
+	subAndroidMkOnce map[subAndroidMkProvider]bool
+}
+
+var _ SourceProvider = (*baseSourceProvider)(nil)
+
+type SourceProvider interface {
+	generateSource(ctx android.ModuleContext) android.Path
+	Srcs() android.Paths
+	sourceProviderProps() []interface{}
+	sourceProviderDeps(ctx DepsContext, deps Deps) Deps
+}
+
+func (sp *baseSourceProvider) Srcs() android.Paths {
+	return android.Paths{sp.outputFile}
+}
+
+func (sp *baseSourceProvider) generateSource(ctx android.ModuleContext) android.Path {
+	panic("baseSourceProviderModule does not implement generateSource()")
+}
+
+func (sp *baseSourceProvider) sourceProviderProps() []interface{} {
+	return []interface{}{&sp.Properties}
+}
+
+func NewSourceProvider() *baseSourceProvider {
+	return &baseSourceProvider{
+		Properties: SourceProviderProperties{},
+	}
+}
+
+func (sp *baseSourceProvider) getStem(ctx android.ModuleContext) string {
+	stem := ctx.ModuleName()
+	if String(sp.Properties.Stem) != "" {
+		stem = String(sp.Properties.Stem)
+	}
+	return stem
+}
+
+func (sp *baseSourceProvider) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
+	return deps
+}
diff --git a/rust/testing.go b/rust/testing.go
index 430b40b..f2d4c5e 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -81,6 +81,7 @@
 	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
+	ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
 	ctx.RegisterModuleType("rust_test", RustTestFactory)
 	ctx.RegisterModuleType("rust_test_host", RustTestHostFactory)
 	ctx.RegisterModuleType("rust_library", RustLibraryFactory)
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 935d348..497f14b 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -73,12 +73,16 @@
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
 			native_shared_libs: ["sdkmember"],
 			compile_multilib: "64",
 		}
 
 		cc_library_shared {
 			name: "sdkmember",
+			device_supported: false,
+			host_supported: true,
 			srcs: ["Test.cpp"],
 			stl: "none",
 			compile_multilib: "64",
@@ -86,8 +90,52 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_sdkmember@current",
+    sdk_member_name: "sdkmember",
+    device_supported: false,
+    host_supported: true,
+    installable: false,
+    stl: "none",
+    compile_multilib: "64",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/lib/sdkmember.so"],
+        },
+    },
+}
+
+cc_prebuilt_library_shared {
+    name: "sdkmember",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "64",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/lib/sdkmember.so"],
+        },
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    native_shared_libs: ["mysdk_sdkmember@current"],
+    target: {
+        linux_glibc: {
+            compile_multilib: "64",
+        },
+    },
+}
+`),
 		checkAllCopyRules(`
-.intermediates/sdkmember/android_arm64_armv8-a_shared/sdkmember.so -> arm64/lib/sdkmember.so
+.intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> x86_64/lib/sdkmember.so
 `))
 }
 
@@ -271,6 +319,7 @@
     name: "mysdk_crtobj@current",
     sdk_member_name: "crtobj",
     stl: "none",
+    compile_multilib: "both",
     arch: {
         arm64: {
             srcs: ["arm64/lib/crtobj.o"],
@@ -285,6 +334,7 @@
     name: "crtobj",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     arch: {
         arm64: {
             srcs: ["arm64/lib/crtobj.o"],
@@ -378,6 +428,7 @@
     sdk_member_name: "mynativelib",
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -394,6 +445,7 @@
     name: "mynativelib",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -621,9 +673,9 @@
     host_supported: true,
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     static_executable: true,
     nocrt: true,
-    compile_multilib: "both",
     arch: {
         x86_64: {
             srcs: ["x86_64/bin/linker"],
@@ -640,9 +692,9 @@
     device_supported: false,
     host_supported: true,
     stl: "none",
+    compile_multilib: "both",
     static_executable: true,
     nocrt: true,
-    compile_multilib: "both",
     arch: {
         x86_64: {
             srcs: ["x86_64/bin/linker"],
@@ -702,6 +754,7 @@
     ],
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -723,6 +776,7 @@
         "apex2",
     ],
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -824,6 +878,7 @@
     sdk_member_name: "mynativelib",
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     shared_libs: [
         "mysdk_myothernativelib@current",
         "libc",
@@ -842,6 +897,7 @@
     name: "mynativelib",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     shared_libs: [
         "myothernativelib",
         "libc",
@@ -861,6 +917,7 @@
     sdk_member_name: "myothernativelib",
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     system_shared_libs: ["libm"],
     arch: {
         arm64: {
@@ -876,6 +933,7 @@
     name: "myothernativelib",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     system_shared_libs: ["libm"],
     arch: {
         arm64: {
@@ -892,6 +950,7 @@
     sdk_member_name: "mysystemnativelib",
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     arch: {
         arm64: {
             srcs: ["arm64/lib/mysystemnativelib.so"],
@@ -906,6 +965,7 @@
     name: "mysystemnativelib",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     arch: {
         arm64: {
             srcs: ["arm64/lib/mysystemnativelib.so"],
@@ -974,6 +1034,7 @@
     installable: false,
     sdk_version: "minimum",
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -994,6 +1055,7 @@
     host_supported: true,
     sdk_version: "minimum",
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -1070,12 +1132,18 @@
     installable: false,
     stl: "none",
     target: {
+        linux_glibc: {
+            compile_multilib: "both",
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
         },
         linux_glibc_x86: {
             srcs: ["linux_glibc/x86/lib/mynativelib.so"],
         },
+        windows: {
+            compile_multilib: "64",
+        },
         windows_x86_64: {
             srcs: ["windows/x86_64/lib/mynativelib.dll"],
         },
@@ -1089,12 +1157,18 @@
     host_supported: true,
     stl: "none",
     target: {
+        linux_glibc: {
+            compile_multilib: "both",
+        },
         linux_glibc_x86_64: {
             srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
         },
         linux_glibc_x86: {
             srcs: ["linux_glibc/x86/lib/mynativelib.so"],
         },
+        windows: {
+            compile_multilib: "64",
+        },
         windows_x86_64: {
             srcs: ["windows/x86_64/lib/mynativelib.dll"],
         },
@@ -1151,6 +1225,7 @@
     sdk_member_name: "mynativelib",
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -1168,6 +1243,7 @@
     name: "mynativelib",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -1236,6 +1312,7 @@
     host_supported: true,
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -1255,6 +1332,7 @@
     device_supported: false,
     host_supported: true,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -1315,6 +1393,7 @@
     sdk_member_name: "mynativelib",
     installable: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -1340,6 +1419,7 @@
     name: "mynativelib",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
     arch: {
         arm64: {
@@ -1416,6 +1496,7 @@
     host_supported: true,
     installable: false,
     stl: "none",
+    compile_multilib: "64",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -1431,6 +1512,7 @@
     device_supported: false,
     host_supported: true,
     stl: "none",
+    compile_multilib: "64",
     export_include_dirs: ["include/include"],
     arch: {
         x86_64: {
@@ -1483,6 +1565,7 @@
     name: "mysdk_mynativeheaders@current",
     sdk_member_name: "mynativeheaders",
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
 }
 
@@ -1490,6 +1573,7 @@
     name: "mynativeheaders",
     prefer: false,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
 }
 
@@ -1532,6 +1616,7 @@
     device_supported: false,
     host_supported: true,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
 }
 
@@ -1541,6 +1626,7 @@
     device_supported: false,
     host_supported: true,
     stl: "none",
+    compile_multilib: "both",
     export_include_dirs: ["include/include"],
 }
 
@@ -1590,6 +1676,7 @@
     sdk_member_name: "mynativeheaders",
     host_supported: true,
     stl: "none",
+    compile_multilib: "both",
     export_system_include_dirs: ["common_os/include/include"],
     target: {
         android: {
@@ -1606,6 +1693,7 @@
     prefer: false,
     host_supported: true,
     stl: "none",
+    compile_multilib: "both",
     export_system_include_dirs: ["common_os/include/include"],
     target: {
         android: {
@@ -1662,6 +1750,7 @@
     name: "mysdk_sslnil@current",
     sdk_member_name: "sslnil",
     installable: false,
+    compile_multilib: "both",
     arch: {
         arm64: {
             srcs: ["arm64/lib/sslnil.so"],
@@ -1675,6 +1764,7 @@
 cc_prebuilt_library_shared {
     name: "sslnil",
     prefer: false,
+    compile_multilib: "both",
     arch: {
         arm64: {
             srcs: ["arm64/lib/sslnil.so"],
@@ -1689,6 +1779,7 @@
     name: "mysdk_sslempty@current",
     sdk_member_name: "sslempty",
     installable: false,
+    compile_multilib: "both",
     system_shared_libs: [],
     arch: {
         arm64: {
@@ -1703,6 +1794,7 @@
 cc_prebuilt_library_shared {
     name: "sslempty",
     prefer: false,
+    compile_multilib: "both",
     system_shared_libs: [],
     arch: {
         arm64: {
@@ -1718,6 +1810,7 @@
     name: "mysdk_sslnonempty@current",
     sdk_member_name: "sslnonempty",
     installable: false,
+    compile_multilib: "both",
     system_shared_libs: ["mysdk_sslnil@current"],
     arch: {
         arm64: {
@@ -1732,6 +1825,7 @@
 cc_prebuilt_library_shared {
     name: "sslnonempty",
     prefer: false,
+    compile_multilib: "both",
     system_shared_libs: ["sslnil"],
     arch: {
         arm64: {
@@ -1780,6 +1874,7 @@
     sdk_member_name: "sslvariants",
     host_supported: true,
     installable: false,
+    compile_multilib: "both",
     target: {
         android: {
             system_shared_libs: [],
@@ -1803,6 +1898,7 @@
     name: "sslvariants",
     prefer: false,
     host_supported: true,
+    compile_multilib: "both",
     target: {
         android: {
             system_shared_libs: [],
@@ -1859,6 +1955,7 @@
     name: "mysdk_stubslib@current",
     sdk_member_name: "stubslib",
     installable: false,
+    compile_multilib: "both",
     stubs: {
         versions: ["3"],
     },
@@ -1875,6 +1972,7 @@
 cc_prebuilt_library_shared {
     name: "stubslib",
     prefer: false,
+    compile_multilib: "both",
     stubs: {
         versions: ["3"],
     },
@@ -1928,6 +2026,7 @@
     sdk_member_name: "stubslib",
     host_supported: true,
     installable: false,
+    compile_multilib: "both",
     stubs: {
         versions: ["3"],
     },
@@ -1951,6 +2050,7 @@
     name: "stubslib",
     prefer: false,
     host_supported: true,
+    compile_multilib: "both",
     stubs: {
         versions: ["3"],
     },
@@ -2003,6 +2103,7 @@
     host_supported: true,
     installable: false,
     unique_host_soname: true,
+    compile_multilib: "both",
     target: {
         android_arm64: {
             srcs: ["android/arm64/lib/mylib.so"],
@@ -2024,6 +2125,7 @@
     prefer: false,
     host_supported: true,
     unique_host_soname: true,
+    compile_multilib: "both",
     target: {
         android_arm64: {
             srcs: ["android/arm64/lib/mylib.so"],
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index aee04a1..a2198e9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -51,10 +51,10 @@
 	name: "core-current-stubs-system-modules",
 }
 java_system_modules_import {
-	name: "legacy-core-platform-api-stubs-system-modules",
+	name: "stable-core-platform-api-stubs-system-modules",
 }
 java_import {
-	name: "legacy.core.platform.api.stubs",
+	name: "stable.core.platform.api.stubs",
 }
 java_import {
 	name: "android_stubs_current",
@@ -1424,8 +1424,8 @@
 .intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
 .intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
 .intermediates/myjavalib.stubs.module_lib/android_common/javac/myjavalib.stubs.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
-.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
-.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
diff --git a/sdk/update.go b/sdk/update.go
index cf25008..b8d73c6 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -794,6 +794,17 @@
 	return !ok
 }
 
+// Add the properties from the given SdkMemberProperties to the blueprint
+// property set. This handles common properties in SdkMemberPropertiesBase and
+// calls the member-specific AddToPropertySet for the rest.
+func addSdkMemberPropertiesToSet(ctx *memberContext, memberProperties android.SdkMemberProperties, targetPropertySet android.BpPropertySet) {
+	if memberProperties.Base().Compile_multilib != "" {
+		targetPropertySet.AddProperty("compile_multilib", memberProperties.Base().Compile_multilib)
+	}
+
+	memberProperties.AddToPropertySet(ctx, targetPropertySet)
+}
+
 type sdkMemberRef struct {
 	memberType android.SdkMemberType
 	variant    android.SdkAware
@@ -1009,7 +1020,7 @@
 	}
 
 	// Add the os specific but arch independent properties to the module.
-	osInfo.Properties.AddToPropertySet(ctx, osPropertySet)
+	addSdkMemberPropertiesToSet(ctx, osInfo.Properties, osPropertySet)
 
 	// Add arch (and possibly os) specific sections for each set of arch (and possibly
 	// os) specific properties.
@@ -1111,11 +1122,11 @@
 func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archPropertySet android.BpPropertySet, archOsPrefix string) {
 	archTypeName := archInfo.archType.Name
 	archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + archTypeName)
-	archInfo.Properties.AddToPropertySet(ctx, archTypePropertySet)
+	addSdkMemberPropertiesToSet(ctx, archInfo.Properties, archTypePropertySet)
 
 	for _, linkInfo := range archInfo.linkInfos {
 		linkPropertySet := archTypePropertySet.AddPropertySet(linkInfo.linkType)
-		linkInfo.Properties.AddToPropertySet(ctx, linkPropertySet)
+		addSdkMemberPropertiesToSet(ctx, linkInfo.Properties, linkPropertySet)
 	}
 }
 
@@ -1221,7 +1232,7 @@
 	extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
 
 	// Add the common properties to the module.
-	commonProperties.AddToPropertySet(ctx, bpModule)
+	addSdkMemberPropertiesToSet(ctx, commonProperties, bpModule)
 
 	// Create a target property set into which target specific properties can be
 	// added.
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 1cb874d..798fc40 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -239,6 +239,21 @@
 	return path
 }
 
+func AutoGenRobolectricTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
+	testSuites []string, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
+	if autogenPath != nil {
+		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+		if templatePath.Valid() {
+			autogenTemplate(ctx, autogenPath, templatePath.String(), nil)
+		} else {
+			autogenTemplate(ctx, autogenPath, "${RobolectricTestConfigTemplate}", nil)
+		}
+		return autogenPath
+	}
+	return path
+}
+
 var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
 	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}",
 	CommandDeps: []string{
diff --git a/tradefed/config.go b/tradefed/config.go
index 34195c3..f7e8349 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -33,6 +33,7 @@
 	pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
 	pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
 	pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
+	pctx.SourcePathVariable("RobolectricTestConfigTemplate", "build/make/core/robolectric_test_config_template.xml")
 	pctx.SourcePathVariable("ShellTestConfigTemplate", "build/make/core/shell_test_config_template.xml")
 
 	pctx.SourcePathVariable("EmptyTestConfig", "build/make/core/empty_test_config.xml")