Merge changes from topics "prebuilt_build_tool", "prebuilt_build_tool_make"

* changes:
  Support per-module MakeVars
  Switch cc's use of bison and flex to prebuilt_build_tool
  Add prebuilt_build_tool to allow genrules to use prebuilt tools
diff --git a/android/Android.bp b/android/Android.bp
index 1627e9c..96f0983 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -47,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..94b4b81 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -165,7 +165,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/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/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..10cc4b6 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
diff --git a/apex/apex.go b/apex/apex.go
index b29017d..d0c4e6e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1145,7 +1145,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
 
@@ -2112,7 +2112,7 @@
 			case android.PrebuiltDepTag:
 				// If the prebuilt is force disabled, remember to delete the prebuilt file
 				// that might have been installed in the previous builds
-				if prebuilt, ok := child.(*Prebuilt); ok && prebuilt.isForceDisabled() {
+				if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
 					a.prebuiltFileToDelete = prebuilt.InstallFilename()
 				}
 			}
diff --git a/apex/builder.go b/apex/builder.go
index 49e4642..a70c767 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))
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index bf574dc..d459f87 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/java"
+
 	"github.com/google/blueprint"
 
 	"github.com/google/blueprint/proptools"
@@ -39,9 +40,55 @@
 		"abis", "allow-prereleased", "sdk-version")
 )
 
+type prebuilt interface {
+	isForceDisabled() bool
+	InstallFilename() string
+}
+
+type prebuiltCommon struct {
+	prebuilt   android.Prebuilt
+	properties prebuiltCommonProperties
+}
+
+type prebuiltCommonProperties struct {
+	ForceDisable bool `blueprint:"mutated"`
+}
+
+func (p *prebuiltCommon) Prebuilt() *android.Prebuilt {
+	return &p.prebuilt
+}
+
+func (p *prebuiltCommon) isForceDisabled() bool {
+	return p.properties.ForceDisable
+}
+
+func (p *prebuiltCommon) checkForceDisable(ctx android.ModuleContext) bool {
+	// If the device is configured to use flattened APEX, force disable the prebuilt because
+	// the prebuilt is a non-flattened one.
+	forceDisable := ctx.Config().FlattenApex()
+
+	// Force disable the prebuilts when we are doing unbundled build. We do unbundled build
+	// to build the prebuilts themselves.
+	forceDisable = forceDisable || ctx.Config().UnbundledBuild()
+
+	// Force disable the prebuilts when coverage is enabled.
+	forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
+	forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
+
+	// b/137216042 don't use prebuilts when address sanitizer is on
+	forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
+		android.InList("hwaddress", ctx.Config().SanitizeDevice())
+
+	if forceDisable && p.prebuilt.SourceExists() {
+		p.properties.ForceDisable = true
+		return true
+	}
+	return false
+}
+
 type Prebuilt struct {
 	android.ModuleBase
-	prebuilt android.Prebuilt
+	prebuiltCommon
 
 	properties PrebuiltProperties
 
@@ -57,8 +104,7 @@
 
 type PrebuiltProperties struct {
 	// the path to the prebuilt .apex file to import.
-	Source       string `blueprint:"mutated"`
-	ForceDisable bool   `blueprint:"mutated"`
+	Source string `blueprint:"mutated"`
 
 	Src  *string
 	Arch struct {
@@ -93,10 +139,6 @@
 	return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
 }
 
-func (p *Prebuilt) isForceDisabled() bool {
-	return p.properties.ForceDisable
-}
-
 func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
 	case "":
@@ -110,12 +152,8 @@
 	return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
 }
 
-func (p *Prebuilt) Prebuilt() *android.Prebuilt {
-	return &p.prebuilt
-}
-
 func (p *Prebuilt) Name() string {
-	return p.prebuilt.Name(p.ModuleBase.Name())
+	return p.prebuiltCommon.prebuilt.Name(p.ModuleBase.Name())
 }
 
 // prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
@@ -128,27 +166,6 @@
 }
 
 func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// If the device is configured to use flattened APEX, force disable the prebuilt because
-	// the prebuilt is a non-flattened one.
-	forceDisable := ctx.Config().FlattenApex()
-
-	// Force disable the prebuilts when we are doing unbundled build. We do unbundled build
-	// to build the prebuilts themselves.
-	forceDisable = forceDisable || ctx.Config().UnbundledBuild()
-
-	// Force disable the prebuilts when coverage is enabled.
-	forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
-	forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
-
-	// b/137216042 don't use prebuilts when address sanitizer is on
-	forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
-		android.InList("hwaddress", ctx.Config().SanitizeDevice())
-
-	if forceDisable && p.prebuilt.SourceExists() {
-		p.properties.ForceDisable = true
-		return
-	}
-
 	// This is called before prebuilt_select and prebuilt_postdeps mutators
 	// The mutators requires that src to be set correctly for each arch so that
 	// arch variants are disabled when src is not provided for the arch.
@@ -177,10 +194,6 @@
 }
 
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if p.properties.ForceDisable {
-		return
-	}
-
 	// TODO(jungjw): Check the key validity.
 	p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
 	p.installDir = android.PathForModuleInstall(ctx, "apex")
@@ -194,6 +207,12 @@
 		Input:  p.inputApex,
 		Output: p.outputApex,
 	})
+
+	if p.prebuiltCommon.checkForceDisable(ctx) {
+		p.SkipInstall()
+		return
+	}
+
 	if p.installable() {
 		ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
 	}
@@ -227,7 +246,7 @@
 
 type ApexSet struct {
 	android.ModuleBase
-	prebuilt android.Prebuilt
+	prebuiltCommon
 
 	properties ApexSetProperties
 
@@ -270,12 +289,8 @@
 	return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
 }
 
-func (a *ApexSet) Prebuilt() *android.Prebuilt {
-	return &a.prebuilt
-}
-
 func (a *ApexSet) Name() string {
-	return a.prebuilt.Name(a.ModuleBase.Name())
+	return a.prebuiltCommon.prebuilt.Name(a.ModuleBase.Name())
 }
 
 func (a *ApexSet) Overrides() []string {
@@ -297,7 +312,7 @@
 		ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
 	}
 
-	apexSet := a.prebuilt.SingleSourcePath(ctx)
+	apexSet := a.prebuiltCommon.prebuilt.SingleSourcePath(ctx)
 	a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
 	ctx.Build(pctx,
 		android.BuildParams{
@@ -311,6 +326,12 @@
 				"sdk-version":       ctx.Config().PlatformSdkVersion(),
 			},
 		})
+
+	if a.prebuiltCommon.checkForceDisable(ctx) {
+		a.SkipInstall()
+		return
+	}
+
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	if a.installable() {
 		ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
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 53528af..4ec98be 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1411,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()
 	}
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/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/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/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..5d2c4f6 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strings"
 
 	"android/soong/android"
 )
@@ -104,7 +105,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,
@@ -262,7 +272,7 @@
 		apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
 	}
 
-	rule.Command().
+	cmd := rule.Command().
 		Text("(").
 		Flag("JAVA_OPTS=-Xmx2048m").
 		FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
@@ -282,9 +292,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())
 
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..e3ba2c7 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)
 	}
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..48f51db 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -107,7 +107,7 @@
 	fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
 
 	srcPath, paths := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
-	deps.SrcDeps = paths
+	deps.SrcDeps = append(deps.SrcDeps, paths...)
 
 	outputFile := android.PathForModuleOut(ctx, fileName)
 	binary.unstrippedOutputFile = outputFile
diff --git a/rust/bindgen.go b/rust/bindgen.go
new file mode 100644
index 0000000..132b1fd
--- /dev/null
+++ b/rust/bindgen.go
@@ -0,0 +1,176 @@
+// 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)
+}
+
+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 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..acca256 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -369,7 +369,7 @@
 	var outputFile android.WritablePath
 
 	srcPath, paths := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
-	deps.SrcDeps = paths
+	deps.SrcDeps = append(deps.SrcDeps, paths...)
 
 	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..2752dc3 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -66,7 +66,7 @@
 	outputFile := android.PathForModuleOut(ctx, fileName)
 
 	srcPath, paths := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
-	deps.SrcDeps = paths
+	deps.SrcDeps = append(deps.SrcDeps, paths...)
 
 	procMacro.unstrippedOutputFile = outputFile
 
diff --git a/rust/rust.go b/rust/rust.go
index 7a98c64..28f8e1a 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 {
@@ -846,7 +877,6 @@
 	// 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 +993,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..89dfb67 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,73 @@
 	}
 }
 
+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())
+	}
+}
+
 // 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")