Merge "Support com.android.gki.* in apex_available."
diff --git a/android/Android.bp b/android/Android.bp
index 6ddcc14..50faa44 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -24,6 +24,7 @@
         "filegroup.go",
         "hooks.go",
         "image.go",
+        "makefile_goal.go",
         "makevars.go",
         "metrics.go",
         "module.go",
diff --git a/android/config.go b/android/config.go
index cafc71b..3387706 100644
--- a/android/config.go
+++ b/android/config.go
@@ -337,8 +337,8 @@
 		config: config,
 	}
 
-	// Sanity check the build and source directories. This won't catch strange
-	// configurations with symlinks, but at least checks the obvious cases.
+	// Soundness check of the build and source directories. This won't catch strange
+	// configurations with symlinks, but at least checks the obvious case.
 	absBuildDir, err := filepath.Abs(buildDir)
 	if err != nil {
 		return Config{}, err
@@ -1271,3 +1271,7 @@
 func (c *deviceConfig) BoardUsesRecoveryAsBoot() bool {
 	return Bool(c.config.productVariables.BoardUsesRecoveryAsBoot)
 }
+
+func (c *deviceConfig) BoardKernelBinaries() []string {
+	return c.config.productVariables.BoardKernelBinaries
+}
diff --git a/android/makefile_goal.go b/android/makefile_goal.go
new file mode 100644
index 0000000..eae3976
--- /dev/null
+++ b/android/makefile_goal.go
@@ -0,0 +1,99 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"fmt"
+	"io"
+	"path/filepath"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	RegisterModuleType("makefile_goal", MakefileGoalFactory)
+}
+
+type makefileGoalProperties struct {
+	// Sources.
+
+	// Makefile goal output file path, relative to PRODUCT_OUT.
+	Product_out_path *string
+}
+
+type makefileGoal struct {
+	ModuleBase
+
+	properties makefileGoalProperties
+
+	// Destination. Output file path of this module.
+	outputFilePath OutputPath
+}
+
+var _ AndroidMkEntriesProvider = (*makefileGoal)(nil)
+var _ OutputFileProducer = (*makefileGoal)(nil)
+
+// Input file of this makefile_goal module. Nil if none specified. May use variable names in makefiles.
+func (p *makefileGoal) inputPath() *string {
+	if p.properties.Product_out_path != nil {
+		return proptools.StringPtr(filepath.Join("$(PRODUCT_OUT)", proptools.String(p.properties.Product_out_path)))
+	}
+	return nil
+}
+
+// OutputFileProducer
+func (p *makefileGoal) OutputFiles(tag string) (Paths, error) {
+	if tag != "" {
+		return nil, fmt.Errorf("unsupported tag %q", tag)
+	}
+	return Paths{p.outputFilePath}, nil
+}
+
+// AndroidMkEntriesProvider
+func (p *makefileGoal) DepsMutator(ctx BottomUpMutatorContext) {
+	if p.inputPath() == nil {
+		ctx.PropertyErrorf("product_out_path", "Path relative to PRODUCT_OUT required")
+	}
+}
+
+func (p *makefileGoal) GenerateAndroidBuildActions(ctx ModuleContext) {
+	filename := filepath.Base(proptools.String(p.inputPath()))
+	p.outputFilePath = PathForModuleOut(ctx, filename).OutputPath
+
+	ctx.InstallFile(PathForModuleInstall(ctx, "etc"), ctx.ModuleName(), p.outputFilePath)
+}
+
+func (p *makefileGoal) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(p.outputFilePath),
+		ExtraFooters: []AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries) {
+				// Can't use Cp because inputPath() is not a valid Path.
+				fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,%s))\n", proptools.String(p.inputPath()), p.outputFilePath)
+			},
+		},
+	}}
+}
+
+// Import a Makefile goal to Soong by copying the file built by
+// the goal to a path visible to Soong. This rule only works on boot images.
+func MakefileGoalFactory() Module {
+	module := &makefileGoal{}
+	module.AddProperties(&module.properties)
+	// This module is device-only
+	InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
+	return module
+}
diff --git a/android/module.go b/android/module.go
index 2062a4d..a12cd9b 100644
--- a/android/module.go
+++ b/android/module.go
@@ -548,6 +548,9 @@
 
 	SkipInstall bool `blueprint:"mutated"`
 
+	// Disabled by mutators. If set to true, it overrides Enabled property.
+	ForcedDisabled bool `blueprint:"mutated"`
+
 	NamespaceExportedToMake bool `blueprint:"mutated"`
 
 	MissingDeps []string `blueprint:"mutated"`
@@ -1022,6 +1025,9 @@
 }
 
 func (m *ModuleBase) Enabled() bool {
+	if m.commonProperties.ForcedDisabled {
+		return false
+	}
 	if m.commonProperties.Enabled == nil {
 		return !m.Os().DefaultDisabled
 	}
@@ -1029,7 +1035,7 @@
 }
 
 func (m *ModuleBase) Disable() {
-	m.commonProperties.Enabled = proptools.BoolPtr(false)
+	m.commonProperties.ForcedDisabled = true
 }
 
 func (m *ModuleBase) SkipInstall() {
diff --git a/android/neverallow.go b/android/neverallow.go
index 526d399..73829f1 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -56,6 +56,7 @@
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
 	AddNeverAllowRules(createCcSdkVariantRules()...)
 	AddNeverAllowRules(createUncompressDexRules()...)
+	AddNeverAllowRules(createMakefileGoalRules()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -231,6 +232,15 @@
 	}
 }
 
+func createMakefileGoalRules() []Rule {
+	return []Rule{
+		NeverAllow().
+			ModuleType("makefile_goal").
+			WithoutMatcher("product_out_path", Regexp("^boot[0-9a-zA-Z.-]*[.]img$")).
+			Because("Only boot images may be imported as a makefile goal."),
+	}
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 45d36a6..56a07dc 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -326,6 +326,20 @@
 			"module \"outside_art_libraries\": violates neverallow",
 		},
 	},
+	{
+		name: "disallowed makefile_goal",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				makefile_goal {
+					name: "foo",
+					product_out_path: "boot/trap.img"
+				}
+			`),
+		},
+		expectedErrors: []string{
+			"Only boot images may be imported as a makefile goal.",
+		},
+	},
 }
 
 func TestNeverallow(t *testing.T) {
@@ -350,6 +364,7 @@
 	ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
 	ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
 	ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
+	ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
 	ctx.PostDepsMutators(RegisterNeverallowMutator)
 	ctx.Register(config)
 
@@ -438,3 +453,22 @@
 
 func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
 }
+
+type mockMakefileGoalProperties struct {
+	Product_out_path *string
+}
+
+type mockMakefileGoalModule struct {
+	ModuleBase
+	properties mockMakefileGoalProperties
+}
+
+func newMockMakefileGoalModule() Module {
+	m := &mockMakefileGoalModule{}
+	m.AddProperties(&m.properties)
+	InitAndroidModule(m)
+	return m
+}
+
+func (p *mockMakefileGoalModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/variable.go b/android/variable.go
index 5826138..c1e1b42 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -344,6 +344,8 @@
 	InstallExtraFlattenedApexes *bool `json:",omitempty"`
 
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
+
+	BoardKernelBinaries []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 82a902b..5c6d6cc 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -50,6 +50,11 @@
 		return moduleNames
 	}
 
+	// b/162366062. Prevent GKI APEXes to emit make rules to avoid conflicts.
+	if strings.HasPrefix(apexName, "com.android.gki.") && apexType != flattenedApex {
+		return moduleNames
+	}
+
 	// b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
 	// APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
 	// as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
@@ -209,7 +214,7 @@
 			if !ok {
 				panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
 			}
-			fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE :=", as.MasterFile())
+			fmt.Fprintln(w, "LOCAL_APK_SET_INSTALL_FILE :=", as.InstallFile())
 			fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
 		case nativeSharedLib, nativeExecutable, nativeTest:
diff --git a/apex/apex.go b/apex/apex.go
index f9c902c..9905b79 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -704,7 +704,7 @@
 		if !ok || !am.CanHaveApexVariants() {
 			return false
 		}
-		if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) && !inAnySdk(child) {
+		if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
 			return false
 		}
 		if excludeVndkLibs {
@@ -2162,10 +2162,7 @@
 							return false
 						}
 						if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
-							// For vendor APEX with use_vndk_as_stable: true, we don't include VNDK libs
-							// and use them from VNDK APEX.
-							// TODO(b/159576928): add "vndk" as requiredDeps so that linkerconfig can make "vndk"
-							// linker namespace avaiable to this apex.
+							requireNativeLibs = append(requireNativeLibs, ":vndk")
 							return false
 						}
 						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 690c2f5..a6f3805 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -236,13 +236,15 @@
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	android.RegisterPrebuiltMutators(ctx)
 
-	// Register this after the prebuilt mutators have been registered (in
-	// cc.RegisterRequiredBuildComponentsForTest) to match what happens at runtime.
+	// Register these after the prebuilt mutators have been registered to match what
+	// happens at runtime.
 	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
@@ -2208,6 +2210,10 @@
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
 	ensureContains(t, androidMk, `LOCAL_MODULE_PATH := /tmp/target/product/test_device/vendor/apex`)
+
+	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
+	ensureListNotContains(t, requireNativeLibs, ":vndk")
 }
 
 func TestVendorApex_use_vndk_as_stable(t *testing.T) {
@@ -2257,6 +2263,10 @@
 		"bin/mybin",
 		"lib64/libvendor.so",
 	})
+
+	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
+	ensureListContains(t, requireNativeLibs, ":vndk")
 }
 
 func TestAndroidMk_UseVendorRequired(t *testing.T) {
@@ -5541,6 +5551,7 @@
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	android.RegisterPrebuiltMutators(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	java.RegisterJavaBuildComponents(ctx)
 	java.RegisterSystemModulesBuildComponents(ctx)
diff --git a/cc/cc.go b/cc/cc.go
index 57fe9ba..962da4c 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -3034,7 +3034,7 @@
 var _ android.ImageInterface = (*Module)(nil)
 
 func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	// Sanity check
+	// Validation check
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 	productSpecific := mctx.ProductSpecific()
 
diff --git a/cc/testing.go b/cc/testing.go
index 4d0b28b..c2353d8 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -20,8 +20,6 @@
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
 	RegisterPrebuiltBuildComponents(ctx)
-	android.RegisterPrebuiltMutators(ctx)
-
 	RegisterCCBuildComponents(ctx)
 	RegisterBinaryBuildComponents(ctx)
 	RegisterLibraryBuildComponents(ctx)
@@ -570,6 +568,7 @@
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	android.RegisterPrebuiltMutators(ctx)
 	RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index fec0c8b..0af2258 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -552,6 +552,13 @@
 	if _, ok := m.linker.(*kernelHeadersDecorator); ok {
 		return false
 	}
+	// skip llndk_library and llndk_headers which are backward compatible
+	if _, ok := m.linker.(*llndkStubDecorator); ok {
+		return false
+	}
+	if _, ok := m.linker.(*llndkHeadersDecorator); ok {
+		return false
+	}
 
 	// Libraries
 	if l, ok := m.linker.(snapshotLibraryInterface); ok {
diff --git a/cc/vndk.go b/cc/vndk.go
index 15843c6..f9adec7 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -340,7 +340,7 @@
 	}
 }
 
-// Sanity check for modules that mustn't be VNDK
+// Check for modules that mustn't be VNDK
 func shouldSkipVndkMutator(m *Module) bool {
 	if !m.Enabled() {
 		return true
diff --git a/cmd/diff_target_files/Android.bp b/cmd/diff_target_files/Android.bp
index 5397f4b..bc6b068 100644
--- a/cmd/diff_target_files/Android.bp
+++ b/cmd/diff_target_files/Android.bp
@@ -5,12 +5,12 @@
         "diff_target_files.go",
         "glob.go",
         "target_files.go",
-        "whitelist.go",
+        "allow_list.go",
         "zip_artifact.go",
     ],
     testSrcs: [
         "compare_test.go",
         "glob_test.go",
-        "whitelist_test.go",
+        "allow_list_test.go",
     ],
 }
diff --git a/cmd/diff_target_files/whitelist.go b/cmd/diff_target_files/allow_list.go
similarity index 77%
rename from cmd/diff_target_files/whitelist.go
rename to cmd/diff_target_files/allow_list.go
index f00fc1e..ca55b43 100644
--- a/cmd/diff_target_files/whitelist.go
+++ b/cmd/diff_target_files/allow_list.go
@@ -25,18 +25,13 @@
 	"unicode"
 )
 
-type jsonWhitelist struct {
-	Paths               []string
-	IgnoreMatchingLines []string
-}
-
-type whitelist struct {
+type allowList struct {
 	path                string
 	ignoreMatchingLines []string
 }
 
-func parseWhitelists(whitelists []string, whitelistFiles []string) ([]whitelist, error) {
-	var ret []whitelist
+func parseAllowLists(allowLists []string, allowListFiles []string) ([]allowList, error) {
+	var ret []allowList
 
 	add := func(path string, ignoreMatchingLines []string) {
 		for _, x := range ret {
@@ -46,24 +41,24 @@
 			}
 		}
 
-		ret = append(ret, whitelist{
+		ret = append(ret, allowList{
 			path:                path,
 			ignoreMatchingLines: ignoreMatchingLines,
 		})
 	}
 
-	for _, file := range whitelistFiles {
-		newWhitelists, err := parseWhitelistFile(file)
+	for _, file := range allowListFiles {
+		newAllowlists, err := parseAllowListFile(file)
 		if err != nil {
 			return nil, err
 		}
 
-		for _, w := range newWhitelists {
+		for _, w := range newAllowlists {
 			add(w.path, w.ignoreMatchingLines)
 		}
 	}
 
-	for _, s := range whitelists {
+	for _, s := range allowLists {
 		colon := strings.IndexRune(s, ':')
 		var ignoreMatchingLines []string
 		if colon >= 0 {
@@ -75,7 +70,7 @@
 	return ret, nil
 }
 
-func parseWhitelistFile(file string) ([]whitelist, error) {
+func parseAllowListFile(file string) ([]allowList, error) {
 	r, err := os.Open(file)
 	if err != nil {
 		return nil, err
@@ -84,27 +79,32 @@
 
 	d := json.NewDecoder(newJSONCommentStripper(r))
 
-	var jsonWhitelists []jsonWhitelist
+	var jsonAllowLists []struct {
+		Paths               []string
+		IgnoreMatchingLines []string
+	}
 
-	err = d.Decode(&jsonWhitelists)
+	if err := d.Decode(&jsonAllowLists); err != nil {
+		return nil, err
+	}
 
-	var whitelists []whitelist
-	for _, w := range jsonWhitelists {
+	var allowLists []allowList
+	for _, w := range jsonAllowLists {
 		for _, p := range w.Paths {
-			whitelists = append(whitelists, whitelist{
+			allowLists = append(allowLists, allowList{
 				path:                p,
 				ignoreMatchingLines: w.IgnoreMatchingLines,
 			})
 		}
 	}
 
-	return whitelists, err
+	return allowLists, err
 }
 
-func filterModifiedPaths(l [][2]*ZipArtifactFile, whitelists []whitelist) ([][2]*ZipArtifactFile, error) {
+func filterModifiedPaths(l [][2]*ZipArtifactFile, allowLists []allowList) ([][2]*ZipArtifactFile, error) {
 outer:
 	for i := 0; i < len(l); i++ {
-		for _, w := range whitelists {
+		for _, w := range allowLists {
 			if match, err := Match(w.path, l[i][0].Name); err != nil {
 				return l, err
 			} else if match {
@@ -126,10 +126,10 @@
 	return l, nil
 }
 
-func filterNewPaths(l []*ZipArtifactFile, whitelists []whitelist) ([]*ZipArtifactFile, error) {
+func filterNewPaths(l []*ZipArtifactFile, allowLists []allowList) ([]*ZipArtifactFile, error) {
 outer:
 	for i := 0; i < len(l); i++ {
-		for _, w := range whitelists {
+		for _, w := range allowLists {
 			if match, err := Match(w.path, l[i].Name); err != nil {
 				return l, err
 			} else if match && len(w.ignoreMatchingLines) == 0 {
@@ -192,18 +192,18 @@
 	return bytes.Compare(bufA, bufB) == 0, nil
 }
 
-func applyWhitelists(diff zipDiff, whitelists []whitelist) (zipDiff, error) {
+func applyAllowLists(diff zipDiff, allowLists []allowList) (zipDiff, error) {
 	var err error
 
-	diff.modified, err = filterModifiedPaths(diff.modified, whitelists)
+	diff.modified, err = filterModifiedPaths(diff.modified, allowLists)
 	if err != nil {
 		return diff, err
 	}
-	diff.onlyInA, err = filterNewPaths(diff.onlyInA, whitelists)
+	diff.onlyInA, err = filterNewPaths(diff.onlyInA, allowLists)
 	if err != nil {
 		return diff, err
 	}
-	diff.onlyInB, err = filterNewPaths(diff.onlyInB, whitelists)
+	diff.onlyInB, err = filterNewPaths(diff.onlyInB, allowLists)
 	if err != nil {
 		return diff, err
 	}
diff --git a/cmd/diff_target_files/whitelist_test.go b/cmd/diff_target_files/allow_list_test.go
similarity index 82%
rename from cmd/diff_target_files/whitelist_test.go
rename to cmd/diff_target_files/allow_list_test.go
index 4b19fdd..8410e5a 100644
--- a/cmd/diff_target_files/whitelist_test.go
+++ b/cmd/diff_target_files/allow_list_test.go
@@ -57,10 +57,10 @@
 
 var f2 = bytesToZipArtifactFile("dir/f2", nil)
 
-func Test_applyWhitelists(t *testing.T) {
+func Test_applyAllowLists(t *testing.T) {
 	type args struct {
 		diff       zipDiff
-		whitelists []whitelist
+		allowLists []allowList
 	}
 	tests := []struct {
 		name    string
@@ -74,7 +74,7 @@
 				diff: zipDiff{
 					onlyInA: []*ZipArtifactFile{f1a, f2},
 				},
-				whitelists: []whitelist{{path: "dir/f1"}},
+				allowLists: []allowList{{path: "dir/f1"}},
 			},
 			want: zipDiff{
 				onlyInA: []*ZipArtifactFile{f2},
@@ -86,7 +86,7 @@
 				diff: zipDiff{
 					onlyInA: []*ZipArtifactFile{f1a, f2},
 				},
-				whitelists: []whitelist{{path: "dir/*"}},
+				allowLists: []allowList{{path: "dir/*"}},
 			},
 			want: zipDiff{},
 		},
@@ -96,7 +96,7 @@
 				diff: zipDiff{
 					modified: [][2]*ZipArtifactFile{{f1a, f1b}},
 				},
-				whitelists: []whitelist{{path: "dir/*"}},
+				allowLists: []allowList{{path: "dir/*"}},
 			},
 			want: zipDiff{},
 		},
@@ -106,20 +106,20 @@
 				diff: zipDiff{
 					modified: [][2]*ZipArtifactFile{{f1a, f1b}},
 				},
-				whitelists: []whitelist{{path: "dir/*", ignoreMatchingLines: []string{"foo: .*"}}},
+				allowLists: []allowList{{path: "dir/*", ignoreMatchingLines: []string{"foo: .*"}}},
 			},
 			want: zipDiff{},
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := applyWhitelists(tt.args.diff, tt.args.whitelists)
+			got, err := applyAllowLists(tt.args.diff, tt.args.allowLists)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("applyWhitelists() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Test_applyAllowLists() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("applyWhitelists() = %v, want %v", got, tt.want)
+				t.Errorf("Test_applyAllowLists() = %v, want %v", got, tt.want)
 			}
 		})
 	}
diff --git a/cmd/diff_target_files/compare.go b/cmd/diff_target_files/compare.go
index 00cd9ca..45b6d6b 100644
--- a/cmd/diff_target_files/compare.go
+++ b/cmd/diff_target_files/compare.go
@@ -21,7 +21,7 @@
 
 // compareTargetFiles takes two ZipArtifacts and compares the files they contain by examining
 // the path, size, and CRC of each file.
-func compareTargetFiles(priZip, refZip ZipArtifact, artifact string, whitelists []whitelist, filters []string) (zipDiff, error) {
+func compareTargetFiles(priZip, refZip ZipArtifact, artifact string, allowLists []allowList, filters []string) (zipDiff, error) {
 	priZipFiles, err := priZip.Files()
 	if err != nil {
 		return zipDiff{}, fmt.Errorf("error fetching target file lists from primary zip %v", err)
@@ -45,7 +45,7 @@
 	// Compare the file lists from both builds
 	diff := diffTargetFilesLists(refZipFiles, priZipFiles)
 
-	return applyWhitelists(diff, whitelists)
+	return applyAllowLists(diff, allowLists)
 }
 
 // zipDiff contains the list of files that differ between two zip files.
diff --git a/cmd/diff_target_files/diff_target_files.go b/cmd/diff_target_files/diff_target_files.go
index 75bc8ee..634565b 100644
--- a/cmd/diff_target_files/diff_target_files.go
+++ b/cmd/diff_target_files/diff_target_files.go
@@ -22,8 +22,8 @@
 )
 
 var (
-	whitelists     = newMultiString("whitelist", "whitelist patterns in the form <pattern>[:<regex of line to ignore>]")
-	whitelistFiles = newMultiString("whitelist_file", "files containing whitelist definitions")
+	allowLists     = newMultiString("allowlist", "allowlist patterns in the form <pattern>[:<regex of line to ignore>]")
+	allowListFiles = newMultiString("allowlist_file", "files containing allowlist definitions")
 
 	filters = newMultiString("filter", "filter patterns to apply to files in target-files.zip before comparing")
 )
@@ -47,9 +47,9 @@
 		os.Exit(1)
 	}
 
-	whitelists, err := parseWhitelists(*whitelists, *whitelistFiles)
+	allowLists, err := parseAllowLists(*allowLists, *allowListFiles)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error parsing whitelists: %v\n", err)
+		fmt.Fprintf(os.Stderr, "Error parsing allowlists: %v\n", err)
 		os.Exit(1)
 	}
 
@@ -67,7 +67,7 @@
 	}
 	defer refZip.Close()
 
-	diff, err := compareTargetFiles(priZip, refZip, targetFilesPattern, whitelists, *filters)
+	diff, err := compareTargetFiles(priZip, refZip, targetFilesPattern, allowLists, *filters)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error comparing zip files: %v\n", err)
 		os.Exit(1)
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index a95aca9..274c8ee 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -429,7 +429,7 @@
 	if maxOpenZips < 3 {
 		panic(fmt.Errorf("open zips limit should be above 3"))
 	}
-	// In the dummy element .older points to the most recently opened InputZip, and .newer points to the oldest.
+	// In the fake element .older points to the most recently opened InputZip, and .newer points to the oldest.
 	head := new(ManagedInputZip)
 	head.older = head
 	head.newer = head
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index e485c60..69e4f69 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -35,14 +35,14 @@
 // A command represents an operation to be executed in the soong build
 // system.
 type command struct {
-	// the flag name (must have double dashes)
+	// The flag name (must have double dashes).
 	flag string
 
-	// description for the flag (to display when running help)
+	// Description for the flag (to display when running help).
 	description string
 
-	// Forces the status output into dumb terminal mode.
-	forceDumbOutput bool
+	// Stream the build status output into the simple terminal mode.
+	simpleOutput bool
 
 	// Sets a prefix string to use for filenames of log files.
 	logsPrefix string
@@ -70,21 +70,21 @@
 		stdio: stdio,
 		run:   make,
 	}, {
-		flag:            "--dumpvar-mode",
-		description:     "print the value of the legacy make variable VAR to stdout",
-		forceDumbOutput: true,
-		logsPrefix:      "dumpvars-",
-		config:          dumpVarConfig,
-		stdio:           customStdio,
-		run:             dumpVar,
+		flag:         "--dumpvar-mode",
+		description:  "print the value of the legacy make variable VAR to stdout",
+		simpleOutput: true,
+		logsPrefix:   "dumpvars-",
+		config:       dumpVarConfig,
+		stdio:        customStdio,
+		run:          dumpVar,
 	}, {
-		flag:            "--dumpvars-mode",
-		description:     "dump the values of one or more legacy make variables, in shell syntax",
-		forceDumbOutput: true,
-		logsPrefix:      "dumpvars-",
-		config:          dumpVarConfig,
-		stdio:           customStdio,
-		run:             dumpVars,
+		flag:         "--dumpvars-mode",
+		description:  "dump the values of one or more legacy make variables, in shell syntax",
+		simpleOutput: true,
+		logsPrefix:   "dumpvars-",
+		config:       dumpVarConfig,
+		stdio:        customStdio,
+		run:          dumpVars,
 	}, {
 		flag:        "--build-mode",
 		description: "build modules based on the specified build action",
@@ -125,7 +125,7 @@
 		os.Exit(1)
 	}
 
-	output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.forceDumbOutput,
+	output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
 
 	log := logger.New(output)
@@ -172,7 +172,7 @@
 	buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
-	defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+	defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
 
 	os.MkdirAll(logsDir, 0777)
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
diff --git a/java/androidmk.go b/java/androidmk.go
index 25dd329..081fcb2 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -728,7 +728,7 @@
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(entries *android.AndroidMkEntries) {
 					entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
-					entries.SetString("LOCAL_APK_SET_MASTER_FILE", apkSet.masterFile)
+					entries.SetString("LOCAL_APK_SET_INSTALL_FILE", apkSet.InstallFile())
 					entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
 					entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...)
 				},
diff --git a/java/app.go b/java/app.go
index 4031cfe..1ede34e 100755
--- a/java/app.go
+++ b/java/app.go
@@ -78,7 +78,7 @@
 
 	properties   AndroidAppSetProperties
 	packedOutput android.WritablePath
-	masterFile   string
+	installFile  string
 	apkcertsFile android.ModuleOutPath
 }
 
@@ -102,8 +102,8 @@
 	return as.packedOutput
 }
 
-func (as *AndroidAppSet) MasterFile() string {
-	return as.masterFile
+func (as *AndroidAppSet) InstallFile() string {
+	return as.installFile
 }
 
 func (as *AndroidAppSet) APKCertsFile() android.Path {
@@ -136,10 +136,10 @@
 func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
 	as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
-	// We are assuming here that the master file in the APK
+	// We are assuming here that the install file in the APK
 	// set has `.apk` suffix. If it doesn't the build will fail.
 	// APK sets containing APEX files are handled elsewhere.
-	as.masterFile = as.BaseModuleName() + ".apk"
+	as.installFile = as.BaseModuleName() + ".apk"
 	screenDensities := "all"
 	if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
 		screenDensities = strings.ToUpper(strings.Join(dpis, ","))
@@ -167,7 +167,7 @@
 
 // android_app_set extracts a set of APKs based on the target device
 // configuration and installs this set as "split APKs".
-// The extracted set always contains 'master' APK whose name is
+// The extracted set always contains an APK whose name is
 // _module_name_.apk and every split APK matching target device.
 // The extraction of the density-specific splits depends on
 // PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
diff --git a/java/app_test.go b/java/app_test.go
index d4323bb..efb4fd2 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -161,11 +161,11 @@
 		t.Errorf("wrong partition value: '%s', expected 'system'", s)
 	}
 	mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
-	actualMaster := mkEntries.EntryMap["LOCAL_APK_SET_MASTER_FILE"]
-	expectedMaster := []string{"foo.apk"}
-	if !reflect.DeepEqual(actualMaster, expectedMaster) {
-		t.Errorf("Unexpected LOCAL_APK_SET_MASTER_FILE value: '%s', expected: '%s',",
-			actualMaster, expectedMaster)
+	actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
+	expectedInstallFile := []string{"foo.apk"}
+	if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
+		t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
+			actualInstallFile, expectedInstallFile)
 	}
 }
 
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 935b839..d2f8d83 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -198,7 +198,7 @@
 	// the generated removed Dex API filename by Doclava.
 	Removed_dex_api_filename *string
 
-	// if set to false, don't allow droiddoc to generate stubs source files. Defaults to true.
+	// if set to false, don't allow droiddoc to generate stubs source files. Defaults to false.
 	Create_stubs *bool
 
 	Check_api struct {
@@ -870,6 +870,10 @@
 	}
 }
 
+func (d *Droiddoc) createStubs() bool {
+	return BoolDefault(d.properties.Create_stubs, false)
+}
+
 func (d *Droiddoc) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
 		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
@@ -892,7 +896,7 @@
 		cmd.FlagWithOutput("-removedDexApi ", d.removedDexApiFile)
 	}
 
-	if BoolDefault(d.properties.Create_stubs, true) {
+	if d.createStubs() {
 		cmd.FlagWithArg("-stubs ", stubsDir.String())
 	}
 
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index bff591c..7afba2a 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -208,7 +208,7 @@
 }
 
 // flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
-// the greylists.
+// the unsupported API.
 func flagsRule(ctx android.SingletonContext) android.Path {
 	var flagsCSV android.Paths
 	var greylistRemovedApis android.Paths
@@ -247,18 +247,18 @@
 		Tool(android.PathForSource(ctx, "frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py")).
 		FlagWithInput("--csv ", stubFlags).
 		Inputs(flagsCSV).
-		FlagWithInput("--greylist ",
+		FlagWithInput("--unsupported ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist.txt")).
-		FlagWithInput("--greylist-ignore-conflicts ", combinedRemovedApis).
-		FlagWithInput("--greylist-max-q ",
+		FlagWithInput("--unsupported-ignore-conflicts ", combinedRemovedApis).
+		FlagWithInput("--max-target-q ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-q.txt")).
-		FlagWithInput("--greylist-max-p ",
+		FlagWithInput("--max-target-p ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-p.txt")).
-		FlagWithInput("--greylist-max-o-ignore-conflicts ",
+		FlagWithInput("--max-target-o-ignore-conflicts ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-o.txt")).
-		FlagWithInput("--blacklist ",
+		FlagWithInput("--blocked ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blacklist.txt")).
-		FlagWithInput("--greylist-packages ",
+		FlagWithInput("--unsupported-packages ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-packages.txt")).
 		FlagWithOutput("--output ", tempPath)
 
diff --git a/java/java_test.go b/java/java_test.go
index 73e6792..a3c7854 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -96,6 +96,8 @@
 	ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
 	ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(sdkPreSingletonFactory))
 
+	android.RegisterPrebuiltMutators(ctx)
+
 	// Register module types and mutators from cc needed for JNI testing
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 
@@ -489,7 +491,7 @@
 
 	ctx, _ := testJavaWithConfig(t, config)
 
-	// first, sanity check that the -g flag is added to target modules
+	// first, check that the -g flag is added to target modules
 	targetLibrary := ctx.ModuleForTests("target_library", "android_common")
 	targetJavaFlags := targetLibrary.Module().VariablesForTests()["javacFlags"]
 	if !strings.Contains(targetJavaFlags, "-g:source,lines") {
@@ -1107,8 +1109,13 @@
 			"bar-doc/a.java": nil,
 			"bar-doc/b.java": nil,
 		})
+	barDocModule := ctx.ModuleForTests("bar-doc", "android_common")
+	barDoc := barDocModule.Rule("javadoc")
+	notExpected := " -stubs "
+	if strings.Contains(barDoc.RuleParams.Command, notExpected) {
+		t.Errorf("bar-doc command contains flag %q to create stubs, but should not", notExpected)
+	}
 
-	barDoc := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc")
 	var javaSrcs []string
 	for _, i := range barDoc.Inputs {
 		javaSrcs = append(javaSrcs, i.Base())
@@ -1117,7 +1124,7 @@
 		t.Errorf("inputs of bar-doc must be []string{\"a.java\"}, but was %#v.", javaSrcs)
 	}
 
-	aidl := ctx.ModuleForTests("bar-doc", "android_common").Rule("aidl")
+	aidl := barDocModule.Rule("aidl")
 	if g, w := barDoc.Implicits.Strings(), aidl.Output.String(); !inList(w, g) {
 		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
 	}
diff --git a/rust/androidmk.go b/rust/androidmk.go
index babbf91..5806017 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -55,7 +55,6 @@
 	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 {
@@ -76,9 +75,11 @@
 			},
 		},
 	}
-	if mod.compiler != nil {
+
+	if mod.compiler != nil && !mod.compiler.Disabled() {
 		mod.subAndroidMk(&ret, mod.compiler)
 	} else if mod.sourceProvider != nil {
+		// If the compiler is disabled, this is a SourceProvider.
 		mod.subAndroidMk(&ret, mod.sourceProvider)
 	}
 	ret.SubName += mod.Properties.SubName
@@ -162,6 +163,7 @@
 	outFile := sourceProvider.outputFile
 	ret.Class = "ETC"
 	ret.OutputFile = android.OptionalPathForPath(outFile)
+	ret.SubName += sourceProvider.subName
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		_, file := filepath.Split(outFile.String())
 		stem, suffix, _ := android.SplitFileExt(file)
diff --git a/rust/bindgen.go b/rust/bindgen.go
index e8bbb35..304f8ec 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -61,7 +61,7 @@
 	Wrapper_src *string `android:"path,arch_variant"`
 
 	// list of bindgen-specific flags and options
-	Flags []string `android:"arch_variant"`
+	Bindgen_flags []string `android:"arch_variant"`
 
 	// list of clang flags required to correctly interpret the headers.
 	Cflags []string `android:"arch_variant"`
@@ -121,7 +121,7 @@
 	}
 
 	bindgenFlags := defaultBindgenFlags
-	bindgenFlags = append(bindgenFlags, strings.Join(b.Properties.Flags, " "))
+	bindgenFlags = append(bindgenFlags, strings.Join(b.Properties.Bindgen_flags, " "))
 
 	wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src)
 	if !wrapperFile.Valid() {
@@ -170,7 +170,13 @@
 		baseSourceProvider: NewSourceProvider(),
 		Properties:         BindgenProperties{},
 	}
+
+	_, library := NewRustLibrary(hod)
+	library.BuildOnlyRust()
+	library.sourceProvider = bindgen
+
 	module.sourceProvider = bindgen
+	module.compiler = library
 
 	return module, bindgen
 }
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 2122ec1..c428348 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -24,8 +24,10 @@
 		rust_bindgen {
 			name: "libbindgen",
 			wrapper_src: "src/any.h",
-			stem: "bindings",
-			flags: ["--bindgen-flag"],
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			bindgen_flags: ["--bindgen-flag"],
 			cflags: ["--clang-flag"],
 			shared_libs: ["libfoo_shared"],
 			static_libs: ["libfoo_static"],
@@ -38,7 +40,6 @@
 			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") {
diff --git a/rust/builder_test.go b/rust/builder_test.go
index 04b67d9..5c11cb7 100644
--- a/rust/builder_test.go
+++ b/rust/builder_test.go
@@ -28,12 +28,14 @@
 		}
 		rust_bindgen {
 			name: "libbindings1",
-			stem: "bindings",
+			source_stem: "bindings",
+			crate_name: "bindings1",
 			wrapper_src: "src/any.h",
 		}
 		rust_bindgen {
 			name: "libbindings2",
-			stem: "bindings",
+			source_stem: "bindings",
+			crate_name: "bindings2",
 			wrapper_src: "src/any.h",
 		}
 	`)
diff --git a/rust/compiler.go b/rust/compiler.go
index 040219d..c2b7e56 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -24,7 +24,7 @@
 	"android/soong/rust/config"
 )
 
-func getEdition(compiler *baseCompiler) string {
+func (compiler *baseCompiler) edition() string {
 	return proptools.StringDefault(compiler.Properties.Edition, config.DefaultEdition)
 }
 
@@ -81,7 +81,10 @@
 	// list of C static library dependencies
 	Static_libs []string `android:"arch_variant"`
 
-	// crate name, required for libraries. This must be the expected extern crate name used in source
+	// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
+	// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
+	// source, and is required to conform to an enforced format matching library output files (if the output file is
+	// lib<someName><suffix>, the crate_name property must be <someName>).
 	Crate_name string `android:"arch_variant"`
 
 	// list of features to enable for this crate
@@ -120,6 +123,14 @@
 	distFile              android.OptionalPath
 }
 
+func (compiler *baseCompiler) Disabled() bool {
+	return false
+}
+
+func (compiler *baseCompiler) SetDisabled() {
+	panic("baseCompiler does not implement SetDisabled()")
+}
+
 func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
 	panic("baseCompiler does not implement coverageOutputZipPath()")
 }
@@ -149,7 +160,7 @@
 	}
 	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
 	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
-	flags.RustFlags = append(flags.RustFlags, "--edition="+getEdition(compiler))
+	flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
 	flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 0204cd2..9c9a136 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -7,6 +7,7 @@
 		"external/crosvm",
 		"external/adhd",
 		"prebuilts/rust",
+		"system/security",
 	}
 
 	RustModuleTypes = []string{
diff --git a/rust/library.go b/rust/library.go
index 4c6da9d..6766d61 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -69,6 +69,10 @@
 	VariantIsShared bool `blueprint:"mutated"`
 	// This variant is a static library
 	VariantIsStatic bool `blueprint:"mutated"`
+
+	// This variant is disabled and should not be compiled
+	// (used for SourceProvider variants that produce only source)
+	VariantIsDisabled bool `blueprint:"mutated"`
 }
 
 type libraryDecorator struct {
@@ -78,6 +82,7 @@
 	Properties        LibraryCompilerProperties
 	MutatedProperties LibraryMutatedProperties
 	includeDirs       android.Paths
+	sourceProvider    SourceProvider
 }
 
 type libraryInterface interface {
@@ -367,8 +372,13 @@
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	var outputFile android.WritablePath
+	var srcPath android.Path
 
-	srcPath, _ := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
+	if library.sourceProvider != nil {
+		srcPath = library.sourceProvider.Srcs()[0]
+	} else {
+		srcPath, _ = srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
+	}
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 
@@ -430,6 +440,14 @@
 	return stem + String(library.baseCompiler.Properties.Suffix)
 }
 
+func (library *libraryDecorator) Disabled() bool {
+	return library.MutatedProperties.VariantIsDisabled
+}
+
+func (library *libraryDecorator) SetDisabled() {
+	library.MutatedProperties.VariantIsDisabled = true
+}
+
 var validCrateName = regexp.MustCompile("[^a-zA-Z0-9_]+")
 
 func validateLibraryStem(ctx BaseModuleContext, filename string, crate_name string) {
@@ -454,25 +472,35 @@
 	if m, ok := mctx.Module().(*Module); ok && m.compiler != nil {
 		switch library := m.compiler.(type) {
 		case libraryInterface:
-
-			// We only build the rust library variants here. This assumes that
-			// LinkageMutator runs first and there's an empty variant
-			// if rust variants are required.
-			if !library.static() && !library.shared() {
-				if library.buildRlib() && library.buildDylib() {
-					modules := mctx.CreateLocalVariations("rlib", "dylib")
-					rlib := modules[0].(*Module)
-					dylib := modules[1].(*Module)
-
-					rlib.compiler.(libraryInterface).setRlib()
-					dylib.compiler.(libraryInterface).setDylib()
-				} else if library.buildRlib() {
-					modules := mctx.CreateLocalVariations("rlib")
-					modules[0].(*Module).compiler.(libraryInterface).setRlib()
-				} else if library.buildDylib() {
-					modules := mctx.CreateLocalVariations("dylib")
-					modules[0].(*Module).compiler.(libraryInterface).setDylib()
+			if library.buildRlib() && library.buildDylib() {
+				variants := []string{"rlib", "dylib"}
+				if m.sourceProvider != nil {
+					variants = append(variants, "")
 				}
+				modules := mctx.CreateLocalVariations(variants...)
+
+				rlib := modules[0].(*Module)
+				dylib := modules[1].(*Module)
+				rlib.compiler.(libraryInterface).setRlib()
+				dylib.compiler.(libraryInterface).setDylib()
+
+				if m.sourceProvider != nil {
+					// This library is SourceProvider generated, so the non-library-producing
+					// variant needs to disable it's compiler and skip installation.
+					sourceProvider := modules[2].(*Module)
+					sourceProvider.compiler.SetDisabled()
+				}
+			} else if library.buildRlib() {
+				modules := mctx.CreateLocalVariations("rlib")
+				modules[0].(*Module).compiler.(libraryInterface).setRlib()
+			} else if library.buildDylib() {
+				modules := mctx.CreateLocalVariations("dylib")
+				modules[0].(*Module).compiler.(libraryInterface).setDylib()
+			}
+
+			if m.sourceProvider != nil {
+				// Alias the non-library variant to the empty-string variant.
+				mctx.AliasVariation("")
 			}
 		}
 	}
diff --git a/rust/project_json.go b/rust/project_json.go
index a50e73a..41dd194 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -92,7 +92,7 @@
 // appendLibraryAndDeps creates a rustProjectCrate for the module argument and
 // appends it to the rustProjectJson struct.  It visits the dependencies of the
 // module depth-first. If the current module is already in knownCrates, its
-// its dependencies are merged. Returns a tuple (id, crate_name, ok).
+// dependencies are merged. Returns a tuple (id, crate_name, ok).
 func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson,
 	knownCrates map[string]crateInfo, module android.Module) (int, string, bool) {
 	rModule, ok := module.(*Module)
@@ -111,12 +111,13 @@
 		// We have seen this crate already; merge any new dependencies.
 		crate := project.Crates[cInfo.ID]
 		mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps)
+		project.Crates[cInfo.ID] = crate
 		return cInfo.ID, crateName, true
 	}
 	crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)}
 	src := rustLib.baseCompiler.Properties.Srcs[0]
 	crate.RootModule = path.Join(ctx.ModuleDir(rModule), src)
-	crate.Edition = getEdition(rustLib.baseCompiler)
+	crate.Edition = rustLib.baseCompiler.edition()
 
 	deps := make(map[string]int)
 	mergeDependencies(ctx, project, knownCrates, module, &crate, deps)
diff --git a/rust/rust.go b/rust/rust.go
index 78bf7ad..1192836 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -63,7 +63,8 @@
 	AndroidMkSharedLibs    []string
 	AndroidMkStaticLibs    []string
 
-	SubName        string `blueprint:"mutated"`
+	SubName string `blueprint:"mutated"`
+
 	PreventInstall bool
 	HideFromMake   bool
 }
@@ -83,9 +84,9 @@
 	cachedToolchain  config.Toolchain
 	sourceProvider   SourceProvider
 	subAndroidMkOnce map[subAndroidMkProvider]bool
-	outputFile       android.OptionalPath
 
-	subName string
+	outputFile    android.OptionalPath
+	generatedFile android.OptionalPath
 }
 
 func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -141,12 +142,8 @@
 
 func (mod *Module) NonCcVariants() bool {
 	if mod.compiler != nil {
-		if library, ok := mod.compiler.(libraryInterface); ok {
-			if library.buildRlib() || library.buildDylib() {
-				return true
-			} else {
-				return false
-			}
+		if _, ok := mod.compiler.(libraryInterface); ok {
+			return false
 		}
 	}
 	panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName()))
@@ -162,16 +159,16 @@
 			return library.static()
 		}
 	}
-	panic(fmt.Errorf("Static called on non-library module: %q", mod.BaseModuleName()))
+	return false
 }
 
 func (mod *Module) Shared() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
-			return library.static()
+			return library.shared()
 		}
 	}
-	panic(fmt.Errorf("Shared called on non-library module: %q", mod.BaseModuleName()))
+	return false
 }
 
 func (mod *Module) Toc() android.OptionalPath {
@@ -289,6 +286,9 @@
 	relativeInstallPath() string
 
 	nativeCoverage() bool
+
+	Disabled() bool
+	SetDisabled()
 }
 
 type exportedFlagsProducer interface {
@@ -399,7 +399,9 @@
 
 func (mod *Module) CcLibraryInterface() bool {
 	if mod.compiler != nil {
-		if _, ok := mod.compiler.(libraryInterface); ok {
+		// use build{Static,Shared}() instead of {static,shared}() here because this might be called before
+		// VariantIs{Static,Shared} is set.
+		if lib, ok := mod.compiler.(libraryInterface); ok && (lib.buildShared() || lib.buildStatic()) {
 			return true
 		}
 	}
@@ -669,16 +671,21 @@
 		flags, deps = mod.clippy.flags(ctx, flags, deps)
 	}
 
-	if mod.compiler != nil {
+	// SourceProvider needs to call generateSource() before compiler calls compile() so it can provide the source.
+	// TODO(b/162588681) This shouldn't have to run for every variant.
+	if mod.sourceProvider != nil {
+		generatedFile := mod.sourceProvider.generateSource(ctx, deps)
+		mod.generatedFile = android.OptionalPathForPath(generatedFile)
+		mod.sourceProvider.setSubName(ctx.ModuleSubDir())
+	}
+
+	if mod.compiler != nil && !mod.compiler.Disabled() {
 		outputFile := mod.compiler.compile(ctx, flags, deps)
+
 		mod.outputFile = android.OptionalPathForPath(outputFile)
-		if !mod.Properties.PreventInstall {
+		if mod.outputFile.Valid() && !mod.Properties.PreventInstall {
 			mod.compiler.install(ctx, mod.outputFile.Path())
 		}
-	} else if mod.sourceProvider != nil {
-		outputFile := mod.sourceProvider.generateSource(ctx, deps)
-		mod.outputFile = android.OptionalPathForPath(outputFile)
-		mod.subName = ctx.ModuleSubDir()
 	}
 }
 
@@ -687,7 +694,8 @@
 
 	if mod.compiler != nil {
 		deps = mod.compiler.compilerDeps(ctx, deps)
-	} else if mod.sourceProvider != nil {
+	}
+	if mod.sourceProvider != nil {
 		deps = mod.sourceProvider.sourceProviderDeps(ctx, deps)
 	}
 
@@ -754,14 +762,9 @@
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
-		if rustDep, ok := dep.(*Module); ok {
+		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
 			//Handle Rust Modules
 
-			linkFile := rustDep.outputFile
-			if !linkFile.Valid() {
-				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
-			}
-
 			switch depTag {
 			case dylibDepTag:
 				dylib, ok := rustDep.compiler.(libraryInterface)
@@ -810,23 +813,19 @@
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
+				linkFile := rustDep.outputFile
+				if !linkFile.Valid() {
+					ctx.ModuleErrorf("Invalid output file when adding dep %q to %q",
+						depName, ctx.ModuleName())
+					return
+				}
 				linkDir := linkPathFromFilePath(linkFile.Path())
 				if lib, ok := mod.compiler.(exportedFlagsProducer); ok {
 					lib.exportLinkDirs(linkDir)
 				}
 			}
 
-		}
-
-		if srcDep, ok := dep.(android.SourceFileProducer); ok {
-			switch depTag {
-			case android.SourceDepTag:
-				// These are usually genrules which don't have per-target variants.
-				directSrcDeps = append(directSrcDeps, srcDep)
-			}
-		}
-
-		if ccDep, ok := dep.(cc.LinkableInterface); ok {
+		} else if ccDep, ok := dep.(cc.LinkableInterface); ok {
 			//Handle C dependencies
 			if _, ok := ccDep.(*Module); !ok {
 				if ccDep.Module().Target().Os != ctx.Os() {
@@ -838,7 +837,6 @@
 					return
 				}
 			}
-
 			linkFile := ccDep.OutputFile()
 			linkPath := linkPathFromFilePath(linkFile.Path())
 			libName := libNameFromFilePath(linkFile.Path())
@@ -886,6 +884,14 @@
 				lib.exportDepFlags(depFlag)
 			}
 		}
+
+		if srcDep, ok := dep.(android.SourceFileProducer); ok {
+			switch depTag {
+			case android.SourceDepTag:
+				// These are usually genrules which don't have per-target variants.
+				directSrcDeps = append(directSrcDeps, srcDep)
+			}
+		}
 	})
 
 	var rlibDepFiles RustLibraries
@@ -974,21 +980,18 @@
 	}
 	actx.AddVariationDependencies(
 		append(commonDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: "rlib"},
-			{Mutator: "link", Variation: ""}}...),
+			{Mutator: "rust_libraries", Variation: "rlib"}}...),
 		rlibDepTag, deps.Rlibs...)
 	actx.AddVariationDependencies(
 		append(commonDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: "dylib"},
-			{Mutator: "link", Variation: ""}}...),
+			{Mutator: "rust_libraries", Variation: "dylib"}}...),
 		dylibDepTag, deps.Dylibs...)
 
 	if deps.Rustlibs != nil {
 		autoDep := mod.compiler.(autoDeppable).autoDep()
 		actx.AddVariationDependencies(
 			append(commonDepVariations, []blueprint.Variation{
-				{Mutator: "rust_libraries", Variation: autoDep.variation},
-				{Mutator: "link", Variation: ""}}...),
+				{Mutator: "rust_libraries", Variation: autoDep.variation}}...),
 			autoDep.depTag, deps.Rustlibs...)
 	}
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index b3bbddb..04de48b 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -233,6 +233,7 @@
 				":my_generator",
 				":libbindings",
 			],
+			rlibs: ["libbindings"],
 		}
 		rust_proc_macro {
 			name: "libprocmacro",
@@ -241,6 +242,7 @@
 				":my_generator",
 				":libbindings",
 			],
+			rlibs: ["libbindings"],
 			crate_name: "procmacro",
 		}
 		rust_library {
@@ -248,7 +250,9 @@
 			srcs: [
 				"foo.rs",
 				":my_generator",
-				":libbindings"],
+				":libbindings",
+			],
+			rlibs: ["libbindings"],
 			crate_name: "foo",
 		}
 		genrule {
@@ -260,7 +264,8 @@
 		}
 		rust_bindgen {
 			name: "libbindings",
-			stem: "bindings",
+			crate_name: "bindings",
+			source_stem: "bindings",
 			host_supported: true,
 			wrapper_src: "src/any.h",
         }
@@ -289,6 +294,21 @@
 	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())
 	}
+
+	// Check that our bindings are picked up as crate dependencies as well
+	libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+	if !android.InList("libbindings", libfooMod.Properties.AndroidMkRlibs) {
+		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
+	}
+	fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
+	if !android.InList("libbindings", fizzBuzzMod.Properties.AndroidMkRlibs) {
+		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
+	}
+	libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module)
+	if !android.InList("libbindings", libprocmacroMod.Properties.AndroidMkRlibs) {
+		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
+	}
+
 }
 
 func TestSourceProviderTargetMismatch(t *testing.T) {
@@ -305,7 +325,8 @@
 		}
 		rust_bindgen {
 			name: "libbindings",
-			stem: "bindings",
+			crate_name: "bindings",
+			source_stem: "bindings",
 			wrapper_src: "src/any.h",
 		}
 	`)
diff --git a/rust/source_provider.go b/rust/source_provider.go
index da6147a..8bb7849 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -19,8 +19,13 @@
 )
 
 type SourceProviderProperties struct {
-	// sets name of the output
-	Stem *string `android:"arch_variant"`
+	// name for the generated source file. Defaults to module name (e.g. moduleNameFoo.rs is produced by default).
+	// Importantly, the inherited "stem" property for this module sets the output filename for the generated library
+	// variants only
+	Source_stem *string `android:"arch_variant"`
+
+	// crate name, used for the library variant of this source provider. See additional details in rust_library.
+	Crate_name string `android:"arch_variant"`
 }
 
 type baseSourceProvider struct {
@@ -28,6 +33,7 @@
 
 	outputFile       android.Path
 	subAndroidMkOnce map[subAndroidMkProvider]bool
+	subName          string
 }
 
 var _ SourceProvider = (*baseSourceProvider)(nil)
@@ -37,6 +43,7 @@
 	Srcs() android.Paths
 	sourceProviderProps() []interface{}
 	sourceProviderDeps(ctx DepsContext, deps Deps) Deps
+	setSubName(subName string)
 }
 
 func (sp *baseSourceProvider) Srcs() android.Paths {
@@ -59,8 +66,8 @@
 
 func (sp *baseSourceProvider) getStem(ctx android.ModuleContext) string {
 	stem := ctx.ModuleName()
-	if String(sp.Properties.Stem) != "" {
-		stem = String(sp.Properties.Stem)
+	if String(sp.Properties.Source_stem) != "" {
+		stem = String(sp.Properties.Source_stem)
 	}
 	return stem
 }
@@ -68,3 +75,7 @@
 func (sp *baseSourceProvider) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	return deps
 }
+
+func (sp *baseSourceProvider) setSubName(subName string) {
+	sp.subName = subName
+}
diff --git a/rust/testing.go b/rust/testing.go
index f2d4c5e..83b2828 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -77,6 +77,7 @@
 
 func CreateTestContext() *android.TestContext {
 	ctx := android.NewTestArchContext()
+	android.RegisterPrebuiltMutators(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 1bc78e6..c7baca7 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -24,7 +24,6 @@
   i18n-module-test-exports
   i18n-module-sdk
   platform-mainline-sdk
-  platform-mainline-host-exports
 )
 
 # List of libraries installed on the platform that are needed for ART chroot
@@ -52,6 +51,13 @@
   "$@"
 }
 
+lib_dir() {
+  case $1 in
+    (aosp_arm|aosp_x86) echo "lib";;
+    (aosp_arm64|aosp_x86_64) echo "lib64";;
+  esac
+}
+
 OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
 DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
 
@@ -69,7 +75,8 @@
     echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/
   done
   for library in "${PLATFORM_LIBRARIES[@]}"; do
-    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/lib/${library}.so ${DIST_DIR}/${TARGET_ARCH}/
+    libdir=$(lib_dir $product)
+    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/${libdir}/${library}.so ${DIST_DIR}/${TARGET_ARCH}/
   done
 done
 
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 497f14b..17afdb8 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -73,7 +73,6 @@
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
-			device_supported: false,
 			host_supported: true,
 			native_shared_libs: ["sdkmember"],
 			compile_multilib: "64",
@@ -81,7 +80,6 @@
 
 		cc_library_shared {
 			name: "sdkmember",
-			device_supported: false,
 			host_supported: true,
 			srcs: ["Test.cpp"],
 			stl: "none",
@@ -96,14 +94,16 @@
 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"],
+    target: {
+        android_arm64: {
+            srcs: ["android/arm64/lib/sdkmember.so"],
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
         },
     },
 }
@@ -111,31 +111,29 @@
 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"],
+    target: {
+        android_arm64: {
+            srcs: ["android/arm64/lib/sdkmember.so"],
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/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",
-        },
-    },
+    compile_multilib: "64",
 }
 `),
 		checkAllCopyRules(`
-.intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> x86_64/lib/sdkmember.so
+.intermediates/sdkmember/android_arm64_armv8-a_shared/sdkmember.so -> android/arm64/lib/sdkmember.so
+.intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> linux_glibc/x86_64/lib/sdkmember.so
 `))
 }
 
@@ -1527,11 +1525,7 @@
     device_supported: false,
     host_supported: true,
     native_static_libs: ["myexports_mynativelib@current"],
-    target: {
-        linux_glibc: {
-            compile_multilib: "64",
-        },
-    },
+    compile_multilib: "64",
 }`),
 		checkAllCopyRules(`
 include/Test.h -> include/include/Test.h
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index a2198e9..931ca3c 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -204,8 +204,8 @@
 		}
 	`)
 
-	sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common_myapex").Rule("combineJar").Output
-	sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common_myapex2").Rule("combineJar").Output
+	sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common").Rule("combineJar").Output
+	sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common").Rule("combineJar").Output
 
 	javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_myapex")
 	javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_myapex2")
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 3e76008..7591020 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -406,13 +406,17 @@
 // Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
 // descendants
 func sdkDepsMutator(mctx android.TopDownMutatorContext) {
-	if m, ok := mctx.Module().(android.SdkAware); ok {
+	if parent, ok := mctx.Module().(interface {
+		android.DepIsInSameApex
+		android.RequiredSdks
+	}); ok {
 		// Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
 		// by reading its own properties like `uses_sdks`.
-		requiredSdks := m.RequiredSdks()
+		requiredSdks := parent.RequiredSdks()
 		if len(requiredSdks) > 0 {
 			mctx.VisitDirectDeps(func(m android.Module) {
-				if dep, ok := m.(android.SdkAware); ok {
+				// Only propagate required sdks from the apex onto its contents.
+				if dep, ok := m.(android.SdkAware); ok && parent.DepIsInSameApex(mctx, dep) {
 					dep.BuildWithSdks(requiredSdks)
 				}
 			})
@@ -423,15 +427,28 @@
 // Step 5: if libfoo.mysdk.11 is in the context where version 11 of mysdk is requested, the
 // versioned module is used instead of the un-versioned (in-development) module libfoo
 func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
-		if sdk := m.ContainingSdk(); !sdk.Unversioned() {
-			if m.RequiredSdks().Contains(sdk) {
-				// Note that this replacement is done only for the modules that have the same
-				// variations as the current module. Since current module is already mutated for
-				// apex references in other APEXes are not affected by this replacement.
-				memberName := m.MemberName()
-				mctx.ReplaceDependencies(memberName)
-			}
+	if versionedSdkMember, ok := mctx.Module().(android.SdkAware); ok && versionedSdkMember.IsInAnySdk() {
+		if sdk := versionedSdkMember.ContainingSdk(); !sdk.Unversioned() {
+			// Only replace dependencies to <sdkmember> with <sdkmember@required-version>
+			// if the depending module requires it. e.g.
+			//      foo -> sdkmember
+			// will be transformed to:
+			//      foo -> sdkmember@1
+			// if and only if foo is a member of an APEX that requires version 1 of the
+			// sdk containing sdkmember.
+			memberName := versionedSdkMember.MemberName()
+
+			// Replace dependencies on sdkmember with a dependency on the current module which
+			// is a versioned prebuilt of the sdkmember if required.
+			mctx.ReplaceDependenciesIf(memberName, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
+				// from - foo
+				// to - sdkmember
+				replace := false
+				if parent, ok := from.(android.RequiredSdks); ok {
+					replace = parent.RequiredSdks().Contains(sdk)
+				}
+				return replace
+			})
 		}
 	}
 }
diff --git a/sdk/testing.go b/sdk/testing.go
index 34ea8f0..b53558d 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -97,6 +97,11 @@
 	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
+
+	android.RegisterPrebuiltMutators(ctx)
+
+	// Register these after the prebuilt mutators have been registered to match what
+	// happens at runtime.
 	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
diff --git a/sdk/update.go b/sdk/update.go
index b8d73c6..25d50d2 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -323,19 +323,37 @@
 	// Add properties common to all os types.
 	s.addMemberPropertiesToPropertySet(builder, snapshotModule, commonDynamicMemberProperties)
 
+	// Optimize other per-variant properties, besides the dynamic member lists.
+	type variantProperties struct {
+		Compile_multilib string `android:"arch_variant"`
+	}
+	var variantPropertiesContainers []propertiesContainer
+	variantToProperties := make(map[*sdk]*variantProperties)
+	for _, sdkVariant := range sdkVariants {
+		props := &variantProperties{
+			Compile_multilib: sdkVariant.multilibUsages.String(),
+		}
+		variantPropertiesContainers = append(variantPropertiesContainers, &dynamicMemberPropertiesContainer{sdkVariant, props})
+		variantToProperties[sdkVariant] = props
+	}
+	commonVariantProperties := variantProperties{}
+	extractor = newCommonValueExtractor(commonVariantProperties)
+	extractCommonProperties(ctx, extractor, &commonVariantProperties, variantPropertiesContainers)
+	if commonVariantProperties.Compile_multilib != "" && commonVariantProperties.Compile_multilib != "both" {
+		// Compile_multilib defaults to both so only needs to be set when it's
+		// specified and not both.
+		snapshotModule.AddProperty("compile_multilib", commonVariantProperties.Compile_multilib)
+	}
+
 	// Iterate over the os types in a fixed order.
 	targetPropertySet := snapshotModule.AddPropertySet("target")
 	for _, osType := range s.getPossibleOsTypes() {
 		if sdkVariant, ok := osTypeToMemberProperties[osType]; ok {
 			osPropertySet := targetPropertySet.AddPropertySet(sdkVariant.Target().Os.Name)
 
-			// Compile_multilib defaults to both and must always be set to both on the
-			// device and so only needs to be set when targeted at the host and is neither
-			// unspecified or both.
-			multilib := sdkVariant.multilibUsages
-			if (osType.Class == android.Host || osType.Class == android.HostCross) &&
-				multilib != multilibNone && multilib != multilibBoth {
-				osPropertySet.AddProperty("compile_multilib", multilib.String())
+			variantProps := variantToProperties[sdkVariant]
+			if variantProps.Compile_multilib != "" && variantProps.Compile_multilib != "both" {
+				osPropertySet.AddProperty("compile_multilib", variantProps.Compile_multilib)
 			}
 
 			s.addMemberPropertiesToPropertySet(builder, osPropertySet, sdkVariant.dynamicMemberTypeListProperties)
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 8503386..711129c 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -67,6 +67,8 @@
 		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
 	})
 
+	android.RegisterPrebuiltMutators(ctx)
+
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("sysprop_java", java.SyspropMutator).Parallel()
diff --git a/ui/build/config.go b/ui/build/config.go
index ba477e6..3fa0479 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -96,7 +96,7 @@
 		environ: OsEnvironment(),
 	}
 
-	// Sane default matching ninja
+	// Default matching ninja
 	ret.parallel = runtime.NumCPU() + 2
 	ret.keepGoing = 1
 
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
index 2c4995b..23a53b4 100644
--- a/ui/build/rbe_test.go
+++ b/ui/build/rbe_test.go
@@ -94,12 +94,12 @@
 	}, {
 		description:         "stopRBE failed",
 		rbeOutputDirDefined: true,
-		bootstrapProgram:    "#!/bin/bash\nexit 1",
+		bootstrapProgram:    "#!/bin/bash\nexit 1\n",
 		expectedErr:         "shutdown failed",
 	}, {
 		description:         "failed to copy metrics file",
 		rbeOutputDirDefined: true,
-		bootstrapProgram:    "#!/bin/bash",
+		bootstrapProgram:    "#!/bin/bash\n",
 		expectedErr:         "failed to copy",
 	}}
 
@@ -139,4 +139,4 @@
 	}
 }
 
-var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s", rbeMetricsPBFilename)
+var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s\n", rbeMetricsPBFilename)
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 1cc2e94..a9346e0 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -44,7 +44,7 @@
 // environment variable. The metrics files are copied to a temporary directory
 // and the uploader is then executed in the background to allow the user to continue
 // working.
-func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStarted time.Time, files ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, files ...string) {
 	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
 	defer ctx.EndTrace()
 
@@ -105,7 +105,7 @@
 	// Start the uploader in the background as it takes several milliseconds to start the uploader
 	// and prepare the metrics for upload. This affects small commands like "lunch".
 	cmd := Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile)
-	if forceDumbOutput {
+	if simpleOutput {
 		cmd.RunOrFatal()
 	} else {
 		cmd.RunAndStreamOrFatal()
diff --git a/ui/terminal/Android.bp b/ui/terminal/Android.bp
index b533b0d..aa6e35d 100644
--- a/ui/terminal/Android.bp
+++ b/ui/terminal/Android.bp
@@ -17,7 +17,7 @@
     pkgPath: "android/soong/ui/terminal",
     deps: ["soong-ui-status"],
     srcs: [
-        "dumb_status.go",
+        "simple_status.go",
         "format.go",
         "smart_status.go",
         "status.go",
diff --git a/ui/terminal/dumb_status.go b/ui/terminal/simple_status.go
similarity index 70%
rename from ui/terminal/dumb_status.go
rename to ui/terminal/simple_status.go
index 201770f..4e8c568 100644
--- a/ui/terminal/dumb_status.go
+++ b/ui/terminal/simple_status.go
@@ -21,31 +21,31 @@
 	"android/soong/ui/status"
 )
 
-type dumbStatusOutput struct {
+type simpleStatusOutput struct {
 	writer    io.Writer
 	formatter formatter
 }
 
-// NewDumbStatusOutput returns a StatusOutput that represents the
+// NewSimpleStatusOutput returns a StatusOutput that represents the
 // current build status similarly to Ninja's built-in terminal
 // output.
-func NewDumbStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
-	return &dumbStatusOutput{
+func NewSimpleStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
+	return &simpleStatusOutput{
 		writer:    w,
 		formatter: formatter,
 	}
 }
 
-func (s *dumbStatusOutput) Message(level status.MsgLevel, message string) {
+func (s *simpleStatusOutput) Message(level status.MsgLevel, message string) {
 	if level >= status.StatusLvl {
 		fmt.Fprintln(s.writer, s.formatter.message(level, message))
 	}
 }
 
-func (s *dumbStatusOutput) StartAction(action *status.Action, counts status.Counts) {
+func (s *simpleStatusOutput) StartAction(action *status.Action, counts status.Counts) {
 }
 
-func (s *dumbStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
 	str := result.Description
 	if str == "" {
 		str = result.Command
@@ -63,9 +63,9 @@
 	}
 }
 
-func (s *dumbStatusOutput) Flush() {}
+func (s *simpleStatusOutput) Flush() {}
 
-func (s *dumbStatusOutput) Write(p []byte) (int, error) {
+func (s *simpleStatusOutput) Write(p []byte) (int, error) {
 	fmt.Fprint(s.writer, string(p))
 	return len(p), nil
 }
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 60dfc70..d8e7392 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -26,12 +26,12 @@
 //
 // statusFormat takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
-func NewStatusOutput(w io.Writer, statusFormat string, forceDumbOutput, quietBuild bool) status.StatusOutput {
+func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild bool) status.StatusOutput {
 	formatter := newFormatter(statusFormat, quietBuild)
 
-	if !forceDumbOutput && isSmartTerminal(w) {
+	if !forceSimpleOutput && isSmartTerminal(w) {
 		return NewSmartStatusOutput(w, formatter)
 	} else {
-		return NewDumbStatusOutput(w, formatter)
+		return NewSimpleStatusOutput(w, formatter)
 	}
 }
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index 9f60829..aa69dff 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -26,64 +26,64 @@
 
 func TestStatusOutput(t *testing.T) {
 	tests := []struct {
-		name  string
-		calls func(stat status.StatusOutput)
-		smart string
-		dumb  string
+		name   string
+		calls  func(stat status.StatusOutput)
+		smart  string
+		simple string
 	}{
 		{
-			name:  "two actions",
-			calls: twoActions,
-			smart: "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
-			dumb:  "[ 50% 1/2] action1\n[100% 2/2] action2\n",
+			name:   "two actions",
+			calls:  twoActions,
+			smart:  "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
+			simple: "[ 50% 1/2] action1\n[100% 2/2] action2\n",
 		},
 		{
-			name:  "two parallel actions",
-			calls: twoParallelActions,
-			smart: "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
-			dumb:  "[ 50% 1/2] action1\n[100% 2/2] action2\n",
+			name:   "two parallel actions",
+			calls:  twoParallelActions,
+			smart:  "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
+			simple: "[ 50% 1/2] action1\n[100% 2/2] action2\n",
 		},
 		{
-			name:  "action with output",
-			calls: actionsWithOutput,
-			smart: "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
-			dumb:  "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
+			name:   "action with output",
+			calls:  actionsWithOutput,
+			smart:  "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
+			simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
 		},
 		{
-			name:  "action with output without newline",
-			calls: actionsWithOutputWithoutNewline,
-			smart: "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
-			dumb:  "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
+			name:   "action with output without newline",
+			calls:  actionsWithOutputWithoutNewline,
+			smart:  "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
+			simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
 		},
 		{
-			name:  "action with error",
-			calls: actionsWithError,
-			smart: "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
-			dumb:  "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n",
+			name:   "action with error",
+			calls:  actionsWithError,
+			smart:  "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
+			simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n",
 		},
 		{
-			name:  "action with empty description",
-			calls: actionWithEmptyDescription,
-			smart: "\r\x1b[1m[  0% 0/1] command1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] command1\x1b[0m\x1b[K\n",
-			dumb:  "[100% 1/1] command1\n",
+			name:   "action with empty description",
+			calls:  actionWithEmptyDescription,
+			smart:  "\r\x1b[1m[  0% 0/1] command1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] command1\x1b[0m\x1b[K\n",
+			simple: "[100% 1/1] command1\n",
 		},
 		{
-			name:  "messages",
-			calls: actionsWithMessages,
-			smart: "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\nFAILED: error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
-			dumb:  "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n",
+			name:   "messages",
+			calls:  actionsWithMessages,
+			smart:  "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\nFAILED: error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
+			simple: "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n",
 		},
 		{
-			name:  "action with long description",
-			calls: actionWithLongDescription,
-			smart: "\r\x1b[1m[  0% 0/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action with very long descrip\x1b[0m\x1b[K\n",
-			dumb:  "[ 50% 1/2] action with very long description to test eliding\n",
+			name:   "action with long description",
+			calls:  actionWithLongDescription,
+			smart:  "\r\x1b[1m[  0% 0/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action with very long descrip\x1b[0m\x1b[K\n",
+			simple: "[ 50% 1/2] action with very long description to test eliding\n",
 		},
 		{
-			name:  "action with output with ansi codes",
-			calls: actionWithOuptutWithAnsiCodes,
-			smart: "\r\x1b[1m[  0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n",
-			dumb:  "[100% 1/1] action1\ncolor\n",
+			name:   "action with output with ansi codes",
+			calls:  actionWithOuptutWithAnsiCodes,
+			smart:  "\r\x1b[1m[  0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n",
+			simple: "[100% 1/1] action1\ncolor\n",
 		},
 	}
 
@@ -103,24 +103,24 @@
 				}
 			})
 
-			t.Run("dumb", func(t *testing.T) {
-				dumb := &bytes.Buffer{}
-				stat := NewStatusOutput(dumb, "", false, false)
+			t.Run("simple", func(t *testing.T) {
+				simple := &bytes.Buffer{}
+				stat := NewStatusOutput(simple, "", false, false)
 				tt.calls(stat)
 				stat.Flush()
 
-				if g, w := dumb.String(), tt.dumb; g != w {
+				if g, w := simple.String(), tt.simple; g != w {
 					t.Errorf("want:\n%q\ngot:\n%q", w, g)
 				}
 			})
 
-			t.Run("force dumb", func(t *testing.T) {
+			t.Run("force simple", func(t *testing.T) {
 				smart := &fakeSmartTerminal{termWidth: 40}
 				stat := NewStatusOutput(smart, "", true, false)
 				tt.calls(stat)
 				stat.Flush()
 
-				if g, w := smart.String(), tt.dumb; g != w {
+				if g, w := smart.String(), tt.simple; g != w {
 					t.Errorf("want:\n%q\ngot:\n%q", w, g)
 				}
 			})